接上篇幅
自定义属性委托的用处很多,例如组合替代继承,给个ViewBinding
在Fragment
中的使用的例子:
委托:
/**
* 自定义属性委托。
*
* ......。
*
*
* 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
} 进行查找
*
* @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