最火開源框架MVVMhabit
一. 準備工作
二. 快速上手
三. 數據綁定
四. 其他
2.1、第一個Activity
以大家都熟悉的登錄操作為例:三個文件LoginActivty.java、LoginViewModel.java、activity_login.xml
2.1.1、關聯ViewModel
在activity_login.xml中關聯LoginViewModel。
<layout>
<data>
<variable
type="com.goldze.mvvmhabit.ui.login.LoginViewModel"
name="viewModel"
/>
</data>
.....
</layout>
variable - type:類的全路徑
variable - name:變量名
2.1.2、繼承BaseActivity
LoginActivity繼承BaseActivity
public class LoginActivity extends BaseActivity<ActivityLoginBinding, LoginViewModel> {
//ActivityLoginBinding類是databinding框架自定生成的,對activity_login.xml
@Override
public int initContentView(Bundle savedInstanceState) {
return R.layout.activity_login;
}
@Override
public int initVariableId() {
return BR.viewModel;
}
@Override
public LoginViewModel initViewModel() {
//View持有ViewModel的引用,如果沒有特殊業(yè)務處理,這個方法可以不重寫
return ViewModelProviders.of(this).get(LoginViewModel.class);
}
}
保存activity_login.xml后databinding會生成一個ActivityLoginBinding類。(如果沒有生成,試著點擊Build->Clean Project)
BaseActivity是一個抽象類,有兩個泛型參數,一個是ViewDataBinding,另一個是BaseViewModel,上面的ActivityLoginBinding則是繼承的ViewDataBinding作為第一個泛型約束,LoginViewModel繼承BaseViewModel作為第二個泛型約束。
重寫B(tài)aseActivity的二個抽象方法
initContentView() 返回界面layout的id
initVariableId() 返回變量的id,對應activity_login中name="viewModel",就像一個控件的id,可以使用R.id.xxx,這里的BR跟R文件一樣,由系統生成,使用BR.xxx找到這個ViewModel的id。
選擇性重寫initViewModel()方法,返回ViewModel對象
@Override
public LoginViewModel initViewModel() {
//View持有ViewModel的引用,如果沒有特殊業(yè)務處理,這個方法可以不重寫
return ViewModelProviders.of(this).get(LoginViewModel.class);
}
注意: 不重寫initViewModel(),默認會創(chuàng)建LoginActivity中第二個泛型約束的LoginViewModel,如果沒有指定第二個泛型,則會創(chuàng)建BaseViewModel
2.1.3、繼承BaseViewModel
LoginViewModel繼承BaseViewModel
public class LoginViewModel extends BaseViewModel {
public LoginViewModel(@NonNull Application application) {
super(application);
}
....
}
BaseViewModel與BaseActivity通過LiveData來處理常用UI邏輯,即可在ViewModel中使用父類的showDialog()、startActivity()等方法。在這個LoginViewModel中就可以盡情的寫你的邏輯了!
BaseFragment的使用和BaseActivity一樣,詳情參考Demo。
2.2、數據綁定
擁有databinding框架自帶的雙向綁定,也有擴展
2.2.1、傳統綁定
綁定用戶名:
在LoginViewModel中定義
//用戶名的綁定
public ObservableField<String> userName = new ObservableField<>("");
在用戶名EditText標簽中綁定
android:text="@={viewModel.userName}"
這樣一來,輸入框中輸入了什么,userName.get()的內容就是什么,userName.set("")設置什么,輸入框中就顯示什么。 注意: @符號后面需要加=號才能達到雙向綁定效果;userName需要是public的,不然viewModel無法找到它。
點擊事件綁定:
在LoginViewModel中定義
//登錄按鈕的點擊事件
public View.OnClickListener loginOnClick = new View.OnClickListener() {
@Override
public void onClick(View v) {
}
};
在登錄按鈕標簽中綁定
android:onClick="@{viewModel.loginOnClick}"
這樣一來,用戶的點擊事件直接被回調到ViewModel層了,更好的維護了業(yè)務邏輯
這就是強大的databinding框架雙向綁定的特性,不用再給控件定義id,setText(),setOnClickListener()。
但是,光有這些,完全滿足不了我們復雜業(yè)務的需求??!MVVMHabit閃亮登場:它有一套自定義的綁定規(guī)則,可以滿足大部分的場景需求,請繼續(xù)往下看。
2.2.2、自定義綁定
還拿點擊事件說吧,不用傳統的綁定方式,使用自定義的點擊事件綁定。
在LoginViewModel中定義
//登錄按鈕的點擊事件
public BindingCommand loginOnClickCommand = new BindingCommand(new BindingAction() {
@Override
public void call() {
}
});
在activity_login中定義命名空間
xmlns:binding="http://schemas.android.com/apk/res-auto"
在登錄按鈕標簽中綁定
binding:onClickCommand="@{viewModel.loginOnClickCommand}"
這和原本傳統的綁定不是一樣嗎?不,這其實是有差別的。使用這種形式的綁定,在原本事件綁定的基礎之上,帶有防重復點擊的功能,1秒內多次點擊也只會執(zhí)行一次操作。如果不需要防重復點擊,可以加入這條屬性
binding:isThrottleFirst="@{Boolean.TRUE}"
那這功能是在哪里做的呢?答案在下面的代碼中。
//防重復點擊間隔(秒)
public static final int CLICK_INTERVAL = 1;
/**
* requireAll 是意思是是否需要綁定全部參數, false為否
* View的onClick事件綁定
* onClickCommand 綁定的命令,
* isThrottleFirst 是否開啟防止過快點擊
*/
@BindingAdapter(value = {"onClickCommand", "isThrottleFirst"}, requireAll = false)
public static void onClickCommand(View view, final BindingCommand clickCommand, final boolean isThrottleFirst) {
if (isThrottleFirst) {
RxView.clicks(view)
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object object) throws Exception {
if (clickCommand != null) {
clickCommand.execute();
}
}
});
} else {
RxView.clicks(view)
.throttleFirst(CLICK_INTERVAL, TimeUnit.SECONDS)//1秒鐘內只允許點擊1次
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object object) throws Exception {
if (clickCommand != null) {
clickCommand.execute();
}
}
});
}
}
onClickCommand方法是自定義的,使用@BindingAdapter注解來標明這是一個綁定方法。在方法中使用了RxView來增強view的clicks事件,.throttleFirst()限制訂閱者在指定的時間內重復執(zhí)行,最后通過BindingCommand將事件回調出去,就好比有一種攔截器,在點擊時先做一下判斷,然后再把事件沿著他原有的方向傳遞。
是不是覺得有點意思,好戲還在后頭呢!
2.2.3、自定義ImageView圖片加載
綁定圖片路徑:
在ViewModel中定義
public String imgUrl = "http://img0.imgtn.bdimg.com/it/u=2183314203,562241301&fm=26&gp=0.jpg";
在ImageView標簽中
binding:url="@{viewModel.imgUrl}"
url是圖片路徑,這樣綁定后,這個ImageView就會去顯示這張圖片,不限網絡圖片還是本地圖片。
如果需要給一個默認加載中的圖片,可以加這一句
binding:placeholderRes="@{R.mipmap.ic_launcher_round}"
R文件需要在data標簽中導入使用,如:
<import type="com.goldze.mvvmhabit.R" />
BindingAdapter中的實現:創(chuàng)建自定義文件ViewAdapter.java寫入下面代碼實現BindingAdapter
@BindingAdapter(value = {"url", "placeholderRes"}, requireAll = false)
public static void setImageUri(ImageView imageView, String url, int placeholderRes) {
if (!TextUtils.isEmpty(url)) {
//使用Glide框架加載圖片
Glide.with(imageView.getContext())
.load(url)
.apply(new RequestOptions().placeholder(placeholderRes))
.into(imageView);
}
}
很簡單就自定義了一個ImageView圖片加載的綁定,學會這種方式,可自定義擴展。