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
對象,並給它提供了Getter
和Setter
方法, 這個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官網也有很是詳細的文檔