ConcatAdapter란 하나의 리사이클러뷰에 여러 가지 어답터를 결합? 하여 순차적으로 나타낼 수 있도록 도와주는 어답터입니다. 처음 나왔을 땐 MergeAdapter로 불렸지만 현재는 ConcatAdapter라고 합니다.
위의 움짤은 3개의 어답터를 ConcatAdapter를 사용하여 하나의 리사이클러뷰에 연결시켜준 모습입니다.
첫 번째, 두 번째, 세 번째 어답터를 순차적으로 넣어주었기 때문에 첫 번째, 두 번째, 세 번째 리사이클러뷰로 순차적으로 나타나는 것을 볼 수 있습니다.
val concatAdapter = ConcatAdapter(firstAdp, secondAdp, thirdAdp)
의 결과
아래와 같이 두번째, 세 번째, 첫 번째 어답터 순으로 넣어주었다면 두 번째, 세 번째, 첫 번째 리사이클러뷰 순으로 보이게 될 것입니다.
val concatAdapter = ConcatAdapter(secondAdp, thirdAdp, firstAdp)
의 결과
의존성 추가
ConcatAdapter을 사용하기위해서는 dependency를 추가해주어야 합니다.
implementation "androidx.recyclerview:recyclerview:1.2.0-rc01"
모듈의 그레들에 위의 의존성을 추가해주면 됩니다.
간단한 레이아웃 구성(XML)
activity_main.xml
메인엑티비티 레이아웃의 소스코드입니다.
<?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:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/recyclerView"
app:layout_constraintBottom_toBottomOf="parent"
android:weightSum="9">
<Button
android:id="@+id/btnAddFirst"
android:text="첫번째에 추가"
android:layout_weight="3"
android:layout_margin="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnAddSecond"
android:text="두번째에 추가"
android:layout_weight="3"
android:layout_margin="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnAddThird"
android:text="세번째에 추가"
android:layout_weight="3"
android:layout_margin="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
first_items.xml
리사이클러뷰에 사용될 아이템들의 레이아웃을 구성해주었습니다.
첫 번째 리사이클러뷰 아이템의 레이아웃의 소스코드는 아래와 같습니다.
xml 파일의 이름은 first_items.xml입니다.
<?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"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/first_color">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="첫번째 리사이클러뷰"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:text="텍스트제목"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
미리보기상 화면으로는 아래와 같습니다.
두 번째, 세 번째 소스코드도 위와 동일합니다.
second_items.xml
<?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"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/second_color">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="두번째 리사이클러뷰"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:text="텍스트제목"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
third_items.xml
<?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"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/second_color">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:text="두번째 리사이클러뷰"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:text="텍스트제목"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
RecyclerVIew를 위한 Adapter 만들기
리사이클러뷰를 위한 어답터를 만들어주도록 하겠습니다.
첫 번째, 두번째, 세번째 어답터가 모두 동일하기 때문에 첫번째 어답터로만 설명하겠습니다.
첫번째 어답터인 FirstAdapter의 전체 소스코드는 아래와 같습니다.
package com.example.concatadapter.Adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.concatadapter.databinding.FirstItemsBinding
class FirstAdapter(private var itemList: MutableList<String> = mutableListOf()) :
RecyclerView.Adapter<FirstHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FirstHolder {
val binding = FirstItemsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return FirstHolder(binding)
}
override fun onBindViewHolder(holder: FirstHolder, position: Int) {
holder.setItem(itemList[position])
}
override fun getItemCount(): Int {
return itemList.size
}
fun addItem(item: String) {
this.itemList.add(item)
notifyItemInserted(itemList.size - 1)
}
}
class FirstHolder(val binding: FirstItemsBinding) : RecyclerView.ViewHolder(binding.root) {
fun setItem(item: String) {
binding.item.text = item
}
}
FirstHolder
FirstAdapter에 뷰 홀더로 쓰일 FirstHolder를 만들어 주었습니다.
class FirstHolder(val binding: FirstItemsBinding) : RecyclerView.ViewHolder(binding.root) {
fun setItem(item: String) {
binding.item.text = item
}
}
first_item 레이아웃을 바인딩하고 setItem 함수를 만들었습니다. FirstAdapter의 onBindViewHolder에서 setItem 함수를 호출하여레이아웃에 아이템을 나타내게 됩니다.
FirstAdapter
class FirstAdapter(private var itemList: MutableList<String> = mutableListOf()) :
RecyclerView.Adapter<FirstHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FirstHolder {
val binding = FirstItemsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return FirstHolder(binding)
}
override fun onBindViewHolder(holder: FirstHolder, position: Int) {
holder.setItem(itemList[position])
}
override fun getItemCount(): Int {
return itemList.size
}
fun addItem(item: String) {
this.itemList.add(item)
notifyItemInserted(itemList.size - 1)
}
}
FirstAdapter는 위와 같이 구성되어 있습니다. 생성자로 itemList를 받아올 수 있도록 하였습니다.
Adapter 내부에 addItem 함수를 만들어서 메인엑티비티에서 버튼이 눌렸을 때 addItem을 호출하여 아이템을 추가할 수 있도록 만들었습니다. itemList에 아이템이 추가된 후 어답터에게 변화가 있음을 알리는 notifyItemInserted 함수를 사용하였습니다.
position은 0부터 시작하기 때문에 itemList.size -1을 통하여 방금 추가된 아이템의 위치(position)를 넘겨주었습니다.
SecondAdapter, ThirdAdapter 또한 위와 동일하게 구성되어있습니다.
SecondAdapter
class SecondAdapter(private var itemList: MutableList<String> = mutableListOf()) :
RecyclerView.Adapter<SecondHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SecondHolder {
val binding =
SecondItemsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SecondHolder(binding)
}
override fun onBindViewHolder(holder: SecondHolder, position: Int) {
holder.setItem(itemList[position])
}
override fun getItemCount(): Int {
return itemList.size
}
fun addItem(item: String) {
itemList.add(item)
notifyItemInserted(itemList.size - 1)
}
}
class SecondHolder(val binding: SecondItemsBinding) : RecyclerView.ViewHolder(binding.root) {
fun setItem(item: String) {
binding.item.text = item
}
}
ThirdAdapter
class ThirdAdapter(private var itemList: MutableList<String> = mutableListOf()) :
RecyclerView.Adapter<ThirdHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThirdHolder {
val binding =
ThirdItemsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ThirdHolder(binding)
}
override fun onBindViewHolder(holder: ThirdHolder, position: Int) {
holder.setItem(itemList[position])
}
override fun getItemCount(): Int {
return itemList.size
}
fun addItem(item: String) {
itemList.add(item)
notifyItemInserted(itemList.size - 1)
}
}
class ThirdHolder(val binding: ThirdItemsBinding) : RecyclerView.ViewHolder(binding.root) {
fun setItem(item: String) {
binding.item.text = item
}
}
MainActivity 구성
메인엑티비티의 전체적인 소스코드는 아래와 같습니다.
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
val firstItemList = mutableListOf<String>()
val secondItemList = mutableListOf<String>()
val thirdItemList = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
getFiveItems(firstItemList)
getFiveItems(secondItemList)
getFiveItems(thirdItemList)
val firstAdp = FirstAdapter(firstItemList)
val secondAdp = SecondAdapter(secondItemList)
val thirdAdp = ThirdAdapter(thirdItemList)
val concatAdapter = ConcatAdapter(firstAdp, secondAdp, thirdAdp)
binding.recyclerView.adapter = concatAdapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
// 추가 버튼이 눌렸을 때 판정들
with(binding) {
btnAddFirst.setOnClickListener {
firstItemList.add("${firstItemList.size + 1}번째 아이템")
firstAdp.notifyItemInserted(firstItemList.size - 1)
}
btnAddSecond.setOnClickListener {
secondItemList.add("${secondItemList.size + 1}번째 아이템")
secondAdp.notifyItemInserted(secondItemList.size - 1)
}
btnAddThird.setOnClickListener {
thirdItemList.add("${thirdItemList.size + 1}번째 아이템")
thirdAdp.notifyItemInserted(thirdItemList.size - 1)
}
}
}
fun getFiveItems(itemList: MutableList<String>) {
for (no in 1..5) {
itemList.add("${no}번째 아이템")
}
}
}
getFiveItems 함수를 만들고 oncreate에서 호출하여 firstItemList, secondItemList, thirdItemList에 각각 5개의 아이템이 담기도록 구성했습니다.
fun getFiveItems(itemList: MutableList<String>) {
for (no in 1..5) {
itemList.add("${no}번째 아이템")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
getFiveItems(firstItemList)
getFiveItems(secondItemList)
getFiveItems(thirdItemList)
//..이하 생략
}
그 후 First, Second, Third 어답터를 순차적으로 생성해주고 ConcatAdapter를 사용하여 연결해주었습니다.
리사이클러뷰의 adapter에 concatAdapter를 사용하여주었습니다.
val firstAdp = FirstAdapter(firstItemList)
val secondAdp = SecondAdapter(secondItemList)
val thirdAdp = ThirdAdapter(thirdItemList)
val concatAdapter = ConcatAdapter(firstAdp, secondAdp, thirdAdp)
binding.recyclerView.adapter = concatAdapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
마지막으로 첫 번째에 추가 / 두 번째에 추가 / 세 번째에 추가 / 버튼이 눌렸을 때 각각 해당하는 어답터에 아이템을 추가할 수 있도록 구성하였습니다.
// 추가 버튼이 눌렸을 때 판정들
with(binding) {
// 첫번째에 추가
btnAddFirst.setOnClickListener {
firstItemList.add("${firstItemList.size + 1}번째 아이템")
firstAdp.notifyItemInserted(firstItemList.size - 1)
}
// 두번째에 추가
btnAddSecond.setOnClickListener {
secondItemList.add("${secondItemList.size + 1}번째 아이템")
secondAdp.notifyItemInserted(secondItemList.size - 1)
}
// 세번째에 추가
btnAddThird.setOnClickListener {
thirdItemList.add("${thirdItemList.size + 1}번째 아이템")
thirdAdp.notifyItemInserted(thirdItemList.size - 1)
}
}
동작하는 모습
세 개의 어답터가 순차적으로 나타나고 아이템이 추가되는 것도 잘 동작하는 것을 볼 수 있습니다!
예제 소스 코드
위에 나와있는 예제의 전체 코드입니다!
https://github.com/soopeach/ConcatAdapter
'안드로이드[Android]' 카테고리의 다른 글
안드로이드[Android] HTTP프로토콜 형식으로 FCM에서 백그라운드 메시지 보내기(POSTMAN) (0) | 2022.04.17 |
---|---|
안드로이드[Android] 알림 만들기 / Notification (채널 설정) (0) | 2022.04.17 |
안드로이드[Android] EditText 입력 글자수 제한, 표기 / 문장수 제한 (0) | 2022.04.05 |
안드로이드[Android] 안드로이드 백그라운드에서 알림보내기(FCM) (2) | 2022.04.01 |
안드로이드[Android] 네이버 클라우드 Geocoding 사용법(Post Man) (0) | 2022.03.31 |
최근댓글