Android官方數據綁定框架DataBinding(三)

11、 Data Binding VS RecyclerView 
有了上面的思路,你們是否是也會在ListView和RecyclerView中使用了?咱們僅以一個RecyclerView來學習一下。 
首先來看看item的佈局,java

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
  
    <data>  
        <variable  
            name="stu"  
            type="org.loader.app6.Student" />  
    </data>  
  
    <RelativeLayout  
        android:layout_width="match_parent"  
        android:layout_height="match_parent">  
  
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{stu.name}"  
            android:layout_alignParentLeft="true"/>  
  
        <TextView  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="@{String.valueOf(stu.age)}"  
            android:layout_alignParentRight="true"/>  
  
    </RelativeLayout>  
</layout>

能夠看到,仍是用了那個Student實體,這樣得代碼,相信你也已經看煩了吧。 
那咱們來看看activity的。android

private RecyclerView mRecyclerView;  
private ArrayList<Student> mData = new ArrayList<Student>() {  
    {  
        for (int i=0;i<10;i++) add(new Student("loader" + i, 18 + i));  
    }  
};  
  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
  
    mRecyclerView = (RecyclerView) findViewById(R.id.recycler);  
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this,  
            LinearLayoutManager.VERTICAL, false));  
    mRecyclerView.setAdapter(new MyAdapter(mData));  
}

這裏給RecyclerView設置了一個Adapter,相信最主要的代碼就在這個Adapter裏。數組

private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {  
  
    private ArrayList<Student> mData = new ArrayList<>();  
  
    private MyAdapter(ArrayList<Student> data) {  
        mData.addAll(data);  
    }  
  
    @Override  
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {  
        ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater  
                .from(viewGroup.getContext()), R.layout.item, viewGroup, false);  
        ViewHolder holder = new ViewHolder(binding.getRoot());  
        holder.setBinding(binding);  
        return holder;  
    }  
  
    @Override  
    public void onBindViewHolder(ViewHolder viewHolder, int i) {  
        viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
        viewHolder.getBinding().executePendingBindings();  
    }  
  
    @Override  
    public int getItemCount() {  
        return mData.size();  
    }  
  
    class ViewHolder extends RecyclerView.ViewHolder {  
  
        private ViewDataBinding binding;  
  
        public ViewHolder(View itemView) {  
            super(itemView);  
        }  
  
        public void setBinding(ViewDataBinding binding) {  
            this.binding = binding;  
        }  
  
        public ViewDataBinding getBinding() {  
            return this.binding;  
        }  
    }

果真,這個adapter的寫法和咱們以前的寫法不太同樣,首先看看ViewHolder,在這個holder裏,咱們保存了一個ViewDataBinding對象,並給它提供了GetterSetter方法, 這個ViewDataBinding是幹嗎的?咱們稍後去講。繼續看看onCreateViewHolder,在這裏面,咱們首先調用DataBindingUtil.inflate方法返回了一個ViewDataBinding的對象,這個ViewDataBinding是個啥?咱們之前沒見過啊,這裏告訴你們咱們以前返回的那些都是ViewDataBinding的子類!繼續看代碼,咱們new了一個holder,參數是確定是咱們的item佈局了,繼續看,接着咱們又把binding設置給了holder,最後返回holder。這時候,咱們的holder裏就保存了剛剛返回的ViewDataBinding對象,幹嗎用呢?繼續看onBindViewHolder就知道了。網絡

@Override  
public void onBindViewHolder(ViewHolder viewHolder, int i) {  
    viewHolder.getBinding().setVariable(org.loader.app6.BR.stu, mData.get(i));  
    viewHolder.getBinding().executePendingBindings();  
}

只有兩行代碼,可是都是咱們沒有見過的,首先第一行,咱們之前都是使用相似binding.setStu這樣方法去設置變量,那這個setVariable呢? 爲何沒有setStu,這裏要記住,ViewDataBinding是咱們以前用的那些binding的父類,只有自動生成的那些子類纔會有setXXX方法,那如今咱們須要在ViewDataBinding中設置變量咋辦?這個類爲咱們提供了setVariable去設置變量,第一個參數是咱們的變量名的引用,第二個是咱們要設置的值。第二行代碼,executePendingBindings的做用是幹嗎的?官方的回答是:
當數據改變時,binding會在下一幀去改變數據,若是咱們須要當即改變,就去調用executePendingBindings方法。app

因此這裏的做用就是去讓數據的改變當即執行。 
ok,如今看起來,咱們的代碼更加簡潔了,並且不須要保存控件的實例,是否是很爽? 來看看效果:框架

12、 View with ID 
在使用Data Binding的過程當中,咱們發現並無保存View的實例,可是如今咱們有需求須要這個View的實例咋辦?難道走老路findViewById?固然不是啦,當咱們須要某個view的實例時,咱們只要給該view一個id,而後Data Binding框架就會給咱們自動生成該view的實例,放哪了?固然是ViewDataBinding裏面。 
上代碼:ide

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
    <data class=".Custom">  
        <variable  
            name="str"  
            type="android.databinding.ObservableField<String>" />  
        <variable  
            name="handler"  
            type="org.loader.app7.MainActivity" />  
    </data>  
  
    <TextView  
        android:id="@+id/textView"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="@{str.get}"  
        android:onClick="@{handler.click}"/>  
</layout>

xml中代碼沒有什麼好說的,都是以前的代碼,若是在這有點迷糊,建議你仍是回頭看看上篇博客。須要注意的是, 
咱們給TextView了一個id-textView。 
activity,佈局

public class MainActivity extends AppCompatActivity {  
  
    private org.loader.app7.Custom mBinding;  
    private ObservableField<String> mString;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        mBinding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        mString = new ObservableField<String>();  
        mString.set("loader");  
        mBinding.setStr(mString);  
        mBinding.setHandler(this);  
    }  
  
    public void click(View view) {  
        mString.set("qibin");  
        mBinding.textView.setTextColor(Color.GREEN);  
    }  
}

經過ViewDataBinding類的實例直接去獲取的。學習

只要咱們給了view一個id,那麼框架就會在ViewDataBinding中自動幫咱們保存這個view的實例,變量名就是咱們設置的id。this

十3、 自定義setter 
想一想這樣的一種情景,一個ImageView須要經過網絡去加載圖片,那咱們怎麼辦?看似好像使用DataBinding不行,恩,咱們上面所學到東西確實不可以解決這個問題,可是DataBinding框架給咱們提供了很好的擴展,容許咱們自定義setter,那該怎麼作呢?這裏就要引出另外一個知識點——BindingAdapter,這是一個註解,參數是一個數組,數組中存放的是咱們自定義的’屬性’。接下來就以一個例子學習一下BindingAdapter的使用。

<layout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto">  
    <data class=".Custom">  
        <variable  
            name="imageUrl"  
            type="String" />  
    </data>  
  
    <ImageView  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        app:image="@{imageUrl}"/>  
</layout>

這裏咱們增長了一個命名空間app,而且注意ImageView的app:image屬性,這裏和咱們自定義view時自定義的屬性同樣,可是這裏並不須要咱們去重寫ImageView,這條屬性的值是咱們上面定義的String類型的imageUrl,從名稱中看到這裏咱們可能會塞給他一個url。 
activity,

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        org.loader.app8.Custom binding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        binding.setImageUrl("http://images.csdn.net/20150810/Blog-Image%E5%89%AF%E6%9C%AC.jpg");  
    }  
}

果真在這裏咱們set了一個url,那圖片怎麼加載呢?這裏就要使用到咱們剛纔說的BindingAdapter註解了。

public class Utils {  
    @BindingAdapter({"bind:image"})  
    public static void imageLoader(ImageView imageView, String url) {  
        ImageLoaderUtils.getInstance().displayImage(url, imageView);  
    }  
}

咱們定義了一個Utils類,這個類你能夠隨便起名,該類中只有一個靜態的方法imageLoader,該方法有兩個參數,一個是須要設置數據的view, 
一個是咱們須要的url。值得注意的是那個BindingAdapter註解,看看他的參數,是一個數組,內容只有一個bind:image,僅僅幾行代碼,咱們不須要 
手工調用Utils.imageLoader,也不須要知道imageLoader方法定義到哪了,一個網絡圖片加載就搞定了,是否是很神奇,這裏面起關鍵做用的就是BindingAdapter 
註解了,來看看它的參數怎麼定義的吧,難道是亂寫?固然不是,這裏要遵循必定的規則,

以bind:開頭,接着書寫你在控件中使用的自定義屬性名稱。

這裏就是image了,不信來看。

<ImageView  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    app:image="@{imageUrl}"/>

看看運行結果:

十4、 Converters 
Converter是什麼呢?舉個例子吧:假如你的控件須要一個格式化好的時間,可是你只有一個Date類型額變量咋辦?確定有人會說這個簡單,轉化完成後在設置,恩,這也是一種辦法,可是DataBinding還給咱們提供了另一種方式,雖然原理同樣,可是這種方式使用的場景更多,那就是——Converter。和上面的BindingAdapter使用方法同樣,這也是一個註解。下面仍是以一段代碼的形式進行學習。

<layout xmlns:android="http://schemas.android.com/apk/res/android">  
    <data class=".Custom">  
        <variable  
            name="time"  
            type="java.util.Date" />  
    </data>  
  
    <TextView  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="@{time}"/>  
</layout>

看TextView的text屬性,咱們須要一個String類型的值,可是這裏確給了一個Date類型的,這就須要咱們去定義Converter去轉換它, 
activity,

public class MainActivity extends AppCompatActivity {  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        org.loader.app9.Custom binding = DataBindingUtil.setContentView(this,  
                R.layout.activity_main);  
        binding.setTime(new Date());  
    }  
}

去給這個Date類型的變量設置值。怎麼去定義Converter呢? 看代碼:

public class Utils {  
  
    @BindingConversion  
    public static String convertDate(Date date) {  
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
        return sdf.format(date);  
    }  
}

和上面同樣,咱們不須要關心這個convertDate在哪一個類中,重要的是他的@BindingConversion註解,這個方法接受一個Date類型的變量,正好咱們的android:text設置的就是一個Date類型的值,在方法內部咱們將這個Date類型的變量轉換成String類型的日期而且返回。這樣UI上就顯示出咱們轉化好的字符串。 
看看效果:

好了,到這裏DataBinding的知識咱們就算學習完了,在學完以後發現這東西也沒什麼難度,學會使用就ok了,並且android官網也有很是詳細的文檔

相關文章
相關標籤/搜索