استفاده از دیتا بایندینگ در ریسایکلرویو (اندروید)

سلام این اولین پست من هست امیدوارم خوشتون بیاد و بتونید ازش استفاده کنید.
خب دیتابایندینگ چه مشکلی رو برای ما حل کرده؟ قبل از به وجود اومدن دیتابایندینگ ما مجبور بودیم تک تک ویو هایی که داشتیم(مثل تکست‌ویو و ادیت‌تکست) با استفاده از findViewById به کد جاوا متصل کنیم(البته اگه از زبان کاتلین برای توسعه اپ استفاده کنید دیگه مجبور نیستید از findViewById استفاده کنید) و بعد از اون دیتای هر ویو رو داخلش بریزیم برای نمونه مثال زیر رو در نظر بگیرید:

TextView tvFirstName = findViewById(R.id.first_name);
TextView tvLastName = findViewById(R.id.last_name);
TextView tvAge = findViewById(R.id.age);
TextView tvDescription = findViewById(R.id.description);

tvFirstName.setText(&quotMohammad&quot);
tvLastName.setText(&quotJahangiri&quot);
tvAge.setText(&quot23&quot);
tvDescription.setText(&quotDescription&quot);


همونطور که بالاتر توضیح دادیم اول ویوبایندینگ(findViewById) کردیم بعد دیتابایندینگ(setText)

شاید این کار برای چهار ویو خیلی آسون باشه اما وقتی تعداد ویو ها بیشتر بشه باید این کار تکراری رو برای هر ویو انجام بدید، اینجاست که دیتابایندینگ به کمک ما میاد و ما رو از شر boilerplate های کسالت آور رها میکنه.

اگر بای دیتابایندینگ آشنایی ندارید تو مقاله زیر میتونید مراحل کامل نحوه پیاده سازی دیتابایندینگ رو به زبان ساده مطالعه کنید.

https://virgool.io/AndroidStudio/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-data-binding-%D8%AF%D8%B1-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-xjfjo3xvmvn3

تو مقاله بالا یاد گرفتید که چطور میشه دیتابایندینگ رو فعال و از اون استفاده کنید، اما موضوع نوشته ما پیاده سازی دیتا بایندینگ تو ریسایکلرویو هست که آموزشش توی مقاله مذکور نبود.

اول برای آیتم های ریسایکلرویو نیاز به یک دیتا مدل داریم، دیتا مدل زیر شامل سه خصوصیت (name, description, isFavorite) میشه:

data class Person(
    var name: String,
    var description: String,
    var isFavorite: Boolean
)

همونطور که میدونید به یک لایه برای آیتم ها نیاز داریم، طراحی لایه هم به صورت زیر هست:

<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?>
<layout xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot
    xmlns:tools=&quothttp://schemas.android.com/tools&quot>

    <data>

        <import type=&quotandroid.view.View&quot />

        <variable
            name=&quotperson&quot
            type=&quotcom.github.masterj3y.Person&quot />

    </data>

    <RelativeLayout
        android:layout_width=&quotmatch_parent&quot
        android:layout_height=&quotwrap_content&quot
        android:background=&quot#FFFFFF&quot
        android:paddingStart=&quot16dp&quot
        android:paddingTop=&quot8dp&quot
        android:paddingEnd=&quot16dp&quot
        android:paddingBottom=&quot8dp&quot>

        <TextView
            android:id=&quot@+id/nameTextView&quot
            android:layout_width=&quotmatch_parent&quot
            android:layout_height=&quotwrap_content&quot
            android:layout_alignParentStart=&quottrue&quot
            android:layout_toStartOf=&quot@id/favorite&quot
            android:text=&quot@{person.name}&quot
            android:textColor=&quot#000&quot
            android:textSize=&quot20sp&quot
            tools:text=&quotTitle&quot />

        <TextView
            android:layout_width=&quotwrap_content&quot
            android:layout_height=&quotwrap_content&quot
            android:layout_below=&quot@id/nameTextView&quot
            android:layout_alignStart=&quot@id/nameTextView&quot
            android:text=&quot@{person.description}&quot
            tools:text=&quotDescription&quot />

        <ImageView
            android:id=&quot@+id/favorite&quot
            android:layout_width=&quotwrap_content&quot
            android:layout_height=&quotwrap_content&quot
            android:layout_alignParentEnd=&quottrue&quot
            android:src=&quot@drawable/ic_turned_in_black_24dp&quot
            android:visibility=&quot@{person.isFavorite?View.VISIBLE:View.INVISIBLE}&quot />

    </RelativeLayout>

</layout>


بعد از ایجاد لایه بالا یک بار پروژه رو Rebuild کنید تا کدهای بایندینگ برای ما Generate بشن، برای این کار از منوی Build گزینه Rebuild Project رو انتخاب کنید.
وقتی که پروژه بیلد شد کلاسی تحت عنوان PersonsAdapter ایجاد کنید
اگر آداپتر های ریسایکلرویو رو بدون استفاده از دیتابایندینگ پیاده‌میکردیم باید یک پارامتر رو به عنوان itemView به کلاس پدر یا همون RecyclerView.ViewHolder پاس میدادیم دقیقا مثل کد زیر:

inner class PersonViewHolder(itemView: View) :
    RecyclerView.ViewHolder(itemView) {
    
    }

طبق معمول باید توی ViewHolder هم تک تک ویو ها رو با استفاده از findViewById باندشون کنیم، ولی به لطف DataBinding دیگه نیازی به این بویلرپلیت ها نداریم کافیه به جای itemView تو کانستراکتور PersonViewHolder یه نمونه از نوع کلاس بایندینگ ایجاد شده بهش بدیم، خب کلاس بایندینگ ایجاد شده چی هست؟ همون کدهایی که بعد از بیلد کردن برامون Generate شدن، این کد ها برای هر لایه تو یک کلاس هم اسم همون لایه ایجاد میشه، برای مثال اگر اسم لایه مربوط به آیتم های ریسایکلرویویی که کد xml ش رو بالاتر گذاشتم list_person_item_layout.xml باشه کلاسی که اندروید استودیو برای DataBinding این لایه برای ما تولید میکنه ListPersonItemLayoutBinding خواهد بود (دقت کنید همه _ ها حذف شد و اسم به صورت CamelCase نوشته شد)
حالا کافیه به جای itemView ما یک نمونه از ListPersonItemLayoutBinding تو کانستراکتور PersonViewHolder داشته باشیم:


inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
    RecyclerView.ViewHolder(binder.root) {

}

کلاس RecyclerView.ViewHolder یک ویو رو به عنوان پارامتر کانستراکتور میگیره ما هم مقدار binder.root بهش پاس میدیم
و حالا برای آخرین مرحله کافیه یک شی از کلاس Person به binder بدیم تا DataBinding ویو ها رو به صورت اتوماتیک بایند و دیتای هر ویو رو ست کنه، کد زیر همین کار رو برای ما انجام میده:

inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
    RecyclerView.ViewHolder(binder.root) {

    fun render(person: Person) {
        binder.person = person
    }

}

بله دیگه نیازی به findViewById و بقیه بویلرپلیت ها نیست، کد ویوهولدر ما فقط شامل همین چند خط میشه و تمام، کد کامل PersonsAdapter هم به صورت زیر هست:


class PersonsAdapter(privateval personsList: List<Person>) :
    RecyclerView.Adapter<PersonsAdapter.PersonViewHolder>() {

    private lateinit var context: Context

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        context = recyclerView.context
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PersonViewHolder(
        DataBindingUtil.inflate(
            LayoutInflater.from(context),
            R.layout.list_person_item_layout,
            parent,
            false
        )
    )

    override fun getItemCount() = personsList.size

    override fun onBindViewHolder(holder: PersonViewHolder, position: Int) =
        holder.render(personsList[position])

    inner class PersonViewHolder(privateval binder: ListPersonItemLayoutBinding) :
        RecyclerView.ViewHolder(binder.root) {

        fun render(person: Person) {
            binder.person = person
        }

    }

}


کد کامل پروژه رو هم تو گیت هاب گذاشتم:

https://github.com/masterj3y/RecyclerViewDataBinding


برای اولین نوشته من میتونه خیلی اشکالات داشته باشه، اگر خیلی سخت بود یا متوجه مورد اشتباهی یا غلط املایی شدین ممنون میشم توی کامنت ها مطلعم کنید‌:) ممنونم که وقتتون رو در اختیار من گذاشتین.