안드로이드 공부 3년동안 findViewById()를 쓰면서 레이아웃을 연결했었다.
하지만 오늘 유튜브를 보면서 findViewById를 안쓰고 레이아웃을 연결할 수 있다는 영상을 보고 신기해서 공부를 해보았다.
먼저 쓰는 방법에 대해서 말하자면
참고: 뷰 결합은 Android Studio 3.6 Canary 11 이상에서 사용할 수 있습니다.(안드로이드 공식 문서 발췌)
build.gradle Module 부분에 android{} 부분에 밑에 코드를 넣어준다.
viewBinding{
enabled true
}
간단한 xml을 따라해보았다.
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
UserProfile 클래스를 만든 후 아래와 같이 생성자를 만들어준다.
public class UserProfile {
String name;
String phone;
String address;
}
MainActivity 코드를 아래와 같이 적어준다.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.modern_architecture.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
fetchUserProfile();
}
private void fetchUserProfile() {
UserProfile userProfile = new UserProfile();
userProfile.name = "홍길동";
userProfile.phone = "010-1234-5678";
userProfile.address = "서울특별시 송파구";
updateUI(userProfile);
}
private void updateUI(UserProfile userProfile) {
binding.name.setText(userProfile.name);
binding.phone.setText(userProfile.phone);
binding.address.setText(userProfile.address);
}
}
위와 같은 결과물이 도출된다.
공식문서 기반으로 정리를 하자면
ViewBinding을 뷰 결합이라고 칭하며
뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있다.
import com.example.modern_architecture.databinding.ActivityMainBinding;
...
private ActivityMainBinding binding;
...
위 코드를 보면 ActivityMainBinding을 import를 하게 되면 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성하게 된다.
그러면 자동으로 생성됐다면 어떤 구성으로 되어있길래 findViewById를 안쓰고도 코드를 쓸수있을까?
위와 같이 탐색창 상단에 Android를 Project로 바꿔준다.
위와 같이
app-build-generated-data_binding_base_class_source_out-debug-out-com-example
-modern_architecture-databinding
(modern_architecture 부분은 자신의 프로젝트 이름으로 되어있다.)
순으로 들어가면 ActivityMainBinding.java 파일이 하나 있을거다.
위 파일을 열어보면
// Generated by view binder compiler. Do not edit!
package com.example.modern_architecture.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.example.modern_architecture.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final LinearLayout rootView;
@NonNull
public final TextView address;
@NonNull
public final TextView name;
@NonNull
public final TextView phone;
private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView address,
@NonNull TextView name, @NonNull TextView phone) {
this.rootView = rootView;
this.address = address;
this.name = name;
this.phone = phone;
}
@Override
@NonNull
public LinearLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.address;
TextView address = ViewBindings.findChildViewById(rootView, id);
if (address == null) {
break missingId;
}
id = R.id.name;
TextView name = ViewBindings.findChildViewById(rootView, id);
if (name == null) {
break missingId;
}
id = R.id.phone;
TextView phone = ViewBindings.findChildViewById(rootView, id);
if (phone == null) {
break missingId;
}
return new ActivityMainBinding((LinearLayout) rootView, address, name, phone);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
위와같은 코드가 보일 것이다.
private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull TextView address,
@NonNull TextView name, @NonNull TextView phone) {
this.rootView = rootView;
this.address = address;
this.name = name;
this.phone = phone;
}
이 부분을 봐보면 우리가 XML에서 id로 선언해준 것들이 생성된게 보인다.
그러기에 생성된 결합 클래스를 활용하기 위해 아래와 같이 코드를 작성하게되면
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
자동 생성된 결합 클래스를 활용하여 findViewById 없이 코드를 작성할 수 있게된다.
그러면 findViewById와 차이점은 무엇일까?
공식문서에 따르면 Null 안전과 유형 안전이 있다고 한다.
Null 안전은 개발자가 XML에 선언도 안한것을 쓰려고 하면 유효하지 않은 것이기에 null 포인터 예외가 발생하게 되는데 이러한 것을 뷰결합을 통해 똑똑한 컴퓨터에게 맡겨버리면 오류가 확실히 덜 날것이다.
그리고 유형 안전은 각 바인딩 클래스에 있는 필드의 유형이 XML 파일에서 참조하는 뷰와 일치하기에 클래스 변환 예외가 발생할 위험이 없게된다.
그러면 뷰 결합의 장점이 무엇일까?
더 빠른 컴파일을 할 수 있다. 뷰 결합에는 주석 처리가 필요하지 않기에 컴파일 시간이 더 짧아진다.
또한 사용 편의성 면에서 뷰 결합에는 특별히 태그된 XML 레이아웃 파일이 필요하지 않기에 앱에서 더 신속하게 채택할 수 있고 모듈에서 뷰 결합을 사용 설정하면 모듈의 모든 레이아웃에 뷰 결합이 자동으로 적용된다.
장점이 있으면 단점도 있기 마련,
뷰 결합은 레이아웃 변수 또는 레이아웃 표현식을 지원하지 않기에 XML 레이아웃 파일에서 직접 동적 UI 콘텐츠를 선언하는 데 사용할 수 없다. 그리고 보기 결합은 양방향 데이터 결합을 지원하지 않는다고 한다.
처음에는 findViewById없이 할 수 있다고? 라는 생각에 앞으로는 안써야지 했지만 단점을 본 순간 findViewbyId는 버릴 수 없을 것 같다...
아마 쓴다면 값이 변하지 않는 레이아웃에 활용한다면 확실히 좋을 것 같다라는 생각이든다.
❗️오류나 정보가 정확하지 않다면 댓글로 써주세요...!❗️
참고자료
Android Developer : https://developer.android.com/topic/libraries/view-binding?hl=ko
뷰 결합 | Android 개발자 | Android Developers
뷰 결합 뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다. 모듈에서 사용 설정된 뷰 결합은 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성합니다. 바인딩
developer.android.com
슬기로운코딩생활님의
모던 안드로이드 아키텍쳐 - 뷰바인딩(findViewById를 없애기) 편
댓글