본문 바로가기

Android/이슈

리스트뷰를 스크롤했을 때 상태가 저장이 안 되는 현상에 대해서

안녕하세요? 닉네임간편입니다. 이번 시간에는 리스트뷰를 스크롤했을 때 스위치 상태나 체크박스의 상태가 저장이 안 되는 현상에 대해서 다루어보겠습니다.

1. 리스트뷰란

리스트뷰(ListView)는 뷰들의 집합을 세로로 스크롤이 가능하게 표시하는 위젯입니다. 각 뷰들은 이전 요소의 바로 아래에 위치합니다. 많은 요소들을 순차적으로 화면에 표시하기 위해 사용합니다.

2. 문제

그런데 리스트뷰를 사용하면서 문제가 하나 있습니다.

아래 동영상처럼 스위치를 선택을 하여도 스크롤을 한 다음 다시 돌아오면 상태가 변하는 것입니다.

3. 발생 원인

리스트뷰는 스크롤 시 퍼포먼스의 기능을 위해서 뷰를 재사용합니다.

만일 100개의 뷰가 있고 화면에는 5개의 뷰만 표시할 수 있다고 가정하겠습니다. 이때 100개의 뷰 모두를 만들어놓는다면 성능 상 문제가 발생할 수 있고, 스크롤이 매끄럽지 못할 수 있습니다.

따라서 리스트뷰는 우선 화면에 보이는 개수, 5개만 뷰를 만들고 표시합니다. 이후 사용자가 스크롤하면 첫 번째 뷰가 사라지고 6번째 뷰가 보입니다. 이때 6번째 뷰는 새로 생성되는 것이 아니라 기존의 뷰를 재사용하여 값만 새로 설정하는 것입니다.

그런데 이때 뷰 안에 표시되었던 '데이터'는 다시 불러오지 않습니다.

따라서 만일 스위치 버튼을 on으로 바꿔준 이후 스크롤한 다음 다시 올라오면, 스위치 버튼이 선택되지 않은 상태로 돌아오는 것입니다. 뷰에 배치되어있는 요소들 자체는 재사용되었지만, 데이터는 복구되지 않기 때문입니다.

즉, 스크롤 시의 성능을 위해서 뷰를 재사용하는데, 이때 데이터는 복구되지 않기 때문에 위와 같은 문제가 발생합니다.

4. 해결 방법

리스트뷰에 사용하는 아이템의 프로퍼티로 스위치 상태를 추가합니다.

그리고 스위치에 OnCheckedChangedListener를 설정해서 스위치 상태가 변할 때마다 이 여부를 해당 position의 아이템에 스위치 상태를 설정합니다.

즉, 아이템에 스위치 상태를 저장하는 것입니다.

이를 통해 getView() 메서드를 통해 화면에 뷰가 재사용될 때마다, 해당 position에 상응하는 아이템의 스위치 상태를 가져와 뷰에 표시하게 됩니다. 그러면 사용자가 변경한 스위치 상태가 화면에 다시 표시될 수 있습니다.

즉, 스위치 상태가 저장이 되게 되고, 따라서 스크롤을 하고 뷰를 재사용하더라도 스위치 상태는 사용자가 선택한 상태에서 변하지 않습니다.

[코드]

class CustomAdapter(val context: Context, private val itemList: 
	ArrayList<Item>) : BaseAdapter() {

    private val inflater = 
					context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) 
				as LayoutInflater
    lateinit var binding: ItemviewBinding

    override fun getCount(): Int {
        return itemList.size
    }

    override fun getItem(position: Int): Any {
        return itemList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, 
										parent: ViewGroup?): View {
        binding = ItemviewBinding.inflate(inflater, parent, false)
        binding.itemTitle.text= itemList[position].title
        binding.itemContent.text= itemList[position].content
        binding.itemImg.setImageResource(itemList[position].img)
        binding.itemSwitch.isChecked= itemList[position].isSwitcehd
        binding.itemSwitch.setOnCheckedChangeListener{buttonView, isChecked->
						itemList[position].isSwitcehd = isChecked
				}
				return binding.root
		}
}

data class Item(val title: String, val content: String, val img: Int, 
								var isSwitcehd: Boolean = false)

아래 영상을 보시면 스위치 상태가 잘 저장되는 걸 확인하실 수 있습니다.

 

5. 마무리

이번 시간에는 리스트뷰가 스크롤되었을 때 상태가 저장되지 않는 현상에 대해서 다루었습니다.

사실 개발자가 아무리 코드를 잘 작성하더라도 의도치 않게 구현이 되는 현상이 존재하는데요, 이번 게시글을 통해 그런 현상을 조금이라도 줄일 수 있었으면 좋겠습니다.

728x90
반응형

'Android > 이슈' 카테고리의 다른 글

ViewPager2 중첩 스크롤 문제 해결  (0) 2021.11.14