Android Binding (ViewBinding, DataBinding)

지훈·2021년 12월 31일
3
post-thumbnail

ViewBinding

ViewBinding을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있다 View Binding은 각 XML 레이아웃 파일의 Binding 클래스를 생성한다. Binding 클래스의 인스턴스에서 레이아웃에 ID가 있는 모든 뷰의 직접참조를 할 수 있다.

즉, 간단하게 findViewByID를 쓰지 않고 XML의 view component에 접근하는 object를 반환받아서 view에 접근하는 방식이다. 여기서 object는 Android Studio에서 자동으로 대신 만들어준다.

findViewById 로 참조하려면 만약 코틀린 파일에서 XML파일에 <TextView>에 접근하려고 한다면

val textview  = findViewById<TextView>(R.id.example_tv)
        textview.text = "바꾼 텍스트"

이런식으로 하나하나 변수를 선언해주면서 해야 한다. findViewById는 Null point Exception (Null값을 처리하지 못함)이 발생할 수 있고 참조할 view component가 많다면 코드의 양이 굉장히 많아지기 때문에 번거롭기 때문에 binding 기능을 많이 사용한다.
추가로 View Binding 작업조차 없이 id를 바로 변수처럼 사용할 수 있는 Kotlin Synthetic이 생겼지만 아직 불안정하여 deprecated 되었다고 한다.

방식은

  • build.gradle에서 enable 시킵니다. (추가 libarary 불필요)
  • 모든 layout에 대해서 binding object가 생성됩니다.
  • Binding object는 id를 갖는 모든 view들을 하나의 property로 가집니다.
    • type도 알아서 맞춰서 생성해 주고, findViewById에서 발생할 수 있는 null도 발생하지 않습니다. (null-safety)
  • java, kotlin을 모두 지원한다.

ViewBinding 사용하기

build.gradle 추가

  • 사용하는 Android Studio 버전이 4.0 이상일시
android {
	...
    buildFeatures {
    	viewBinding = true
    	}
    }
  • 사용하는 Android Studio 버전이 3.6 ~ 4.0
android {
	...
    viewBinding{
      enabled true
      }
    }

ViewBinding 옵션을 활성화하면 프로젝트 상의 레이아웃 파일들이 Binding 클래스가 생겨서 각 파일들에 선언했던 뷰들의 id 참조가 클래스에 포함된다.

Activity에서 ViewBinding 사용

  • activity_main_xml
  • 두개의 TextView (id 가 example1, example2)
  • 하나의 Button (id : button)

그렇다면 아래와 같이 사용 가능

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)     
	val binding = ActivitymainBinding.inflate(layoutInflater)
	binding.example1.text = "Hello"
	binding.example2.text = "Concise, safe code"
	binding.button.setOnClickListener { /* ... */ }      
    	setContentView(binding.root) } 

위 코드를 보면 각 view의 id 가 속성으로 제공된다.

xml 전체를 감싸는 최상단의 부모를 root라는 속성으로 제공한다. 따라서 setContentView의 인자로 넘겨주어 사용할 수 있다.

setContentView의 인자로 root를 사용하는 것은 필수이다!!!

만약 viewBinding도 사용하고 setContentView에 기존의 방식대로 R.layout.activityMain 을 넘겨준다면 두번의 layout inflate를 하게 된다. inflate 되는 root와 layout object는 서로 다른 객체이므로 각각 다른 view를 가리킨다. -> 원하는 동작이 일어나지 않는다.

즉, viewBinding 사용시 반드시

setContnetView(binding.root)

Fragment에서 ViewBinding 사용

프래그먼트에서 viewBinding을 사용하려면 프래그먼트의 onCreateView() 메서드에서
1. 생성된 Binding 클래스에 포함된 inflate() 메서드를 호출한다. 그러면 프래크먼트에서 사용할 binding 클래스 인스턴스가 생성된다.
2. getRoot() 메서드를 호출해서 root View 참조를 가져온다.
3. onCreateView() 메서드에서 root View를 리턴해서 화면 활성 뷰로 만든다.

class LockerFragment : Fragment() {

    private var _binding: FragmentLockerBinding? = null	// 처음에 null로 값 할당해주기
    
    private val binding get() = _binding!! // 이 binding은 onCreateView와 onDestroyView 사이에서만 유효하다.

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentLockerBinding.inflate(inflater, container, false)
        val view = binding.root
        return view
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textView11.text = "안녕"
    }
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

처음에 _binding을 null로 할당해주는 것과 onCreateView와 onDestroyView 사이에서만 유효한 binding을 따로 선언해주는 것, 그리고 ondestroyView에서 _binding을 다시 null로 초기화해주는 이유는 Fragment의 생명주기 때문이다. 프래그먼트는 뷰보다 더 오래 살아남는다. Binding 클래스는 뷰에 대한 참조를 가지고 있는데 뷰가 제거될 때(=onDestroyView) 이 Binding 클래스의 인스턴스도 같이 정리해 주는 것이다.
자세한 내용은 프래그먼트의 생명주기를 공부할 때 다시 Remind하기로 하자.

What code does it generate?

위 이미지를 클릭하면 자세한 설명을 볼수 있다. (출처 https://tourspace.tistory.com/314)

위 링크에서 실제 Viewbinding 옵션을 활성화하면 안드로이드 스튜디오 내의 어떤 코드가 활성화되면서 실행되는지를 알 수 있다.

각 binding object에는 세개의 static function이 존재한다.

  • inflate(inflater) : Activity의 onCreate 에서 사용하며, parent view로 넘길 것이 없을 경우 사용한다.
  • inflate(inflater, parent, attachToParent) : Fragment나 RecyclerView의 Adapter(혹은 ViewHolder)처럼 parent ViewGroup을 넘겨야 하는 경우 사용한다.
  • bind(rootView) : 이미 view를 inflate한 상태에서 findViewById를 피하고 싶거나 기존 코드를 refactoring 할 때 유용하게 사용할 수 있다.

View Binding 마무리

  • Null 안정성 : 개발자가 실수로 유효하지 않은 id를 사용했을 때 null 오류가 방지
  • Type 안정성 : TextView의 타입을 잘못 적어서 캐스팅하면 cast exception이 발생할 수 있다.
  • 속도가 findViewById 보다 빠르다.

viewBinding은 findViewById를 대체하기 위한 방법으로만 사용된다. 따라서 기존에 제공되는 dataBinding과 viewBinding을 같은 모듈에서 동시에 사용할 수도 있다.
(viewBinding은 dataBinding을 사용하지 않고 간단한 사용을 위한 요청으로 인해 개발되었다고 한다.)

DataBinding

bind : 결합하다, 묶다. DataBinding : 데이터 묶기(결합)

UI 요소와 데이터를 프로그램적 방식으로 연결하지 않고, 선언적 형식으로 결합할 수 있게 도와주는 라이브러리를 말한다.

프로그램적 방식 vs 선언적 방식

레이아웃은 흔히 뷰가 있는 XML 파일과 연결된 액티비티에서 정의된다. 예를 들어 아래 코드는 findViewById()를 호출해서 TextView 위젯을 찾아 viewModel 변수의 userName 속성에 결합한다.

  • TextView에 문자열을 넣기 위해 코드상에서 값을 집어넣는 작업(프로그램적)
// findViewById 방식
val textview = findViewById<TextView>(R.id.textview11)
textview.text = "안녕"
// ViewBinding 방식
binding.textview11.text = "안녕"
    
  • XML에 코드를 직접 집어넣어서 해결하는 방법(선언적)
<TextView
	android:layout_width="match_parent"
    	android:layout_height="wrap_content"
        android:text="@{viewmodel.userName}" />
        

선언적 방식 -> 액티비티에는 로직만을 위한 코드만 남고 뷰와 관련된 작업은 레이아웃 파일에 정의된다.
즉, 데이터와 뷰를 연결하는 작업을 레이아웃에서 처리하는 기술을 dataBinding이라고 부른다.

DataBinding 사용하기

build.gradle 추가

  • Android Studio 4.0 이상
// Android Studio 4.0 이상
adroid{
	...
    buildFeatures {
    	dataBidning = true
        }
}
  • Android Studio 3.6 ~ 4.0
// Android Studio 3.6 ~ 4.0
adroid{
	...
    dataBinding {
    	enabled true
        }
}

Activity에서 DataBinding 사용

액티비티에서 DataBinding을 사용하기 위해서는

  1. 먼저 app/build.gradle에 dataBinding 요소를 추가한다.
  2. XML 파일에 <layout> 태그 안에 XML 레이아웃을 작성해야 한다.
<layout :android="http://schemas.android.com/apk/res/android"
   ...>
        <androidx.drawerlayout.widget.DrawerLayout
        ...
            android:id="@+id/drawer">

            <TextView
                android:id="@+id/primary_tv"
                ...
                android:text="PRIMARY CONTENTS"/>
                ...
        </androidx.drawerlayout.widget.DrawerLayout>
</layout>
  1. Activity 에서 기존의 setContnetView() 함수를 DataBindingUtil.setContentView()로 교체한다.
  1. View에 보여줄 data class를 선언한다.
    data객체를 view와 binding하기 위해서 Data11이라는 data class를 만들었다.
data class Data11(

    val data1 : String = "",
    val binding1 : String = ""
)
  1. layout에서 <data> 태그 안에 <variable> 태그를 추가한다. 이는 데이터와 뷰를 연결하는 태그이다.
  • name -> 사용할 이름,
  • type -> 연결하고 싶은 데이터가 있는 경로
<layout :android="http://schemas.android.com/apk/res/android"
    ...>

    <data>
        <variable
            name="data11"
            type="com.example.drawerlayout.Data11" />
    </data>

        <androidx.drawerlayout.widget.DrawerLayout
            ...>

            <TextView
                ...
                android:text="@{data11.data1}"
               	/>

            <TextView
                android:text="@{data11.binding1}"
                />
        </androidx.drawerlayout.widget.DrawerLayout>

</layout>

위처럼 <data> 태그를 추가하고 그 아래에 우리가 사용하던 layout을 넣어주면 된다.
위 소스의 경우 'data11'를 통해 데이터클래스 'Data11' 의 데이터에 접근이 가능하다 .변수를 사용할 때는 @{'XML에서 선언한 이름'.'액티비티에서 선언한 데이터 태그'} 로 입력하면 된다.

사실 viewModel 과 LiveData와 많이 쓰인다.

@{} 구문 안에는 간단한 식을 넣을 수도 있다.

  1. Activity에서 앞에서 정의한 XML의 <data><variable>의 name을 binding하여 데이터 클래스 Data11의 데이터를 가져온다. (Activity에서 data11 객체를 초기화한다.)
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.data11 = Data11("data", "binding")
        // data11은 activity_main.XML에서 선언한 <data>의 <variable> 의 name
    }
}


이제 Activity에서 DataBinding을 사용해 DataClass의 데이터를 변경할 수 있고 그에 따라 XML이 한 번에 바뀐다.

DataBinding 사용 이유

  • xml에 데이터를 binding 하여 불필요한 코드를 줄일 수 있다. (보통 MVVM 패턴을 구현할 때 사용
    예시에서 단순히 두개의 TextView의 text를 binding 했지만 실제 사용할 때 DTO나 데이터 클래스에 연결된 클래스 변경시 연결된 많은 View가 한번에 변경되기 때문에 굉장히 편해진다.
    또 DataBinding과 같이 BindingAdapter를 이용해 ImageView에 이미지 로딩 라이브러리를 이용해서 이미지 출력을 쉽게하고, LiveData를 사용하면 Data가 실시간으로 변경될 때 View도 같이 변경되어 MVVM 패턴 구현 시 편리해진다.

  • Databinding을 사용하면 findViewById()를 쓰지 않아도 xml에 만든 View들을 자동으로 만들어 준다.

  • Data가 바뀌면 알아서 바뀐 Data로 View를 변경하게 할수도 있다. (옵저블 사용시)

  • RecyclerView에서 각각의 item을 세팅 해주는 작업도 XML에서 다 써주면 알아서 값이 들어간다.

  • 구글에서 DataBinding 사용을 권장함.

DataBinding Vs ViewBinding

DataBinding은 ViewBinding의 역할도 수행. 추가로 동적 UI 콘텐츠 선언, 양방향 데이터 binding도 지원한다.

ViewBinding이 DataBinding보다 퍼포먼스 효율이 좋고 용량이 절약되는 장점이 있다.
단순히 findViewById를 대체하기 위해 Binding을 쓴다면 ViewBinding 사용 권장.

출처:
https://tourspace.tistory.com/314
https://todaycode.tistory.com/29
https://developer.android.com/topic/libraries/view-binding?hl=ko#fragments
https://velog.io/@jojo_devstory/Android-Databinding%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

profile
안드로이드 개발 공부

2개의 댓글

comment-user-thumbnail
2022년 11월 23일

글너 너무 잘봤습니다!
감사합니다!

답글 달기
comment-user-thumbnail
2024년 5월 2일

정말 깔끔한 정리네요!

답글 달기