889
技術社區[雲棲]
Android MVVM(使用經驗篇)
MVVM的大名相信做手機開發的肯定不會陌生,我第一次聽到它是從做IOS開發的同學那裏聽到的,我們的項目之前應用了MVP,要說服大家從MVP到MVVM,肯定得說說為啥,他優秀在那裏?
首先我們看看正常MVP的依賴關係圖:
這是個經典的MVP依賴關係,View 層和Presenter,Presenter和Model層彼此依賴,但是不會出現MVC那種跨層依賴,例如如果你寫出來的View和Model層有依賴的話,那麼就不是正常的MVP結構咯~這個結構好處很明顯,Model和View層脫藕。因此修改Model層代碼View層代碼不用動~
好了說了那麼多MVP的,MVP是個很優秀的脫藕的架構。那麼MVVM比MVP優秀在那裏,還是那句,看圖:
我們看到,VM沒了對View層的依賴~依賴的代碼不由我們寫是由框架代碼生成~因此我們的進一步看到升級版的MVP~當然我知道最優秀當然是Spring~這裏不離題了。MVVM為我們帶來的就是這個,那麼MVVM運用困難嗎?我先跟你說:
1.用了MVVM後,隻要寫一次後以後不用再寫Adapter!
2.用了MVVM後,隻要寫一次後以後不用再寫Dialog!
最重要是,Android官方提供了官方的針對MVVM開發的dataBinding . 所以主角出場,DataBinding!
基本:
然後我們說說配置,很簡單一句話,在BuildGradle增加:
android {
....
dataBinding {
enabled = true
}
}
沒有其他了,除了你的Android Studio 要超過1.3版本。
DataBinding XML和我們平時有點不一樣他,他的頂層必須是layout:
<layout xmlns:andro
xmlns:bind="https://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.silver.testdatabinding.AMainPresenterVM" />
<variable
name="user"
type="com.silver.model.BindingUser" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:layout_marginTop="20dp"
android:onClick="@{()->vm.onClickInit()}"
android:text="initList" />
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:layout_marginTop="20dp"
android:onClick="@{()->vm.onClickTestUpdate()}"
android:text="initList" />
<ImageView
android:
android:layout_width="150dp"
android:layout_height="150dp"
bind:imageUrl="@{user.url}"/>
</LinearLayout>
</layout>
其中data標題表示數據,要綁定的數據,variable表示綁定變量,那麼怎麼綁定到某些Text或者什麼的,上麵代碼也看到”@{}“加這個標誌,譬如上麵的Text我要顯示User的Name的話,就直接user.name,當然這個name 是public ,那麼馬上有人問,我不喜歡這樣,我是經過特殊處理的方法出來的字段,是否能顯示,答案是可以的。例如user有過處理過Name的方法,叫getFirstName(),那麼 TextView哪裏要引用的話變成 以下:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.getFirstName()}"/>
除了字段顯示字段這些我們還有很多的才能做到VM層不再依賴V層的,所以我們再看,點擊事件回調,包括最普通的Onclick
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:onClick="@{()->vm.onClickUser(view)}"/>
就如上麵,是直接調用Lambda表達式,默認是省略了View 如果你要引用呢?
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:onClick="@{(view)->vm.onClickUser(view)}"/>
當然其他View中有的點擊事件也是類似炮製例如(onTextChange),這裏不再多說~那如果木有定義過的事件呢?例如上麵的 我要為ImageView設置 圖片的URL?這裏先留個懸念,下麵說。因為那個是個人認為DataBinding非常牛逼有用的地方。
接下來先說說在Activity怎麼綁定這些~先看代碼:
ActivityMainDataBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this ,R.layout.activity_main);
AMainVM vm = new AMainVM();
binding.setVm(vm);
binding.setUser(vm.user);
}
@Override
protected void onDestroy() {
// 這裏不寫也可以,寫了加快回收~
binding.unbind();
super.onDestroy();
}
其中 ActivityMainDataBinding,是自動生成的,生成規則是layout的名字去掉下劃線並首字母大寫加上DataBinding,不用clean ,rebuiding,寫完XML就有~爽吧~然後開始綁定View,調用DataBindingUtil 的靜態方法自動setContentView就自動綁定了View,然後調用進行進一步數據綁定,包括VM和其他數據。這裏有人發現其實這樣寫不是累贅了嗎?其實我們大可以不綁定User,而直接調用vm裏的User。優化如下:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.user.name}"/>
then,我們的Activity隻要綁定vm就可以咯~是不是覺得突然Activity什麼都沒了?我的項目也差不多這樣的~
再看看我們的User結構跟vm裏麵是啥:
public class BindingUser{
public String name;
public Int clickCount;
public String url;
}
public class BindingUser{
public ObservableField<String> name = new ObservableField<>();
public final ObservableInt clickCount = new ObservableInt();
public final ObservableField<String> url = new ObservableField<>();
}
這個是User類,其中上麵的是我們這裏用的,不需要動態更新的可以這樣,但是實際上我們需要動態更新因此引入Observable觀察者類,這樣可以動態刷新,當我們改變其中的值得時候,View會得到及時的通知刷新界麵。這裏可能有同學又要問,我們平時都是用Gson啥的序列化進去,你這樣寫,我們就不能反序列化進去了,其實上麵的BindingUser可以寫到如下麵的效果,看代碼:
public class BindingUser extends BaseObservable{
@Bindable
public String name;
@Bindable
public Int clickCount;
@Bindable
public String url;
public void setName(String name) {
this.name= name;
notifyPropertyChanged(BR.name);
}
public void setClickCount(String clickCount) {
this.clickCount= clickCount;
notifyPropertyChanged(BR.clickCount);
}
public void setUrl(String url) {
this.url= url;
notifyPropertyChanged(BR.url);
}
}
其中BaseObservable是基礎觀察者,我們首先需要繼承他,否則之後的無效~然後在你需要動態刷新的字段上加上@Bindable 和寫他們的set方法進行刷新,其中BR是DataBinding類動態生成的,對應每一個綁定變量variabl的ID值。
再來看看VM裏的代碼:
public class AMainVM{
private ObservableField<BindingUser> bindingUser;
public AMainVM(){
bindingUser = new ObservableField<>();
}
@Override
public void onClickUser() {
// 每點一此加1
bindingUser.clickCount.set(bindingUser.clickCount.get() + 1);
}
@Override
public void onClickInit() {
BindingUser bindingUserModel = new BindingUser();
bindingUser.name.set("just test");
bindingUser.clickCount.set(0);
bindingUser.url.set("https://img0.imgtn.bdimg.com/it/u=2770060730,47109478&fm=21&gp=0.jpg");
bindingUser.set(bindingUserModel);
}
@Override
public void onClickTestUpdate() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
bindingUser.name.set("update in thread");
return null;
}
}.execute();
}
}
這裏強烈推薦Observable這個類,超級方便,但是要注意的是,我為什麼要再初始化VM的時候去初始化我們的User類的ObservableField,而不是點擊的時候在方法onClickInit()中才懶加載? 那是因為如果那時我才初始化,會發現View界麵木有任何變化。為啥?因為在Activity裏綁定VM的時候,dataBinding會幫我們綁定了VM以及VM裏麵其他綁定在其他View的變量,所以那時如果你木有初始化VM內的變量需要綁定的變量,那麼DataBinding會注銷哪個對應BR ID的變量。因此,當你在下麵再重新新建一個Observable的時候壓根是沒做過任何View的綁定注冊,因此就算你怎麼更新數據,View也不會更新,除非這時你持有View,然後強製重新綁定,但是這樣就違背我們VM不能依賴View的原則,因此這裏要注意初始化。
大家都看到我還在下麵做了個很邪惡的實驗,在非UI線程更新BindingUser的name,驗證下當我在非UI線程時候更新UI是否會出現問題,答案是不會。所以放心更新地操作數據,這裏不多說裏麵實現了,無非就一個Handler嘛。
這樣像上麵的就是一個最簡單的MVVM的結構,what 's more?除了這些我們還想有其他控製,例如我想決定某個View是否顯示,看如下代碼:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:visibility="@{user.name == null ? View.GONE : View.VISIBLE}" />
當然我們在引用View.Gone這個靜態常量時候要在上麵data裏聲明,應用import字段:
<data>
...
<import type="android.view.View" />
</data>
如果需要增加某個@String字段拚接我們的變量,或者我們自己隨便寫的一個String組拚那麼怎樣?
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bind:message='@{"名字:" + user.name}'/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bind:message="@{@string/name + user.name}"/>
新增的特殊符號:
我們平時遇到的,我們兩個字段,誰不空顯示誰,那麼按我們平時寫的如下:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bind:message="@{user.name == null? user.count:user.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
bind:message="@{user.name ?? user.count}"/>
下麵就是DataBinding提供的簡化寫法~用操作符??,上下意思兩種表達意思一樣。
DataBinding的大概使用就是這樣,當然還有很多我們探索的包括ObservableMap ,ObservableList這裏使用也很方便,喜歡的同學自行查詢~都很簡單,以下介紹DataBinding最強大功能。
自定義設置器(Custom setter):
注釋 @BindingAdapter ,為什麼我不說BindMethod,因為我覺得作用不是太大,沒使用,如果有同學覺得有大用,請一定告訴我~好繼續,大家看到我開始的XML中有個很奇怪的字段,“bind:imageURL”這個是哪裏來的?首先這個就是自定義設置器,首先我們要使用的話必須在定義如下:
<layout xmlns:andro
xmlns:bind="https://schemas.android.com/apk/res-auto">
......
<ImageView
android:
android:layout_width="150dp"
android:layout_height="150dp"
bind:imageUrl="@{user.url}"/>
</layout>
加上“xmlns:bind="https://schemas.android.com/apk/res-auto"” 有些同學會在之前的自定義View裏用到“xmlns:app="https://schemas.android.com/apk/res-auto"”,這個沒關係,將"bind:imageURL"改為"app:imageURL"就可以。那麼我們定義了這個後還要寫一個擺放這個imageUrl的處理方法,我自己起了一個類專門處理的,BindingUtil下麵我們看看裏麵怎樣的~
public class BindingUtil {
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView imageView, String imageUrl) {
if (imageUrl != null) {
Picasso.with(imageView.getContext()).load(imageUrl).into(imageView);
}
}
@BindingAdapter({"showLoading", "message"})
public static void showLoading(View view, boolean showLoading, String message) {
if (showLoading) {
// showLoading
} else {
// cancelLoading
}
}
@BindingAdapter("showLoading")
public static void showLoading(View view, boolean showLoading) {
if (showLoading) {
// showLoading
} else {
// cancelLoading
}
}
}
做的不多方法頭加上注解@BindingAdapter ,方法必須是靜態方法哦,其中函數第一個參數是你綁定的View,這個函數調用的前提是ImageView裏有個參數bind:imageUrl,當綁定的user.url初始化或者改變了值的時候會調用這個loadImage方法。因此我們VM裏做數據轉換變化,View馬上就有響應去加載圖片。VM不用管View更新問題~這個給了我啟發,我們的ShowLoading,ShowToast是不是也可以這樣,因此有了下麵方法。注意:當方法中有多個參數的時候,例如上麵的ShowLoading 有一個布爾的showLoading參數和message參數,隨便一個更改參數都會調用我們設定的方法,因此我們寫這裏方法時需要特別注意參數處理哦~
BaseRecycleAdapter:
接下來我會說說我寫的一個BaseRecycleAdapter主要針對RecycleView的,大家覺得好的可以直接拿去用,以後就不用再寫關於RecycleView的Adapter了,當然對於ListView和Dialog?也是類似的思想~先說說思路:
我們用DataBinding寫一個Adapter會怎樣?
public class MainAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
private List<String> listData;
public MainAdapter(Activity activity,List<String> listData) {
this.listData = listData;
layoutInflater = LayoutInflater.from(activity);
}
@Override
public int getCount() {
if (getData() != null) {
return getData().size();
} else {
return 0;
}
}
@Override
public Object getItem(int position) {
if (getData() != null) {
return getData().get(position);
} else {
return null;
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BindingHolder bindingHolder;
if (convertView == null) {
AdapterMainBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.adapter_main, null, false);
bindingHolder = new BindingHolder();
bindingHolder.setBinding(binding);
convertView = binding.getRoot();
convertView.setTag(bindingHolder);
} else {
bindingHolder = (BindingHolder) convertView.getTag();
}
bindingHolder.getBinding().setVariable(com.silver.testdatabinding.BR.testString, listData .get(position));
bindingHolder.getBinding().executePendingBindings();
return convertView;
}
public static class BindingHolder {
private AdapterMainBinding binding;
public AdapterMainBinding getBinding() {
return binding;
}
public void setBinding(AdapterMainBinding binding) {
this.binding = binding;
}
}
}
首先我們寫一個Holder,其中我們的Holder中其實隻有一個binding,而binding在DataBindingUtil.inflate()時已經對ContentView進行了綁定,因此我們重新數據來刷新界麵就可以,如上麵setVariable然後再executePendingBindings()進行數據刷新操作。
那麼我們得思考一下,我們其實很多DataBinding的Adapter會非常類似,因此很有必要寫一個通用,以後省去我們很多寫Adapter的時間了~我們來考慮下一個Adapter會需要些什麼。
1.我們需要告訴Adapter我們contentView的LayoutId是啥。
2.首先我們需要一個List ,滾動的時候動態綁定裏麵的每個Model進行數據刷新。
3.我們需要告訴getView方法裏我們需要綁定的Model 變量名 (BR的ID值)。
4.我們要有個onItemClickListener作為事件點擊。
5.其他item裏的onClick事件。
6.當List變化的時候通知Adapter進行notify。
這些基本構成了我們Adapter的要求了~如果還有還可以自行添加~
首先看看初始化方法
public class BaseRecycleViewAdapter<T> extends RecyclerView.Adapter<BaseRecycleViewAdapter.BindingHolder> {
private static final Object DATA_INVALIDATION = new Object();
public @LayoutRes ObservableInt layoutId;
private AdapterModule adapterModule;
private final WeakReferenceOnListChangedCallback<T> callback = new WeakReferenceOnListChangedCallback<>(this);
private LayoutInflater inflater;
// Currently attached recyclerview, we don't have to listen to notifications if null.
@Nullable
private RecyclerView recyclerView;
public BaseRecycleViewAdapter(int layoutId) {
this.layoutId = new ObservableInt(layoutId);
}
public void setAdapterModule(AdapterModule<T> adapterModule) {
if (this.adapterModule == adapterModule || adapterModule == null) {
return;
}
// If a recyclerview is listening, set up listeners. Otherwise wait until one is attached.
// No need to make a sound if nobody is listening right?
if (recyclerView != null) {
if (this.adapterModule != null && this.adapterModule.list != null && this.adapterModule.list instanceof ObservableArrayList) {
this.adapterModule.list.removeOnListChangedCallback(callback);
}
if (adapterModule != null && adapterModule.list != null && adapterModule.list instanceof ObservableList) {
adapterModule.list.addOnListChangedCallback(callback);
}
}
this.adapterModule = adapterModule;
notifyDataSetChanged();
}
......
}
這是構造整個函數的基本參數,構造函數需要我們ContentView的LayoutId,而AdapterModule是針對Adapter的數據變量,其中對AdapterModule中list 進行addOnListChangedCallback 監聽更新從而對Adapter進行刷新。Adapter是我們的數據變量組成類,我們來看看他內部:
public class AdapterModule<T> {
public ObservableArrayList<T> list = new ObservableArrayList<>();
// bindingVaiable id
public ObservableInt bindingVaiable;
// bind PositionId
public ObservableInt bindPositionVaiableId;
// the key is the resourceId!
public WeakReference<SparseArray<OnClickListener>> listeners;
public AdapterModule(ArrayList<T> list, int bindingVaiable,int bindPositionVaiableId) {
if (list != null && !list.isEmpty()) {
this.list.addAll(list);
}
this.bindingVaiable = new ObservableInt(bindingVaiable);
this.bindPositionVaiableId = new ObservableInt(bindPositionVaiableId);
}
public AdapterModule(ArrayList<T> list, int bindingVaiable, SparseArray<OnClickListener> listeners) {
if (list != null && !list.isEmpty()) {
this.list.addAll(list);
}
this.bindingVaiable = new ObservableInt(bindingVaiable);
this.listeners = new WeakReference<>(listeners);
}
public void setListeners(SparseArray<OnClickListener> listeners) {
this.listeners = new WeakReference<>(listeners);
}
}
list就是我們顯示數據,bindingVaiable是我們每個getView 裏contentView要綁定的Module變量 ,第三個參數是個很有意思的參數,綁定當前getView的點position,隻有BR指定的ID值,實際值是在Adapter中進行綁定。這裏下麵看代碼~最後一個是我們的onClickListener。這個 onClickListener是我自己寫的,其中也很簡單隻有一個帶position參數的onClick方法。至於onClickListener綁定變量的BR值巧用了下SparserArray的Key就是我們綁定的變量BR值。
我們繼續看看Adapter的OnCreateViewHolder和OnBindHolder:
@Override
public BindingHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (inflater == null) {
inflater = LayoutInflater.from(parent.getContext());
}
ViewDataBinding binding = DataBindingUtil.inflate(inflater, layoutId.get(), null, false);
final BindingHolder holder = new BindingHolder(binding, (SparseArray<OnClickListener>) adapterModule.listeners.get());
binding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
return recyclerView != null && recyclerView.isComputingLayout();
}
@Override
public void onCanceled(ViewDataBinding binding) {
if (recyclerView == null || recyclerView.isComputingLayout()) {
return;
}
int position = holder.getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
notifyItemChanged(position, DATA_INVALIDATION);
}
}
});
return holder;
}
@Override
public void onBindViewHolder(BindingHolder holder, int position) {
// bindPosition
onBindBinding(holder.binding, adapterModule.bindPositionVaiableId.get(), position, false);
onBindBinding(holder.binding, adapterModule.bindingVaiable.get(), adapterModule.list.get(position), true);
}
private void onBindBinding(ViewDataBinding binding, int bindingVariable, Object item, boolean executePendingBindings) {
if (bindingVariable != 0) {
boolean result = binding.setVariable(bindingVariable, item);
if (!result) {
throw new IllegalArgumentException("can't bind variable adapterModule because can't find the id,is it correct?");
}
// refresh
if (executePendingBindings) {
binding.executePendingBindings();
}
}
}
onCreateViewHolder 裏不多說了,主要增加了在binging進行時綁定檢測是否recycleView已經在onAttach,在onBindView進行數據綁定第一個綁定的是position,setVariable(bindPosiionVaiableId,position);其中bindpositionVaiableId就是顯示ContentView的position變量值,然後綁定當前position。同時重新綁定我們list中對應position的module變量進行綁定數據刷新,最後執行 binding.executePendingBindings();進行數據刷新。這就是一個recycleView綁定的數據過程。
那麼在哪裏進行綁定OnClickListener?為甚麼這裏進行數據綁定還有要對position進行綁定?其實我們每個點擊回調隻需要一個回調變量函數,因此不用每次都進行綁定隻要在ViewHolder裏進行綁定,我們變化的隻有Position。
下麵我們再看看ViewHolder代碼:
public static class BindingHolder<T> extends RecyclerView.ViewHolder {
ViewDataBinding binding;
private SparseArray<OnClickListener> listeners;
public BindingHolder(ViewDataBinding binding, SparseArray<OnClickListener> listeners) {
super(binding.getRoot());
this.binding = binding;
this.listeners = listeners;
onBindListeners();
}
public void onBindListeners() {
if (listeners != null && listeners.size() > 0) {
for (int i = 0; i < listeners.size(); i++) {
binding.setVariable(listeners.keyAt(i), listeners.get(listeners.keyAt(i)));
}
}
}
}
最後當我們List進行刷新的時候需要對我們的Adapter進行刷新,因此看看OnListChangeListener代碼:
private static class WeakReferenceOnListChangedCallback<T> extends ObservableArrayList.OnListChangedCallback<ObservableArrayList<T>> {
final WeakReference<BaseRecycleViewAdapter<T>> adapterRef;
WeakReferenceOnListChangedCallback(BaseRecycleViewAdapter<T> adapter) {
this.adapterRef = new WeakReference<>(adapter);
}
@Override
public void onChanged(ObservableArrayList<T> ts) {
BaseRecycleViewAdapter<T> adapter = adapterRef.get();
if (adapter == null) {
return;
}
Utils.ensureChangeOnMainThread();
adapter.notifyDataSetChanged();
}
...
}
主要是數據更改是對Adapter進行notify。
這樣看還是有點抽象,我們再看看怎麼用,就一目了然。
<layout xmlns:andro
xmlns:bind="https://schemas.android.com/apk/res-auto">
<variable
name="position"
type="Integer" />
<variable
name="user"
type="com.silver.model.BindingUser" />
<variable
name="onItemClick"
type="com.ykse.mvvm.adapter.listener" />
<variable
name="onClick"
type="com.ykse.mvvm.adapter.listener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
android:onClick="@{()->onItemClick.onClick(position)}"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:gravity="center"
android:layout_marginTop="20dp"
android:onClick="@{()->onClick.onClick(position)}"
android:text="initList" />
<ImageView
android:
android:layout_width="150dp"
android:layout_height="150dp"
bind:imageUrl="@{user.url}"/>
</LinearLayout>
</layout>
這個就是我們寫的Adapter的ContentView。我們的點擊事件,還有變量還有OnItemClick都解決了。
還有那麼我們的Activity的ContentView是怎麼將AdapterModule和layoutId跟recycleView綁定的?看看XML:
<layout xmlns:andro
xmlns:bind="https://schemas.android.com/apk/res-auto">
<variable
name="layoutId"
type="Integer" />
<variable
name="vm"
type="com.silver.model.AMainVM" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
>
<android.support.v7.widget.RecyclerView
android:
android:layout_width="match_parent"
android:layout_height="match_parent"
bind:layoutId="@{layoutId}"
bind:listData="@{vm.adapterModule}"/>
</LinearLayout>
</layout>
這次@BindingAdapter又發揮作用~
public class BindingAdapterUtil {
@BindingAdapter({"layoutId"})
public static void bindRecycleViewLayoutId(RecyclerView recyclerView, int layoutId) {
BaseRecycleViewAdapter baseRecycleViewAdapter = new BaseRecycleViewAdapter(layoutId);
recyclerView.setAdapter(baseRecycleViewAdapter);
}
@BindingAdapter({"listData"})
public static <T> void bindRecycleViewAdapterModule(RecyclerView recyclerView, AdapterModule<T> adapterModule) {
BaseRecycleViewAdapter<T> adapter;
if (recyclerView.getAdapter() != null) {
adapter = (BaseRecycleViewAdapter<T>) recyclerView.getAdapter();
adapter.setAdapterModule(adapterModule);
} else {
return;
}
}
}
還有更多的雙向綁定~大家可以參考下資料,原本DataBinding也有一些原生的~
到這裏我們DataBinding的使用篇算是完了,更多的功能需要大家更多發現~謝謝能看到這裏的人,你們都是熱愛技術的人!
最後更新:2017-04-08 12:27:56
上一篇:
Java新手學習圖-打好基礎圖
下一篇:
你試過弱引用弱爆了嗎?原因竟然是 ... ...
解決PHP中使用header輸出頭報錯問題
ITEXT實例學習與研究(二) 之 創建一個細長的淺黃色背景的頁麵以及縱向頁麵與橫向頁麵之間的切換
百度地圖之收索視野內的建築物
阿裏雲容器服務支持SwarmMode
為Android視圖添加徽章:Android ViewBadger
傳統架構下的上雲之路——來伊份上雲曆程分享
secrets of the javascript Ninja( with(){} 的用法)(javascript忍者的秘密)
劍指Offer之打印1到最大的N位數
論創業者心態心智
PostgreSQL 10.0 preview 功能增強 - 自由定義統計信息維度