Android MVVM架構模式 詳解和綜合運用(四)

DataBinding結合RecyclerView使用

在日常的開發中,使用最頻繁的組件莫過於列表控件了,例如RecyclerView。DataBinding庫也添加了對RecyclerView的adapter的支持,因爲adapter是用來管理和分配RecyclerView中數據的組件。
使用DataBinding來開發RecyclerView很簡單,首先創建一個item的佈局,在這個佈局中聲明一個user變量:

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="user"
            type="bean.User"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:textColor="@android:color/black"
            android:text="@{user.name}"
            tools:text="Jason"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:textColor="@android:color/darker_gray"
            android:text="@{String.valueOf(user.age)}"
            tools:text="12"/>

    </LinearLayout>

</layout>

然後創建一個適配器ListAdapter,只需要繼承RecyclerView的Adapter即可,重點在ViewHolder的創建。我們在ListAdapter創建了一個靜態類ListHolder,這個ListHolder使用了上面佈局自動生成的ListItemBinding類,在create方法中初始化binding,並解析佈局,但是卻沒有引用到任何佈局名,原因就是ListItemBinding中已經封裝了它綁定的佈局名了。然後在super父類方法中初始化,從binding的getRoot方法中獲取item佈局。最後在bindTo方法中綁定每個item的數據源,注意必須調用executePendingBindings。例如:

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ListHolder>{
    private List<User> data;

    public ListAdapter(ArrayList<User> list){
        this.data=list;
    }

    @Override
    public ListHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return ListHolder.create(LayoutInflater.from(parent.getContext()),parent);
    }

    @Override
    public void onBindViewHolder(ListHolder holder, int position) {
        holder.bindTo(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

     static class ListHolder extends RecyclerView.ViewHolder{
        private ListItemBinding binding;

        public static ListHolder create(LayoutInflater inflater,ViewGroup parent){
            ListItemBinding binding=ListItemBinding.inflate(inflater,parent,false);
            return new ListHolder(binding);
        }

        private ListHolder(ListItemBinding binding) {
            super(binding.getRoot());
            this.binding=binding;
        }

        public void bindTo(User user){
            binding.setUser(user);
            binding.executePendingBindings();
        }

    }
}

executePendingBindings方法是用來通知View立即刷新視圖的,因爲當變量或者observable數據源改變的時候,binding會在下一幀纔開始刷新View,這可能會導致延遲,因爲當快速刷新列表的時候,對View的刷新必須立即實時的。所以爲了強制系統對View進行刷新,必須在設置數據源後調用binding.executePendingBindings()。
然後在Activity中對recyclerView綁定adapater就可以了,和平時的使用方法一毛一樣。
首先是activity的佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/ListActivity_RecyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </LinearLayout>

</layout>

然後在activity中使用binding.ID名來獲取在layout中聲明id的控件,而不用findViewById了,炒雞方便:

public class ListActivity extends BaseActivity {
    private ActivityListBinding binding;
    private ListAdapter adapter;

    @Override
    protected void initView() {
        binding= DataBindingUtil.setContentView(this, R.layout.activity_list);
    }

    @Override
    protected void initData() {
        ArrayList<User> data=new ArrayList<>();
        data.add(new User("Jason",23));
        data.add(new User("Sakura",20));
        data.add(new User("bilibi",18));
        data.add(new User("Afun",17));
        data.add(new User("Tom",26));

        adapter=new ListAdapter(data);
        LinearLayoutManager manager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);
        binding.ListActivityRecyclerView.setLayoutManager(manager);
        binding.ListActivityRecyclerView.setAdapter(adapter);
    }

    @Override
    protected void initListener() {

    }
}

可以在後臺子線程中改變任何一個非集合model對象中的數據,DataBinding庫會運行時局部化對象中的變量或屬性以避免併發問題的發生。

轉換器

  • 自動轉換
    當在Binding表達式@{ }中返回一個對象時,例如@{ user },DataBinding庫會去尋找與這個對象的數據類型一致的View屬性的setter方法,例如android:text=”@{ user.age},age爲int型時,會去尋找setText(int)的方法。
    當對象中屬性爲Object類型時,DataBinding會根據原始setter方法參數類型來強制類型轉換Object, 例如在people對象中有一個Object屬性:
  • public class People extends BaseObservable{
        private String firstName;
        private String lastName;
        private Object convert;
    
        @Bindable
        public String getFirstName(){
            return firstName;
        }
    
        @Bindable
        public String getLastName(){
            return lastName;
        }
    
        public void setFirstName(String firstName){
            this.firstName=firstName;
            notifyPropertyChanged(BR.firstName);
        }
    
        public void setLastName(String lastName){
            this.lastName=lastName;
            notifyPropertyChanged(BR.lastName);
        }
    
        public Object getConvert() {
            return convert;
        }
    
        public void setConvert(Object convert) {
            this.convert = convert;
        }
    }

    在Layout佈局文件的TextView中關聯這個people對象,例如:

                <TextView
                    android:textSize="14sp"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="@{people.convert}"
                    tools:text="Jason"/>

    setText(CharSequence)中的參數類型爲CharSequence,那麼這個people.convert的Object類型的對象會被強制類型轉換爲CharSequence。
    當然我們也可以在@{ }表達式中進行顯式強制類型轉換,例如:

    android:text="@{(String)people.convert}"
    android:text="@{String.valueOf(people.convert)}"

  • 自定義轉換器
    有時在Binding@{ }表達式中特定類型之間的轉換是自動的,例如,設置View的background屬性:
  • <View
       android:background="@{isError ? @color/red : @color/white}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

    android:background屬性的參數類型爲Drawable,但是這裏@color/red爲int類型。但是DataBinding自動把int類型轉換爲了ColorDrawable對象類型。這個轉換系統已經幫我們實現了,主要是使用@BindingConversion 註解一個靜態方法來實現的,例如:

    @BindingConversion
    public static ColorDrawable convertColorToDrawable(int color) {
       return new ColorDrawable(color);
    }

    注意表達使中不支持混用的數據類型,例如 下面的做法是錯誤的:

    <View
       android:background="@{isError ? @drawable/error : @color/white}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    發佈了76 篇原創文章 · 獲贊 68 · 訪問量 19萬+
    發表評論
    所有評論
    還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
    相關文章