[Android] ViewBinding과 findViewById

neoneoneo·2024년 4월 3일
0

android

목록 보기
10/16

안드로이드 개발을 처음 배울 때에는 findViewById의 사용법을 먼저 배웠으나, 찾아보니 2021년 정도 이후로 구글쪽에서는 ViewBinding의 사용을 권장하고 있는 것 같다. (왜??)

이번 글에서는 findViewById와 ViewBinding의 공통점과 차이점을 알아보고, 코드상에서 어떻게 작성해야하는지에 대해 정리해본다.

공통점

  1. View 검색 : 둘 다 레이아웃 XML 파일에 정의된 뷰를 검색하고 참조할 수 있다.
  2. UI 요소에 접근 : 둘 다 UI 요소를 프로그래밍 방식으로 조작하고 속성을 설정할 수 있다.

차이점

findViewByIdViewBinding
Type safety뷰 검색 시, 해당 뷰 유형을 명시적으로 캐스팅해야하므로 런타입 오류의 가능성이 있다.뷰바인딩 클래스를 사용하여 뷰를 검색하므로 잘못된 타입의 뷰에 접근하는 오류를 방지한다.
Null safety검색한 뷰가 존재하지 않을 경우 null을 반환할 수 있어 null 체크가 필요하다.뷰바인딩 안에는 레이아웃에 정의된 뷰가 담겨져 있으므로 null을 반환할 일이 없다.
성능뷰를 검색할 때마다 뷰 계층 구조를 탐색해야한다.바인딩 클래스를 사용하여 뷰를 직접 참조하므로 더 효율적이다.
레이아웃 XML 파일과 바인딩 클래스 생성XML 파일과 별도로 뷰를 연결할 코드를 작성해야한다.컴파일러가 자동으로 XML 파일을 기반으로한 바인딩 클래스를 생성하므로 작성할 코드가 줄어든다.

차이점을 살펴보고나니, 왜 구글에서 ViewBinding의 사용을 권장했는지 알 것 같다. 실제로 코드에서 어떤식으로 작성해야하는지도 살펴보자.

코드 작성

findViewById

findViewById<뷰 타입>(뷰 아이디)

<FrameLayout
    android:id="@+id/btn"
    android:layout_width="80dp"
    android:layout_height="30dp"
    android:layout_marginStart="16dp"
    android:background="@drawable/mainpage_fl_btn_rec"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/myPage"
        android:textSize="15sp"
        android:textStyle="bold" />
</FrameLayout>

이렇게 생긴 FrameLayout이 있다면, 이 요소를 버튼으로 사용하기 위해 kotlin 파일에는 아래와 같이 작성해줘야 한다.

val myPageBtn = findViewById<FrameLayout>(R.id.btn)
myPageBtn.setOnClickListener {
    goMyPage()
}

작성하기에는 간단하여 마구 사용했지만,,, 솔직히 쓰다가 null 오류가 날 때가 종종 있었다. 보통은 id의 이름을 잘 못 썼거나 했을 때에 null 오류가 일어났었다.

ViewBinding - activity에서 사용하기

build.gradle.kts 수정하기

아래 내용을 작성해줘야 한다.

android {
    //이것저것다른것들
    viewBinding {
        enable = true
    }
}
  • 이렇게 하면, 모듈에 포함된 각 XML 파일에 대한 바인딩 클래스가 생성된다.
  • 각 클래스의 이름은 XML 파일 이름을 파스칼 표기법으로 바꾸고 끝에 Binding을 추가한 것이다.
    • activity_main.xml -> ActivityMainBinding

즉, 내가 XML에 그려놓은 모든 요소에 대한 주소 값을 담고 있는 주소록이 내 손에 쥐어지는 것이다..! findViewById의 방식이 모든 집을 다 돌아다니면서 "여기가 neo씨네 집인가요?" 문을 두들기는 방식였다면, ViewBinding은 주소록을 보고 "음 neo씨네 집은 00아파트 00호군" 이러고 바로 해당 주소로 찾아가는 것이다. 세상 편하다~

그 이후 코드 상에서 사용하면 되는데, 이때 기억해야 하는 것들이 있다.

onCreate()밖에서 뷰 바인딩 변수를 선언할 것

private lateinit var binding: MainActivityBinding

onCreate()안에서 뷰 바인딩 클래스의 인스턴스를 생성할 것

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = MainActivityBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}
  • inflate() 메서드를 호출하면 MainActivityBinding 클래스 인스턴스가 생성된다.
  • binding.root로 해당 바인딩의 root 뷰, 즉 그려놓은 activity_main.xml 뷰의 참조 값을 가져온다.
  • 해당 값을 setContentView()안에 넣어주어 화면의 활성 뷰로 만든다.

만들어놓은 바인딩 변수를 사용하여 뷰 요소를 참조하고 사용한다.

binding.btn.setOnClickListenet { goMyPage() }

ViewBinding - fragment에서 사용하기

마찬가지로 build.gradle.kts를 수정해준다.

android {
    //이것저것다른것들
    viewBinding {
        enable = true
    }
}

onCreateView() 밖에서 뷰 바인딩 변수를 선언할 것

private var _binding: MainFragmentBinding? = null
private val binding get() = _binding!!
  • 변수를 두 개 선언했는데, _binding은 이 프래그먼트가 사용되지 않았을 때 자원을 바로 반환해줄 수 있도록 준비해두는 역할이라고 한다.
  • 실제로 코드상에서 요소들에 대해 접근할 때 사용할 변수는 binding이 되겠다.

onCreateView() 안에서 뷰 바인딩 클래스의 인스턴스를 생성하여 사용할 것

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = MainFragmentBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}
override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}
  • _binding에 .inflate()하여 뷰바인딩 클래스를 생성해주면, binding에서 get()으로 해당 인스턴스를 가져가게 된다. 그래서 코드 상에서는 binding으로 각 요소에 접근할 수 있게 된다.
  • inflate를 보면 activity에서의 사용법과 조금 다른 부분들이 있는데, inflate 안에 들어가는 매개변수들이다. 기본적인 패턴인 것 같으니 자주 쓰게 되면 외워두는 것이 좋겠다.
  • onDestroyView()에서 뷰바인딩을 null로 두는 부분이 있다.
    • 프래그먼트가 뷰보다 오래 지속되므로, 프래그먼트를 날릴 때 뷰바인딩에 대한 참조를 해제하는 것까지 해야 깔끔하게 정리가 될 것 같다.

reference


[TIL-240403]

0개의 댓글