ViewBinding与Kotlin委托

接上篇幅

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

委托:

/**
 * 自定义属性委托。
 *
 * 

......。

*
*
* Time    2020/5/18 14:54
* CopyRight    2020, tt.reducto
* * @version : 1.0.0 * @author : TT */ fun Fragment.viewBindingByLazy(initialise: () -> T): ReadOnlyProperty = object : ReadOnlyProperty, 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
 * @return T 存储容器-> map
 */
private inline fun  View.getOrPutTag(@IdRes id: Int, initializer: () -> T) =
    getTag(id) as? T ?: initializer().also {
        setTag(id, it)
    }
    
    
 /**
 *  
 */
private inline val BindingViewHolder<*>.propertyByMap
    get() = itemView.getOrPutTag>(
        R.id.recyclerview_view_binding_map,
        ::mutableMapOf
    )

避免重名


/**
 * 用于ViewBinding的通用ViewHolder。
 *  
 * 

对外提供数据与视图的匹配。

*
*
* Time    2020/5/18 12:38
* CopyRight    2020, tt.reducto
* * @version : 1.0.0 * @author : TT */ open class BindingViewHolder 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 : ReadWriteProperty, 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 */ fun ViewGroup.getViewHolder( creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T ): BindingViewHolder = BindingViewHolder(this, creator) /** * */ private inline val BindingViewHolder<*>.propertyByMap get() = itemView.getOrPutTag>( R.id.recyclerview_view_binding_map, ::mutableMapOf ) /** /** * getTag()内部使用 { private SparseArray mKeyedTags; } 进行查找 * * @receiver View * @param id Int 保证ID唯一 * @param initializer 容器初始化 * @return T 存储容器-> map */ private inline fun View.getOrPutTag(@IdRes id: Int, initializer: () -> T) = getTag(id) as? T ?: initializer().also { setTag(id, it) }

ViewPage2的Adapter写个简单item_multiple.xml




    

        
    

    

    


数据源:

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

Fragment或者Activity中:

private var BindingViewHolder.result by BindingViewHolder.BindingDataLazy()

fun BindingViewHolder.bindView(data: Category) {
    result = data
    binding.tvItemMulti.text = result.name
    binding.imgItemMulti.load(result.id)
    binding.ckItemMulti.setOnCheckedChangeListener { buttonView, isChecked ->

    }
}

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

class BindingAdapter (
    private val itemsData: () -> List,
    private val viewHolderCreator: (ViewGroup, Int) -> VH,
    private val viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,
    private val itemIdFunction: ((ItemT) -> Long)? = null
) : RecyclerView.Adapter() {

    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  adapterByBinding(
    itemsData: () -> List,
    viewHolderCreator: (ViewGroup, Int) -> VH,
    viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,
    itemIdFunction: ((ItemT) -> Long)? = null
): RecyclerView.Adapter =
    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.result by BindingViewHolder.BindingDataLazy()

fun BindingViewHolder.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 = 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

你可能感兴趣的:(Android,kotlin)