이런 유튜브처럼, 스크롤을 내려보면 리스트가 계속 나오는 어플을 먼저 구현하라고 하셨다
리소스 관리하면서 하라고 하셔서 리소스 재사용을 할 수 있는 RecyclerView로 구현하게 되었다
RecycelrView는 이렇게 리스트가 새로 생길 때마다 추가로 view를 만드는 것이 아니라
이전에 만들어뒀던, 지금은 사용자에게 보이지 않는 뷰를 가져와서 재사용한다
1. import recyclerview
// RecyclerView
implementation "androidx.recyclerview:recyclerview:$version_recyclerview"
// For control over item selection of both touch and mouse driven selection
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc03"
2. item list layout 구성하기
우선 리스트에 반복적으로 표시해줄 하나의 아이템을 만들었다
content_list_item.xml 에 ImageView와 TextView로 구성
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="item"
type="com.example.firstapp.data.ContentModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
android:background="@drawable/item_border"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="2dp"
android:paddingTop="2dp"
android:paddingRight="2dp"
android:paddingBottom="2dp">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="154dp"
android:layout_height="85dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:scaleType="fitCenter"
app:imageUrl="@{item.thumbnail}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher_round"
android:layout_marginLeft="8dp" />
<TextView
android:id="@+id/contentTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/thumbnail"
app:layout_constraintTop_toTopOf="@+id/thumbnail"
tools:text="@{item.title}"
android:layout_marginLeft="16dp" />
<TextView
android:id="@+id/contentDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/thumbnail"
app:layout_constraintStart_toStartOf="@+id/contentTitle"
tools:text="@{item.details}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
그리고 이 아이템들이 RecyclerView로 구성될 수 있도록 만들었다
fragment_content_list.xml 에 RecyclerView 추가
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contentListRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:gravity="center"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:listData="@{contentView.contents}"
tools:layout_editor_absoluteY="6dp"
tools:listitem="@layout/content_list_item" />
LayoutManager는 말 그대로 Recycler의 변경하고 싶은 레이아웃 설정이 있으면 변경하는 곳이다
(리스트를 1열 여러 행으로 할 지, 그리드 형태로 할지, 스크롤을 좌우로 할지 위아래로 할지 등등)
3. 재사용할 뷰 객체를 기억하고 있을 ViewHolder
ContentListAdapter.kt
class ContentViewHolder(private var binding: ContentListItemBinding):
RecyclerView.ViewHolder(binding.root) {
fun bind(content: ContentModel) {
binding.item = content
binding.executePendingBindings()
}
}
원하는 데이터에 bind를 걸어주면, 뷰 객체는 유지하면서 안에 데이터값만 바꾸며 사용하게 된다
4. Adapter
Adapter는 뷰 객체에 데이터를 바인딩하기 전 사전 작업이 이루어지는 객체이다
ContentListAdapter.kt
class ContentListAdapter(private val onClickListener: OnClickListener ) :
ListAdapter<ContentModel,
ContentListAdapter.ContentViewHolder>(DiffCallback){
class ContentViewHolder(private var binding: ContentListItemBinding):
RecyclerView.ViewHolder(binding.root) {
fun bind(content: ContentModel) {
binding.item = content
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
return ContentViewHolder(ContentListItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
val content = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(content)
}
holder.bind(content)
}
}
데이터는 ListAdapter를 사용해 가져온다
ListAdapter는 RecyclerView에서 사용되고 background thread에서 돌아가고 데이터의 변화가 생기면 그 변화를 처리한다
:: ListAdapter<DATA CLASS, RecyclerView HOLDER>(CALLBACK) 으로 구성되어있다
- onCreateViewHolder: 정의한 ViewHolder 생성
- onBindViewHolder: ViewHolder에 데이터 위치시키기
- getItem: Apdater 내에 List를 인덱싱할 때 사용 (리스트를 알아서 관리함)
companion object DiffCallback : DiffUtil.ItemCallback<ContentModel>() {
override fun areItemsTheSame(oldItem: ContentModel, newItem: ContentModel): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: ContentModel, newItem: ContentModel): Boolean {
return oldItem.title == newItem.title
}
}
그래서 이렇게 데이터 변화가 생기면 처리하는 DiffCallBack 함수를 구현해준다
- areItemsTheSame : item이 동일한지 비교
- areContentsTheSame: item의 내용이 같은지 비교 (areItemsTheSame 다음으로 수행됨)
- Reference
화성 사진 가져오기: https://developer.android.com/codelabs/kotlin-android-training-internet-filtering#1
recycler view 설명: https://wooooooak.github.io/android/2019/03/28/recycler_view/
ListAdapter: https://developer.android.com/reference/androidx/recyclerview/widget/ListAdapter
'Web | App > Android' 카테고리의 다른 글
Android Emulator for AMD Processors failed 해결하기 (1) | 2021.05.15 |
---|---|
[Kotlin] 프로젝트에 모듈 추가하기 (0) | 2021.05.15 |
[Kotlin] 코루틴 coroutine (0) | 2021.01.06 |
[Kotlin] 갑자기 Rejecting re-init on previously-failed class java.lang.Class<androidx.core.view.ViewCompat$2>: 어쩌구,, (0) | 2020.12.14 |
[Kotlin] Retrofit2 + OkHttp로 API request 시작기 (0) | 2020.12.06 |