Android 数据绑定和 MVVM - 对不同布局文件夹中的布局文件使用相同的名称
Android DataBinding & MVVM - Using same name for layout files in different layout folders
我一直在开发具有数据绑定和 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。
到目前为止我尝试了什么:
使用 layout 和 layout-land 文件夹(失败,上面解释了错误)
使用布局别名Use Layout Aliases which I found here Issue 199344: Data binding does not work with layout aliases。在尝试这种方法时,我没有更改 xml 文件中的任何内容。这也失败了,错误是Could not write to com.myapp.package.databinding.MyBinding
是否不能在多个布局文件中使用数据绑定 data
标签?在使用数据绑定时,我应该使用什么来为不同的状态使用不同的布局?谢谢!
编辑:删除class="MyBinding"
没有改变错误。
By default, a Binding class will be generated based on the name of the layout file, converting it to Pascal case and suffixing "Binding" to it. The above layout file was main_activity.xml so the generate class was MainActivityBinding. --Binding Data
并在编译时生成。
所以,select 不同的布局 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);
}
}
我在我的应用程序中大量使用 MVVM,并且还在围绕它构建一个库。
我遵循每个 XML 中只有一个 ViewModel 的惯例。此外,viewmodel 变量的名称在所有 XML 中都是相同的。
因此,在您的情况下,您可以创建另一个包含 VMFirst
和 VMSecond
的 ViewModel class。
public class ParentVM {
VMFirst first;
VMSecond second;
}
两个 XMLs(纵向和横向)将具有相同的名称,例如 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 中都遵循相同的变量名,所以我能够在基础 class 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。
如果有人搜索这个问题,2 年后我尝试做同样的事情,我发现它现在工作正常。
我在 layout
和 layout_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_square
和 big_square
。
我 运行 phone 和平板电脑上的项目。这是我的发现:
- DataBinding 在不同布局文件夹中的所有同名布局文件下创建一个包含 ALL 视图和变量的实现。
- 存在于所有布局中的视图不可为空,所有其他视图均可为空。在上面 XML 中,
myRoot
在使用来自 Kotlin 的绑定时 不是可空视图 ,而 big_square
和 small_square
可以为 null 视图。变量可以为空,无论它们是否存在于所有布局中(这是预期的行为)。
- 您不能在每个文件中命名绑定 类 不同。它必须相同(上面示例中的
MainBinding
,或者如果您未定义它,则默认为 LayoutResourceName
+ Binding
)。
- 关于绑定实现的视图和变量的名称是驼峰式的。所以我的
small_variable
& small_square
在代码方面是 binding.smallVariable
和 binding.smallSquare
。
- 使用 Kotlin,您可以只使用像
binding.bigSquare?.operation
这样的视图,这很棒,您不需要事先检查它是平板电脑还是 phone 或者视图是否为 null。
- 提示一下,您可以分配
binding
个字段,即使它们所在的布局不会被使用。您仍然可以在代码上说 binding.smallVariable = 3
,它会进行分配并保存值。我觉得还是小心点好。
我一直在开发具有数据绑定和 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。
到目前为止我尝试了什么:
使用 layout 和 layout-land 文件夹(失败,上面解释了错误)
使用布局别名Use Layout Aliases which I found here Issue 199344: Data binding does not work with layout aliases。在尝试这种方法时,我没有更改 xml 文件中的任何内容。这也失败了,错误是
Could not write to com.myapp.package.databinding.MyBinding
是否不能在多个布局文件中使用数据绑定 data
标签?在使用数据绑定时,我应该使用什么来为不同的状态使用不同的布局?谢谢!
编辑:删除class="MyBinding"
没有改变错误。
By default, a Binding class will be generated based on the name of the layout file, converting it to Pascal case and suffixing "Binding" to it. The above layout file was main_activity.xml so the generate class was MainActivityBinding. --Binding Data
并在编译时生成。
所以,select 不同的布局 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);
}
}
我在我的应用程序中大量使用 MVVM,并且还在围绕它构建一个库。
我遵循每个 XML 中只有一个 ViewModel 的惯例。此外,viewmodel 变量的名称在所有 XML 中都是相同的。
因此,在您的情况下,您可以创建另一个包含 VMFirst
和 VMSecond
的 ViewModel class。
public class ParentVM {
VMFirst first;
VMSecond second;
}
两个 XMLs(纵向和横向)将具有相同的名称,例如 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 中都遵循相同的变量名,所以我能够在基础 class 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。
如果有人搜索这个问题,2 年后我尝试做同样的事情,我发现它现在工作正常。
我在 layout
和 layout_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_square
和 big_square
。
我 运行 phone 和平板电脑上的项目。这是我的发现:
- DataBinding 在不同布局文件夹中的所有同名布局文件下创建一个包含 ALL 视图和变量的实现。
- 存在于所有布局中的视图不可为空,所有其他视图均可为空。在上面 XML 中,
myRoot
在使用来自 Kotlin 的绑定时 不是可空视图 ,而big_square
和small_square
可以为 null 视图。变量可以为空,无论它们是否存在于所有布局中(这是预期的行为)。 - 您不能在每个文件中命名绑定 类 不同。它必须相同(上面示例中的
MainBinding
,或者如果您未定义它,则默认为LayoutResourceName
+Binding
)。 - 关于绑定实现的视图和变量的名称是驼峰式的。所以我的
small_variable
&small_square
在代码方面是binding.smallVariable
和binding.smallSquare
。 - 使用 Kotlin,您可以只使用像
binding.bigSquare?.operation
这样的视图,这很棒,您不需要事先检查它是平板电脑还是 phone 或者视图是否为 null。 - 提示一下,您可以分配
binding
个字段,即使它们所在的布局不会被使用。您仍然可以在代码上说binding.smallVariable = 3
,它会进行分配并保存值。我觉得还是小心点好。