【问题标题】:Android DataBinding & MVVM - Using same name for layout files in different layout foldersAndroid DataBinding & MVVM - 对不同布局文件夹中的布局文件使用相同的名称
【发布时间】:2017-01-14 20:15:54
【问题描述】:

我一直在开发一个带有数据绑定和 MVVM 的应用程序。

我正在尝试在横向模式下为我的应用使用替代布局。我有:

layout/fragment_content.xml
layout-land/fragment_content.xml

两种布局都有不同外观的相同视图,并从相同的视图模型获取提要,如下所示:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

<data class="MyBinding">

    <variable
        name="viewModel"
        type="com.myapp.package.viewModel.VMFirst"/>

    <variable
        name="controlModel"
        type="com.myapp.package.viewModel.VMSecond"/>
</data>

<DIFFERENT CONTENT HERE>

所有视图和 id 都存在于两种布局中。

好吧,问题是,它无法编译,错误只是 "cannot find symbol method getViewModel" 和另一个变量的 getter。

到目前为止我尝试了什么:

  1. 使用 layout 和 layout-land 文件夹(失败,错误在上面解释)

  2. 使用我在这里找到的布局别名 Use Layout Aliases Issue 199344: Data binding does not work with layout aliases。在尝试这种方法时,我没有更改 xml 文件中的任何内容。这也失败了,错误是Could not write to com.myapp.package.databinding.MyBinding

不能在多个布局文件中使用数据绑定data标签吗?在使用数据绑定时,我应该使用什么来为不同的状态使用不同的布局?谢谢!

编辑:删除 class="MyBinding" 并没有改变错误。

【问题讨论】:

  • 您可以发布您的VMFirstVMSecond 课程吗?
  • 您找到解决方案了吗?我有完全相同的问题。我有一个标准布局文件和一个 sw720dp 文件,我得到了确切的问题。
  • 您为什么不简单地创建第三个布局文件,例如 base_data_xyz,其中包含 &lt;data&gt; 但没有视图,然后在两个布局中都使用 &lt;include&gt;

标签: android android-layout android-studio android-databinding


【解决方案1】:

如果有人搜索这个问题,2 年后我尝试做同样的事情,我发现现在一切正常。

我在layoutlayout_sw600dp 下创建了一个布局文件activity_main。这是layout资源下的布局:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <variable
        name="small_variable"
        type="Integer"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/myRoot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            android:id="@+id/small_square"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

这是layout_sw600dp文件夹下的布局:

<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <variable
        name="big_variable"
        type="Long"/>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/myRoot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <View
            android:id="@+id/big_square"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:background="@android:color/holo_blue_bright"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

两者都有一个视图,但每个都有不同的 id:small_squarebig_square

我在手机和平​​板电脑上运行该项目。以下是我的发现:

  • DataBinding 创建一个实现,其中包含不同布局文件夹中所有同名布局文件下的ALL视图和变量。
  • 存在于所有布局中的视图都不能为空,所有其他的都可以为空。在上面的 XML 中,myRoot 在使用 Kotlin 绑定时是不可为空的视图,而 big_squaresmall_square 是可空的视图。变量可以为空,无论它们是否存在于所有布局中(这是预期行为)。
  • 您不能在每个文件中命名不同的绑定类。它必须是相同的(MainBinding 在上面的例子中,或者如果你没有定义它 LayoutResourceName + Binding 默认情况下)。
  • 绑定实现中视图和变量的名称是驼峰式的。所以我的small_variablesmall_square 在代码方面是binding.smallVariablebinding.smallSquare
  • 使用 Kotlin,您可以只使用像 binding.bigSquare?.operation 这样的视图,这非常棒,您无需事先检查它是平板电脑还是手机或视图是否为空。
  • 只是提示,您可以分配binding 字段,即使它们所在的布局不会被使用。您仍然可以在代码上说binding.smallVariable = 3,它会执行分配并保存值。我认为小心点很好。

【讨论】:

  • 感谢您的见解!
【解决方案2】:

我在我的应用程序中大量使用 MVVM,并且还在围绕它构建一个库。

我遵循每个 XML 中都有一个 ViewModel 的约定。此外,viewmodel 变量的名称在所有 XML 中都是相同的。

因此,在您的情况下,您可以创建另一个包含 VMFirstVMSecond 的 ViewModel 类。

public class ParentVM {
   VMFirst first;
   VMSecond second;
}

两个 XML(纵向和横向)将具有相同的名称,例如 activity_main.xml

<layout>
    <data>
      <variable 
          type="ParentViewModel"
          name="vm"/>
    </data>

那么MainActivity代码中就不需要检查了。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.setVariable(BR.vm, new ParentViewModel());
}

这行得通。

单一 ViewModel 的优势

事实上,因为我在所有 xml 中都遵循相同的变量名,所以我能够将绑定逻辑包含在基类 MvvmActivity 本身中。所以,我所有的活动都是这样的:

public class MainActivity extends MvvmActivity {

    @NonNull
    @Override
    protected ViewModel createViewModel() {
        return new MainViewModel();
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }
}

MvvmActivity 实现:MvvmActivity.java

保持数据绑定变量不变的另一个好处是您可以在 XML 本身中设置 RecyclerView 或 ViewPager 适配器。详情请见Setup RecyclerView from XML

【讨论】:

    【解决方案3】:

    默认情况下,会根据布局文件的名称生成一个Binding类,将其转换为Pascal大小写,并为其加上“Binding”后缀。上面的布局文件是 main_activity.xml,所以生成的类是 MainActivityBinding。 --Binding Data

    并在编译时生成。

    所以,通过java代码选择不同的布局。

    layout/
    R.layout.activity_main
    R.layout.activity_main_tablet
    
    values/
        <bool name="is_mobile">true</bool>
        <bool name="is_tablet">false</bool>
    values-w820dp/
        <bool name="is_mobile">false</bool>
        <bool name="is_tablet">true</bool>
    
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        if(getResources().getBoolean(R.bool.is_mobile)) {
            ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);            
        } else {
            ActivityMainTabletBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_tablet);   
        }
    
    }
    

    【讨论】:

    • 是的,但我寻求的是为同一视图使用具有不同布局品种的数据绑定的正确方法。据我尝试更改绑定名称并不能解决此错误。同一页面建议的方法(布局别名)也不起作用。如果我要检查设备大小并使用 if / else 块进行膨胀,我看不到使用不同布局的意义。你知道/遇到过任何可用的代码示例吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多