뷰 결합 Android Jetpack의 구성요소

뷰 결합은 뷰와 상호작용하는 코드를 더 쉽게 작성할 수 있게 해주는 기능입니다. 모듈에서 사용 설정된 뷰 결합은 모듈에 있는 각 XML 레이아웃 파일의 바인딩 클래스를 생성합니다. 결합 클래스의 인스턴스에는 상응하는 레이아웃에 ID가 있는 모든 뷰의 직접 참조가 포함됩니다.

대부분의 경우 뷰 결합이 findViewById를 대체합니다.

설정

뷰 바인딩은 모듈별로 사용 설정됩니다. 모듈에서 뷰 결합을 사용 설정하려면 다음 예와 같이 모듈 수준 build.gradle 파일에서 viewBinding 빌드 옵션을 true로 설정하세요.

Groovy

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

Kotlin

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

결합 클래스를 생성하는 동안 레이아웃 파일을 무시하려면 tools:viewBindingIgnore="true" 속성을 레이아웃 파일의 루트 뷰에 추가하세요.

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

사용법

모듈에 뷰 결합을 사용 설정하면 모듈에 포함된 각 XML 레이아웃 파일의 결합 클래스가 생성됩니다. 각 결합 클래스에는 루트 뷰 및 ID가 있는 모든 뷰의 참조가 포함됩니다. 결합 클래스 이름은 XML 파일의 이름을 파스칼 표기법으로 변환하고 끝에 'Binding'을 추가하여 생성됩니다.

예를 들어 다음을 포함하는 result_profile.xml이라는 레이아웃 파일을 살펴보겠습니다.

<LinearLayout ... >
    <TextView android:id="@+id/name" />
    <ImageView android:cropToPadding="true" />
    <Button android:id="@+id/button"
        android:background="@drawable/rounded_button" />
</LinearLayout>

생성된 결합 클래스 이름은 ResultProfileBinding이 됩니다. 이 클래스에는 name이라는 TextViewbutton라는 Button의 두 필드가 있습니다. 레이아웃의 ImageView에는 ID가 없으므로 결합 클래스에 참조가 없습니다.

모든 결합 클래스에는 상응하는 레이아웃 파일의 루트 뷰 직접 참조를 제공하는 getRoot() 메서드도 포함됩니다. 이 예에서 ResultProfileBinding 클래스의 getRoot() 메서드는 LinearLayout 루트 뷰를 반환합니다.

다음 섹션에서는 생성된 결합 클래스를 활동과 프래그먼트에서 사용하는 방법을 보여줍니다.

활동에서 뷰 결합 사용

활동에 사용할 결합 클래스 인스턴스를 설정하려면 활동의 onCreate() 메서드에서 다음 단계를 따르세요.

  1. 생성된 결합 클래스에 포함된 정적 inflate() 메서드를 호출합니다. 그러면 활동에서 사용할 결합 클래스 인스턴스가 생성됩니다.
  2. getRoot() 메서드를 호출하거나 Kotlin 속성 문법을 사용하여 루트 뷰 참조를 가져옵니다.
  3. 루트 뷰를 setContentView()에 전달하여 화면의 활성 뷰로 만듭니다.

이러한 단계는 다음 예에 나와 있습니다.

Kotlin

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

Java

private ResultProfileBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = ResultProfileBinding.inflate(getLayoutInflater());
    View view = binding.getRoot();
    setContentView(view);
}

이제 결합 클래스 인스턴스를 사용하여 뷰를 참조할 수 있습니다.

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

프래그먼트에서 뷰 결합 사용

프래그먼트와 함께 사용할 결합 클래스 인스턴스를 설정하려면 프래그먼트의 onCreateView() 메서드에서 다음 단계를 따르세요.

  1. 생성된 결합 클래스에 포함된 정적 inflate() 메서드를 호출합니다. 그러면 프래그먼트에서 사용할 결합 클래스 인스턴스가 생성됩니다.
  2. getRoot() 메서드를 호출하거나 Kotlin 속성 문법을 사용하여 루트 뷰 참조를 가져옵니다.
  3. onCreateView() 메서드에서 루트 뷰를 반환하여 화면의 활성 뷰로 만듭니다.

Kotlin

private var _binding: ResultProfileBinding? = 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 = ResultProfileBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}

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

Java

private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
                          ViewGroup container,
                          Bundle savedInstanceState) {
    binding = ResultProfileBinding.inflate(inflater, container, false);
    View view = binding.getRoot();
    return view;
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    binding = null;
}

이제 결합 클래스 인스턴스를 사용하여 뷰를 참조할 수 있습니다.

Kotlin

binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

Java

binding.name.setText(viewModel.getName());
binding.button.setOnClickListener(new View.OnClickListener() {
    viewModel.userClicked()
});

다양한 구성에 대한 힌트 제공

여러 구성에서 뷰를 선언할 때 특정 레이아웃에 따라 다른 뷰 유형을 사용하는 것이 적합한 경우도 있습니다. 다음 코드 스니펫은 이를 보여주는 예입니다.

# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

이 경우 TextView가 공통 기본 클래스이므로 생성된 클래스가 TextView 유형의 userBio 필드를 노출할 수 있습니다. 기술적 제한으로 인해 뷰 결합 코드 생성기는 이를 확인할 수 없으며 대신 View 필드를 생성합니다. 이렇게 하려면 나중에 binding.userBio as TextView를 사용하여 필드를 변환해야 합니다.

이 제한을 해결하기 위해 뷰 결합은 tools:viewBindingType 속성을 지원하므로 생성된 코드에서 사용할 유형을 컴파일러에 알릴 수 있습니다. 이전 예에서는 이 속성을 사용하여 컴파일러가 필드를 TextView로 생성하도록 할 수 있습니다.

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />

또 다른 예로, BottomNavigationView가 포함된 레이아웃과 NavigationRailView가 포함된 레이아웃이 두 개 있다고 가정해 보겠습니다. 두 클래스 모두 대부분의 구현 세부정보를 포함하는 NavigationBarView를 확장합니다. 코드가 현재 레이아웃에 어떤 서브클래스가 있는지 정확하게 알 필요가 없다면 다음과 같이 tools:viewBindingType를 사용하여 생성된 유형을 두 레이아웃에서 모두 NavigationBarView로 설정할 수 있습니다.

# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

코드를 생성할 때 뷰 결합에서 이 속성의 값을 검증할 수 없습니다. 컴파일 시간 및 런타임 오류를 방지하려면 값이 다음 조건을 충족해야 합니다.

  • 값은 android.view.View에서 상속되는 클래스여야 합니다.
  • 값은 배치된 태그의 슈퍼클래스여야 합니다. 예를 들어 다음 값은 작동하지 않습니다.

      <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
      <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->
    
  • 최종 유형은 모든 구성에서 일관되게 확인되어야 합니다.

findViewById와의 차이점

뷰 결합에는 findViewById를 사용하는 것에 비해 다음과 같은 중요한 장점이 있습니다.

  • Null 안전성: 뷰 결합은 뷰의 직접 참조를 생성하므로 잘못된 뷰 ID로 인해 null 포인터 예외가 발생할 위험이 없습니다. 또한 뷰가 레이아웃의 일부 구성에서만 존재하는 경우 결합 클래스에 참조가 포함된 필드가 @Nullable로 표시됩니다.
  • 유형 안전성: 각 바인딩 클래스의 필드에는 XML 파일에서 참조하는 뷰와 일치하는 유형이 있습니다. 즉, 클래스 변환 예외가 발생할 위험이 없습니다.

이러한 차이점은 레이아웃과 코드 간의 비호환성으로 인해 런타임이 아닌 컴파일 시간에 빌드가 실패한다는 것을 의미합니다.

데이터 결합과 비교

뷰 결합과 데이터 결합은 모두 뷰를 직접 참조하는 데 사용할 수 있는 결합 클래스를 생성합니다. 그러나 뷰 결합은 더 간단한 사용 사례를 처리하기 위한 것이며 데이터 결합에 비해 다음과 같은 이점을 제공합니다.

  • 더 빠른 컴파일: 뷰 결합에는 주석 처리가 필요하지 않으므로 컴파일 시간이 더 짧습니다.
  • 사용 편의성: 뷰 결합에는 특별히 태그가 지정된 XML 레이아웃 파일이 필요하지 않으므로 앱에서 더 빠르게 채택할 수 있습니다. 모듈에서 뷰 결합을 사용 설정하면 모듈의 모든 레이아웃에 뷰 결합이 자동으로 적용됩니다.

반면 뷰 결합에는 데이터 결합에 비해 다음과 같은 제한사항이 있습니다.

이러한 고려사항을 감안하여 경우에 따라 프로젝트에서 뷰 결합과 데이터 결합을 모두 사용하는 것이 가장 좋습니다. 고급 기능이 필요한 레이아웃에서 데이터 결합을 사용하고 고급 기능이 필요하지 않은 레이아웃에서 뷰 결합을 사용할 수 있습니다.

추가 리소스

뷰 결합에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.

샘플

블로그

동영상