Android 具有程序化 UI 自定义的 MVVM

Android MVVM with Programmatic UI Customization

我正在尝试使用 Android 数据绑定库在应用程序中确定正确的 MVVM 分层。真正简单的布局,就像在大多数示例中一样,很好地抽象了 XML 布局绑定到我的视图模型中的方法,以通过可观察对象进行事件处理和模型更新。但是,通常情况下,UI 自定义需要以编程方式完成(findViewById()...)and/or 通过将样式属性注入字符串资源等方式。

我是否应该将 Activity/Fragment 也视为视图的一部分,并通过数据绑定在 VM 和布局之间做任何无法处理的事情,还是最好只使用来自VM 到 Activity/Fragment(同时尝试将 VM 保持为 POJO)?

-- 编辑:示例 1 --

在同一字符串中渲染具有多种颜色的 TextView:我最初的实现方式是将 CDATA 和字体标签包装在字符串资源中并使用 findViewById().setText 进行渲染(Html.fromHtml(getString(..)))。我修改了我的布局,改为在我的 VM 中绑定为 android:text="@{viewModel.text1}",它调用一个接口方法到我的片段 returns Html.fromHtml(text) ,我的 VM returns a 跨越到布局。考虑严格的 MVVM,我可能不会以这种方式定义 VM,所以感觉有点老套。

如果您有数据绑定,则不必使用 findViewById()。大多数东西都可以通过数据绑定来绑定,因为它目前是双向的。查看最新更新: https://developer.android.com/tools/data-binding/guide.html

您可以在 XML 布局中直接将字符串 属性 与 TextWatcher 绑定在一起。你可以用 android:onClick 做同样的事情。 此外,还有一种通过生成的 class 访问数据绑定中的 class 的方法,如下所示:YourClassBinding.nameOfYourView(假设您的布局名称是 your_class.xml 并且包含一个带有 @[= 的视图17=].

对于其他更改,您可以编写自己的 BindingAdapter。我更愿意保持 VM 清洁 POJO。

根据经验,我不会将任何需要 Android 上下文的内容放入 ViewModel。另一方面,所有逻辑都应位于 ViewModel 中,以便您可以对其进行单元测试。

例如,如果您希望 TextView 根据在 ViewModel 中做出的决定显示字符串 A 或 B,您可以创建一个接口,该接口是 Android 资源的抽象,并通过它从 View 到 ViewModel 的接口。

public MyViewModel {

 private MyStrings strings;

 public MyViewModel(MyStrings strings) { 
   this.strings = strings;
 }

 public String getMyString() {
   return becauseOfReasons() ? strings.getA() : strings.getB();
 }

  public interface MyStrings {
   String getA();
   String getB();
  } 
}

对于您的示例,您想以编程方式使用 Html.fromHtml。 Android 数据绑定库的真正优点在于您可以使用 xml 中的访问 Html.fromHtml()。因此,在您的情况下,xml 可能如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
  <data>

    <import type="android.text.Html"/>

    <variable
        name="viewModel"
        type="some.package.MyViewModel"/>

  </data>

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{Html.fromHtml(viewModel.myString)}"/>
</layout>

然而,这是一种非常激进的方法。我建议始终考虑您的 ViewModel 是否仍然可测试。例如,当您从 ViewModel 中的 R class 访问资源 ID 时,您仍然可以创建 JUnit 测试。但另一方面,你不应该在 ViewModel 中使用 Html.fromHtml 因为,你会得到一个 not mocked 错误。