Android/Android

View Binding 사용해보기

donghune 2020. 11. 5. 17:38

 

Kotlin 1.4.20 버전에서 Kotlin Android Extensions Compiler 플러그인이 Deprecated 됬다

왜 Kotlin Android Extensions Compiler 플러그인을 Deprecated 했을까?

  1. 글로벌 네임스페이스로 노출이 되어 현재 Activity 에서 사용중이지 않은 View 를 참조 할 수 있다.
    이에 Null Safty 하지 않다.
  2. 오직 Kotlin 언어로만 접근 및 사용이 가능하다.

그러면 무엇으로 대체를 해야 할까?

  • View Binding
    • View Binding 은 Binding 뿐만 아니라 View Lookup 에도 권장되지만 Android Kotlin Extensions와 비교할 때 오버헤드가 약간 추가된다.
    • Kotlin Extensions 와 비교했을 때, View Lookup 과 타입 안전성의 컴파일 시간 체크가 추가되었다.
  • findViewById
    • 예전부터 계속 해오던 방식

findViewById를 계속 쓰기에는 매우 unpretty 하다. 그래서 ViewBinding 을 한번 써보도록 하겠다.


목차


  1. 뷰 바인딩은 무엇인가?
  2. 뷰 바인딩 준비하기
  3. 액티비티에서 뷰 바인딩 사용하기
  4. 프래그먼트에서 뷰 바인딩 사용하기
  5. Include 태그에서 뷰 바인딩 사용하기

뷰 바인딩은 무엇인가?


  • 뷰 바인딩은 View 와 상호 작용하는 코드를 보다 쉽게 작성할 수 있는 기능이다.
  • 모듈에서 뷰 바인딩을 활성화하면 해당 모듈에 있는 각 XML 레이아웃 파일에 대한 바인딩 클래스를 생성한다.
  • 바인딩 클래스의 인스턴스는 해당 레이아웃에 ID가 있는 모든 뷰에 대한 직접 참조를 포함한다.
  • 뷰 바인딩은 뷰가 XML 있는지 탐지하고 @Nullable 속성을 생성하기에 Null-safe 하다.
  • View Binding은 Java 및 Kotlin과 함께 작동한다.

무엇보다 가장 큰 장점은 NullSafty 하다는 소리인데 예를 들어 이런 경우를 생각해 보도록 하자.

ex ) RegisterActivity, LoginActivity 에 edit_password 라는 뷰를 둘다 가지고 있을 때, 둘중 하나의 액티비티에서 참조를 하려면 아래와 같이 뜰 것이다.

물론 잘못쓰면 다시 고치면 되지만 매번 실수를 할 수 있는거고 이러한 문제는 런타임에서 나타난다는 것이다.

이러한 문제점은 버그를 수정해서 기쁘다는 표정으로 빌드를 하고 테스트하다가 죽는 상황이 발생한다. ( 히히... )

뷰 바인딩 준비하기


그러면 뷰바인딩을 한번 사용해 보도록 하자

우선 build gralde 파일에 아래와 같이 추가해주도록 하자 그러면 이제 끝이다.

android {
        buildFeatures {
        viewBinding true
      }
}

만약에 Binding 클래스를 생성하기 싫은 레이아웃은 아래와 같이 viewBindingIgnore 를 true 해주면 된다.

<LinearLayout 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:viewBindingIgnore="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

<LinearLayout>

추가적으로 테스트를 하기 위해 아래와 같이 코드를 구성하였다.

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

    <TextView ... />

    <ImageView ... />

    <Button ... />

</LinearLayout>

액티비티에서 뷰 바인딩 사용하기


뷰 바인딩을 활성화 하기위해서는 onCreate() 에서 아래의 2가지를 진행 해주면 된다.

  1. 생성된 바인딩 클래스에 포함되어있는 정적 함수인 inflate 를 호출해줘서 생성된 바인딩 클래스를 해당 액티비티에서 사용하도록 한다.
  2. setContentView에 위에서 생성한 binding 객체로 부터 getRoot() 나 코틀린 문법을 통해 root 객체를 얻어 넣어주면 된다.
class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }

}

binding 변수에 접근을 하면 아래와 같이 아까 레이아웃에서 설정한 3개의 뷰 컴포넌트들이 보인다.

무려 Nullable 하다! 즉, 안전하다는 소리다.

사용은 엄청 간단하다. 그냥 평소에 하던대로 사용을 하면 된다.

binding.button.setOnClickListener {
    binding.textView.text = "Hello?"
    binding.imageView.setImageDrawable(
        resources.getDrawable(
            R.drawable.ic_launcher_foreground,
            null
        )
    )
}

프래그먼트에서 뷰 바인딩 사용하기


프래그먼트를 하나 생성하고 바로 바인딩 객체를 생성해 주도록 하자

private lateinit var binding: FragmentSubMainBinding

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = FragmentSubMainBinding.inflate(layoutInflater, container, false)
    return binding.root
}

바인딩 객체를 생성하는 거 자체는 액티비티와 다를 것이 없다.

굳이 뽑자면 프래그먼트는 onCreateView에서 뷰 객체를 생성하는 것이다.

override fun onStart() {
  super.onStart()
  binding.button1.setOnClickListener {
      Toast.makeText(this@SubMainFragment.context, binding.button1.text, Toast.LENGTH_SHORT)
          .show()
  }
    // ...
}

onStart 에서 바인딩 객체를 통해 리스너 및 부가적인 작업을 해주면된다.

Include 태그에서 뷰 바인딩 사용하기


Include 태그로 삽입 된 뷰의 경우는 더욱이 쉽게 바인딩이 가능하다.

우선 아래의 코드를 프래그먼트나 액티비티에 삽입해준다.

<include
    android:id="@+id/layout_include"
    android:layout_width="600dp"
    android:layout_height="600dp"
    layout="@layout/include_layout"/>

필자는 프래그먼트 객체에 넣어보았고 프래그먼트 클래스로 가서 아래와 같이 변수 선언 및 초기화를 해주면 된다.

private lateinit var binding: FragmentSubMainBinding
private lateinit var includeBinding: IncludeLayoutBinding

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = FragmentSubMainBinding.inflate(layoutInflater, container, false)
    includeBinding = binding.layoutInclude
    return binding.root
}

끝.

마무리


간단하게 ViewBinding을 사용해 보았다 이렇게 간단하게 사용이 가능하고 Android Kotlin Extension 을 사용 하면서 nullable 하게 접근하지 않아도 되고 findViewByID를 사용하지 않아도 된다.

다들 뷰 바인딩 하자.