接上篇
自定义属性委托的用处很多,例如组合替代继承,给个ViewBinding
在Fragment
中的使用的例子:
委托:
/** * 自定义属性委托。 * * <p>......。</p> * <ul><li></li></ul> * <br> * <strong>Time</strong> 2020/5/18 14:54<br> * <strong>CopyRight</strong> 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> 2020/5/18 12:38<br> * <strong>CopyRight</strong> 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