开发者问题收集

Android Jetpack Compose - 动态布局

2021-06-21
4806

背景

我目前正在调查创建布局的选项,在项目的开发过程中,我希望将UI迁移到JetPack撰写或发布后发行版本,根据库的稳定性/灵活性。

项目的一部分将使用服务器驱动的UI。 但是,扭曲是UI未提前知道,并且将是动态的(服务器和数据驱动的)。

我在处理业务逻辑和演示层时没有任何问题将需要根据演示数据和查看模型动态构建UI的要求。使用JetPack组成?

创建动态布局(不要与动态布局数据混淆)作为最小示例,使用传统 viewviewgroup 这可以很容易实现:

395708537

您如何使用JetPack组成?

更新 >按照@commonsware的答案,我以撰写为单位。 由于我的实际代码具有非常薄的UI层,因此所有听众和事件都使用一个和双向数据绑定,并且在我项目中已经解决的答案中“未知”,只需将UI交换很容易。

说我很快意识到, scrollview and view :: tooltiptext 诸如compose view :: becollview view :: becollview view view。 同样,与XML布局 /资源相比,基于运行时配置(屏幕方向 /屏幕桶大小等)的布局没有简单的方法。 这对我来说意味着,使用所有Rich View 框架和库仍然是更好的解决方案。未来。

1个回答

With this in mind is it possible to create dynamic layouts (not to be confused with dynamic layout data) using Jetpack Compose?

当然。Compose 全部都是函数。您可以解析数据并根据该数据调用函数,无论该数据是“填写此预定义的 UI 结构”还是“定义 UI 结构”。

例如,假设您的服务器有一个返回以下 JSON 的端点:

[
  {
    "element": "label",
    "attributes": {
      // values omitted for brevity
    }
  },
  {
    "element": "field",
    "attributes": {
      // values omitted for brevity
    }
  },
  // additional elements omitted for brevity
]

您的工作是根据该 JSON 组装 UI。 label 元素应为固定文本, field 元素应为文本输入字段,各种类型亦是如此。 attributes 对象包含因元素而异的详细信息。

因此,您可以解析它。假设您最终得到一个 List<UiElement> ,其中 UiElement 是一个接口或抽象类,其子类型基于受支持的元素(例如 LabelElementFieldElement )。现在您的工作是基于该 List<UiElement> 构建 UI。


View 空间中,您可以使用一个函数根据提供的 UiElement 创建 View

fun buildView(element: UiElement) = when (element) {
    is LabelElement -> buildTextView(element)
    is FieldElement -> buildEditText(element)
    else -> TODO("add other element cases here")
}

buildTextView() 将组装 TextView ,无论是从布局扩充还是调用构造函数。 buildEditText() 将组装 EditText ,无论是从布局中扩充还是调用构造函数。等等。每个函数都负责从 attributes 中获取值并使用它们执行一些有用的操作,例如在 TextView 中设置文本或在 EditText 中设置提示。

在问题的代码片段中,而不是使用 getChildren() 和循环方法,您将遍历 List<UiElement> 并为列表中的每个 UiElement 调用 buildView() ,并将结果添加到 LinearLayout


Compose 等效项将是这样的:

@Composable
fun buildNode(element: UiElement) {
    when (element) {
        is LabelElement -> buildTextNode(element)
        is FieldElement -> buildTextFieldNode(element)
        else -> TODO("add other element cases here")
    }
}

换句话说,它几乎完全相同。主要区别在于:

  • @Composable 注释( buildTextNode()buildTextFieldNode() 也需要)
  • 无需返回任何内容,因为可组合项会自动添加到父级
  • buildTextNode()buildTextFieldNode() 中的内容细节让人联想到 buildTextView()buildEditText() ,但基于可组合项

您的活动将具有类似以下内容:

Column {
    uiElements.forEach { buildNode(it) }
}

...作为 LinearLayout 的替代品。

(实际上,这两个示例都需要一个滚动容器,但我们在这里也会忽略它)


服务器定义的 UI 的所有复杂性都在于超出代码示例的范围:

  • 如何解析服务器响应?
  • 如何将该服务器响应映射到表示所需 UI 的对象模型中?
  • 如何使每个元素的 UI 位工作?
  • 如何处理事件侦听器?
  • 更一般地说,我们对此 UI 上的用户输入做了什么响应?
  • 我们将如何根据需要重新生成此 UI(对于视图,基于配置更改;对于可组合项,基于重组)?
  • 等等

基于 View 的 UI 和基于 Compose 的 UI 之间的某些功能相同 — 例如 JSON 解析。某些功能会有很大差异,例如处理用户输入。

但是,对于“解析服务器响应并根据该响应创建 UI 元素”的一般方法,视图和可组合项同样可以应对挑战。特别是,在您问题中的代码示例级别,视图和可组合项都可以处理您的高级场景。细节决定成败。

CommonsWare
2021-06-27