- (RecyclerView) ListAdapter
- RecyclerView - ListAdapter
- 기존의 RecyclerView와 AndroidX ListAdapter의 차이
- ItemTouchHelper
- (RecyclerView) ListAdapter 실습
RecyclerView는 안드로이드 앱에서 리스트 형식의 데이터를 표시하기 위한 위젯입니다.
리스트 형식의 데이터를 표시하기 위해서는 어댑터를 구현해야 합니다.
RecyclerView는 Adapter와 ViewHolder를 사용하여 데이터와 뷰를 연결합니다.
ListAdapter는 RecyclerView에서 데이터를 표시하는 데 사용되는 AndroidX 라이브러리입니다.
ListAdapter를 사용하면 표시할 데이터 목록을 관리할 수 있으며, 변경 사항을 적용하는 방법도 간단합니다.
ListAdapter는 DiffUtil이라는 유틸리티 클래스를 사용하여 데이터가 변경되었을 때 이전 목록과 새로운 목록 간의 차이점을 계산하고, 최소한의 업데이트를 수행합니다.
이를 통해 앱의 성능을 개선하고, 불필요한 뷰 업데이트를 줄일 수 있습니다.
ListAdapter를 사용하면 다음과 같은 이점이 있습니다.
ListAdapter는 RecyclerView.Adapter를 상속합니다.
따라서 onCreateViewHolder 및 onBindViewHolder와 같은 기본적인 작업을 오버라이드할 수 있습니다.
하지만 ListAdapter는 DiffUtil을 사용하여 효율적으로 업데이트를 처리하므로, 데이터 변경 시에도 작성해야 하는 코드 양이 적습니다.
즉, ListAdapter를 사용하면 간단하게 RecyclerView의 데이터를 관리하고 업데이트할 수 있으며, DiffUtil을 사용하여 성능을 최적화할 수 있습니다.
기존의 RecyclerView와 AndroidX ListAdapter의 가장 큰 차이점은 업데이트 처리 방식입니다.
기존의 RecyclerView에서는 데이터 변경 시에 모든 아이템을 다시 그리는 방식으로 업데이트를 처리했습니다.
이는 큰 데이터 세트의 경우 성능 문제를 야기할 수 있었습니다.
하지만 AndroidX ListAdapter에서는 DiffUtil이라는 유틸리티 클래스를 사용하여 변경된 데이터만 업데이트합니다.
이를 통해 RecyclerView의 성능을 개선할 수 있습니다.
또한 ListAdapter는 기본적으로 ViewHolder를 관리하는 방식이 다릅니다.
ListAdapter는 ViewHolder의 생성과 바인딩을 자동으로 처리합니다.
이를 통해 onCreateViewHolder 및 onBindViewHolder와 같은 기본 작업을 자동으로 처리할 수 있습니다.
또한 ListAdapter는 데이터가 변경될 때 이를 알리기 위한 notifyItem*() 메서드를 대신하여 submitList() 메서드를 제공합니다.
이 메서드는 데이터 세트 전체를 다시 계산하지 않고 변경된 데이터만 전달합니다.
이를 통해 DiffUtil을 사용하여 성능을 개선할 수 있습니다.
따라서 ListAdapter는 기존의 RecyclerView보다 성능 개선과 코드 양 감소에 큰 장점을 가지고 있습니다.
바인딩이란?
RecyclerView에서 ViewHolder 패턴을 사용하여 각 아이템의 뷰를 표시합니다. ViewHolder는 각각의 아이템 뷰를 저장하는 객체로, RecyclerView의 스크롤이나 아이템 추가/삭제와 같은 이벤트가 발생할 때 재활용되어 효율적인 메모리 관리를 가능하게 합니다.
ViewHolder는 onCreateViewHolder() 메서드에서 뷰를 생성하고, onBindViewHolder() 메서드에서 데이터를 바인딩합니다. 이때 바인딩은 ViewHolder에 데이터를 연결하여 뷰를 업데이트하는 것을 의미합니다.
Android RecyclerView에서 ItemTouchHelper는 사용자가 아이템을 드래그하여 이동하거나 스와이프하여 삭제하는 등의 동작을 구현하는 데 사용됩니다.
이 때 ItemTouchHelper.SimpleCallback은 ItemTouchHelper.Callback 인터페이스의 구현체 중 하나로, 이동 및 스와이프 이벤트를 처리하는 방법을 지정합니다.
ListAdapter를 사용할 때 ItemTouchHelper.SimpleCallback을 RecyclerView에 연결하여 사용자가 아이템을 이동하거나 삭제할 때 처리되는 동작을 제어할 수 있습니다.
이 때 ItemTouchHelper.SimpleCallback의 메소드 중 일부는 다음과 같습니다.
onMove() : 아이템이 이동될 때 호출됩니다. 이 메소드에서는 이동할 아이템의 위치와 이동할 위치를 받아서 데이터를 업데이트해야 합니다.
onSwiped() : 아이템이 스와이프될 때 호출됩니다. 이 메소드에서는 스와이프된 아이템의 위치를 받아서 데이터를 업데이트해야 합니다.
getMovementFlags() : 아이템을 이동할 수 있는 방향과 스와이프할 수 있는 방향을 지정합니다.
clearView() : 아이템이 움직이거나 스와이프되고 나서 원래 위치로 되돌아갈 때 호출됩니다.
onSelectedChanged() : 아이템을 선택했을 때 호출됩니다. 이 메소드에서는 선택한 아이템을 강조 표시하거나 다른 작업을 수행할 수 있습니다.
ListAdapter와 ItemTouchHelper.SimpleCallback을 함께 사용하면 RecyclerView에서 아이템 이동 및 삭제 동작을 구현할 수 있습니다.
class HelloListView : AppCompatActivity(){
private val adapter: MyAdapter by lazy {
MyAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val data = foodList
// submitList로 데이터를 제공한다.
// 기존의 목록과 새로운 목록을 비교하기 때문에, 두 목록의 reference는 달라야 한다. toMutableList로 새로 생성.
adapter.submitList(data.toMutableList())
val rview = findViewById<RecyclerView>(R.id.recyclerView)
rview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
rview.adapter = adapter
//구분선 추가.
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
rview.addItemDecoration(dividerItemDecoration)
// 아이템 터치를 만들고 리사이클러 뷰를 적용시킨다.
val itemTouchCallback = ItemTouchHelper(ItemTouchCallback(rview))
itemTouchCallback.attachToRecyclerView(rview)
}
//ListAdapter에서 목록관리하므로, collection으로 받을 필요 없음.
class MyAdapter : ListAdapter<String, MyAdapter.CustomViewHolder>(StringComparator){
companion object StringComparator: DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
// Item의 id 값으로 비교한다. object에 id가 있으면 그 id를 사용하여 비교.
return oldItem == newItem
}
}
inner class CustomViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// 아이템을 이동시키면 배경 색 변경
fun setBackground(color:Int){
itemView.setBackgroundColor(color)
}
var name:TextView = itemView.findViewById<TextView>(R.id.name)
fun bindInfo(data:String){
name.setText(data)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_row, parent, false)
Log.d(TAG, "inflate")
return CustomViewHolder(view)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.apply{
//getItem : 위치의 값을 가져옴.
bindInfo(getItem(position))
}
}
// 자리이동.현재 데이터 가져와서 바꿔치기한다. 위치가 한칸씩 바뀔때 마다 호출된다.
fun moveItem(fromPosition:Int, toPosition:Int){
val newList = currentList.toMutableList()
Collections.swap(newList, fromPosition, toPosition)
submitList(newList)
}
// 삭제. 해당위치의 아이템을 삭제한다.
fun removeItem(position: Int){
val newList = currentList.toMutableList()
newList.removeAt(position)
submitList(newList)
}
}
companion object {
val foodList = arrayListOf(
"피자", "스테이크", "파스타", "초밥", "치킨",
"짜장면", "짬뽕", "탕수육", "김치찌개", "된장찌개",
"순두부찌개", "불고기", "샐러드", "닭갈비", "돈까스",
"떡볶이", "쌀국수", "라면", "우동", "오므라이스",
"카레", "햄버거", "핫도그", "감자튀김", "치즈스틱",
"치킨텐더", "나쵸", "프렌치프라이", "마라탕", "마라샹궈",
"해물파전", "갈비탕", "설렁탕", "순대국", "콩나물국",
"쭈꾸미볶음", "곱창전골", "보쌈", "삼겹살", "목살",
"양념치킨", "간장치킨", "황금올리브치킨", "마늘치킨", "또띠아",
"토스트", "샌드위치", "베이글", "팬케이크", "와플"
)
}
}
// 리사이클러뷰를 파라미터로 받았다.
class ItemTouchCallback(private val recyclerView: RecyclerView) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT){
// 아이템을 클릭해서 이동할 때
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder, // 이동하려는 아이템의 현재 위치
target: RecyclerView.ViewHolder // 이동하려는 곳
): Boolean {
(recyclerView.adapter as HelloListView.MyAdapter).moveItem(viewHolder.layoutPosition, target.layoutPosition)
return true
}
// 아이템을 스와이프 할떄
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
(recyclerView.adapter as HelloListView.MyAdapter).removeItem(viewHolder.layoutPosition) // layoutPosition는 화면에 보이는 아이템의 위치
}
// 아이템을 선택하면 호출 -> 예제에서는 선택시 아이템의 배경색을 변경
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
when(actionState){
ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.ACTION_STATE_SWIPE ->
(viewHolder as HelloListView.MyAdapter.CustomViewHolder).setBackground(Color.RED)
}
}
// 선택 해제시 호출 -> 예제에서는 선택을 해제시 배경색을 복구
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
(viewHolder as HelloListView.MyAdapter.CustomViewHolder).setBackground(Color.WHITE)
}
}