카테고리 보관물: Android

Android RecyclerView 체크박스 상태 관리

개요

Android RecyclerView 는 화면에 보여지는 영역에서만 정의된 항목들이 유효합니다. 스크롤되어 다른 자료가 보여지게 되면 이전에 적용된 속성이 사라지게 됩니다. 대표적인 예가 체크박스 상태입니다. 화면에 보여질 때 체크하고 스크롤 후 다시 그 자료로 돌아와 보면 체크되지 않는 상태로 됩니다. 그래서 체크된 자료를 대상으로 처리하거나 전체선택/해제 기능을 구현할 때 이런 특성을 알고 있어야 합니다. 이번글에서는 전체선택/해제, 체크박스의 상태 유지 기능을 구현하는 방법을 알아보도록 하겠습니다.

체크박스 상태 저장

어디에선가는 체크박스 상태를 저장하고 있어야 합니다. 몇번째 체크박스가 체크되어 있는지에 관한 정보가 될 것입니다. 이글에서는 해시맵(Hashmap)을 이용해서 체크박스 상태를 저장해 보도록 하겠습니다. 다음과 같이 해시맵을 선언합니다. 리스트자료의 고유값을 키(Key)로 하고 값(Value)은 위치(Position) 값으로 저장해 보겠습니다.

companion object {
    lateinit var selectCheckBoxPosition:HashMap<Int, Int>
    private set
}

전체선택/해제

먼저 체크박스를 전체 선택하는 체크박스가 정의되어 있고 그것을 클릭했을 때 전체선택/해제 정보를 해시맵에 할당해 보겠습니다.

viewBinding.checkAllCheckBox.setOnClickListener {
    selectCheckBoxPosition.clear()
    if (viewBinding.checkAllCheckBox.isChecked) {
        for ((index, listData:yourClass) in yourList.withIndex()) {
            selectCheckBoxPosition[listData.Seq] = index
        }
    }
    listAdapter.notifyDataSetChanged()
}

체크박스를 클릭하면 해시맵을 비웁니다. 체크가 되었다면 연결된 리스트의 자료를 대상으로 고유값을 키로해서 위치 정보를 저장합니다. 그 다음 어댑터에 자료가 변경되었다는 것을 알립니다.

체크된 항목 정보 저장

전체선택/해제와 방식은 비슷합니다. 리스트내의 체크박스를 체크하면 해시맵에 저장하고 해제하면 제외합니다.

override fun onBindViewHolder(holder: Holder, position: Int) {
    holder.bind(yourList[position])
    val seqCheckBox = holder.itemView.findViewById<CheckBox>(R.id.seq)
    seqCheckBox.setOnCheckedChangeListener(null)
    seqCheckBox.isChecked = yourActivity.selectCheckBoxPosition.containsValue(position)
    seqCheckBox.setOnClickListener {
        val seq = seqCheckBox.tag.toString().toInt()
        if (seqCheckBox.isChecked) {
            yourActivity.selectCheckBoxPosition[seq] = position
        } else {
            yourActivity.selectCheckBoxPosition.remove(seq)
        }
    }
}

onBindViewHolder 메소드에 체크박스의 정보를 가져와서 해시맵에 위치 값이 존재하면 체크상태로 변경합니다. 그 내부에 체크박스 클릭 리스너를 정의합니다. 체크하면 해시맵에 정보를 저장하고 해제하면 제외합니다.

직접 구현한 코드와 예제 코드의 스타일이 다를 수 있습니다. 그렇지만 체크박스의 정보를 저장하고 관리해야 한다는 것만 염두에 두시면 문제없이 기능을 구현하실 수 있을겁니다.

Android view binding 적용

개요

개발 중인 앱에서 kotlin 버전 변경 후 다음과 같은 경고가 나타났습니다.

The ‘kotlin-android-extensions’ Gradle plugin is deprecated. Please use this migration guide (https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.android.com/topic/libraries/view-binding) and the ‘kotlin-parcelize’ plugin.

경고 메시지

kotlin-android-extensions gradle 플러그인이 deprecated 되었으니 가이드를 참고해서 마이그레이션하라는 내용입니다. 해당 링크를 확인해 보면 그 방법이 잘 안내되어 있습니다. View binding의 기능과 기존 Activity 에 적용하는 방법을 알아보도록 하겠습니다.

View binding

View binding 에 대한 내용은 이곳에서 확인하실 수 있습니다. 요약하자면 View 와 상호작용하는 코드를 더 쉽게 작성할 수 있게 해 주는 기능입니다. 대부분의 경우 findViewById 를 대치한다고 합니다. 코드내에서 findViewById 를 사용할 필요가 없게 됩니다. findViewById 와는 달리 Null 참조 위험이 없고 Type 도 레이아웃내의 요소와 일치하기 때문에 이전에 발생할 수 있었던 불일치 문제가 없다고 합니다. 레이아웃 XML 과 대응되는 binding class 가 생성되고 그 이름은 레이아웃 파일명에 camel case 가 적용된 후 뒤쪽에 Binding 이 추가된 형태가 됩니다. 예를 들면 다음과 같습니다.

activity_main.xml -> ActivityMainBinding

마이그레이션

먼저 모듈 수준 build.gradle 파일에 다음 내용을 추가합니다.

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

그 다음 앱 수준 build.gradle 파일에서 다음 내용을 삭제합니다.

apply plugin: `kotlin-android-extensions`

Activity 코드에서 다음과 같이 수정합니다.

private lateinit var viewBinding: ActivityMainBinding

super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
val view = viewBinding.root
setContentView(view) //setContentView(R.layout.activity_main) 기존 코드

viewBinding.userName.text = "text" //userName.text = "text" 기존 코드

1행에서 변수를 정의합니다. 4~6 행과 같이 레이아웃 설정 부분을 수정합니다. 8행과 같이 레이아웃에 있는 요소를 참조하면 됩니다. 참고로 기존의 코드도 주석 뒤쪽에 넣었습니다.

이상으로 view binding 을 기존 activity 에 적용하는 방법을 알아보았습니다.