प्रोस्टो: RecyclerView के साथ काम करते समय बॉयलरप्लेट हटा दें

हमारे द्वारा उपयोग किए जाने वाले डेटा की सूची प्रदर्शित करने के लिए RecyclerView(- धन्यवाद, कैप!)। वह बॉक्स और अन्य प्रसिद्ध ब्लोबैब्स से बहुत सी चीजें जानता है। लेकिन उसके साथ बहुत दर्द है। कोई भी एक ही बॉयलरप्लेट कोड लिखना पसंद नहीं करता है। और मैं वास्तव में नहीं हूँ ...



साजिश का एक संक्षिप्त इतिहास "कोड को थोड़ा कम करें":



उदाहरण के लिए, एक साधारण डेटा वर्ग व्यक्ति () बनाया गया था: पहला नाम, अंतिम नाम, ईमेल। मेल और कुत्ते की उपलब्धता।


लोगों की सूची प्रदर्शित करने के लिए, आपको बनाना होगा RecyclerView.Adapterऔर RecyclerView.ViewHolder, जिसका अधिकांश कोड समान है।


Adapter ViewHolder-, . , , ViewHolder, .


Adapter ViewHolder, .


RecyclerView.Adapter<RecyclerView.ViewHolder>


Adapter . , .
class ClassicAdapter : RecyclerView.Adapter<ClassicHolder>() {

    private val viewModel = PersonItemViewModel()

    private val data: List<Person>
        get() = viewModel.data

    fun setData(persons: List<Person>) {
        viewModel.data = persons
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ClassicHolder =
        ClassicHolder.create(parent)

    override fun getItemCount(): Int = data.size

    override fun onBindViewHolder(holder: ClassicHolder, position: Int) {
        holder.bind(viewModel, position)
    }
}

ViewHolder MVVM.
class ClassicHolder(private val binding: ItemPersonBinding) : RecyclerView.ViewHolder(binding.root) {
    fun bind(viewModel: PersonItemViewModel, position: Int) {
        binding.setVariable(BR.viewModel, viewModel)
        binding.setVariable(BR.position, position)
        binding.executePendingBindings()
    }

    companion object {
        fun create(parent: ViewGroup): ClassicHolder {
            val inflater = LayoutInflater.from(parent.context)
            val binding: ItemPersonBinding =
                DataBindingUtil.inflate(inflater, R.layout.item_person, parent, false)
            return ClassicHolder(binding)
        }
    }
}

item_person.xml binding-: ViewModel Position - RecyclerView.
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable
            name="position"
            type="Integer" />

        <variable
            name="viewModel"
            type="plus.yeti.prostoadapter.ui.main.PersonItemViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout>

        <TextView
            android:text="@{viewModel.getName(position)}"
            ... />

        <TextView
            android:text="@{viewModel.getEmail(position)}"
            .../>

        <ImageView
            app:visible="@{viewModel.hasDog(position)}" 
            .../>

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

PersonItemViewModel .
class PersonItemViewModel : ProstoViewModel<Person>() {
    override var data: List<Person> = emptyList()

    fun getName(position: Int) = data[position].lastName + ", " + data[position].firstName
    fun getEmail(position: Int) = data[position].email
    fun hasDog(position: Int): Boolean = data[position].hasDog
}

ProstoAdapter ProstoHolder


, Adapter ViewHolder , .


ProstoViewModel. , ProstoViewModel, , , . ViewModel -:


abstract class ProstoViewModel<T>: ViewModel() {
    abstract var data: List<T>
}

ProstoHolder


open class ProstoHolder<TBinding : ViewDataBinding>(val binding: TBinding) : RecyclerView.ViewHolder(binding.root) {
    open fun <TData, TViewModel : ProstoViewModel<TData>> bind(viewModel: TViewModel, position: Int) {
        binding.setVariable(BR.viewModel, viewModel)
        binding.setVariable(BR.position, position)
        binding.executePendingBindings()
    }

    companion object {
        fun <TBinding : ViewDataBinding> create(parent: ViewGroup, layoutId: Int): ProstoHolder<TBinding> {
            val inflater = LayoutInflater.from(parent.context)
            val binding: TBinding = DataBindingUtil.inflate(inflater, layoutId, parent, false)
            return ProstoHolder(binding)
        }
    }
}

, , ProstoAdapter:


abstract class ProstoAdapter<TBinding : ViewDataBinding, TData> : RecyclerView.Adapter<ProstoHolder<TBinding>>() {

    abstract val viewModel: ProstoViewModel<TData>
    abstract val layoutId: Int

    private var dataSize: Int = 0

    open fun setData(data: List<TData>) {
        this.dataSize = data.size
        viewModel.data = data
        notifyDataSetChanged()
    }

    open var onBind: ((ProstoHolder<TBinding>) -> Unit)? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProstoHolder<TBinding> =
        ProstoHolder.create(parent, layoutId)

    override fun getItemCount(): Int = dataSize

    override fun onBindViewHolder(holder: ProstoHolder<TBinding>, position: Int) {
        holder.bind(viewModel, position)
        onBind?.invoke(holder)
    }
}


Adapter-a ViewModel c , item's layout id Binding-, layout-, item-.


, :)


class MainFragment : Fragment() {
    private val adapter =
        object : ProstoAdapter<ItemPersonBinding, Person>() {
            override val viewModel = PersonItemViewModel()
            override val layoutId = R.layout.item_person
        }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        mainRecyclerView.adapter = adapter
    }

    fun setNewPersonList(persons: List<Person>){
        adapter.setData(personList)
    }
}

4 .


github.com/klukwist/Prosto


ViewHolder-.


:)


All Articles