如何使用
在Android Studio 3.6的穩(wěn)定版本中乞娄,我們就可以使用ViewBinding替代findViewById
另外關(guān)于ViewBinding
與Kotlin Android Extensions
的區(qū)分這里不多做介紹,
可以參考下stackoverflow中的討論
ViewBinding如何使用?如果是Kotlin DSL的話(huà)這樣添加:
android {
...
viewBinding.isEnabled = true
}
否則:
android {
...
viewBinding {
enabled = true
}
}
簡(jiǎn)單例子
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
app:layout_constraintTop_toTopOf="parent"
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
然后在activity中:
private lateinit var mBinding: ActivityTabBinding
override 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
類(lèi)
// 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結(jié)合
關(guān)注下ActivityTabBinding
類(lèi)中的inflate
方法是不是跟我們RecycleView中的onCreateViewHolder
方法特別像?創(chuàng)建View root
時(shí)都不會(huì)將其添加到父對(duì)象ViewGroup
上茴恰,一般我們創(chuàng)建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類(lèi)傳入ViewBinding引用 :
open class BaseBindingViewHolder<T : ViewBinding> 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))
}
我們?cè)俳oViewGroup提供一個(gè)擴(kuò)展方法省去ViewHolder在onCreateViewHolder
中的創(chuàng)建 :
fun <T : ViewBinding> ViewGroup.getViewHolder(
creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T
): BaseBindingViewHolder<T> = BaseBindingViewHolder(this, creator)
利用ViewBinding 一個(gè)簡(jiǎn)單的Adapter就這樣:
CategoryItemBinding
是根據(jù)xml文件自動(dòng)生成的
class CategoryAdapter : RecyclerView.Adapter<BaseBindingViewHolder<CategoryItemBinding>>() {
private var list: List<Category> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseBindingViewHolder<CategoryItemBinding> {
return parent.getViewHolder(CategoryItemBinding::inflate)
}
override fun onBindViewHolder(holder: BaseBindingViewHolder<CategoryItemBinding>, position: Int) {
holder.mBinding.categoryName.text = list[position].name
}
fun setItem(list: List<Category>) {
this.list = list
notifyDataSetChanged()
}
override fun getItemCount(): Int = list.size
}
綜上,這些是比較簡(jiǎn)單的操作..
自定義kotlin屬性委托
kotlin源碼中的實(shí)現(xiàn)判空的委托屬性:
/**
* 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 <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
......
}
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public 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 = value
}
}
其中 NotNullVar 繼承了 ReadWriteProperty,并實(shí)現(xiàn)了他的兩個(gè)方法沛厨,而Delegates.notNull() 屬于委托屬性。
看一個(gè)自定義委托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 <T: View> bindView( id : Int): FindView<T> = FindView(id)
class FindView<T : View >(val id:Int) : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
if(this.value == null) {
this.value = (thisRef as Activity).findViewById<T>(id)
}
return this.value?:throw RuntimeException("can not find this view")
}
var value : T? = null
}
如果我們把itemView與數(shù)據(jù)源的綁定通過(guò)自定義委托來(lái)代理摔认,那是不是會(huì)方便很多逆皮??
屬性?xún)?chǔ)存在映射中
簡(jiǎn)單說(shuō)就是在一個(gè)map里存儲(chǔ)屬性的值参袱,可以使用映射實(shí)例自身作為委托來(lái)實(shí)現(xiàn)委托屬性电谣。例如json解析
那itemView的setTag與getTag是否可以放在MutableMap中進(jìn)行處理?
未完待續(xù)
adapter中還有大量工作需要去做抹蚀,比如itemView的setTag剿牺、OnClickListener()、ViewHolder中進(jìn)行數(shù)據(jù)源與itemView的綁定环壤,那么如何利用kotlin特性將這些行為進(jìn)一步抽壬估础?......