XML数据绑定太多

时间:2019-07-18 10:34:58

标签: android kotlin android-databinding android-viewmodel

我做了一个View,我想在许多页面上重复使用。它包含用户的反馈元素,例如ProgressBarTextView等。

由于其中包含大量物品,因此将所有这些物品绑定在一起是这样的:

<layout ... >

    <data>
        <variable
            name="screenObserver"
            type="my.namespace.ScreenStateObserver" />
    </data>


    <androidx.constraintlayout.widget.ConstraintLayout ... >

        <my.namespace.view.ScreenStateView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:loading="@{screenObserver.isProgressVisible}"
            app:errorText="@{screenObserver.errorTxt}"
            app:buttonText="@{screenObserver.errorBtnTxt}"
            app:errorVisible="@{screenObserver.isTextVisible}"
            app:buttonVisible="@{screenObserver.isButtonVisible}"
            app:onButtonClick="@{() -> screenObserver.onErrorResolve()}" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

我发现复制/粘贴整个XML块比较麻烦且容易出错。有什么办法可以简化这个过程?

ScreenStateObserver只是我在ViewModel中实现并按如下所示绑定的接口:

override fun onCreateView(...): View? {

    val factory = InjectorUtils.provideViewModelFactory()
    viewmodel = ViewModelProviders.of(this, factory).get(MyViewModel::class.java)
    binding = MyFragmentBinding.inflate(inflater, container, false).apply {
        screenObserver = viewmodel
    }
}
class AtoZViewModel() : ViewModel(), ScreenStateObserver { ... }
interface ScreenStateObserver {
    val isProgressVisible : MutableLiveData<Boolean>
    val isTextVisible : MutableLiveData<Boolean>
    val isButtonVisible : MutableLiveData<Boolean>

    // [..]
}

谢谢!

3 个答案:

答案 0 :(得分:0)

这是我减少代码的建议。
首先声明一个这样的类

interface ScreenState {

    class Loading : ScreenState

    class Error(val errorMessage: String, val errorButtonText: String) : ScreenState
}

在您的CustomView里面

internal class ScreenStateView {

    fun setState(state: ScreenState) {
        if (state is ScreenState.Loading) {
            // show loading
        } else {
            // hide loading
        }

        if (state is ScreenState.Error) {
            //show {state.errorMessage} and {state.errorButtonText}
        } else {
            // hide error
        }
    }
}

在xml中使用

    <my.namespace.view.ScreenStateView
        ...
        app:state="@{screenObserver.screenState}"
        ...
        app:onButtonClick="@{() -> screenObserver.onErrorResolve()}" /> // for onButtonClick I think it still better if we keep like this

希望有帮助

答案 1 :(得分:0)

我减少代码的解决方案是先为ScreenStateView定义类(此类中ScreenStateView的不同属性),然后根据需要使用它多次

答案 2 :(得分:-1)

您可以在数据绑定布局中使用<include>。包含的布局文件可以具有自己的数据和变量,您也可以从主绑定类访问它们。

您必须创建一个布局文件(例如layout_state_view.xml,其中包含您的视图和与视图相关的数据变量:

<layout>

    <data>
        <variable
            name="screenObserver"
            type="my.namespace.ScreenStateObserver" />
    </data>


        <my.namespace.view.ScreenStateView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:loading="@{screenObserver.isProgressVisible}"
            app:errorText="@{screenObserver.errorTxt}"
            app:buttonText="@{screenObserver.errorBtnTxt}"
            app:errorVisible="@{screenObserver.isTextVisible}"
            app:buttonVisible="@{screenObserver.isButtonVisible}"
            app:onButtonClick="@{() -> screenObserver.onErrorResolve()}" />


</layout>

现在您可以在根布局文件中添加它:

<layout>
       <data>
         ...
       </data>

    <LinearLayout     //Can be any layout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

         <include 
 layout:="@layout/layout_state_view">

    </LinearLayout>
</layout>

现在,当您使用绑定类时,如果您的根布局文件是R.layout.mainActivity,则它看起来像这样:

binding.layoutStateView.setScreenObserver(...)

您还可以在根目录布局中创建一个变量,然后使用文档上提到的bind标签将该变量传递给子布局,但是由于您希望减少代码,因此这是不必要的。

注意:由于您只有一个视图,因此可能会想使用<merge>标签。数据绑定的布局标签不支持merge作为直接子级。

  

文档参考:
  https://developer.android.com/topic/libraries/data-binding/expressions#includes