接上篇

自定义属性委托的用处很多,例如组合替代继承,给个ViewBindingFragment中的使用的例子:

委托:

/** * 自定义属性委托。 * * <p>......。</p> * <ul><li></li></ul> * <br> * <strong>Time</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020/5/18 14:54<br> * <strong>CopyRight</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020, tt.reducto<br> * * @version  : 1.0.0 * @author   : TT */fun <T> Fragment.viewBindingByLazy(initialise: () -> T): ReadOnlyProperty<Fragment, T> =    object : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver {        private var binding: T? = null        private var viewLifecycleOwner: LifecycleOwner? = null        private val mainHandler = Handler(Looper.getMainLooper())        init {            this@viewBindingByLazy                .viewLifecycleOwnerLiveData                .observe(this@viewBindingByLazy, Observer { newLifecycleOwner ->                    viewLifecycleOwner                        ?.lifecycle                        ?.removeObserver(this)                    viewLifecycleOwner = newLifecycleOwner.also {                        it.lifecycle.addObserver(this)                    }                })        }        @MainThread        override fun onDestroy(owner: LifecycleOwner) {            super.onDestroy(owner)            mainHandler.post {                binding = null            }        }        @MainThread        override fun getValue(            thisRef: Fragment,            property: KProperty<*>        ): T {            return this.binding ?: initialise().also {                this.binding = it            }        }    }

使用:

class MultipleFragment : Fragment(R.layout.fragment_multiple) {    .....    private val binding: FragmentMultipleBinding by viewBindingByLazy {        FragmentMultipleBinding.bind(            requireView()        )    }    .....}

注意添加依赖:

 implementation  "androidx.lifecycle:lifecycle-common-java8:2.2.0"

那我们这里同样利用自定义属性委托让ViewHolder可以存储和检索任何类型的任何属性

by Map委托

/** * * @receiver View * @param id Int * @param initializer Function0<T> * @return T 存储容器-> map */private inline fun <reified T> View.getOrPutTag(@IdRes id: Int, initializer: () -> T) =    getTag(id) as? T ?: initializer().also {        setTag(id, it)    }         /** *   */private inline val BindingViewHolder<*>.propertyByMap    get() = itemView.getOrPutTag<MutableMap<String, Any?>>(        R.id.recyclerview_view_binding_map,        ::mutableMapOf    )

避免重名

/** * 用于ViewBinding的通用ViewHolder。 *   * <p>对外提供数据与视图的匹配。</p> * <ul><li></li></ul> * <br> * <strong>Time</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020/5/18 12:38<br> * <strong>CopyRight</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020, tt.reducto<br> * * @version  : 1.0.0 * @author   : TT */open class BindingViewHolder<T : ViewBinding> private constructor(val binding: T) :    RecyclerView.ViewHolder(binding.root) {    constructor(        parent: ViewGroup,        creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T    ) : this(        creator(            LayoutInflater.from(parent.context),            parent,            false        )    )    /**     *  委托     *     * @param T itemView适配数据。     */    class BindingDataLazy<T> : ReadWriteProperty<BindingViewHolder<*>, T> {        override fun getValue(thisRef: BindingViewHolder<*>, property: KProperty<*>): T {            @Suppress("UNCHECKED_CAST")            return thisRef.propertyByMap[property.name] as T        }        override fun setValue(thisRef: BindingViewHolder<*>, property: KProperty<*>, value: T) {            thisRef.propertyByMap[property.name] = value        }    }}/** * * @receiver ViewGroup * @param creator 布局生成器 * @return BindingViewHolder<T> */fun <T : ViewBinding> ViewGroup.getViewHolder(    creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T): BindingViewHolder<T> =    BindingViewHolder(this, creator)/** * */private inline val BindingViewHolder<*>.propertyByMap    get() = itemView.getOrPutTag<MutableMap<String, Any?>>(        R.id.recyclerview_view_binding_map,        ::mutableMapOf    )/**/** * getTag()内部使用 { <code>private SparseArray<Object> mKeyedTags;</code> } 进行查找 * * @receiver View * @param id Int 保证ID唯一 * @param initializer 容器初始化 * @return T 存储容器-> map */private inline fun <reified T> View.getOrPutTag(@IdRes id: Int, initializer: () -> T) =    getTag(id) as? T ?: initializer().also {        setTag(id, it)    }

ViewPage2的Adapter写个简单item_multiple.xml

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:card_view="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent">    <androidx.cardview.widget.CardView        android:id="@+id/cd_item_multi"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginLeft="@dimen/pageMarginAndOffset"        android:layout_marginRight="@dimen/pageMarginAndOffset"        android:background="@color/cardview_light_background"        android:paddingLeft="8dp"        android:paddingRight="8dp"        app:cardUseCompatPadding="true"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf="parent"        card_view:cardCornerRadius="6dp">        <ImageView            android:id="@+id/img_item_multi"            android:layout_width="match_parent"            android:layout_height="180dp"            android:scaleType="fitXY" />    </androidx.cardview.widget.CardView>    <TextView        android:id="@+id/tv_item_multi"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:gravity="center"        android:text="1111"        android:textColor="@color/colorPrimaryDark"        android:textSize="16sp"        android:textStyle="bold"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toBottomOf="@id/cd_item_multi" />    <CheckBox        android:id="@+id/ck_item_multi"        app:layout_constraintTop_toBottomOf="@id/cd_item_multi"        app:layout_constraintEnd_toStartOf="@+id/tv_item_multi"        android:layout_width="48dp"        android:layout_height="48dp" /></androidx.constraintlayout.widget.ConstraintLayout>

数据源:

data class Category(val name: String,val id:Int)

Fragment或者Activity中:

private var BindingViewHolder<ItemMultipleBinding>.result by BindingViewHolder.BindingDataLazy<Category>()fun BindingViewHolder<ItemMultipleBinding>.bindView(data: Category) {    result = data    binding.tvItemMulti.text = result.name    binding.imgItemMulti.load(result.id)    binding.ckItemMulti.setOnCheckedChangeListener { buttonView, isChecked ->    }}

如何与Adapter结合?我们利用闭包写个简单的Adapter:

class BindingAdapter <ItemT : Any?, VH : RecyclerView.ViewHolder>(    private val itemsData: () -> List<ItemT>,    private val viewHolderCreator: (ViewGroup, Int) -> VH,    private val viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,    private val itemIdFunction: ((ItemT) -> Long)? = null) : RecyclerView.Adapter<VH>() {    init {        setHasStableIds(itemIdFunction != null)    }    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH = viewHolderCreator(parent, viewType)    override fun onBindViewHolder(holder: VH, position: Int) = viewHolderBinder(holder, itemsData()[position], position)    override fun getItemCount(): Int = itemsData().size    override fun getItemId(position: Int): Long =        itemIdFunction?.invoke(itemsData()[position]) ?: super.getItemId(position)}fun <ItemT, VH : RecyclerView.ViewHolder> adapterByBinding(    itemsData: () -> List<ItemT>,    viewHolderCreator: (ViewGroup, Int) -> VH,    viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,    itemIdFunction: ((ItemT) -> Long)? = null): RecyclerView.Adapter<VH> =    BindingAdapter(        itemsData,        viewHolderCreator,        viewHolderBinder,        itemIdFunction    )

如何使用?

 binding.vpMultiple.apply {            // 横向滑动            orientation = ViewPager2.ORIENTATION_HORIZONTAL            //            clipToPadding = false            //            clipChildren = false            // 可见页面数            offscreenPageLimit = 3                                    adapter = adapterByBinding(                itemsData = Test()::mCategoryList,                viewHolderCreator = { viewGroup, _ ->                    viewGroup.getViewHolder(ItemMultipleBinding::inflate).apply {                        itemView.setOnClickListener {                            toast("${this.adapterPosition}")                        }                    }                },                viewHolderBinder = { viewHolder, data, _ ->                    viewHolder.bindView(data)                }             )          .......       }    private var BindingViewHolder<ItemMultipleBinding>.result by BindingViewHolder.BindingDataLazy<Category>()fun BindingViewHolder<ItemMultipleBinding>.bindView(data: Category) {    result = data    binding.tvItemMulti.text = result.name    binding.imgItemMulti.load(result.id)    binding.ckItemMulti.setOnCheckedChangeListener { buttonView, isChecked ->    }}class Test {    val mCategoryList: MutableList<Category> = mutableListOf()    private val ids = arrayListOf(        R.mipmap.image1,        R.mipmap.image2,        R.mipmap.image3,        R.mipmap.image4,        R.mipmap.image5    )    init {        for (i in ids.indices) {            mCategoryList.add(i, Category("page_${i + 1}", ids[i]))        }    }}

如果需要支持DiffUtil或者多ItemType等等其他也可以加上 ,不过有了MergeAdapter更方便

参考:

ViewBindingPropertyDelegate

view-binding-internals