Adaptörleri bağlama

Değerleri ayarlamak için uygun çerçeve çağrıları yapmaktan bağdaştırıcıları bağlama sorumluluğu vardır. Örnek olarak, setText() yöntemini çağırmak gibi bir özellik değeri belirlemek verilebilir. Diğer bir örnek de setOnClickListener() yöntemini çağırmak gibi bir etkinlik işleyici ayarlamaktır.

Veri Bağlama Kitaplığı, bir değer ayarlamak için çağrılan yöntemi belirtmenize, kendi bağlama mantığınızı sağlamanıza ve bağdaştırıcıları kullanarak döndürülen nesnenin türünü belirtmenize olanak tanır.

Özellik değerlerini ayarlama

Bir bağlı değer değiştiğinde, oluşturulan bağlama sınıfı, görünümde bağlama ifadesiyle bir setter yöntemini çağırmalıdır. Veri Bağlama Kitaplığı'nın yöntemi otomatik olarak belirlemesine izin verebilir veya yöntemi açıkça beyan edebilir ya da yöntem seçmek için özel mantık sağlayabilirsiniz.

Otomatik yöntem seçimi

Kitaplık, example adlı bir özellik için uyumlu türleri bağımsız değişken olarak kabul eden setExample(arg) yöntemini otomatik olarak bulur. Özelliğin ad alanı dikkate alınmaz. Yöntem aranırken yalnızca özellik adı ve türü kullanılır.

Örneğin, android:text="@{user.name}" ifadesiyle kitaplık, user.getName() tarafından döndürülen türü kabul eden bir setText(arg) yöntemi arar. user.getName() dönüş türü String ise kitaplık, String bağımsız değişkenini kabul eden bir setText() yöntemi arar. İfade bir int döndürürse kitaplık, int bağımsız değişkenini kabul eden bir setText() yöntemini arar. İfade doğru türü döndürmelidir. Gerekirse döndürülen değeri yayınlayabilirsiniz.

Verilen ada sahip herhangi bir özellik bulunmasa bile veri bağlama çalışır. Veri bağlama özelliğini kullanarak herhangi bir setter için özellik oluşturabilirsiniz. Örneğin, DrawerLayout destek sınıfının özellikleri yoktur ama çok sayıda belirleyici vardır. Aşağıdaki düzen, app:scrimColor ve app:drawerListener özellikleri için ayarlayıcı olarak sırasıyla setScrimColor(int) ve addDrawerListener(DrawerListener) yöntemlerini otomatik olarak kullanır:

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

Özel bir yöntem adı belirtin

Bazı özelliklerde ada göre eşleşmeyen ayarlayıcılar var. Bu durumlarda, BindingMethods notasyonu kullanılarak bir özellik belirleyici ile ilişkilendirilebilir. Ek açıklama bir sınıfla kullanılır ve yeniden adlandırılmış her yöntem için bir tane olmak üzere birden fazla BindingMethod ek açıklaması içerebilir. Bağlama yöntemleri, uygulamanızdaki her sınıfa ekleyebileceğiniz ek açıklamalardır.

Aşağıdaki örnekte android:tint özelliği, setTint() yöntemiyle değil setImageTintList(ColorStateList) yöntemiyle ilişkilendirilmiş:

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

Genellikle Android çerçeve sınıflarında ayarlayıcıları yeniden adlandırmanız gerekmez. Özellikler, eşleştirme yöntemlerini otomatik olarak bulmak için ad kuralı kullanılarak halihazırda uygulanmıştır.

Özel mantık sağlayın

Bazı özellikler için özel bağlama mantığı gerekir. Örneğin, android:paddingLeft özelliği için ilişkili bir ayarlayıcı yoktur. Bunun yerine setPadding(left, top, right, bottom) yöntemi sağlanır. BindingAdapter notlu statik bağlama bağdaştırıcısı yöntemi, bir özellik için ayarlayıcının nasıl çağrılacağını özelleştirmenize olanak tanır.

Android çerçeve sınıflarının özellikleri zaten BindingAdapter notlarına sahiptir. Aşağıdaki örnekte paddingLeft özelliği için bağlama bağdaştırıcısı gösterilmektedir:

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

Parametre türleri önemlidir. İlk parametre, özellikle ilişkilendirilmiş görünümün türünü belirler. İkinci parametre, belirtilen özelliğin bağlama ifadesinde kabul edilen türü belirler.

Adaptörleri bağlamak, diğer özelleştirme türleri için de yararlı olur. Örneğin, özel yükleyici bir resim yüklemek için bir çalışan iş parçacığından çağrılabilir.

Aşağıdaki örnekte gösterildiği gibi, birden çok özellik alan bağdaştırıcılarınız da olabilir:

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

Bağdaştırıcıyı düzeninizde, aşağıdaki örnekte gösterildiği gibi kullanabilirsiniz. @drawable/venueError öğesinin, uygulamanızdaki bir kaynağı ifade ettiğini unutmayın. Kaynağın @{} içine alınması, geçerli bir bağlama ifadesi olmasını sağlar.

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

ImageView nesnesi için imageUrl ve error kullanılıyorsa imageUrl dize, error ise Drawable ise bağdaştırıcı çağrılır. Özelliklerden herhangi biri ayarlandığında bağdaştırıcının çağrılmasını istiyorsanız bağdaştırıcının isteğe bağlı requireAll işaretini aşağıdaki örnekte gösterildiği gibi false olarak ayarlayın:

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

Bağdaştırıcı yöntemleri bağlama, işleyicilerindeki eski değerleri alabilir. Eski ve yeni değerler alan bir yöntemde önce özellikler için tüm eski değerler bildirilmelidir, ardından aşağıdaki örnekte gösterildiği gibi yeni değerler belirtilmelidir:

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

Etkinlik işleyiciler, aşağıdaki örnekte gösterildiği gibi yalnızca tek bir soyut yöntem içeren arayüzlerle veya soyut sınıflarla kullanılabilir:

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

Bu etkinlik işleyiciyi düzeninizde aşağıdaki gibi kullanın:

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

Bir dinleyicinin birden fazla yöntemi varsa birden çok dinleyiciye bölünmelidir. Örneğin, View.OnAttachStateChangeListener iki yönteme sahiptir: onViewAttachedToWindow(View) ve onViewDetachedFromWindow(View). Kitaplık, özellikleri ve işleyicileri ayırt edebilmeniz için iki arayüz sağlar:

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

Bir işleyicinin değiştirilmesi diğerini etkileyebileceğinden, bu iki özellik için veya her ikisi için de çalışan bir bağdaştırıcıya ihtiyacınız vardır. Aşağıdaki örnekte gösterildiği gibi, her özelliğe bir bağlama ifadesi atanması gerekmediğini belirtmek için ek açıklamada requireAll değerini false olarak ayarlayabilirsiniz:

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

Yukarıdaki örnek, View sınıfının OnAttachStateChangeListener için setter yöntemi yerine addOnAttachStateChangeListener() ve removeOnAttachStateChangeListener() yöntemlerini kullanması nedeniyle biraz karmaşıktır. android.databinding.adapters.ListenerUtil sınıfı, bu dinleyicilerin kaydını tutar. Böylece bu dinleyiciler, bağlama bağdaştırıcısında kaldırılabilir.

Nesne dönüşümleri

Otomatik nesne dönüştürme

Bir bağlama ifadesinden Object döndürüldüğünde, kitaplık, özelliğin değerini ayarlamak için kullanılan yöntemi seçer. Object, seçilen yöntemin bir parametre türüne yayınlanır. Bu davranış, veri depolamak için ObservableMap sınıfını kullanan uygulamalarda aşağıdaki örnekte gösterildiği gibi kullanışlıdır:

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

İfadedeki userMap nesnesi, bir değer döndürür. Bu değer, android:text özelliğinin değerini ayarlamak için kullanılan setText(CharSequence) yönteminde bulunan parametre türüne otomatik olarak yayınlanır. Parametre türü belirsizse ifadedeki döndürme türünü yayınlayın.

Özel dönüşümler

Bazı durumlarda, belirli türler arasında özel dönüşüm gerekir. Örneğin, bir görünümün android:background özelliği için Drawable değeri beklenir ancak belirtilen color değeri tam sayıdır. Aşağıdaki örnekte Drawable değeri bekleyen bir özellik gösterilmektedir ancak bunun yerine bir tam sayı sağlanır:

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

Bir Drawable beklendiğinde ve bir tam sayı döndürüldüğünde int değerini ColorDrawable biçimine dönüştürün. Dönüştürmeyi gerçekleştirmek için aşağıdaki gibi BindingConversion notlu statik bir yöntem kullanın:

Kotlin

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

Java

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

Ancak, bağlama ifadesinde sağlanan değer türleri tutarlı olmalıdır. Aşağıdaki örnekte gösterildiği gibi aynı ifadede farklı türler kullanamazsınız:

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

Ek kaynaklar

Veri bağlama hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın.

Sana Özel

Codelab uygulamaları

Blog yayınları