ANDROID/Android 앱 프로그래밍

[Android] 뷰 바인딩(View Binding) in Activity, Fragment, RecyclerView

주 녕 2021. 8. 10. 17:15
728x90

View Binding에 대한 지식은 아래의 이전 포스팅을 참고하자!

 

[지식] Kotlin Android Extensions deprecated

시간이 좀 지난 일이지만 개발 방식을 조금 바꾸게 되어 정리해본다. 안드로이드 4.1 버전에서 새로운 프로젝트 생성 시 기본 플러그인으로 제공하던 apply plugin: 'kotlin-android-extensions'이 제거되고,

junyoung-developer.tistory.com

이번 포스팅은 View Binding을 이용한 구현 방법을 알아볼 예정이다.

Activity, Fragment, RecyclerView, Dialog에서의 사용법을 순서대로 설명하며, 앞으로 여러 화면에서의 예시를 추가할 예정이다!

 

 

View Binding을 사용하기에 앞서 build.gradle 파일에 아래 코드를 추가하여 sync 해야한다.

android {
...
    buildFeatures{
        viewBinding = true
    }
}

 

Activity에서 View Binding 사용하기

  1. 생성된 바인딩 클래스의 inflate() 메서드를 사용하여 Activity에서 사용할 바인딩 클래스의 인스턴스를 생성함
  2. 바인딩 클래스의 getRoot() 메서드를 통해 레이아웃 내 최상위 뷰의 인스턴스를 얻음
  3. setContentView() 메서드에 최상위 뷰의 인스턴스를 넘겨줌
class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        ...
    }
  • root(=xml 전체를 감싸는 최상단의 부모)는 setContentView의 인자로 넘겨주어 사용함 (필수적인 요소)
  • view binding과 기존의 setContentView(R.layout.~) 방식을 사용하면 2번의 layout inflate가 발생 → root layout object는 서로 다른 객체이므로 각각 가리키는 view가 달라짐 (오류)

 

🤷‍♀️ inflate는 어떤 개념일까?

→ 레이아웃 인플레이션에 대한 자세한 설명은 아래의 이전 포스팅 참고!

간단하게 설명하자면 XMl 레이아웃의 내용이 메모리에 객체화 되는 과정을 인플레이션(inflation)이라고 한다!!

 

[Android] 레이아웃 인플레이션 (layout inflation)

모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다. 레이아웃 인플레이션 안드로이드 앱을 개발할 때, 우리는 2가지 파일에 나누어서 개발함. 화면 배치를 알려주는 XML

junyoung-developer.tistory.com

 

 

Fragment에서 View Binding 사용하기

fragment는 뷰에 비해 수명이 더 길다. 바인딩 클래스는 뷰에 대한 참조를 가지고 있으므로, 뷰가 제거될 때 호출되는 onDestroyView() 콜백 내에서 바인딩 클래스의 인스턴스도 함께 정리해야 함.

* DialogFragment를 사용하는 경우에도 동일하게 사용하면 된다.

class HomeFragment : Fragment() {

    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!

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

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

 

onDestroyView()를 사용하는 이유는

  1. Activity와 다르게 Fragment는 2개의 lifecycle이 존재한다.
    • Fragment Lifecycle과 Fragment View Lifecycle이 달라져서 생기는 문제인 것!
    • 기본적으로 Fragment는 Fragment가 가지는 View보다 오래 유지됨
  2. View Binding은 Data Binding과 다르게 lifecycle을 모르기 때문에 Binding 객체를 적절히 메모리에서 초기화 작업을 해야 함
    • 명시적으로 초기화 작업을 하지 않으면 메모리 누수가 발생하게 됨

 

🤔 하지만.. 모든 Fragment에 onDestoryView()를 만들어서 바인딩 인스턴스를 해제하는 것은 너무 번거롭다!

github에 kirich1409님이 솔루션을 올려주셨는데 많은 분들이 이 방법을 사용하고 있는 것 같다..!

 

GitHub - kirich1409/ViewBindingPropertyDelegate: Make work with Android View Binding simpler

Make work with Android View Binding simpler. Contribute to kirich1409/ViewBindingPropertyDelegate development by creating an account on GitHub.

github.com

 

 

RecyclerView에서 View Binding 사용하기

RecyclerView의 Adapter에서 View Binding을 사용하기 위해서는 아래와 같이 적용할 수 있음

onBindViewHolder에서 item의 위젯을 사용하고 싶다면 holder.binding.~로 사용할 수 있다.

class CategoryAdapter() : ListAdapter<>(...){

    // recyclerview의 아이템으로 사용할 레이아웃을 가져옴
    class CategoryViewHolder(val binding: ItemCategoryBinding): RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
        // 여기서 inflate
        val binding = ItemCategoryBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return CategoryViewHolder(binding)
    }

    override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
        val category = getItem(position)
        with(holder.binding) {
            ...
        }
    }

}

 

728x90