XML 数据绑定过多

Too many XML data bindings

我制作了一个 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>

我发现 copy/pasting 整个 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>

    // [..]
}

谢谢!

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

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

希望对您有所帮助

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

您必须创建一个布局文件(例如 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>

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

binding.layoutStateView.setScreenObserver(...)

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

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

Documentation Reference:
https://developer.android.com/topic/libraries/data-binding/expressions#includes

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