데이터 목록을 LinearLayout으로 구성할 경우, 증가하는 데이터에 대한 관리가 어렵다. 데이터마다 xml을 생성하고 view를 추가해야 하기 때문이다.
이럴 때 사용하는 것이 ListView로, 공통점을 가지는 데이터들을 비슷한 형태로 표현할 수 있다. 나타나는 데이터의 값이 항상 동일하지 않고 변할 수 있기 때문에, 반복되는 레이아웃을 재사용하여 데이터를 나타내주는 것이 바로 ListView다.
RecyclerView는 view를 재활용하는 기능을 추가한 효율적인 ListView다.
ListView / RecyclerView의 구성요소는 다음과 같다:
1. 아이템을 표현하는 data class
2. 아이템을 담을 ArrayList
3. 아이템의 개수
4. 아이템이 표시될 layout
5. ListView가 있을 layout
6. 데이터를 layout에 넣을 함수
어댑터란 기능 분산을 위해 데이터 관리를 해주는 기능이다. 데이터를 적절히 가공하여 view에 전달하는 역할를 한다.
RecyclerView Adapter의 구성요소는 다음과 같다:
1. 실제 item을 담고있는 ArrayList
2. viewholder 객체
3. viewholder 생성 시에 수행할 작업을 담은 함수
4. viewholder가 보일 때 수행할 작업
5. 표현해야 할 아이템의 총 개수
- ViewHolder는 itemVIew의 각 요소를 바로 액세스 할 수 있도록 저장해두고 사용하기 위한 객체다.
class MainActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
private var dataList: ArrayList<Item> = arrayListOf()
var count: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater);
setContentView(viewBinding.root)
val itemRVadapter = ItemRVAdapter(dataList)
viewBinding.btnMain.setOnClickListener {
if (viewBinding.etMain.text != null) {
dataList.apply {
add(Item(viewBinding.etMain.text.toString()))
count++
}
viewBinding.etMain.text.clear()
}
itemRVadapter.notifyItemInserted(dataList.size)
}
viewBinding.rvItem.adapter = itemRVadapter
viewBinding.rvItem.layoutManager = LinearLayoutManager(this)
}
}
class ItemRVAdapter(
private val dataList: ArrayList<Item>,
) : RecyclerView.Adapter<ItemRVAdapter.ItemViewHolder>(){
inner class ItemViewHolder(val viewbinding:
ItemDataBinding):RecyclerView.ViewHolder(viewbinding.root){
fun bind(data: Item){
viewbinding.tv2.text = data.text
}
}
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): ItemViewHolder {
val viewbinding = ItemDataBinding.inflate(LayoutInflater.from(parent.context))
return ItemViewHolder(viewbinding)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val listposition = dataList[position]
holder.bind(dataList[position])
}
override fun getItemCount(): Int = dataList.size
}
Item.kt : item 데이터 클래스
위 코드의 경우 요소는 문자열 1개
item_data.xml : 각 item을 표현할 레이아웃 xml
위 코드의 경우 textView 1개
activity_main.xml : RecyclerView를 포함하는 MainActivity의 xml 파일
위 코드의 경우 EditText, Button, RecyclerView 1개씩
----- 발생할 수 있는 에러 -----
리사이클러의 아이템뷰는 호율적인 메모리 사용 및 성능을 위해 스크롤 시 매번 뷰를 생성하지 않는다. 화면에 보이는 개수만큼만 뷰를 생성하고 재활용하기 때문에, 스크롤를 하는 경우 기존 뷰가 유지되어 오류가 발생한다.
이를 해결하기 위해 특정 인덱스의 아이템뷰를 반환하는 getItemViewType를 오버라이딩하여 스크롤 시 나와야하는 데이터를 직접 지정하여 사용한다.
class DataRVAdapter(private val dataList: ArrayList<Data>):
RecyclerView.Adapter<DataRVAdapter.DataViewHolder>() {
...
//*** 추가 부분 : 중복 방지
override fun getItemViewType(position: Int): Int {
return position
}
}