본문으로 바로가기

스킬 선택화면

위의 스킬 선택 화면에서, BindingAdapter를 통해서 한쪽 스킬을 클릭하면 반대 쪽 스킬은 흑백처리가 되도록 하고 싶었다.

동작 예시는 다음과 같다.

레벨 5에서 스킬 하나 선택 시

레벨 5때 선택 스킬 중 하나를 선택시, 반대편 스킬은 흑백화 되는 것을 확인할 수 있다. 해당 기능을 databinding 으로 구현하고 싶었고, 다음과 같이 구현하였다.

 

BindingAdapter

@BindingAdapter("isSkillSelected")
fun bindIsSkillSelected(view: ImageView, isSelected: Boolean?) {
    val greymatrix = ColorMatrix().apply { setSaturation(0.0f) }
    view.colorFilter = if (isSelected==false) {
        ColorMatrixColorFilter(greymatrix)
    } else {
        null
    }
}

layout(xml)

<?xml version="1.0" encoding="utf-8"?>

<layout 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"
    tools:context=".Fragments.WritingFragment">

    <data>
        <variable
            name="IsSelected"
            type="boolean"/>
    </data>
    
    <ImageView
    android:id="@+id/skill1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isSkillSelected="@{IsSelected}">
    
    <ImageView
    android:id="@+id/skill2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isSkillSelected="@{!IsSelected}">
    
.....

IsSelected 가 ! 로 서로 반대 값으로 배치되어 있는 것을 확인할 수 있다.

이로써 첫번째 스킬을 선택하면 두번째가 흑백이 되고, 두번째 스킬을 선택하면 첫번째가 흑백이 되도록 프래그먼트에서 다음과 같이 구현할 수 있다.

 

Fragment

binding.apply{
skill1.setOnClickListener { isSelected = true }
skill2.setOnClickListener { isSelected= false }
}

 

허나 문제가 있다. 위에서는 isSelected 변수를 layout xml 파일 안에서 <variable> 태그안에 boolean 타입으로 선언했는데,

데이터 바인딩에서 boolean 의 디폴트값은 무조건 false 기 때문에 첫 화면에 들어갔을 때 두번째 스킬이 선택된 상태로 시작하는 것이다. isSelected 의 디폴트값을 null 로 주면 해결이 되지만, 데이터 바인딩에서 저런 식으로 변수를 선언한 경우 불가능하다.

 

공식문서 에도 나와있듯이 databinding의 variable은 각 타입 별로 기본값은 null 이 아닌 다른 값을 가지고 있다.

예를 들어 boolean 은 false , Integer 은 0 이다. 

첫 화면에 들어갔을 때 두번째 스킬이 선택된 상태로 시작하는 문제를 해결하고 싶었다.

 

문제 해결을 위해선 false, true 이지선다의 값이 아니라 null 까지 추가로 가질 수 있는 변수를 사용해야 했다.

그러기 위해 레이아웃안에서 variable 을 boolean 타입으로 선언하는 것이 아니라 ViewModel 안에서 MutableLiveData<Boolean?>타입의 변수를 하나 선언해 해당 변수를 사용하였다.

 

ViewModel

val isSelected = MutableLiveData<Boolean?>()

layout(xml)

<?xml version="1.0" encoding="utf-8"?>

<layout 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"
    tools:context=".Fragments.WritingFragment">

    <data>
        <variable
            name="viewModel"
            type="com.app.unitewiki.viewmodels.WritingViewModel"/>
    </data>
    
    <ImageView
    android:id="@+id/skill1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isSkillSelected="@{viewModel.isSelected}">
    
    <ImageView
    android:id="@+id/skill2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isSkillSelected="@{!viewModel.isSelected}">
    
.....

Fragment

binding.apply{
skill1.setOnClickListener { viewmodel.isSelected.value = true }
skill2.setOnClickListener { viewmodel.isSelected.value= false }
}

위와 같이 하면 isSelected 의 디폴트값이 false 가 아닌 null 에서 시작하게 되고 첫 화면 진입시 아무 스킬도 선택되지 않은 화면에서 시작하게 할 수 있었다.