ViewBinding与Kotlin委托

接上篇幅

自定义属性委托的用处很多,例如组合替代继承,给个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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值