바인딩 어댑터

결합 어댑터는 적절한 프레임워크를 호출하여 값을 설정할 수 있습니다. 한 가지 예는 setText() 메서드를 사용하여 지도 가장자리에 패딩을 추가할 수 있습니다. 다른 예를 들어 이벤트 리스너를 설정하는 것입니다. setOnClickListener() 드림 메서드를 사용하여 축소하도록 요청합니다.

데이터 결합 라이브러리를 사용하면 값을 설정하기 위해 호출되는 메서드를 지정할 수 있습니다. 자체 바인딩 로직을 제공하고, 다음과 같이 반환된 객체의 유형을 어댑터 사용

속성 값 설정

바인딩된 값이 변경될 때마다 생성된 바인딩 클래스는 setter를 호출해야 합니다. 메서드를 호출할 수 있습니다. 데이터 결합이 라이브러리에서 자동으로 메서드를 결정하거나 개발자가 메서드를 사용하거나 커스텀 로직을 제공하여 메서드를 선택할 수 있습니다.

자동 메서드 선택

이름이 example인 속성의 경우 라이브러리가 자동으로 메서드를 찾습니다. 호환되는 유형을 인수로 허용하는 setExample(arg)입니다. 네임스페이스 속성이 고려되지 않습니다. 속성 이름과 유형만 사용됩니다. 을 사용하세요.

예를 들어 android:text="@{user.name}" 표현식이 있는 경우 라이브러리는 다음과 같이 반환된 유형을 허용하는 setText(arg) 메서드를 찾습니다. user.getName()입니다. user.getName()의 반환 유형이 String이면 라이브러리는 String 인수를 허용하는 setText() 메서드를 찾습니다. 만약 표현식이 int을 반환하면 라이브러리는 setText() 메서드를 int 인수를 허용합니다. 표현식은 올바른 유형을 반환해야 합니다. 다음과 같은 작업을 할 수 있습니다. 필요한 경우 반환 값을 변환합니다.

지정된 이름의 속성이 없더라도 데이터 결합은 작동합니다. 다음과 같은 작업을 할 수 있습니다. 데이터 결합을 사용하여 setter에 대한 속성을 만들 수 있습니다. 예를 들어 지원 서비스는 클래스 DrawerLayout 드림 속성은 없지만 setter가 많습니다. 다음 레이아웃 자동으로 setScrimColor(int) 드림 및 addDrawerListener(DrawerListener) app:scrimColorapp:drawerListener의 setter인 메서드 다음과 같습니다.

<androidx.drawerlayout.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}">

맞춤 메서드 이름 지정

일부 속성에는 이름이 일치하지 않는 setter가 있습니다. 이러한 상황에서는 속성은 BindingMethods 드림 주석을 추가합니다. 주석은 클래스와 함께 사용되며 여러 클래스를 포함할 수 있습니다. BindingMethod 드림 주석을 추가합니다. 바인딩 메서드는 앱의 모든 클래스에 추가할 수 있습니다.

다음 예에서 android:tint 속성은 setImageTintList(ColorStateList) 메서드를 사용하세요. setTint() 메서드를 사용하지 않는 것이 좋습니다.

Kotlin

@BindingMethods(value = [
    BindingMethod(
        type = android.widget.ImageView::class,
        attribute = "android:tint",
        method = "setImageTintList")])

자바

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

일반적으로 Android 프레임워크 클래스에서 setter의 이름을 바꿀 필요는 없습니다. 이 속성은 이름 규칙을 사용하여 이미 구현되어 있으며 일치하는 메서드를 찾습니다.

맞춤 로직 제공

일부 속성에는 맞춤 결합 로직이 필요합니다. 예를 들어 android:paddingLeft 속성의 setter입니다. 대신 setPadding(left, top, right, bottom) 메서드가 제공됩니다. 정적 결합 어댑터 메서드 BindingAdapter 주석을 사용하면 속성의 setter를 호출하는 방법을 맞춤설정할 수 있습니다.

Android 프레임워크 클래스의 속성에는 이미 BindingAdapter가 있습니다. 주석을 달 수 있습니다. 다음 예는 paddingLeft 속성:

Kotlin

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
    view.setPadding(padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom())
}

자바

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
  view.setPadding(padding,
                  view.getPaddingTop(),
                  view.getPaddingRight(),
                  view.getPaddingBottom());
}

매개변수 유형은 중요합니다. 첫 번째 매개변수는 속성과 연결된 뷰를 반환합니다. 두 번째 매개변수는 지정된 속성에 대한 바인딩 식에서 허용되는 유형입니다.

결합 어댑터는 다른 유형의 맞춤설정에도 유용합니다. 예를 들어 커스텀 로더는 작업자 스레드에서 호출되어 이미지를 로드할 수 있습니다.

아래와 같이 여러 속성을 수신하는 어댑터도 있을 수 있습니다. 다음 예를 참고하세요.

Kotlin

@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
    Picasso.get().load(url).error(error).into(view)
}

자바

@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
  Picasso.get().load(url).error(error).into(view);
}

다음 예와 같이 레이아웃에서 어댑터를 사용할 수 있습니다. 참고 @drawable/venueError는 앱의 리소스를 나타냅니다. 주변 리소스를 @{}로 설정하면 이를 유효한 결합 표현식으로 만듭니다.

<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />

imageUrlerror가 다음에 사용되는 경우 어댑터가 호출됩니다. ImageView 객체에서 imageUrl는 문자열이며 errorDrawable 원하는 경우 속성이 설정되었을 때 호출할 어댑터인 경우 선택적 requireAll 드림 어댑터의 플래그를 false에 추가합니다.

Kotlin

@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {
    if (url == null) {
        imageView.setImageDrawable(placeholder);
    } else {
        MyImageLoader.loadInto(imageView, url, placeholder);
    }
}

자바

@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false)
public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) {
  if (url == null) {
    imageView.setImageDrawable(placeholder);
  } else {
    MyImageLoader.loadInto(imageView, url, placeholder);
  }
}

결합 어댑터 메서드는 핸들러의 이전 값을 사용할 수 있습니다. 메서드 기존 값과 새 값을 취하려면 먼저 속성에 대한 모든 기존 값을 선언해야 하며 다음 예와 같이 새 값이 뒤에 옵니다.

Kotlin

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
    if (oldPadding != newPadding) {
        view.setPadding(newPadding,
                    view.getPaddingTop(),
                    view.getPaddingRight(),
                    view.getPaddingBottom())
    }
}

자바

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
  if (oldPadding != newPadding) {
      view.setPadding(newPadding,
                      view.getPaddingTop(),
                      view.getPaddingRight(),
                      view.getPaddingBottom());
   }
}

이벤트 핸들러는 인터페이스가 하나인 인터페이스 또는 추상 클래스에서만 추상 메서드를 지원합니다.

Kotlin

@BindingAdapter("android:onLayoutChange")
fun setOnLayoutChangeListener(
        view: View,
        oldValue: View.OnLayoutChangeListener?,
        newValue: View.OnLayoutChangeListener?
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        if (oldValue != null) {
            view.removeOnLayoutChangeListener(oldValue)
        }
        if (newValue != null) {
            view.addOnLayoutChangeListener(newValue)
        }
    }
}

자바

@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
       View.OnLayoutChangeListener newValue) {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    if (oldValue != null) {
      view.removeOnLayoutChangeListener(oldValue);
    }
    if (newValue != null) {
      view.addOnLayoutChangeListener(newValue);
    }
  }
}

다음과 같이 레이아웃에서 이 이벤트 핸들러를 사용할 수 있습니다.

<View android:onLayoutChange="@{() -> handler.layoutChanged()}"/>

리스너에 여러 메서드가 있으면 여러 리스너로 분할해야 합니다. 예를 들어 View.OnAttachStateChangeListener 드림 에는 두 가지 방법이 있습니다. onViewAttachedToWindow(View)onViewDetachedFromWindow(View): 라이브러리는 속성과 핸들러를 구별하는 두 개의 인터페이스를 제공합니다. 다음과 같습니다.

Kotlin

// Translation from provided interfaces in Java:
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewDetachedFromWindow {
    fun onViewDetachedFromWindow(v: View)
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
interface OnViewAttachedToWindow {
    fun onViewAttachedToWindow(v: View)
}

자바

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
  void onViewDetachedFromWindow(View v);
}

@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
  void onViewAttachedToWindow(View v);
}

하나의 리스너를 변경하면 다른 리스너가 영향을 받을 수 있으므로 둘 중 하나 또는 둘 다에 사용할 수 있습니다 다음에서 requireAllfalse로 설정할 수 있습니다. 일부 속성에 바인딩을 할당할 필요가 없음을 지정하는 주석 표현식을 사용해야 합니다.

Kotlin

@BindingAdapter(
        "android:onViewDetachedFromWindow",
        "android:onViewAttachedToWindow",
        requireAll = false
)
fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
        val newListener: View.OnAttachStateChangeListener?
        newListener = if (detach == null && attach == null) {
            null
        } else {
            object : View.OnAttachStateChangeListener {
                override fun onViewAttachedToWindow(v: View) {
                    attach.onViewAttachedToWindow(v)
                }

                override fun onViewDetachedFromWindow(v: View) {
                    detach.onViewDetachedFromWindow(v)
                }
            }
        }

        val oldListener: View.OnAttachStateChangeListener? =
                ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener)
        if (oldListener != null) {
            view.removeOnAttachStateChangeListener(oldListener)
        }
        if (newListener != null) {
            view.addOnAttachStateChangeListener(newListener)
        }
    }
}

자바

@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}, requireAll=false)
public static void setListener(View view, OnViewDetachedFromWindow detach, OnViewAttachedToWindow attach) {
    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
        OnAttachStateChangeListener newListener;
        if (detach == null && attach == null) {
            newListener = null;
        } else {
            newListener = new OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(View v) {
                    if (attach != null) {
                        attach.onViewAttachedToWindow(v);
                    }
                }
                @Override
                public void onViewDetachedFromWindow(View v) {
                    if (detach != null) {
                        detach.onViewDetachedFromWindow(v);
                    }
                }
            };
        }

        OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view, newListener,
                R.id.onAttachStateChangeListener);
        if (oldListener != null) {
            view.removeOnAttachStateChangeListener(oldListener);
        }
        if (newListener != null) {
            view.addOnAttachStateChangeListener(newListener);
        }
    }
}

위의 예는 View 클래스는 addOnAttachStateChangeListener()removeOnAttachStateChangeListener() setter 메서드 대신 OnAttachStateChangeListener. android.databinding.adapters.ListenerUtil 클래스는 이러한 항목을 추적하는 데 도움이 됩니다. 리스너가 결합 어댑터에서 삭제될 수 있도록 해야 합니다.

객체 변환

자동 객체 변환

결합에서 Object가 반환되는 경우 라이브러리는 속성 Object는 선택한 메서드의 매개변수 유형으로 변환됩니다. 이 편리한 동작은 ObservableMap 클래스를 다음 대상에게: 데이터를 저장합니다.

<TextView
   android:text='@{userMap["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content" />

표현식의 userMap 객체는 자동으로 값을 반환하며 다음을 수행하는 데 사용된 setText(CharSequence) 메서드에서 찾을 수 있는 매개변수 유형으로 변환 android:text 속성의 값을 설정합니다. 매개변수 유형이 모호하지 않으면 표현식의 반환 유형을 변환합니다.

맞춤 변환

어떤 상황에서는 특정 유형 간에 맞춤 변환이 필요합니다. 대상 예를 들어 뷰의 android:background 속성에는 Drawable이 필요하지만 지정된 color 값이 정수입니다. 다음 예는 속성: Drawable을 예상하지만 정수가 대신 제공됩니다.

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

Drawable가 예상되고 정수가 반환될 때마다 int를 변환합니다. ColorDrawable로 변경합니다. 변환을 수행하려면 BindingConversion 드림 주석을 추가합니다.

Kotlin

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)

자바

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
    return new ColorDrawable(color);
}

그러나 결합 표현식에 지정하는 값 유형은 일관되어야 합니다. 다음과 같이 동일한 표현식에 다른 유형을 사용할 수 없습니다. 예:

// The @drawable and @color represent different value types in the same
// expression, which causes a build error.
<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

추가 리소스

데이터 결합에 관한 자세한 내용은 다음 리소스를 참고하세요.

샘플

Codelab

블로그 게시물