استفاده از یک adapter برای recyclerview های مختلف

برای یک پروژه که تعداد recyclerview هاش زیاد بود داشتم فکر میکردم که چطور میتونم یه adapter بنویسم برای همه ی recyclerview. و هرجا خواستم ازش استفاده کنم.روال کلی به این صورت که یک کلاس اصلی برای recyclerview adapter درست کردم بعد از اونجایی که چند تا متد از اون برای هر recyclerview میتونه شرایط متفاوت داشته باشه رو بصورت abstract method تعریف کردم تا حتما مقدار دهی بشه مثلا item_view هرکدوم از recyclerview ها. یه مثال بخوام ازش بزنم: مثلا فرض کنید یه recyclerview داریم برای محصولان و یکی دیگه برای لیست سفارشات. خب طبیعتا هردوتا ماهیت یکسان که نمایش لیستی از عناوین باشد رو دارند اما تفاوت هایی هم دارند: * یکی باید property خاصی از product entity رو برای عنوان نمایش بده یکی دیگه از order entity، * با کلیک روی هر سطر از این recyclerview مربوط به product به صفحه جزئیات محصول میرود و برای order به صفحه جزئیات order* ممکن است view که برای هر item درنظر گرفته می شود نیز متفاوت باشد. مثلا برای product از product_view و برای order از order_view که با طراحی های متفاوت میتواند باشد. * ...خب اول یک کلاس درست کردم تا به عنوان کلاس اصلی adapter باشه که اسمش رو گذاشتم BaseRecyclerViewAdapter .abstract class BaseRecyclerViewAdapter<T>(diffCallback: DiffUtil.ItemCallback<T?>, var itemClickListener: RecyclerViewItemClickListener<T>) : ListAdapter<T?, BaseRecyclerViewAdapter<T>.AdapterViewHolder?>( diffCallback ) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): AdapterViewHolder { return AdapterViewHolder( LayoutInflater.from(parent.context) .inflate(getRecyclerViewItem(), parent, false) ) } abstract fun getRecyclerViewItem(): Int abstract fun onItemGenerated(position: Int, view: View) abstract fun bind(itemView: View, entity: T) override fun onBindViewHolder(holderSelect: AdapterViewHolder, position: Int) { holderSelect.Bind(getItem(position)) val view = holderSelect.itemView holderSelect.itemView.setOnClickListener { itemClickListener.(getItemAt(position) as T) } onItemGenerated(position, view) } fun getItemAt(position: Int): T? { return getItem(position) } inner class AdapterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { internal fun Bind(entity: T?) { if (entity != null) { bind(itemView, entity) } } } }توی این کلاس که به صورت abstract تعریف شده نوع پارامتر های ورودی و خروجی از نوع generic type یا همون T هستند چون نوع های مختلفی رو ممکن به عنوان ورودی بگیره یا خروجی بده ، مثلا یه جا نوع ProductEntity رو میفرستیم تا وقتی تابع getItemAt فراخوانی شد خروجی باید ProductEntity باشه یه جای دیگه OrderEntity رو میخوایم تابع هایی که به عنوان abstract تعریف شدند :: تابع getRecyclerViewItem: خروجی این تابع همان item view هر سطر از recyclerview می باشدتابع onItemGenerated: وقتی هرکدام از item ها ساخته شد چه اتفاقی بیافتدتابع bind: برای bind کردن مقادیر هر سطراگر به ورودی های این کلاس نگاه کنید میبینید که یک ورودی از نوع DiffUtilItemCallback و یک ورودی دیگر از نوع RecyclerViewItemClickListener دارد که یک interface می باشد. برای تعریف رویداد کلیک که روی هر سطر اتفاق می افتدinterface RecyclerViewItemClickListener<T> { fun (entity: T) }یک کلاس دیگر برای DiffUtil.Itelcallback درست کردم تا تفاوت آیتم ها را بر اساس منطقی که اختصاصا برای هر recyclerview تعریف می شود به عنوان ورودی دریافت می کند. (از Diffutil بیشتر بدانید )در اینجا هم چون برای هر Recyclerview بسته به entity آن منطق مختلفی برای مشخص کردن این تفاوت ها وجود دارد یک کلاس اصلی به اسم BaseDiffCallback درست کردم تا کلاس های DiffUtilCallback ازش ارث بری کنند class ProductAdapter(RecyclerViewItemClickListener: RecyclerViewItemClickListener<ProductEntity>) : BaseRecyclerViewAdapter<ProductEntity>(ProductAdapterOnDiffCallback(), RecyclerViewItemClickListener) { override fun getRecyclerViewItem(): Int { return R.layout.view_item_product } override fun onItemGenerated(position: Int, view: View) { } override fun bind(itemView: View, entity: ProductEntity) { itemView.findViewById<TextView>(R.id.textView).text = entity.title } }خب تا اینجا هر چیزی که مربوط به کلاس های اصلی بود رو توضیح دادم و نوشتم حالا بریم کلاس های فرزند که باید از این کلاس ها ارث بری کنند خب اول باید adapter مربوط به product recyclerview رو بسازیم ولی قبلش نیاز به DiffUtilItemCallback داریم برای product تا به Recyclerview بگیم که منطق تشخصی تفاوت دو آیتم به این صورت است، class ProductAdapterOnDiffCallback : BaseDiffCallback<ProductEntity>() { override fun checkItemsTheSame(oldItem: ProductEntity, newItem: ProductEntity): Boolean { return oldItem.id == newItem.id } override fun checkContentsTheSame(oldItem: ProductEntity, newItem: ProductEntity): Boolean { return oldItem.title === newItem.title } } حالا adapter مربوط به product رو میسازیم :class ProductAdapter(RecyclerViewItemClickListener: RecyclerViewItemClickListener<ProductEntity>) : BaseRecyclerViewAdapter<ProductEntity>(ProductAdapterOnDiffCallback(), RecyclerViewItemClickListener) { override fun getRecyclerViewItem(): Int { return R.layout.view_item_product } override fun onItemGenerated(position: Int, view: View) { } override fun bind(itemView: View, entity: ProductEntity) { itemView.findViewById<TextView>(R.id.textView).text = entity.title } }میبینید که اون سه تا تابع abstract رو اینجا override کردم و یادتون نره که یک view برای هر سطر بسازید و اینکه یک entity برای product به اسم ProductEntity بسازیدو بصورت زیر هم توی fragment یا activity تعریف کردم val categoryAdapter = ProductAdapter(object : RecyclerViewItemClickListener<ProductEntity> { override fun (entity: ProductEntity) { Log.d(&quotasd&quot, entity.toString()) } })Github Repository::: https://github.com/dousthagh/SingleRecyclerViewAdapter