DataBinding
android UI 控件從xml文件的對(duì)象化存在大量的重復(fù)操作,相信每一個(gè)android開(kāi)發(fā)者都經(jīng)歷過(guò)findViewById的階段。
直到注解被重視后,通過(guò)注解省略findViewById這個(gè)流程變得簡(jiǎn)單了,我們最初是通過(guò)反射加注解在編譯期完成注冊(cè),看上很像是ButterKnife
ButterKnife為了適配更多的場(chǎng)景,不僅僅使用了注解,還有注解處理器(annotationProcess)類(lèi)似 APT(Annotation Processing Tool), 手機(jī)玩目標(biāo)類(lèi)基本信息后,再用JavaPoet生成Java類(lèi)文件。
而我今天想說(shuō)的DataBinding則給了Android開(kāi)發(fā)者另外一種獨(dú)特的體驗(yàn):
1.如果你寫(xiě)過(guò)Js項(xiàng)目,想必雙向綁定的印象肯定會(huì)特別深刻,而databinding在xml文件中的使用方式,將會(huì)讓你更深刻
<data>
<import type="com.google.samples.apps.sunflower.data.Plant"/>
<variable
name="viewModel"
type="com.google.samples.apps.sunflower.viewmodels.PlantDetailViewModel" />
<variable
name="callback"
type="com.google.samples.apps.sunflower.PlantDetailFragment.Callback" />
</data>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorSurface"
app:statusBarScrim="?attr/colorSurface"
app:collapsedTitleGravity="center"
app:collapsedTitleTextAppearance="@style/TextAppearance.Sunflower.Toolbar.Text"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:title="@{viewModel.plant.name}" // viewModel的這樣使用是不是很特別
app:titleEnabled="false"
app:toolbarId="@id/toolbar">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:onClick="@{() -> callback.add(viewModel.plant)}" // 甚至我的天 方法回調(diào)也能實(shí)現(xiàn)
android:tint="@android:color/white"
app:shapeAppearance="@style/ShapeAppearance.Sunflower.FAB"
app:isFabGone="@{viewModel.isPlanted}"
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@drawable/ic_plus" />
2.上面那個(gè)例子可以讓你驚訝它的雙向綁定的能力,而下面則是釋放雙手
// RegisterActivity.java 實(shí)現(xiàn)注冊(cè)功能
public class RegisterActivity extends MVVMActivity {
private final static String TAG = "RegisterActivity";
ActivityRegisterBinding mDataBinding;
RegisterViewModel mViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void initViewModel() {
mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_register);
}
@Override
public void init(){
mLoading = new LoadingDialog.Builder(RegisterActivity.this);
mLoading.setMessage(getString(R.string.register_loading));
mLoading.create();
}
@Override
public void bindUi(){
// 點(diǎn)擊上方關(guān)閉按鈕
RxView.clicks(mDataBinding.closeImg)
.to(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(unit -> finish());
// 點(diǎn)擊注冊(cè)按鈕
RxView.clicks(mDataBinding.submitBtn)
.to(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(unit -> mViewModel.register(mDataBinding.registerUserNameEdt.getText().toString().trim()
, mDataBinding.registerPasswordEdt.getText().toString().trim()
, mDataBinding.repeatRegisterPasswordEdt.getText().toString().trim()));
}
@Override
public void subscribeUi() {
// 注冊(cè)頁(yè)面狀態(tài)更變通知
mViewModel.getRegisterState().observe(this, state -> {
switch (state) {
case ERROR_CUSTOMER_SUCCESS_PASS:
mLoading.getObj().show(); // 通過(guò)校驗(yàn) 開(kāi)始網(wǎng)絡(luò)請(qǐng)求
break;
case ERROR_CUSTOMER_PASSWORD_ERROR: // 賬號(hào)錯(cuò)誤
case ERROR_CUSTOMER_USERNAME_ERROR: // 密碼錯(cuò)誤
case ERROR_CUSTOMER_REPEAT_ERROR: // 賬號(hào)密碼不一致
ToastUtil.showToast(this, TCErrorConstants.getErrorInfo(state));
break;
}
});
// 注冊(cè)接口回調(diào)通知
LiveEventBus.get(RequestTags.REGISTER_REQ, BaseResponBean.class)
.observe(this, bean -> {
Optional.ofNullable(mLoading).ifPresent(builder -> mLoading.getObj().dismiss()); // 取消 Loading
if (bean != null && bean.getCode() == 200) { // 注冊(cè)成功 就開(kāi)始自動(dòng)登錄
ToastUtil.showToast(RegisterActivity.this, "注冊(cè)成功!");
mLoading.setMessage(getString(R.string.login_loading_text)).create().show(); // 顯示登錄中的loading
mViewModel.login(mDataBinding.registerUserNameEdt.getText().toString().trim() // 注冊(cè)成功后 進(jìn)行登錄請(qǐng)求
, mDataBinding.registerPasswordEdt.getText().toString().trim());
} else {
ToastUtil.showToast(RegisterActivity.this, "注冊(cè)失敗:" + TCErrorConstants.getErrorInfo(bean.getCode()));
}
});
// 登錄接口回調(diào)通知
LiveEventBus.get(RequestTags.LOGIN_REQ, BaseResponBean.class)
.observe(this, bean -> {
if (bean == null) return;
Optional.ofNullable(mLoading).ifPresent(builder -> mLoading.getObj().dismiss()); // 取消 Loading
if (bean.getCode() == 200) { // 登錄成功
ToastUtil.showToast(this, "登錄成功!");
startActivity(new Intent(RegisterActivity.this, MainActivity.class));
finish();
} else { // 登錄失敗
ToastUtil.showToast(this, "登錄失敗:" + TCErrorConstants.getErrorInfo(bean.getCode()));
}
});
}
@Override
public void initRequest() {
}
@Override
protected void onDestroy() {
super.onDestroy();
Optional.ofNullable(mLoading).ifPresent(builder -> mLoading.getObj().dismiss()); // 取消 Loading
}
}
上面是我用DataBinding + ViewModel + LiveData + LiveDataBus + RxView 實(shí)現(xiàn)的一個(gè)注冊(cè)功能
3.DataBinding在Adapter中的使用呢
// MessageAdapter.java
public class MessageAdapter extends BaseQuickAdapter<MessageItemBean, BaseDataBindingHolder<LayoutMessageSignBinding>> {
Context context;
public MessageAdapter(Context context, int layoutResId, @Nullable List<MessageItemBean> data) {
super(layoutResId, data);
this.context = context;
}
@Override
protected void convert(@NotNull BaseDataBindingHolder<LayoutMessageSignBinding> layoutMessageSignBindingBaseDataBindingHolder, MessageItemBean messageItemBean) {
LayoutMessageSignBinding binding = DataBindingUtil.getBinding(layoutMessageSignBindingBaseDataBindingHolder.itemView);
if(binding == null || messageItemBean == null) return;
binding.titleTv.setText(messageItemBean.getMessageTitle());
binding.timeTv.setText(messageItemBean.getMessageTime());
binding.messageContent.setText(messageItemBean.getMessageContent());
}
}
上面是DataBinding + Bravh ,讓Adapter變得無(wú)比簡(jiǎn)潔
4.這里說(shuō)一下databinding的原理吧
首先,databinding會(huì)把xml拆分成數(shù)據(jù)和布局兩部分 剩下的明天繼續(xù)