閱讀這篇筆記你需要了解安卓的數(shù)據(jù)綁定框架databinding
首先貼上校長看到的感覺寫得最好的兩篇 介紹databinding的文章:
1. CornorLin:Android Data Binding 系列(一) -- 詳細(xì)介紹與使用
2. QQ音樂技術(shù)團(tuán)隊(duì):Android DataBinding 數(shù)據(jù)綁定
不管作為一名安卓還是android程序猿昨登,總是少不了一直沒完沒了的重復(fù)制造adapter禽捆,viewholder,就像
等等,啊喂,這根本是同一個(gè)類呀!哦不好意思代虾,實(shí)在太像了学辱,搞錯(cuò)了,真實(shí)的情況是這樣的:
這簡直可以做一個(gè)找茬游戲了,整天在這弄重復(fù)的代碼,不禁要想妆偏,除了那些骯臟(滑稽)的大洋我們整天這樣圖的是什么挪鹏。作為一名有追求的立志成為一名架構(gòu)師返顺,從來懶得多寫代碼的程序猿這樣的情況怎么能忍!
于是不禁讓人想我們寫這些代碼屎味的什么?
Adapter與ViewHolder的作用:
-
ViewHolder
是用來通過findviewById來存放item對(duì)應(yīng)的layout里邊的View控件的, -
Adapter
中onCreateViewHolder(ViewGroup parent, int viewType)
方法負(fù)責(zé)將獲取將ViewHolder取出;void onBindViewHolder(BindingHolder holder, int position)
負(fù)責(zé)將實(shí)體類的內(nèi)容一條一條的通過set方法顯示到對(duì)應(yīng)的界面上。
再看看databinding的作用:
- 通過DatabindingUtils的
public static <T extends ViewDataBinding> T inflate(LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent)
方法獲取一個(gè)ViewDataBinding
,包含了Layout中所有的控件; -
boolean setVariable(int variableId, Object value)
負(fù)責(zé)將數(shù)據(jù)與界面綁定自動(dòng)完成類似textview.setText(item.text)
這樣的工作妙色,
是不是感覺職能高度重合呢煌珊,而且databinding好像用起來更省力,那是不是可以利用ViewDataBinding
替代ViewHoldr里邊的沒完沒了的findViewById呢,能不能用一個(gè)boolean setVariable(int variableId, Object value)
來替代onBindViewHolder(BindingHolder holder, int position)
中沒完沒了的set房呢。答案是可以的
現(xiàn)在假定你已經(jīng)閱讀過那兩篇文章,對(duì)于databinding有了一定的理解溢吻。先上demo的代碼地址
先看一下效果圖:
可以看出來在demo中有三種item且蓬,按照以前的慣例。我們需要三個(gè)Adapter焦匈,三個(gè)ViewHolder,三個(gè)實(shí)體Bean命辖,三個(gè)layout文件。但是呢,讓我們看一下demode代碼結(jié)構(gòu)
三個(gè)實(shí)體bean昧辽,三個(gè)layout框咙,但是只有一個(gè)Adapter者铜,里邊有一個(gè)ViewHolder如蚜。但是實(shí)現(xiàn)了三種item的效果好神奇吧兴猩,并且即使我想再加一種item,只需要添加一個(gè)實(shí)體Bean,再加一個(gè)layout文件就好了不用去寫什么ViewHolder跟Adapter了茂契,哈哈神奇吧厌小。
首先看看我們是怎么用的吧:
List<BindingAdapterItem> items = new ArrayList<>();
items.add(new TextBean("哈哈哈哈"));
items.add(new ImageBean());
items.add(new Image2Bean());
items.add(new Image2Bean());
items.add(new TextBean("我又來啦"));
items.add(new Image2Bean());
items.add(new ImageBean());
items.add(new TextBean("我還來"));
items.add(new TextBean("就是不讓你看美女"));
items.add(new Image2Bean());
items.add(new ImageBean());
items.add(new TextBean("哈哈你當(dāng)不住我看見啦"));
BindingAdapter adapter = new BindingAdapter();
adapter.setItems(items);
//這也是一個(gè)坑涨岁,經(jīng)常忘了加LayoutManger導(dǎo)致東西Item無法顯示规阀,RecyclerView把測量屠缭,布局的工作甩給了LayoutManager
LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
binding.rv.setLayoutManager(manager);
binding.rv.setAdapter(adapter);
adapter.notifyDataSetChanged();
就像平常使用RecyclerView一樣之剧,用一個(gè)List包裝要顯示的數(shù)據(jù)蟹肘,其中TextBean阳欲,ImageBean,Image2Bean是對(duì)應(yīng)三種不同布局的實(shí)體類。那么這些實(shí)體類里邊一定要有一些信息能夠讓BindingAdapter識(shí)別他們的布局信息钾军,最簡單的方法就是在這些實(shí)體重直接返回布局文件阅束,把他們返回布局的共同方法命名為int getViewType()
并創(chuàng)造一個(gè)新的iterface來封裝這個(gè)方法:
public interface BindingAdapterItem {
int getViewType();
}
以后每一個(gè)Item只需要實(shí)現(xiàn)這個(gè)接口中的int getViewType()方法就能告訴Adapter自己的布局了呼胚。
例如TextItem的實(shí)現(xiàn)為:
public class TextBean extends BaseObservable implements BindingAdapterItem {
@Override
public int getViewType() {
return R.layout.adapter_text;
}
public TextBean(String text) {
this.text = text;
}
private String text;
@Bindable
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
notifyPropertyChanged(BR.text);
}
}
繼承BaseObservalable是為了將數(shù)據(jù)與界面綁定,詳情請(qǐng)閱讀開頭的兩篇文章息裸。
再看一下TextItem的layout的內(nèi)容:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="com.example.m.bean.TextBean"/>
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.text}"
android:gravity="center"
android:textSize="25sp"
/>
</layout>
剛剛講過ViewDataBing
通過
boolean setVariable(int variableId, Object value)
方法來將數(shù)據(jù)綁定到界面上,其中int variableId
指的是變量在BR類中的ID,
<data>
<variable
name="item"
type="com.example.m.bean.TextBean"/>
</data>
中的name,而Object value
對(duì)應(yīng)其中的type,在
android:text="@{item.text}"
中將TextItem
中的text
屬性綁定到對(duì)對(duì)應(yīng)的控件上.
<data>
<variable
name="item"
type="com.example.m.bean.TextBean"/>
</data>
好的下面去往通用的BindingAdapter去看看
public class BindingAdapter extends RecyclerView.Adapter<BindingAdapter.BindingHolder> {
public List<BindingAdapterItem> getItems() {
return items;
}
public void setItems(List<BindingAdapterItem> items) {
this.items = items;
}
List<BindingAdapterItem> items = new ArrayList<>();
/**
* @return 返回的是adapter的view
*/
@Override
public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), viewType, parent, false);
return new BindingHolder(binding);
}
/*
* 數(shù)據(jù)綁定
* */
@Override
public void onBindViewHolder(BindingHolder holder, int position) {
holder.bindData(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public int getItemViewType(int position) {
return items.get(position).getViewType();
}
static class BindingHolder extends RecyclerView.ViewHolder {
ViewDataBinding binding;
/**
* @param binding 可以看作是這個(gè)hodler代表的布局的馬甲蝇更,getRoot()方法會(huì)返回整個(gè)holder的最頂層的view
* */
public BindingHolder(ViewDataBinding binding) {
//
super(binding.getRoot());
this.binding = binding;
}
public void bindData(BindingAdapterItem item) {
binding.setVariable(BR.item,item);
}
}
}
這個(gè)類的基本要求是:
- 能夠根據(jù)傳進(jìn)來的對(duì)定的item判斷對(duì)應(yīng)的布局,
- 能夠自動(dòng)的把傳進(jìn)來的數(shù)據(jù)顯示到對(duì)應(yīng)的布局上沪编;
adpter獲取正確的布局很簡單,只需要重寫int getItemViewType(int position)方法年扩,在里邊直接返回item里邊的layout就行了:
@Override
public int getItemViewType(int position) {
return items.get(position).getViewType();
}
現(xiàn)在先用ViewDataBinding來取代View蚁廓,標(biāo)準(zhǔn)的VIewholder應(yīng)該是通過layout的rootView來構(gòu)造,我們可以通過ViewDataBinding.getRoot()來返回這個(gè)rootview
ViewDataBinding binding;
/**
* @param binding 可以看作是這個(gè)hodler代表的布局的馬甲厨幻,getRoot()方法會(huì)返回整個(gè)holder的最頂層的view
* */
public BindingHolder(ViewDataBinding binding) {
//
super(binding.getRoot());
this.binding = binding;
}
綁定數(shù)據(jù)的時(shí)候只需要將實(shí)例化后的實(shí)體類對(duì)象傳入ViewDataBinding的對(duì)應(yīng)的virable中就好了:
public void bindData(BindingAdapterItem item) {
//
binding.setVariable(BR.item,item);
}
因?yàn)檫@里的int variableId
是固定的BR.item
所以每一個(gè)layout中variable的name
屬性必須為item!
在Adapter的onCreateViewHolder(ViewGroup parent, int viewType)
中改用獲取對(duì)應(yīng)的ViewDataBing來初始化ViewHodler:
@Override
public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), viewType, parent, false);
return new BindingHolder(binding);
}
然后在onBindViewHolder(BindingHolder holder, int position)
中調(diào)用就好了:
@Override
public void onBindViewHolder(BindingHolder holder, int position) {
holder.bindData(items.get(position));
}