2021. 1. 14. 02:40ใ๐ฑAndroid/๐ฉ๐ป๐ป Android ๊ฐ๋ฐ ์ผ์ง
์ค๋์ ๊ฐ๋จํ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์๋ฆฌ์ฆ 1ํ์ธ RecyclerView ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ํด ์๊ฐํด๋ณด๊ณ ์ ํฉ๋๋ค!
RecyclerView ๋?!
RecyclerView๋ ? ๋ฐ์ดํฐ ์งํฉ๋ค์ ๊ฐ๊ฐ์ ๊ฐ๋ณ ์์ดํ ๋จ์๋ก ๊ตฌ์ฑํ์ฌ ํ๋ฉด์ ์ถ๋ ฅํด์ฃผ๋ ๋ทฐ ๊ทธ๋ฃน์ด๋ฉฐ, ์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์คํฌ๋กค ๊ฐ๋ฅํ ๋ฆฌ์คํธ ํํ๋ก ํ์ํด์ฃผ๋ ์์ ฏ์ ์๋ฏธํฉ๋๋ค.
์ฆ, ์๋ ์ฌ์ง๊ณผ ๊ฐ์ด ๋์ผํ ํ์์ ๊ฐ๋ ์ ๋ง์ ๋ฆฌ์คํธ๋ค์ ๊ตฌํํ ๋ ์ฃผ๋ก ์ฌ์ฉํฉ๋๋ค.
RecyclerView์ ListView์ ์ฐจ์ด๋ ?!
๋๋ค ๋์ผํ ํ์์ ๋ฆฌ์คํธ๋ค์ ๊ตฌํํ ๋ ์ฌ์ฉํ์ง๋ง ListView์ RecyclerView์๋ ๋น์ทํ๊ฑฐ ๊ฐ์ง๋ง ์ธ๋ฐํ ์ฐจ์ด๊ฐ ์กด์ฌํฉ๋๋ค.
RecyclerView | ListView | |
ViewHolder | ViewHolder ํจํด์ ์ด์ฉํ๋ค. | ViewHolder ํจํด ์ด์ฉํ์ง ์๋๋ค. |
Item Layout | ๊ฐ๋ก, ์ธ๋ก, Grid ํ์ ๋ชจ๋ ์ง์ | ์ธ๋ก ๋ฐฉํฅ๋ง ์ง์ |
Item Animation | ์์ดํ ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ ํด๋์ค ์กด์ฌ | ์์ดํ ์ถ๊ฐ/์ ๊ฑฐ์์ ์ ์ฉ๊ฐ๋ฅํ ์ ๋๋ฉ์ด์ ์๋ค. |
Decoration | RecyclerView.ItemDecoration ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ๋ถ์ ์ ์ค์ ํด์ผํ๋ค. | Android:divider ์์ฑ์ ์ด์ฉํ์ฌ ๋ฆฌ์คํธ์ ์๋ ์์ดํ ์ ์ฝ๊ฒ ๊ตฌ๋ถํ ์ ์๋ค. |
Click Detection | ๊ฐ๋ณ ํฐ์น ์ด๋ฒคํธ๋ฅผ ๊ด๋ฆฌํ์ง๋ง ํด๋ฆญ ์ฒ๋ฆฌ ๊ธฐ๋ฅ์ด ๋ด์ฅ๋์ด ์์ง ์๋ค. | ๋ชฉ๋ก์ ๊ฐ๋ณ ํญ๋ชฉ์ ๋ํ ํด๋ฆญ ์ด๋ฒคํธ์ ๋ฐ์ธ๋ฉํ๊ธฐ ์ํ AdapterView.OnItemClickListener ์ธํฐํ์ด์ค๊ฐ ์๋ค. |
์ด๋ ListView๋ณด๋ค RecyclerView๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฅ ํฐ ์ด์ ๋ ์ฌ์ฌ์ฉ์ฑ ์ด ํจ์ฌ ์ข๊ธฐ ๋๋ฌธ์ ๋๋ค.
ListView๋ ViewHolder ํจํด์ ์ฌ์ฉํ์ ์๊ณ getView()๋ฅผ ์ด์ฉํ์ฌ View์ ์ ๊ทผํ๋๋ฐ , ์ด์ฒ๋ผ getView()๋ก ๋์ํ๊ฒ ๋๋ฉด ๋ฆฌ์คํฌ ๊ฐฏ์๋งํผ getView()๊ฐ ํธ์ถ ๋๋ฏ๋ก ๋งค์ฐ ๋นํจ์จ์ ์ ๋๋ค. ํ์ง๋ง RecyclerView๋ ViewHolder๋ฅผ ํตํด ๋ง๋ ๊ฐ์ฒด๋ฅผ ์ฌ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ListView๋ณด๋ค RecyclerView๊ฐ ์ฌ ์ฌ์ฉ์ฑ ๋ถ๋ถ์์ ๋ ํจ์จ์ ์ ๋๋ค.
RecyclerView ์ฃผ์ ํด๋์ค
View Holder
๊ฐ๊ฐ์ ๋ทฐ๋ฅผ ๋ณด๊ดํ๋ Holder ๊ฐ์ฒด ์ ๋๋ค.
Item ๋ทฐ๋ค์ ์ฌํ์ฉํ๊ธฐ ์ํด ๊ฐ ์์๋ฅผ ์ ์ฅํด๋๊ณ ์ฌ์ฉํฉ๋๋ค. ์ฆ, ์์ดํ ์์ฑ์ ๋ทฐ ๋ฐ์ธ๋ฉ์ ํ ๋ฒ๋งํ๋ฉฐ, ๊ทธ ๋ฐ์ธ๋ฉ ๋ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ๋ค ์ฌ์ฉํ์ฌ ์ฑ๋ฅ ๋ถ๋ถ์์ ํจ์จ์ ์ ๋๋ค.
LayoutManager
์์ดํ ์ ๋ฐฐ์น๋ฅผ ๋ด๋นํฉ๋๋ค.
LinearLayoutManager - ๊ฐ๋ก / ์ธ๋ก
GridLayoutManager - ๊ทธ๋ฆฌ๋ ํ์
StaggeredGridLayoutManager - ์ง๊ทธ์ฌ๊ทธํ์ ๊ทธ๋ฆฌ๋ ํ์
Adapter
ListView์ ๋์ผํ Adapter์ ๊ฐ๋ ์ผ๋ก ๋ฐ์ดํฐ์ ์์ดํ ์ ๊ดํ View๋ฅผ ์์ฑํ๋ ๊ธฐ๋ฅ์ ๋ด๋นํฉ๋๋ค.
ItemAnimation
Item ์ถ๊ฐ / ์ญ์ ์์ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ ๋ ์ฌ์ฉํฉ๋๋ค.
ItemDecoration
RecyclerView์ ์์ดํ ์ ๊พธ๋ฏธ๋ ์ญํ ์ ํฉ๋๋ค.
์ฃผ๋ก Divider๋ฅผ ์ค์ ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ฉ๋๋ค.
Click Detection
Click Listener๊ฐ ListView ์ฒ๋ผ ๋ด์ฅ๋์ด ์์ง ์์ผ๋ฏ๋ก, onClickListner๋ฅผ ํตํด ์ง์ ๊ตฌํํด์ฃผ์ด์ผ ํฉ๋๋ค.
RecyclerView ๋ง๋ค๊ธฐ
์ด์ RecyclerView๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
๋จผ์ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ์ ๋ฐ๋ณต๋ ์์ดํ ์ View๋ก ์ฌ์ฉ๋ Layout์ ํ๋ ์ถ๊ฐํด์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ item_recycler_ex.xml
layout -> New -> Layout Resource File
ImageView์ TextView๋ก ์ด๋ฃจ์ด์ง Layout ํ๋๋ฅผ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
android:background="@color/purple_500"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/img_rv_photo"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@color/black"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/tv_rv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="์ด๋ฆ"
android:textStyle="bold"
android:layout_marginLeft="20dp"
android:textSize="24sp"
android:textColor="@color/white"
app:layout_constraintLeft_toRightOf="@id/img_rv_photo"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv_rv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="๋์ด"
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:textSize="18sp"
android:textColor="@color/white"
app:layout_constraintLeft_toRightOf="@id/img_rv_photo"
app:layout_constraintTop_toBottomOf="@id/tv_rv_name"/>
</androidx.constraintlayout.widget.ConstraintLayout>
๐ activity_main.xml ์ RecyclerView ์ถ๊ฐํด์ฃผ๊ธฐ
Linear ํ์์ ์ธ๋ก๋ก ๋์ด ๋ ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค์ด์ฃผ๊ธฐ ์ํด์ ์ ๋ LinearLayout, Vertical ๋ก ์ค์ ํด์ฃผ์์ต๋๋ค.
app:layoutMananger = "androidx.recyclerview.widget.LinearLayoutManager"
android:orientation ="vertical"
<?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="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/rv_profile"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical"
tools:listitem="@layout/item_recycler_ex"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
RecyclerView ๋ง๋ค๊ธฐ - Adapter / ViewHolder ๋ง๋ค์ด์ฃผ๊ธฐ
๋ทฐ ๊ฐ์ฒด๋ฅผ ๊ทธ๋ ค์ฃผ๋ ViewHolder์ Data์ View๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ Adapter๋ฅผ ๊ตฌํํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ ProfileAdpater.kt
ProfileAdpater์ ViewHolder๋ฅผ ๊ฐ์ด inner class๋ก ๋ง๋ค์ด์ฃผ์์ผ๋ฉฐ, ProfileData๋ฅผ ๋ง๋ค์ด data๋ฅผ view์ ์ฐ๊ฒฐ์์ผ์ฃผ์์ต๋๋ค.
img๋ฅผ ์ฝ๊ฒ imgProfile์ ์ฐ๊ฒฐ์์ผ์ฃผ๊ธฐ ์ํด์ Glide๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
package com.example.recyclerview_ex
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
class ProfileAdapter(private val context: Context) : RecyclerView.Adapter<ProfileAdapter.ViewHolder>() {
var datas = mutableListOf<ProfileData>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.item_recycler_ex,parent,false)
return ViewHolder(view)
}
override fun getItemCount(): Int = datas.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(datas[position])
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val txtName: TextView = itemView.findViewById(R.id.tv_rv_name)
private val txtAge: TextView = itemView.findViewById(R.id.tv_rv_age)
private val imgProfile: ImageView = itemView.findViewById(R.id.img_rv_photo)
fun bind(item: ProfileData) {
txtName.text = item.name
txtAge.text = item.age.toString()
Glide.with(itemView).load(item.img).into(imgProfile)
}
}
}
๐ ProfileData.kt
package com.example.recyclerview_ex
data class ProfileData (
val name : String,
val age : Int,
val img : Int
)
RecyclerView ๋ง๋ค๊ธฐ - MainActivity.kt Adatper์ RecyclerView ์ฐ๊ฒฐ
RecyclerView๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์์์ ๋ง๋ค์ด์ค Adpater์ RecyclerView๋ฅผ ์ฐ๊ฒฐํด์ฃผ์ด์ผํฉ๋๋ค. ๋ํ ์๋ฒ์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ์๋๋ผ๋ฉด ๋๋ฏธ data๋ฅผ Adapter์ ๋ฃ์ด์ค์ผํฉ๋๋ค.
๐ MainActivity.kt
package com.example.recyclerview_ex
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
lateinit var profileAdapter: ProfileAdapter
val datas = mutableListOf<ProfileData>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initRecycler()
}
private fun initRecycler() {
profileAdapter = ProfileAdapter(this)
rv_profile.adapter = profileAdapter
datas.apply {
add(ProfileData(img = R.drawable.profile1, name = "mary", age = 24))
add(ProfileData(img = R.drawable.profile3, name = "jenny", age = 26))
add(ProfileData(img = R.drawable.profile2, name = "jhon", age = 27))
add(ProfileData(img = R.drawable.profile5, name = "ruby", age = 21))
add(ProfileData(img = R.drawable.profile4, name = "yuna", age = 23))
profileAdapter.datas = datas
profileAdapter.notifyDataSetChanged()
}
}
}
์ ์์๋ฅผ ์ฐจ๋ก๋ก ๋ฐ๋ผ์ค๋ฉด ์๋์ ๊ฐ์ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ๊ฐ ์์ฑ๋ฉ๋๋ค!
ํ์ง๋ง ! ๊ฐ๊ฒฉ ์กฐ์ ์ด ๋์ด์์ง ์์ต๋๋ค.
RecyclerView์ ์กด์ฌํ๋ ItemDecoration()์ ์ด์ฉํ์ฌ Item๋ง๋ค์ ๊ฐ๊ฒฉ์ ์กฐ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
VerticalItemDecorator / HorizontalItemDecorator Class ๋ง๋ค๊ธฐ
RecyclerView์ ItemDecoration()์ ์ด์ฉํ์ฌ Item์ ๊ฐ๊ฒฉ์ ์กฐ์ ํด์ฃผ๋ ItemDecoratorClass๋ฅผ ๋ง๋ค์ด์ฃผ์์ต๋๋ค.
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class VerticalItemDecorator(private val divHeight : Int) : RecyclerView.ItemDecoration() {
@Override
override fun getItemOffsets(outRect: Rect, view: View, parent : RecyclerView, state : RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.top = divHeight
outRect.bottom = divHeight
}
}
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class HorizontalItemDecorator(private val divHeight : Int) : RecyclerView.ItemDecoration() {
@Override
override fun getItemOffsets(outRect: Rect, view: View, parent : RecyclerView, state : RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = divHeight
outRect.right = divHeight
}
}
๐ RecyclerView์ ItemDecorator ์ ์ฉํ๊ธฐ (MainActivity.kt)
rv_profile.addItemDecoration(VerticalItemDecorator(20))
rv_profile.addItemDecoration(HorizontalItemDecorator(10))
MainActivity์ ์์ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด !? ์๋์ ๊ฐ์ด ๊ฐ๊ฒฉ์ด ์กฐ์ ๋ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ๋ฅผ ๋ง๋์ค ์ ์์ต๋๋ค!
๋ค์ ํฌ์คํ ์์๋ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ์์ ์ ์ฉํ๊ฒ ์ฐ์ด๋ ํด๋ฆญ์ด๋ฒคํธ์, Animation_transition์ RecyclerView์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์ฑํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!
์ฐธ๊ณ
'๐ฑAndroid > ๐ฉ๐ปโ๐ป Android ๊ฐ๋ฐ ์ผ์ง' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android/Kotlin] RecyclerView ํด๋ฆญ ์ด๋ฒคํธ ์ ์ฉํ๊ธฐ (13) | 2021.01.22 |
---|---|
[Android/Kotlin] ๊ฐ๋จํ Animation Transition ๊ตฌํํ๊ธฐ (0) | 2021.01.02 |
[Android/Kotlin] Round TabLayout ๋ง๋ค๊ธฐ (2) | 2020.12.30 |