বাইন্ডিং অ্যাডাপ্টার

ভ্যালু সেট করার জন্য উপযুক্ত ফ্রেমওয়ার্ক কল করার দায়িত্ব বাইন্ডিং অ্যাডাপ্টারের। একটি উদাহরণ হল, প্রপার্টি ভ্যালু সেট করা, যেমন setText() মেথড কল করা। অন্য একটি উদাহরণ হল ইভেন্ট লিসনার সেট করা, যেমন setOnClickListener() মেথড কল করা।

ডেটা বাইন্ডিং লাইব্রেরি আপনাকে কোনও ভ্যালু সেট করার জন্য কল করা মেথড নির্দিষ্ট করতে, আপনার নিজস্ব বাইন্ডিং লজিক প্রদান করতে এবং অ্যাডাপ্টার ব্যবহার করে রিটার্ন করা অবজেক্টের ধরন নির্দিষ্ট করতে দেয়।

অ্যাট্রিবিউট ভ্যালু সেট করুন

যখনই কোনও বাউন্ড ভ্যালু পরিবর্তিত হয়, তখন জেনারেট করা বাইন্ডিং ক্লাসকে অবশ্যই বাইন্ডিং এক্সপ্রেশন সহ ভিউতে একটি সেটার মেথড কল করতে হবে। আপনি ডেটা বাইন্ডিং লাইব্রেরিকে অটোমেটিক পদ্ধতি নির্ধারণ করতে দিতে পারেন অথবা আপনি স্পষ্টভাবে পদ্ধতি ঘোষণা করতে পারেন বা পদ্ধতি বেছে নেওয়ার জন্য কাস্টম লজিক প্রদান করতে পারেন।

অটোমেটিক পদ্ধতি বেছে নেওয়া

example নামের অ্যাট্রিবিউটের জন্য, লাইব্রেরি অটোমেটিক setExample(arg) পদ্ধতি খুঁজে নেয় যা আর্গুমেন্ট হিসেবে মানানসই ধরন গ্রহণ করে। অ্যাট্রিবিউটের নেমস্পেস বিবেচনা করা হয়নি। কোনও পদ্ধতি সার্চ করার সময় শুধুমাত্র অ্যাট্রিবিউটের নাম ও ধরন ব্যবহার করা হয়।

যেমন, android:text="@{user.name}" এক্সপ্রেশন দেওয়া হলে, লাইব্রেরি setText(arg) মেথড খোঁজে যা user.getName()-এর মাধ্যমে ফেরত পাঠানো টাইপ গ্রহণ করে। user.getName()-এর রিটার্ন টাইপ String হলে, লাইব্রেরি setText() পদ্ধতি খোঁজে যা String আর্গুমেন্ট গ্রহণ করে। যদি এক্সপ্রেশনটি একটি int রিটার্ন করে, তাহলে লাইব্রেরিটি একটি setText() মেথড সার্চ করে যা একটি int আর্গুমেন্ট গ্রহণ করে। এক্সপ্রেশন থেকে সঠিক ধরন রিটার্ন করতে হবে। প্রয়োজন হলে আপনি রিটার্ন ভ্যালু কাস্ট করতে পারবেন।

প্রদত্ত নামের সাথে কোনও অ্যাট্রিবিউট না থাকলেও ডেটা বাইন্ডিং কাজ করে। আপনি ডেটা বাইন্ডিং ব্যবহার করে যেকোনও সেটারের জন্য অ্যাট্রিবিউট তৈরি করতে পারবেন। যেমন, সাপোর্ট ক্লাস DrawerLayout -এর কোনও অ্যাট্রিবিউট নেই, তবে এতে প্রচুর সেটার আছে। নিম্নলিখিত লেআউট অটোমেটিক setScrimColor(int) এবং addDrawerListener(DrawerListener) পদ্ধতিকে যথাক্রমে app:scrimColor এবং app:drawerListener অ্যাট্রিবিউটের সেটার হিসেবে ব্যবহার করে:

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

কাস্টম পদ্ধতির নাম উল্লেখ করুন

কিছু অ্যাট্রিবিউটে সেটার আছে যা নামের সাথে মেলে না। এইসব পরিস্থিতিতে, 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 ফ্রেমওয়ার্ক ক্লাসে সেটার রিনেম করার প্রয়োজন হয় না। ম্যাচিং পদ্ধতি অটোমেটিক খুঁজে পেতে, নামকরণ কনভেনশন ব্যবহার করে অ্যাট্রিবিউট আগে থেকেই প্রয়োগ করা হয়েছে।

কাস্টম লজিক প্রদান করুন

কিছু অ্যাট্রিবিউটের জন্য কাস্টম বাইন্ডিং লজিক প্রয়োজন। যেমন, android:paddingLeft অ্যাট্রিবিউটের জন্য কোনও সংশ্লিষ্ট সেটার নেই। পরিবর্তে, setPadding(left, top, right, bottom) পদ্ধতি প্রদান করা হয়েছে। BindingAdapter অ্যানোটেশন সহ স্ট্যাটিক বাইন্ডিং অ্যাডাপ্টার পদ্ধতি আপনাকে কোনও অ্যাট্রিবিউটের সেটার কীভাবে কল করা হয় তা কাস্টমাইজ করতে দেয়।

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

ImageView অবজেক্টের জন্য imageUrl এবং error ব্যবহার করা হলে অ্যাডাপ্টারকে কল করা হয়, imageUrl হল একটি স্ট্রিং এবং error হল একটি Drawable। আপনি যদি চান যে যেকোনও অ্যাট্রিবিউট সেট করা হলে অ্যাডাপ্টারকে কল করা হোক, তাহলে অ্যাডাপ্টারের ঐচ্ছিক 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);
}

একটি লিসনার পরিবর্তন করলে অন্যটির উপর প্রভাব পড়তে পারে, তাই আপনার এমন একটি অ্যাডাপ্টার প্রয়োজন যা একটি বা দুটি অ্যাট্রিবিউটের জন্যই কাজ করে। আপনি অ্যানোটেশনে requireAll-কে false-এ সেট করতে পারেন যাতে নির্দিষ্ট করা যায় যে প্রতিটি অ্যাট্রিবিউটে অবশ্যই একটি বাইন্ডিং এক্সপ্রেশন অ্যাসাইন করতে হবে না, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

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

উপরের উদাহরণটি একটু জটিল কারণ OnAttachStateChangeListener-এর জন্য সেটার পদ্ধতির পরিবর্তে View ক্লাস addOnAttachStateChangeListener() এবং removeOnAttachStateChangeListener() পদ্ধতি ব্যবহার করে। android.databinding.adapters.ListenerUtil ক্লাস এইসব লিসনার ট্র্যাক করতে সাহায্য করে যাতে বাইন্ডিং অ্যাডাপ্টারে সেগুলি সরিয়ে দেওয়া যায়।

অবজেক্ট কনভার্সন

অটোমেটিক অবজেক্ট কনভার্সন

বাইন্ডিং এক্সপ্রেশন থেকে Object রিটার্ন করা হলে, লাইব্রেরি প্রপার্টির ভ্যালু সেট করার জন্য ব্যবহৃত পদ্ধতি বেছে নেয়। Object বেছে নেওয়া পদ্ধতির প্যারামিটার টাইপে কাস্ট করা হয়। এই আচরণটি সেইসব অ্যাপের ক্ষেত্রে সুবিধাজনক যেগুলি ডেটা স্টোর করার জন্য ObservableMap ক্লাস ব্যবহার করে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

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

এক্সপ্রেশনে userMap অবজেক্ট একটি ভ্যালু রিটার্ন করে, যা অটোমেটিক android:text অ্যাট্রিবিউটের ভ্যালু সেট করতে ব্যবহৃত setText(CharSequence) পদ্ধতিতে পাওয়া প্যারামিটার টাইপে কাস্ট করা হয়। প্যারামিটারের ধরন অস্পষ্ট হলে, এক্সপ্রেশনে রিটার্ন টাইপ কাস্ট করুন।

কাস্টম কনভার্সন

কিছু পরিস্থিতিতে, নির্দিষ্ট ধরনের মধ্যে একটি কাস্টম কনভার্সন প্রয়োজন। যেমন, কোনও ভিউয়ের 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"/>

অতিরিক্ত রিসোর্স

ডেটা বাইন্ডিং সম্পর্কে আরও জানতে, নিম্নলিখিত রিসোর্স দেখুন।

নমুনা

কোডল্যাবস

ব্লগ পোস্ট