본문 바로가기

Dev/[Android]

[Android Kotlin] View Binding 과 Data Binding

반응형

 

 

INTRO


 

Android Kotlin에서 

일반적으로 View를 제어하는 방법과,

이를 보다 편리하게 해주는 View Binding을 사용하는 방법

그리고 조금 더 발전한 DataBinding을 사용하는 방법에 대해 포스팅한다.

 

 


 

 

0. 준비

우선 아래와 같은 레이아웃을 만들고, TextView, EditText, Button을 하나씩 배치했다.

버튼을 클릭할 때 EditText에 있는 내용을 TextView로 Set 하는 기능을 3가지 방법으로 만들어보고자 한다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="268dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="48dp"
        android:layout_marginTop="168dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="320dp"
        android:layout_height="52dp"
        android:layout_marginTop="80dp"
        android:ems="10"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>


 

 

 

 

1. 기초적인 방법으로 제어하기

아래와 같이 findViewById 메서드를 이용하여 View객체를 얻어온 후, 해당 View의 get/set메서드를 호출하여 제어하는 방법이다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var textView = findViewById<TextView>(R.id.textView1)
        textView.text = "this is TextView"

        findViewById<Button>(R.id.button1).setOnClickListener{
            val editText : String = findViewById<EditText>(R.id.editText1).text.toString()
            findViewById<TextView>(R.id.textView1).text = editText
        }
    }

 

여기서 이렇게 매 번 findViewById 메서드를 호출하는 것이

1. 불편하기도 하고,

2. View 내부를 전체 순회하여 속도가 느리며,

3. Null Safe 하지 못하다는 단점이 있다고 한다.

 

이를 보완하고자 ButterKnife 라는 라이브러리 출시되었으나, 이 또한 쓸데없는 코드가 많이 발생함. 

@BindView(R.id.textView1) val textView : TextView

 

이후 kotlin-android-extension 이라는 기능이 추가되어, 아래와 같이 gradle에 추가하고, MainActivity에서 import 하면 findViewById를 사용하지 않아도 되었다. 내부 캐싱을 통한 재사용성도 높아졌다고 한다.

plugins {
    id 'kotlin-android-extensions'
}
textView1.text = "this is TextView"

 

하지만 여기에도 단점이 존재했으니..

  • null-safe가 제한적으로 적용되며,
  • 같은 ID를 가진 다른 레이아웃을 참조할 수도 있고, 
  • kotlin에서만 사용할 수 있다.

이로 인해 kotlin 1.4.20부터는 deprecate 되었다.

 

위 단점들을 보완하고자 나온것들이 ViewBinding과 DataBinding이다.

이 2가지는 현재까지 활발히 사용되며, 각각의 특징이 있다.

 


 

 

2. ViewBinding을 통해 제어하기

build.gradle 에 아래와 같이 추가한다.

android {
    .
    .
    .
    
    // Binding 설정
    buildFeatures{
        viewBinding true
    }
}

 

MainActivity.kt 에 아래와 같이 작성한다.

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.textView1.text = "this is TextView"

        binding.button1.setOnClickListener{
            binding.textView1.text =binding.editText1.text
        }
    }
}

위의 [기초적인 제어] 코드와 비교했을 때, 사용 방법이 보다 명확해지고 간결해진 것을 볼 수 있다.

현재 코드는 3개 정도의 View만 있기 때문에 코드량의 차이가 많이 없어보일 수 있으나, 더 많아졌을 때를 생각해본다면..?

 

또한 ActivityMainBinding  = activity_main.xml 이기 때문에 null 위험도 줄어든다.


 

 

3. DataBinding을 통해 제어하기

build.gradle에 아래와 같이 추가한다.

android {
    .
    .
    .
    
    // Binding 설정
    buildFeatures{
        dataBinding true
    }
}

 

MainActivity.kt 를 아래와 같이 ViewBinding처럼 작성할 수도 있다. 

Binding이 된 상태이기 때문이다.

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)

        binding.textView1.text = "this is TextView"

        binding.button1.setOnClickListener{
            binding.textView1.text =binding.editText1.text
        }
    }
}

 

 

하지만 이렇게 사용한다면 DataBinding의 특징을 사용한 것이라고 볼 수 없다.

 

DataBinding의 특징을 좀 더 잘 살려보자

activity_main을 아래와 같이 <layout> 코드로 감싸주고, <data> 태그 선언 후 안에 <variable>태그를 선언한다.

간략하게 설명하자면, MainActivity클래스를 activity라는 이름으로 연결(Binding)해준다.

이후 MainActivity클래스 안에 있는 멤버변수들을 각 View에 @{activity.text1}과 같은 형태로 연결해준다.

주의해서 봐야 할 점은, EditText에는 @={activity.text2} 와 같이 적혀있는 것을 볼 수 있다.

이는 Activity -> XML 방향으로만 바인딩 하는 것이 아니라 Activity <-> XML 과 같이 양방향 바인딩을 한다는 말이다.

EditText에 있는 Text를 가져와 TextView에 다시 set하기 위해 양방향 바인딩이 필요하다.

<?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="activity"
            type="com.example.viewcontrolbindingtest.MainActivity" />

    </data>

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

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="268dp"
            android:text="@{activity.text1}"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="48dp"
            android:layout_marginTop="168dp"
            android:text="Button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <EditText
            android:id="@+id/editText1"
            android:layout_width="320dp"
            android:layout_height="52dp"
            android:layout_marginTop="80dp"
            android:ems="10"
            android:text="@={activity.text2}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

MainActivity.kt에 아래와 같이 작성한다.

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding
    var text1 : String = ""
    var text2 : String = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)

        binding.activity = this@MainActivity

        text1 = "this is TextView"

        binding.button1.setOnClickListener{
            text1 = text2
            binding.invalidateAll()
        }
    }
}

 

여기까지 왔다면 3가지 방법에 대한 감이 올 것이다.

DataBinding은 위와 같이 단독으로 사용될 때 보다는

Data class, ViewModel등과 같이 사용되어 MVVM패턴을 구축할 때 효율적이다.

 

또한 button Click 이벤트도, 별도의 메서드를 만들어서 xml의 button 태그 내 아래와 같이 사용할 수도 있다.

android:onClick="@{() -> activity.onSaveClick()}"

 

 

마무리

여기까지 View를 제어하는 3가지 방법에 대해 알아보았다.

이를 Activity에 사용할때는 위와 같이 사용하면 되지만, Fragment에서는 사용 방법이 또 다르다.

이 방법도 함께 알아보는 것을 추천한다.

 

 

 

-퍼가실 때는 출처를 꼭 같이 적어서 올려주세요!

 

반응형