Bindungsadapter

Bindungsadapter sind für die Durchführung der entsprechenden Framework-Aufrufe an Werte festlegen. Ein Beispiel ist das Festlegen eines Eigenschaftswerts, z. B. das Aufrufen der setText()-Methode. Anderes ist das Festlegen eines Event-Listeners, z. B. das Aufrufen der setOnClickListener() .

Mit der Datenbindungsbibliothek können Sie die aufgerufene Methode angeben, um einen Wert festzulegen. stellen Sie Ihre eigene Bindungslogik bereit und geben Sie den Typ des zurückgegebenen Objekts an, mit Adaptern.

Attributwerte festlegen

Wenn sich ein Grenzwert ändert, muss die generierte Bindungsklasse einen Setter aufrufen für die Ansicht mit dem Bindungsausdruck. Sie können die Datenbindung Die Methode wird automatisch von der Bibliothek festgelegt. Alternativ können Sie die Methode explizit deklarieren oder benutzerdefinierte Logik zur Auswahl einer Methode bereitstellen.

Automatische Methodenauswahl

Für ein Attribut namens example sucht die Bibliothek automatisch nach der Methode setExample(arg), die kompatible Typen als Argument akzeptiert. Namespace des Attributs nicht berücksichtigt. Nur Attributname und -typ werden verwendet wenn Sie nach einer Methode suchen.

Mit dem Ausdruck android:text="@{user.name}" würde die Bibliothek beispielsweise sucht nach einer setText(arg)-Methode, die den von user.getName() Wenn der Rückgabetyp von user.getName() String ist, gibt der Wert sucht nach einer setText()-Methode, die ein String-Argument akzeptiert. Wenn die wenn der Ausdruck ein int zurückgibt, sucht die Bibliothek nach einer setText()-Methode, akzeptiert ein int-Argument. Der Ausdruck muss den richtigen Typ zurückgeben. Sie können wandeln Sie den Rückgabewert gegebenenfalls um.

Die Datenbindung funktioniert auch dann, wenn kein Attribut mit dem angegebenen Namen vorhanden ist. Sie können mithilfe von Datenbindung Attribute für beliebige Setter erstellen. Zum Beispiel hat der Support Klasse DrawerLayout hat keine Attribute, aber viele Setter. Das folgende Layout verwendet automatisch die setScrimColor(int) und addDrawerListener(DrawerListener) als Setter für app:scrimColor und app:drawerListener Attribute:

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

Geben Sie einen benutzerdefinierten Methodennamen an

Einige Attribute haben Setter, die nicht namentlich zugeordnet werden. In diesen Fällen wird ein kann mithilfe der Methode BindingMethods . Die Annotation wird mit einer Klasse verwendet und kann mehrere BindingMethod -Annotationen, eine für jede umbenannte Methode. Bindungsmethoden sind Annotationen, die Sie jedem Kurs in Ihrer App hinzufügen können.

Im folgenden Beispiel wird das Attribut android:tint mit dem Parameter setImageTintList(ColorStateList) und nicht mit der Methode setTint():

Kotlin

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

Java

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

Normalerweise müssen Sie Setter in Android-Framework-Klassen nicht umbenennen. Die -Attribute sind bereits mithilfe der Namenskonvention implementiert, um automatisch Abgleichmethoden suchen.

Benutzerdefinierte Logik bereitstellen

Einige Attribute erfordern eine benutzerdefinierte Bindungslogik. Zum Beispiel gibt es keine zugehörigen Setter für das Attribut android:paddingLeft. Stattdessen wird die Methode setPadding(left, top, right, bottom) bereitgestellt. Eine statische Bindungsadaptermethode mit BindingAdapter Mit Annotation können Sie anpassen, wie ein Setter für ein Attribut aufgerufen wird.

Die Attribute der Android-Framework-Klassen haben bereits BindingAdapter Anmerkungen. Das folgende Beispiel zeigt den Bindungsadapter für die Attribut paddingLeft:

Kotlin

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

Java

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

Die Parametertypen sind wichtig. Der erste Parameter bestimmt die Art der die mit dem Attribut verknüpfte Ansicht. Der zweite Parameter bestimmt, Der im Bindungsausdruck für das jeweilige Attribut akzeptierte Typ.

Bindungsadapter sind auch für andere Arten der Anpassung nützlich. Beispiel: Ein benutzerdefiniertes Ladeprogramm kann aus einem Worker-Thread aufgerufen werden, um ein Image zu laden.

Sie können auch Adapter verwenden, die mehrere Attribute erhalten, wie in den folgendes Beispiel:

Kotlin

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

Java

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

Sie können den Adapter in Ihrem Layout verwenden, wie im folgenden Beispiel gezeigt. Hinweis dass @drawable/venueError auf eine Ressource in Ihrer Anwendung verweist. Um die Ressource mit @{} macht sie zu einem gültigen Bindungsausdruck.

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

Der Adapter wird aufgerufen, wenn imageUrl und error für eine ImageView-Objekt, imageUrl ist ein String und error ist ein Drawable Bei Bedarf Den Adapter, der aufgerufen werden soll, wenn eines der Attribute festgelegt ist, legen Sie den optionalen requireAll des Adapters auf false, wie im folgenden Beispiel gezeigt:

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);
    }
}

Java

@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);
  }
}

Bindungsadaptermethoden können die alten Werte in ihren Handlern verwenden. Methode Wenn Sie alte und neue Werte übernehmen, müssen zuerst alle alten Werte für die Attribute deklariert werden. gefolgt von den neuen Werten, wie im folgenden Beispiel gezeigt:

Kotlin

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

Java

@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());
   }
}

Event-Handler können nur mit Schnittstellen oder abstrakten Klassen mit einer abstrakte Methode verwenden, wie im folgenden Beispiel gezeigt:

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)
        }
    }
}

Java

@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);
    }
  }
}

Verwenden Sie diesen Ereignis-Handler in Ihrem Layout wie folgt:

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

Wenn ein Listener mehrere Methoden hat, muss er in mehrere Listener aufgeteilt werden. Beispiel: View.OnAttachStateChangeListener verfügt über zwei Methoden: onViewAttachedToWindow(View) und onViewDetachedFromWindow(View) Die Bibliothek bietet zwei Schnittstellen zur Unterscheidung der Attribute und Handler. für sie:

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)
}

Java

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

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

Da sich Änderungen an einem Listener auf den anderen auswirken können, benötigen Sie einen Adapter, funktioniert für eines der Attribute oder für beide. Sie können requireAll auf false setzen in Die Annotation, die angibt, dass nicht jedem Attribut eine Bindung zugewiesen werden muss wie im folgenden Beispiel gezeigt:

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)
        }
    }
}

Java

@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);
        }
    }
}

Das obige Beispiel ist etwas kompliziert, da die Für die Klasse View wird die Methode addOnAttachStateChangeListener() und removeOnAttachStateChangeListener() anstelle einer Setter-Methode für OnAttachStateChangeListener Mit der Klasse android.databinding.adapters.ListenerUtil behältst du den Überblick. Listener, sodass sie aus dem Bindungsadapter entfernt werden können.

Objekt-Conversions

Automatische Objektkonvertierung

Wenn ein Object von einer Bindung zurückgegeben wird verwendet, wählt die Bibliothek die Methode aus, mit der der Wert des Property. Object wird in einen Parametertyp der ausgewählten Methode umgewandelt. Dieses in Apps mit der Funktion ObservableMap Kurs für Daten speichern, wie im folgenden Beispiel gezeigt:

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

Das userMap-Objekt im Ausdruck gibt einen Wert zurück, der automatisch in den Parametertyp umwandeln, der in der Methode setText(CharSequence) für Legen Sie den Wert des Attributs android:text fest. Wenn der Parametertyp nicht eindeutig ist, wandeln Sie den Rückgabetyp in den Ausdruck um.

Benutzerdefinierte Conversions

In einigen Situationen ist zwischen bestimmten Typen eine benutzerdefinierte Conversion erforderlich. Für Beispiel: Das Attribut android:background eines Aufrufs erwartet ein Drawable, aber der angegebene Wert für color eine Ganzzahl ist. Das folgende Beispiel zeigt eine Attribut, das ein Drawable erwartet, aber stattdessen eine Ganzzahl angegeben wird:

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

Immer wenn eine Drawable erwartet und eine Ganzzahl zurückgegeben wird, wandeln Sie int um in ColorDrawable. Verwenden Sie für die Konvertierung eine statische Methode mit einem BindingConversion wie folgt an:

Kotlin

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

Java

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

Die im Bindungsausdruck angegebenen Werttypen müssen jedoch einheitlich sein. Sie können in einem Ausdruck nicht verschiedene Typen verwenden, wie im Folgenden gezeigt: Beispiel:

// 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"/>

Weitere Informationen

Weitere Informationen zur Datenbindung finden Sie in den folgenden Ressourcen.

Produktproben

Codelabs

Blogposts