Enlace de datos ligero para Android

Hola queridos lectores. A todos nos encanta y usamos DataBinding , que Google introdujo hace unos años, para vincular el modelo de datos a las vistas a través de ViewModel. En este artículo, quiero compartir con ustedes cómo se puede unificar este proceso usando el lenguaje Kotlin, y ajustar la creación de adaptadores para RecyclerView (en adelante RV), ViewPager y ViewPager2 en varias líneas de código.





Para empezar, solíamos desarrollar adaptadores personalizados que fueron creados por ViewHolders bajo el capó, y su escritura, e incluso más soporte, tomó bastante tiempo. A continuación se muestra un ejemplo de un adaptador de RV típico:





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
}
      
      



A medida que el proyecto crece, puede haber muchos más de estos adaptadores. Recuerdo que una vez, el adaptador era tan grande, varios cientos de líneas de código, que tomó una cantidad enorme de tiempo averiguar qué estaba sucediendo allí, y aún más agregar algo nuevo, ya que funcionaba con diferentes modelos de datos, y también tuvo que crear diferentes asignaciones para cada tipo de datos. Para ser honesto, fue difícil.





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 proceso similar para crear adaptadores para ViewPager y ViewPager2 se presenta en un ejemplo en github junto con el código abierto, un enlace al que publiqué al final del artículo. Por el momento, la biblioteca aún se está finalizando, y me gustaría recibir comentarios adecuados y deseos para su posterior desarrollo. También incluye funciones auxiliares para crear cómodamente licitaciones, incluso en conjunto con ViewModel. (LayoutInflater.createBinding, Fragment.createBindingWithViewModel, etc.)





Gracias por leer hasta el final. Disfruta la codificación y el buen humor)








All Articles