Android DataBinding 使用及心得

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ù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容