Liaison de données légère pour Android

Bonjour chers lecteurs. Nous aimons tous et utilisons le DataBinding , que Google a introduit il y a quelques années, pour relier le modèle de données aux vues via le ViewModel. Dans cet article, je souhaite partager avec vous comment vous pouvez unifier ce processus en utilisant le langage Kotlin et adapter la création d'adaptateurs pour RecyclerView (ci-après RV), ViewPager et ViewPager2 en plusieurs lignes de code.





Pour commencer, nous développions des adaptateurs personnalisés créés par ViewHolders sous le capot, et leur écriture, et même plus de support, prenait beaucoup de temps. Voici un exemple d'adaptateur de VR typique:





class CustomAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {    
    
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView        
        init {
            textView = view.findViewById(R.id.textView)
        }
    }   
    
    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.text_row_item, viewGroup, false)        
      return ViewHolder(view)
    }    
     
    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {        // Get element from your dataset at this position and replace the
        viewHolder.textView.text = dataSet[position]
    }    
    
    override fun getItemCount() = dataSet.size
}
      
      



Au fur et à mesure que le projet se développe, il peut y avoir beaucoup plus de ces adaptateurs. Je me souviens qu'une fois, l'adaptateur était si énorme, plusieurs centaines de lignes de code, qu'il a fallu un temps colossal pour comprendre ce qui se passait là-bas, et encore plus pour ajouter quelque chose de nouveau, car il fonctionnait avec différents modèles de données, et aussi a dû créer des mappages différents pour chaque type de données. Pour être honnête, c'était dur.





DataBinding , , onCreateViewHolder



, LayoutInflater



, DataBindingUtil.inflate



, .





class BindingViewHolder(val binding: ItemTextRowBinding) : RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
        val binding = DataBindingUtil.inflate<ItemTextRowBinding>(LayoutInflater.from(parent.context), viewType, parent, false)
        val viewHolder = BindingViewHolder(binding)
        return viewHolder
}

override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {    
  holder.binding.setVariable(BR.item, dataSet[position])
}
      
      



, RV, , . BindingAdapter androidx.databinding. , , RV, - DataBindingRecyclerViewConfig



, .





, EasyRecyclerBinding. BindingAdapters ViewPager ViewPager2. :

1) , , , RV, - app:items



app:rv_config



.





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

    <data>
    	<variable
        	name="vm"
        	type="com.rasalexman.erb.ui.base.ExampleViewModel" />

        <variable
            name="rvConfig"
            type="com.rasalexman.easyrecyclerbinding.DataBindingRecyclerViewConfig" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:items="@{vm.items}"
            app:rv_config="@{rvConfig}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:listitem="@layout/item_recycler"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
      
      



ViewModel, , , RV, DataBindingRecyclerViewConfig.





//       
class ExampleViewModel : ViewModel() {  
   val items: MutableLiveData<MutableList<RecyclerItemUI>> = MutableLiveData()
}

data class RecyclerItemUI(
    val id: String,
    val title: String
)
      
      



, , .





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

    <data>

        <variable
            name="item"
            type="com.rasalexman.erb.models.RecyclerItemUI" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:drawable/list_selector_background">

        <TextView
            android:id="@+id/titleTV"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingTop="8dp"
            android:paddingEnd="16dp"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:text="@{item.title}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Hello world" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingEnd="16dp"
            android:paddingBottom="8dp"
            android:textColor="@color/gray"
            android:textSize="14sp"
            android:text="@{item.id}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/titleTV"
            tools:text="Hello world" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
      
      



2) dataBinding, - createRecyclerConfig<I : Any, BT : ViewDataBinding>



, DataBindingRecyclerViewConfig, id , , .





class RecyclerViewExampleFragment : BaseBindingFragment<RvExampleFragmentBinding, ExampleViewModel>() {    
  override val layoutId: Int get() = R.layout.rv_example_fragment
  override val viewModel: ExampleViewModel by viewModels()    
  
  override fun initBinding(binding: RvExampleFragmentBinding) {
        super.initBinding(binding)
        binding.rvConfig = createRecyclerConfig<RecyclerItemUI, ItemRecyclerBinding> {
            layoutId = R.layout.item_recycler
        		itemId = BR.item            
        }
    }
}
      
      



, , ViewModel RV. , onItemClick, onItemCreate, onItemBind



  .





, , EasyRecyclerBinding - IBindingModel



  layoutResId



, - id , .





data class RecyclerItemUI(    
		val id: String,
    val title: String
) : IBindingModel {    
    override val layoutResId: Int        
    		get() = R.layout.item_recycler
}
data class RecyclerItemUI2(
    val id: String,
    val title: String
) : IBindingModel {
    override val layoutResId: Int
        get() = R.layout.item_recycler2
}
      
      



, , - createRecyclerMultiConfig



, .





class RecyclerViewExampleFragment : BaseBindingFragment<RvExampleFragmentBinding, RecyclerViewExampleViewModel>() {    
  override val layoutId: Int get() = R.layout.rv_example_fragment
    override val viewModel: RecyclerViewExampleViewModel by viewModels()
    
    override fun initBinding(binding: RvExampleFragmentBinding) {
        super.initBinding(binding)
        binding.rvConfig = createRecyclerMultiConfig {
            itemId = BR.item
        }
    }
}

class RecyclerViewExampleViewModel : BasePagesViewModel() {
    open val items: MutableLiveData<MutableList<IBindingModel>> = MutableLiveData()
}
      
      



, RV, , , , , presentation . , , , , .







Un processus similaire pour créer des adaptateurs pour ViewPager et ViewPager2 est présenté dans un exemple sur github avec l'open source, un lien vers lequel j'ai publié à la fin de l'article. Pour le moment, la bibliothèque est toujours en cours de finalisation, et je voudrais recevoir des commentaires et des souhaits adéquats pour son développement ultérieur. Il comprend également des fonctions auxiliaires pour créer facilement des enchères, y compris en conjonction avec le ViewModel. (LayoutInflater.createBinding, Fragment.createBindingWithViewModel, etc.)





Merci d'avoir lu jusqu'au bout. Profitez du codage et de la bonne humeur)








All Articles