DataBinding 기초

강민아·2024년 3월 12일
post-thumbnail

DataBinding


📖 programmatic 방식이 아닌 선언적 방식으로 layout의 UI 구성요소를 앱의 Datasource와 결합할 수 있는 지원 Library

he Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically.

DataBinding의 세가지 특징


1) Remove findViewById

MainActivity.kt

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)

        val factory = MyViewModelFactory(100,this)
        val myViewModel by viewModels<MyViewModel>(){factory}
        binding.lifecycleOwner = this
        binding.viewmodel = myViewModel

        binding.textView.text = myViewModel.counter.toString()

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

activity_main.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">
    <data>
        <variable
            name="viewmodel"
            type="com.getupmina.android.databinding.MyViewModel" />
    </data>

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

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewmodel.modifiedCounter.toString()}"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:text="Increase"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

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

2) Custom Binding Adapter

MainActivity.kt

package com.getupmina.android.databinding

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil

import com.getupmina.android.databinding.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 by viewModels<MyViewModel>(){factory}
        binding.lifecycleOwner = this
        binding.viewmodel = myViewModel

        binding.button.setOnClickListener {

            myViewModel.liveCounter.value = myViewModel.liveCounter.value?.plus(1)
        }

    }
}

BindingAdapter.kt

package com.getupmina.android.databinding

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

@BindingAdapter("app:progressScaled")
fun setProgress(progressBar: ProgressBar, counter: Int) {
    progressBar.progress = counter
    //app:progressScaled가 값을 전달받을 떄마다 BindingAdapter에서는 자동으로 setProgress함수가 실행되게 된다.
}
  • 만약 2개 이상의 값을 동시에 관리하고 싶다면..!
    package com.getupmina.android.databinding
    
    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)
    }
    app:progressScaledandroid:max 를 동시에 관찰하면서 변화가 있을 때 progressbar의 증가량을 counter값의 2배로 하되, max값 이상이 되지 않도록 한다.

activity_main.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">

    <data>
        <variable
            name="viewmodel"
            type="com.getupmina.android.databinding.MyViewModel" />
    </data>

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

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Increase"
            android:layout_margin="30dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="30dp"
            android:max="@{100}"
            app:layout_constraintBottom_toTopOf="@+id/textView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintWidth_percent="0.3"
            app:progressScaled="@{viewmodel.liveCounter}" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

3) Two-way Data Binding

  • @을 바인딩은 Data를 View로 보내기만 한다. 하지만 View가 Data를 보내게도 할 수 있다.

MyViewModel.kt

package com.getupmina.android.databinding

import androidx.lifecycle.*

class MyViewModel(_counter: Int, private val savedStateHandle: SavedStateHandle) : ViewModel() {
    var liveCounter: MutableLiveData<Int> = MutableLiveData(_counter)
    var counter:Int = savedStateHandle.get<Int>(SAVE_STATE_KEY)?:_counter
    val modifiedCounter : LiveData<String> = Transformations.map(liveCounter) { counter ->
        "$counter 입니다"
    }
    val hasChecked : MutableLiveData<Boolean> = MutableLiveData<Boolean>(false)

    //null이면 전달받은 초기값을 사용할 수 있도록 설정
    fun saveState(){
        savedStateHandle.set(SAVE_STATE_KEY,counter)
    }

    companion object{
        private const val SAVE_STATE_KEY = "counter"
    } //key를 설정
}

activity_main.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">

    <data>
        <variable
            name="viewmodel"
            type="com.getupmina.android.databinding.MyViewModel" />
    </data>

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

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Increase"
            android:layout_margin="30dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginBottom="30dp"
            android:max="@{100}"
            app:layout_constraintBottom_toTopOf="@+id/textView"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="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" />

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

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

출처


강의

냉동코더의 알기 쉬운 Modern Android Development 입문 강의 - 인프런

출처 : 냉동코더의 알기 쉬운 Modern Android Development 입문

코드

https://github.com/cliearl/book-search-app

Designed and developed by 2022 FrozenCoder

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

썸네일 사진

UnsplashMohamed Nohassi

profile
개발자꿈나무

0개의 댓글