android|ViewBinding与RecycleView(一)

如何使用 在Android Studio 3.6的稳定版本中,我们就可以使用ViewBinding替代findViewById
官方介绍
另外关于ViewBindingKotlin Android Extensions 的区分这里不多做介绍,
可以参考下stackoverflow中的讨论
ViewBinding如何使用?如果是Kotlin DSL的话这样添加:

android { ... viewBinding.isEnabled = true}

否则:
android { ... viewBinding { enabled = true } }

简单例子

然后在activity中:
private lateinit var mBinding: ActivityTabBindingoverride fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mBinding = ActivityTabBinding.inflate(layoutInflater) setContentView(mBinding.root) attachTabsOnViewPager2() }

app/buildle/generated/data_binding_base_class_source_out/...目录看下生成的ActivityTabBinding
// Generated by view binder compiler. Do not edit! package tt.reducto.instantsearch.databinding; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.viewbinding.ViewBinding; import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; import java.lang.NullPointerException; import java.lang.Override; import java.lang.String; import tt.reducto.instantsearch.R; public final class ActivityTabBinding implements ViewBinding { @NonNull private final ConstraintLayout rootView; @NonNull public final TabLayout tabs; @NonNull public final ViewPager2 vp2; private ActivityTabBinding(@NonNull ConstraintLayout rootView, @NonNull TabLayout tabs, @NonNull ViewPager2 vp2) { this.rootView = rootView; this.tabs = tabs; this.vp2 = vp2; }@Override @NonNull public ConstraintLayout getRoot() { return rootView; }@NonNull public static ActivityTabBinding inflate(@NonNull LayoutInflater inflater) { return inflate(inflater, null, false); }@NonNull public static ActivityTabBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) { View root = inflater.inflate(R.layout.activity_tab, parent, false); if (attachToParent) { parent.addView(root); } return bind(root); }@NonNull public static ActivityTabBinding bind(@NonNull View rootView) { // The body of this method is generated in a way you would not otherwise write. // This is done to optimize the compiled bytecode for size and performance. String missingId; missingId: { TabLayout tabs = rootView.findViewById(R.id.tabs); if (tabs == null) { missingId = "tabs"; break missingId; } ViewPager2 vp2 = rootView.findViewById(R.id.vp2); if (vp2 == null) { missingId = "vp2"; break missingId; } return new ActivityTabBinding((ConstraintLayout) rootView, tabs, vp2); } throw new NullPointerException("Missing required view with ID: ".concat(missingId)); } }

与RecycleView结合 关注下ActivityTabBinding类中的inflate方法是不是跟我们RecycleView中的onCreateViewHolder方法特别像?创建View root时都不会将其添加到父对象ViewGroup上,一般我们创建ViewHolder像这样:
class CategoryViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { constructor(parent: ViewGroup) : this(LayoutInflater.from(parent.context).inflate(R.layout.category_item, parent, false))fun bind(category: Category) { itemView.categoryName.text = category.name itemView.categoryID.text = " " } }

所以我们可以给自定义ViewHolder类传入ViewBinding引用 :
open class BaseBindingViewHolder private constructor(val mBinding: T) : RecyclerView.ViewHolder(mBinding.root) { // constructor( parent: ViewGroup, creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T ) : this(creator(LayoutInflater.from(parent.context), parent, false))}

我们再给ViewGroup提供一个扩展方法省去ViewHolder在onCreateViewHolder中的创建 :
fun ViewGroup.getViewHolder( creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T ): BaseBindingViewHolder = BaseBindingViewHolder(this, creator)

利用ViewBinding 一个简单的Adapter就这样:
CategoryItemBinding是根据xml文件自动生成的
class CategoryAdapter : RecyclerView.Adapter>() { private var list: List = listOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseBindingViewHolder { return parent.getViewHolder(CategoryItemBinding::inflate) }override fun onBindViewHolder(holder:BaseBindingViewHolder, position: Int) { holder.mBinding.categoryName.text = list[position].name }fun setItem(list: List) { this.list = list notifyDataSetChanged() }override fun getItemCount(): Int = list.size }

综上,这些是比较简单的操作..
自定义kotlin属性委托 kotlin源码中的实现判空的委托属性:
/** * Standard property delegates. */ public object Delegates {/** * Returns a property delegate for a read/write property with a non-`null` value that is initialized not during * object construction time but at a later time. Trying to read the property before the initial value has been * assigned results in an exception. * * @sample samples.properties.Delegates.notNullDelegate */ public fun notNull(): ReadWriteProperty = NotNullVar()......}private class NotNullVar() : ReadWriteProperty { private var value: T? = nullpublic override fun getValue(thisRef: Any?, property: KProperty<*>): T { return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") }public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { this.value = https://www.it610.com/article/value } }

其中 NotNullVar 继承了 ReadWriteProperty,并实现了他的两个方法,而Delegates.notNull() 属于委托属性。
看一个自定义委托findViewById的例子:
class MainActivity : AppCompatActivity(){private val etSearch : FixedKeyboardEditText by bindView(R.id.et_search)override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) etSearch.setText("test") } }fun bindView( id : Int): FindView = FindView(id)class FindView(val id:Int) : ReadOnlyProperty {override fun getValue(thisRef: Any?, property: KProperty<*>): T {if(this.value =https://www.it610.com/article/= null) { this.value = (thisRef as Activity).findViewById(id) } return this.value?:throw RuntimeException("can not find this view") }var value : T? = null }

如果我们把itemView与数据源的绑定通过自定义委托来代理,那是不是会方便很多??
属性储存在映射中
简单说就是在一个map里存储属性的值,可以使用映射实例自身作为委托来实现委托属性。例如json解析
那itemView的setTag与getTag是否可以放在MutableMap中进行处理?
未完待续 【android|ViewBinding与RecycleView(一)】adapter中还有大量工作需要去做,比如itemView的setTag、OnClickListener()、ViewHolder中进行数据源与itemView的绑定,那么如何利用kotlin特性将这些行为进一步抽取?......

    推荐阅读