ViewBinding與RecycleView(一)

如何使用

在Android Studio 3.6的穩(wěn)定版本中乞娄,我們就可以使用ViewBinding替代findViewById

官方介紹

另外關(guān)于ViewBindingKotlin 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)一步抽壬估础?......

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末郑现,一起剝皮案震驚了整個(gè)濱河市湃崩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌接箫,老刑警劉巖攒读,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異辛友,居然都是意外死亡薄扁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)废累,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泌辫,“玉大人,你說(shuō)我怎么就攤上這事九默≌鸱牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵驼修,是天一觀(guān)的道長(zhǎng)殿遂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乙各,這世上最難降的妖魔是什么墨礁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮耳峦,結(jié)果婚禮上恩静,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好驶乾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布邑飒。 她就那樣靜靜地躺著,像睡著了一般级乐。 火紅的嫁衣襯著肌膚如雪疙咸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天风科,我揣著相機(jī)與錄音撒轮,去河邊找鬼。 笑死贼穆,一個(gè)胖子當(dāng)著我的面吹牛题山,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播故痊,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼召边,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袋狞!你這毒婦竟也來(lái)了炮车?” 一聲冷哼從身側(cè)響起衷笋,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤客峭,失蹤者是張志新(化名)和其女友劉穎豫领,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體舔琅,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡等恐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了备蚓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片课蔬。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖郊尝,靈堂內(nèi)的尸體忽然破棺而出二跋,到底是詐尸還是另有隱情,我是刑警寧澤流昏,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布扎即,位于F島的核電站,受9級(jí)特大地震影響况凉,放射性物質(zhì)發(fā)生泄漏谚鄙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一刁绒、第九天 我趴在偏房一處隱蔽的房頂上張望闷营。 院中可真熱鬧,春花似錦知市、人聲如沸傻盟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)莫杈。三九已至互例,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筝闹,已是汗流浹背媳叨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留关顷,地道東北人糊秆。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像议双,于是被迫代替她去往敵國(guó)和親痘番。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容