
1. 뷰 바인딩
1-1. 라떼는 말이야...
1-2. 변천사
1-3. findViewById와의 차이점
2. 사용법
2-1. gradle 추가
2-2. 액티비티
2-3. 프래그먼트
2-4. viewBindingIgnore
1. 뷰 바인딩
1-1. 라떼는 말이야...

예를 들어, xml에서 이렇게 텍스트뷰를 3개 쓰고 있다고 치자
public class MainActivity extends AppCompatActivity { private TextView nameView, phoneView, addressView; // 변수 선언하고 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); nameView = findViewById(R.id.name); // findViewById로 id를 찾아서 넣어준다. phoneView = findViewById(R.id.phone); addressView = findViewById(R.id.address); } }
예전에는 액티비티에서 텍스트뷰의 값을 변경하거나 뭔가 작업을 하려면
이렇게 변수를 선언하고, findViewById를 이용해서 xml의 뷰와 변수를 연결시켜주는 그런 작업을 해야 했다.
이게 한 두 개 정도는 상관없다. 근데 한 페이지에 들어가는 뷰가 좀 많아야지...
덕분에 코드가 항상 길어지고 더러워졌다.
(빙고 게임 만들기 위해서 버튼 25개 추가하는 상상을 해보자)
view binding은 이 findViewById를 대체할 수 있는 기능이다.
1-2. 변천사

3.5 버전까지는 개발자들이 findViewById를 사용했었다.
Butter knife라는 라이브러리나 extension을 이용해서 불편함을 해결했다고 한다.
그러다가 3.6 버전에서 이를 대체할 수 있는 view binding이 나오게 된다.
코틀린에서는 이 뷰 바인딩 작업조차 안 하고
id를 바로 변수처럼 사용할 수 있는 Kotlin Synthetic이 생겼다.
하지만 다음과 같은 문제가 있어 4.1 버전에서 Kotlin Synthetic은 deprecated 되었다.
- 전역 네임 스페이스가 오염된다.
- 개발자가 실수로 다른 레이아웃의 동일한 id를 가진 뷰를 가져오면서 NullPointException이 발생할 가능성이 있다.
- Kotlin만 지원이 가능하다.
현재는 자바와 코틀린 둘 다 view binding을 사용한다.
findViewById도 사용할 수 있지만, 속도도 느리고 안전하지 못하다.
Kotlin Synthetic이 deprecated 된 이유는 이 해외 블로그를 참고했다.
1-3. findViewById와의 차이점

뷰 바인딩을 사용하면 직접 id를 적고 타입을 정하고 이런 작업을 하지 않아도 된다.
자동으로 클래스 파일을 생성해주기 때문이다.
그에 비해 findViewById의 문제점은 다음과 같다.
- Null 안정성: 개발자가 실수로 유효하지 않은 id를 사용하면 null 오류가 발생할 수 있다.
- Type 안정성: textView의 타입을 imageView라고 잘못 적어서 캐스팅하면 cast exception이 발생할 수 있다.
- 속도가 상대적으로 느리다.
2. 사용법
아래 내용들은 Android Developer 공식문서를 참고하였다.
2-1. gradle 추가
// 안드로이드 스튜디오 4.0 이상 android { ... buildFeatures { viewBinding = true } }
사용하고 있는 안드로이드 스튜디오 버전이 4.0 이상이라면 이렇게
// 안드로이드 스튜디오 3.6 ~ 4.0 android { ... viewBinding { enabled true } }
이하라면 이렇게 적어주면 된다.
(3.6 보다 낮으면 안 됨)
2-2. 액티비티
public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); // 1 setContentView(binding.getRoot()); // 2 } private void updateUI(UserProfile userProfile){ binding.name.setText("아이유"); // 3 binding.phone.setText("010-1111-2222"); binding.address.setText("사랑시 고백구 행복동"); } }
1. [목차 1-2]에서 보여주었던 클래스를 만들기 위하여 위와 같이 적어준다. inflate는 xml에 있는 뷰를 객체화해준다고 생각하면 된다.
2. 원래는 R.layout.activity_main을 넘겨주지만 이번에는 우리가 생성한 루트 뷰를 넘겨준다.
3. 바인딩된 객체 안에 있는 name, phone, address에 접근하여 사용하면 끝!
사용하기도 간단하고 코드도 깔끔해진 것을 볼 수 있다.
Activity 이름 | Binding Class 이름 |
MainActivity | ActivityMainBinding |
HelloActivity | ActivityHelloBinding |
XXXActivity | ActivityXXXBinding |
바인딩 클래스 이름은 규칙이 정해져 있다.
마치 자바 파일 이름에 맞는 xml 파일이 생성되듯이..
규칙에 맞게 사용해주자.
private lateinit var binding: ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.textView.text = "안녕" } }
코틀린은 이런 식으로 작성해주면 된다.
2-3. 프래그먼트
class BlankFragment : Fragment() { private FragmentBlankBinding binding; @Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentBlankBinding.inflate(inflater, container, false); View view = binding.getRoot(); return view; } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } }
프래그먼트는 위와 같이 하면 된다.
다른 건 다 똑같은데 다른 점이 onDestroyView에서 binding에 null을 집어넣어 준다는 것이다.
액티비티에서는 안 하던 짓을 왜 하지? 싶었는데 공식문서에 답이 적혀있었다.
Fragments outlive their views. Make sure you clean up any references to the binding class instance in the fragment's onDestroyView() method.
프래그먼트는 뷰보다 더 오래 살아남는다고 한다. (오..? 처음 알았다. 프래그먼트 생명 주기도 다시 공부해야지)
바인딩 클래스는 뷰에 대한 참조를 가지고 있는데
뷰가 제거될 때(=onDestroyView) 이 바인딩 클래스의 인스턴스도 같이 정리해주는 것이다.
class BlankFragment : Fragment() { private var _binding: FragmentBlankBinding? = null // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentBlankBinding.inflate(inflater, container, false) val view = binding.root return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.textView.text = "안녕" } override fun onDestroyView() { super.onDestroyView() _binding = null } }
코틀린의 경우 이렇게 작성해주면 된다.
2-4. viewBindingIgnore
<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:viewBindingIgnore="true" // 뷰 바인딩 클래스 생성을 안하고 싶을 때 tools:context=".HelloActivity"> </androidx.constraintlayout.widget.ConstraintLayout>
공식문서에서 viewBindingIgnore 속성을 사용하면 바인딩 클래스를 생성 안 한다길래
액티비티에서 바인딩 클래스를 사용해야 생성되는 거 아닌가?
사용 안 하면 되는 거 아녀? 싶었는데
바인딩 클래스는 레이아웃 별로 무조건 생성되는 거고
만약 이 레이아웃은 바인딩 클래스가 필요 없다! 싶으면 저 속성을 루트 뷰에 추가해주면 된다.
그렇다면 데이터 바인딩은 무엇일까? 공부한 내용을 이곳에 적어두었다.
그 외 참고한 김초희님 블로그, 알면 쓸모있는 개발지식님 블로그. 감사합니다 :D
'오늘은 뭘 배울까? > Android' 카테고리의 다른 글
안드로이드 Clean Architecture (2) | 2021.03.07 |
---|---|
코틀린 데이터 바인딩(Data binding) (0) | 2021.03.06 |
[Kotlin] 뷰페이저2 활용 예제 : tabLayout, indicator, fragment, 자동 스크롤, 무한스크롤, 배너 등 (13) | 2021.02.27 |
코틀린 viewPager2 : 사용법, 애니메이션 등 (50) | 2021.02.24 |
안드로이드 액티비티 생명주기 (Activity Life cycle) (10) | 2021.02.23 |
댓글