Android/Kotlin

<정리> 메모 앱 만들기 5일차

re트 2023. 12. 21. 19:34
728x90

1. 메모 리스트 만들기(1)

처음에는 이걸 리스트뷰를 가지고 구현했다.

순서는 Memo 데이터 클래스를 만들고

data class Memo(val title: String, val content: String, val day: String)

 

메인 액티비티 레이아웃에서 리스트뷰를 추가하고

<!-- 메인 화면 -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list_view"
        android:layout_marginTop="60dp"
        android:dividerHeight="4dp"
        android:divider="@color/white"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

 

리스트 하나하나에 대한 레이아웃을 만들고

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:background="@drawable/bg_listview"
        android:paddingHorizontal="20dp"
        android:paddingVertical="15dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/tv_item_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="12월 둘째주"
            android:textSize="16sp"
            app:layout_constraintStart_toStartOf="@+id/tv_item_content"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_item_content"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_marginBottom="40dp"
            android:text="다같이 공부할까요?"
            android:textSize="12sp"
            app:layout_constraintTop_toBottomOf="@+id/tv_item_title" />

        <TextView
            android:id="@+id/tv_item_day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="12.10"
            android:textColor="@color/grey"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/tv_item_content"
            app:layout_constraintTop_toBottomOf="@+id/tv_item_content" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

 

메인 액티비티에서 리스트뷰 객체와 들어갈 내용들을 배열리스트로 만든 다음

// 리스트뷰 변수
private val listView: ListView by lazy { findViewById(R.id.list_view) }

val memoList = arrayListOf<Memo>(
    Memo("1212", "dkdkfjfk", "12.01"),
    Memo("1212", "dkdkfjfk", "12.01"),
    Memo("1212", "dkdkfjfk", "12.01"),
    Memo("1212", "dkdkfjfk", "12.01"),
    Memo("1212", "dkdkfjfk", "12.01"),
    Memo("1212", "dkdkfjfk", "12.01"),
)

 

만들어둔 어댑터와 연결했다.

class MemoAdapter(val context: Context, private val memoList: ArrayList<Memo>): BaseAdapter() {
    override fun getCount(): Int {
        return memoList.size
    }

    override fun getItem(pos: Int): Any {
        return memoList[pos]
    }

    override fun getItemId(pos: Int): Long {
        return 0
    }

    override fun getView(pos: Int, convertView: View?, parent: ViewGroup?): View {
        val view: View = LayoutInflater.from(context).inflate(R.layout.listview_layout, null)

        val tvItemTitle: TextView = view.findViewById(R.id.tv_item_title)
        val tvItemContent: TextView = view.findViewById(R.id.tv_item_content)
        val tvItemDay: TextView = view.findViewById(R.id.tv_item_day)

        val memo = memoList[pos]

        tvItemTitle.text = memo.title
        tvItemContent.text = memo.content
        tvItemDay.text = memo.day

        return view
    }
}

 

val adapter = MemoAdapter(this, memoList)
listView.adapter = adapter

 

그리고 결과화면을 봤는데... 뭔가 좀 어색했다.

처음 결과를 봤을 때는 상하좌우간격이 원하는대로 조절하기 어려웠다. 이 부분은 리스트 레이아웃에서 레이아웃을 하나 감싸서 해결했는데 뭔가 좋지는 않았다.

크롤을 하다보면 리스트가 흐려지거나 그러는 경우도 있더라(모두 해골물이었다....)

지금까지 리스트뷰로 했던 거를 나열해봤고 지금까지 했던 거를 리사이클러뷰로 바꾸기로 했다...!

그냥 리사이클러뷰가 쓰고 싶었다.

 

2. 메모 리스트 만들기(2)

먼저 리스트 하나하나에 대한 레이아웃은 손 댈 필요가 없었다.

데이터 클래스도 마찬가지...!

 

가장 먼저 수정하게 된 부분은 어댑터다.

리스트뷰 어댑터를 만들 때는 BaseAdapter를 상속받았었지만 리사이클러뷰 어댑터를 만들 때는 RecylerView.Adapter를 상속받아야한다.

뷰홀더는 각 뷰의 내용을 저장해둘 장소이다.

class MemoAdapter(private val memoList: ArrayList<Memo>): RecyclerView.Adapter<MemoAdapter.MemoViewHolder>() {

    inner class MemoViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        val tvItemTitle: TextView = itemView.findViewById(R.id.tv_item_title)
        val tvItemContent: TextView = itemView.findViewById(R.id.tv_item_content)
        val tvItemDay: TextView = itemView.findViewById(R.id.tv_item_day)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemoViewHolder {
        val view: View = LayoutInflater.from(parent.context).inflate(R.layout.listview_layout, parent, false)
        return MemoViewHolder(view)
    }

    override fun getItemCount(): Int {
        return memoList.count()
    }

    override fun onBindViewHolder(holder: MemoViewHolder, position: Int) {
        holder.tvItemTitle.text = memoList[position].title
        holder.tvItemContent.text = memoList[position].content
        holder.tvItemDay.text = memoList[position].day
    }

}

 

이전에 프래그먼트와 뷰페이저를 사용하던게 약간 떠올랐다.

그리고 메인 액티비티 레이아웃에서 리스트뷰를 리사이클러뷰로 변경했다.

<!-- 메인 화면 -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="60dp"
        tools:listitem="@layout/listview_layout"/>
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

 

마지막으로 메인 액티비티에서 코드를 변경했다.

// 리스트뷰 변수
private val recyclerView: RecyclerView by lazy { findViewById(R.id.list_view) }

val adapter = MemoAdapter(memoList)
adapter.notifyDataSetChanged()

recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

 

기본적인 구조는 리스트뷰를 연결하는 것과 거의 동일했다.

추가된 것은 데이터가 변경됐다고 알려주는 adapter.notifyDataSetChanged()와 layoutManager 세팅뿐이다.

 

3. 리사이클러뷰 클릭 이벤트

리스트뷰에서는 클릭 이벤트가 아주 간단하게 구현이 됐지만 리사이클러뷰는 좀 몇 가지 과정이 필요하더라

먼저는 어댑터에서 인터페이스를 만들고 인터페이스를 상속받는 변수도 만들고 리스트가 클릭되었을 때 체크할 클릭리스너를 뷰홀더 내에 선언해놨다.

class MemoAdapter(private val memoList: ArrayList<Memo>): RecyclerView.Adapter<MemoAdapter.MemoViewHolder>() {

    lateinit var itemClickListener: OnItemClickListener

    interface OnItemClickListener {
        fun onItemClick(position: Int) {
        }
    }

    inner class MemoViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        val tvItemTitle: TextView = itemView.findViewById(R.id.tv_item_title)
        val tvItemContent: TextView = itemView.findViewById(R.id.tv_item_content)
        val tvItemDay: TextView = itemView.findViewById(R.id.tv_item_day)

        init {
            itemView.setOnClickListener {
                itemClickListener.onItemClick(adapterPosition)
            }
        }
    }
    ...
}

 

그리고 메인 액티비티로 갔다.

거기서 어댑터의 변수에 접근해서 인터페이스의 메소드를 오버라이딩했다.

지금은 그냥 토스트메세지를 띄우도록 했지만 나중에는 해당 메모장으로 이동하도록 할 예정이다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    initView()

    val adapter = MemoAdapter(memoList)
    adapter.notifyDataSetChanged()

    recyclerView.adapter = adapter
    recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

    adapter.itemClickListener = object: MemoAdapter.OnItemClickListener {
        override fun onItemClick(position: Int) {
            val item = memoList[position]
            Toast.makeText(this@MainActivity, "${item.title}입니다.", Toast.LENGTH_SHORT).show()
        }
    }
}

 

<결과화면>

 

다음은 Floating Button이다..!!

반응형