Auswählen von recylerView-Elementen mithilfe von dataBinding

Hallo. Neulich stieß ich auf das Problem, mithilfe von dataBinding eine Auswahl mehrerer Elemente in RecyclerView zu implementieren.





Loslegen

Schreiben wir zunächst einen Basisadapter, der dataBinding unterstützt.






/**
 *    data binding'.
 * @param layoutRes id layout',     
 * @param lifecycleOwner lifecycle owner   ,    recycler view
 * @param itemBindingId id   layout',     
 * @param onClick ,     
 */
class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
    private val items = mutableListOf<Item>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        //  ViewDataBinding    layoutRes
        val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, layoutRes, parent, false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        //    onBind   
        val item = items[position]
        holder.onBind(item)
    }

    /**
     *    
     *
     * @param newItems  
     */
    fun setItems(newItems: List<Item>) {
        val diffUtilCallback = DiffUtilCallback(newItems)
        val diffResult = DiffUtil.calculateDiff(diffUtilCallback)
        items.apply {
            clear()
            addAll(newItems)
        }
        diffResult.dispatchUpdatesTo(this)
    }

    //    DataBinding'
    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)

                root.setOnClickListener { onClick?.invoke(item) }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }

    private inner class DiffUtilCallback(private val newItems: List<Item>) : DiffUtil.Callback() {
        override fun getOldListSize(): Int = itemCount
        override fun getNewListSize(): Int = newItems.size

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition].id == items[oldItemPosition].id
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition] == items[oldItemPosition]
        }
    }
}
      
      



Damit DiffUtil funktioniert, habe ich eine Schnittstelle erstellt, die zeigt, dass das Element ein eindeutiges Feld hat





/**
 *   ui ,   RecyclerViewAdapter.
 * @property id   
 */
interface IRecyclerViewItem {
    val id: Int
}
      
      



, . onClick onBind: (binding: ViewDataBinding) -> Unit, .





Selection helper

, SelectionHelper, dataBinding' .





, , , .





, , :





class SelectionHelper<T : IRecyclerViewItem> : ISelectionHelper<T>() {
    //    
    private val selectedItems = mutableMapOf<Int, T>()
   
    // ,     -  ,  - 
    override fun handleItem(item: T) {
        if (selectedItems[item.id] == null) {
            selectedItems[item.id] = item
        } else {
            selectedItems.remove(item.id)
        }
        // dataBining,     ui)
        notifyChange()
    }

    override fun isSelected(id: Int): Boolean = selectedItems.containsKey(id)
    override fun getSelectedItems(): List<T> = selectedItems.values.toList()
    override fun getSelectedItemsSize(): Int = selectedItems.size
}

//    BaseObservable,  ,   dataBinding    
//   
abstract class ISelectionHelper<T : IRecyclerViewItem> : BaseObservable() {
    abstract fun handleItem(item: T)
    abstract fun isSelected(id: Int): Boolean
    abstract fun getSelectedItems(): List<T>
    abstract fun getSelectedItemsSize(): Int
}
      
      



:





  • viewModel selectionHelper.getSelectedItems, .





  • DataBinding, - adapter





  • , onBind





:





  1. viewModel/presenter ,









  2. xml





- ,





adadpter

class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    //  ,     ,     
    private val selectionHelper: ISelectionHelper<Item>? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
  ...
	    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)
                selectionHelper?.let { setVariable(BR.selectionHelper, it) }

                root.setOnClickListener { 
                  	//  
                		selectionHelper?.handleItem(item)
                  	onClick?.invoke(item)
                }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }  
}
      
      



/ , , onBind, - .





class RecyclerViewAdapter<Item : IRecyclerViewItem>(
    @LayoutRes private val layoutRes: Int,
    private val lifecycleOwner: LifecycleOwner,
    private val itemBindingId: Int? = null,
    private val selectionHelper: ISelectionHelper<Item>? = null,
    private val onClick: ((Item) -> Unit)? = null
) : RecyclerView.Adapter<RecyclerViewAdapter<Item>.ViewHolder>() {
    private val items = mutableListOf<Item>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ViewDataBinding>(inflater, layoutRes, parent, false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = items[position]
        holder.onBind(item)
    }

    /**
     *    
     *
     * @param newItems  
     */
    fun setItems(newItems: List<Item>) {
        val diffUtilCallback = DiffUtilCallback(newItems)
        val diffResult = DiffUtil.calculateDiff(diffUtilCallback)
        items.apply {
            clear()
            addAll(newItems)
        }
        diffResult.dispatchUpdatesTo(this)
    }

    inner class ViewHolder(
        private val binding: ViewDataBinding
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(item: Item) {
            binding.apply {
                // 
                setVariable(itemBindingId ?: BR.item, item)
                selectionHelper?.let { setVariable(BR.selectionHelper, it) }

                root.setOnClickListener { 
                  	//  
                		selectionHelper?.handleItem(item)
                  	onClick?.invoke(item)
                }
                lifecycleOwner = this@RecyclerViewAdapter.lifecycleOwner
            }
        }
    }

    private inner class DiffUtilCallback(private val newItems: List<Item>) : DiffUtil.Callback() {
        override fun getOldListSize(): Int = itemCount
        override fun getNewListSize(): Int = newItems.size

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition].id == items[oldItemPosition].id
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return newItems[newItemPosition] == items[oldItemPosition]
        }
    }
}
      
      







, xml , selectionHelper xml





<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="item"
            type="ImageItemUi" />
				<!--    ) -->
        <variable
            name="selectionHelper"
            type="dev.syncended.ctime.utils.ui.ISelectionHelper&lt;ImageItemUi>" />

        <import type="dev.syncended.ctime.models.ui.ImageItemUi" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_margin="@dimen/ui_spacing_normal"
            android:padding="@dimen/1dp"
            android:scaleType="centerCrop"
            app:file="@{item.file}"
            app:item_id="@{item.id}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="1:1"
            app:layout_constraintTop_toTopOf="parent"
            app:selection_helper="@{selectionHelper}"
            tools:ignore="ContentDescription" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
      
      



padding = 1dp, , , .





bindingAdapter selectionHelper





//  selectionHelper',     id 
@BindingAdapter("selection_helper", "item_id", requireAll = true)
fun <T : IRecyclerViewItem> handleSelection(
    view: View,
    selectionHelper: ISelectionHelper<T>,
    itemId: Int
) {
    //   
    val isSelected = selectionHelper.isSelected(itemId)
    //     
    val color = if (isSelected) {
        R.color.color_primary
    } else {
        android.R.color.transparent
    }
    view.setBackgroundColor(ContextCompat.getColor(view.context, color))
}
      
      



, background.





:





:





. , , , , , .





android:checked=@{selectionHelper.isSelected(item.id)}
      
      



.





, , , .












All Articles