안드로이드/정리(Android)

Android) Databinding 정리

김염인 2022. 8. 16. 23:23

Databinding 특징 세가지

1) Remove findviewById

2) Custom Binding Adapter

3) Two-way DataBinding

 

- xml 에 데이터를 바인딩하여 불필요한 코드를 줄이는 방법으로, 보통 MVVM 패턴을 구현 할 때 사용된다.

 

<Databinding 과정>

1. 먼저 gradle에 아래와 같이 추가해준다.

buildFeatures.dataBinding true

 

2. xml으로 이동하여 ShowContextActions - convert to layout databinding을 눌러준다.

 

3. 이후 XML에 viewmodel을 구독하도록 data추가

4.MainViewXml

<?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">

    <data>

        <variable
            name="viewmodel"
            type="com.example.databindingrepositoryapp.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <ProgressBar
            android:id="@+id/progressbar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:max="@{100}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintWidth_percent="0.3"
            app:progressScaled="@{viewmodel.liveCounter}" />

        <CheckBox
            android:id="@+id/checkbox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:checked="@={viewmodel.hasChecked}"
            android:text="CheckBox"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/button"
            app:layout_goneMarginTop="30dp" />

        <TextView
            android:id="@+id/hasCheckedCheckbox"
            android:layout_marginTop="30dp"
            app:layout_constraintTop_toBottomOf="@id/checkbox"
            android:layout_width="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:text="@{viewmodel.hasChecked.toString()}"
            android:layout_height="wrap_content"/>

        <TextView
            android:id="@+id/numberTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewmodel.modifiedCounter.toString()}"
            app:layout_constraintBottom_toTopOf="@id/button"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/progressbar" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:backgroundTint="@color/teal_200"
            android:text="클릭"
            android:textColor="@color/black"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/numberTextView" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

5. MainActivity

package com.example.databindingrepositoryapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil
import com.example.databindingrepositoryapp.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)


        val factory = MyViewModelFactory(10, this)
        val myViewModel : MyViewModel by viewModels<MyViewModel>() { factory }

        binding.lifecycleOwner = this
        binding.viewmodel = myViewModel

        binding.button.setOnClickListener {
            myViewModel.liveCounter.value = myViewModel.liveCounter.value?.plus(1)
        }

        myViewModel.liveCounter.observe(this) {
            counter -> binding.numberTextView.text = counter.toString()
        }

        myViewModel.modifiedCounter.observe(this){
                counter -> binding.numberTextView.text = counter
        }
    }


}

 

6.BindingAdapter (DataBinding을 위한 어댑터 설정)

package com.example.databindingrepositoryapp

import android.widget.ProgressBar
import androidx.databinding.BindingAdapter

@BindingAdapter(value =["app:progressScaled", "android:max"], requireAll = true)
fun setProgress(progressBar: ProgressBar, counter:Int, max: Int){
    progressBar.progress = (counter * 2).coerceAtMost(max)
}

 

7. MainViewModel

package com.example.databindingrepositoryapp

import androidx.lifecycle.*

class MyViewModel(
    _counter : Int,
    private val savedStateHandle: SavedStateHandle,
) : ViewModel(){
    var counter : Int = savedStateHandle.get<Int>(SAVE_STATE_KEY) ?: _counter

    val liveCounter : MutableLiveData<Int> = MutableLiveData(_counter)
    val modifiedCounter : LiveData<String> = Transformations.map(liveCounter) {
        counter -> "${counter} 입니다"
    }

    val hasChecked : MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)

    fun saveState(){
        savedStateHandle.set(SAVE_STATE_KEY, counter)
    }

    companion object{
        private const val SAVE_STATE_KEY = "counter"
    }
}

 

8. MainViewModelFactory

package com.example.databindingrepositoryapp

import android.os.Bundle
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.savedstate.SavedStateRegistryOwner

//
//@Suppress("UNCHECKED_CAST")
//class MyViewModelFactory(private val counter : Int) : ViewModelProvider.Factory {
//    override fun <T : ViewModel> create(key:String, modelClass: Class<T>, handle : SavedStateHandle): T {
//    }
//}

class MyViewModelFactory(
    private val counter : Int,
    owner : SavedStateRegistryOwner,
    defaultArgs: Bundle?= null
    ) : AbstractSavedStateViewModelFactory(owner, defaultArgs){
    override fun <T : ViewModel?> create(
        key: String,
        modelClass: Class<T>,
        handle: SavedStateHandle
    ): T {
        if(modelClass.isAssignableFrom(MyViewModel::class.java)){
            return MyViewModel(counter, handle) as T
        }
        throw IllegalAccessException("ViewModel Class not Found")
    }

}

 

 

결과

'안드로이드 > 정리(Android)' 카테고리의 다른 글

Android) WorkManager 워크매니저  (2) 2022.09.11
안드로이드 HTTP 통신  (2) 2022.09.09
Android - Hilt(의존성주입)  (0) 2022.08.15
[Android] ViewModel 알아보기  (0) 2022.08.04
Android - CustomView 만들기  (0) 2022.05.12