繁体   English   中英

Android Jetpack Compose - 动态布局

[英]Android Jetpack Compose - Dynamic layouts

背景

我目前正在研究创建布局的选项,在项目开发期间,我希望根据库的稳定性/灵活性将 UI 迁移到 Jetpack Compose 或发布后。

该项目的一部分将使用服务器驱动的 UI。 然而,UI 的扭曲不是提前知道的,而是动态的(服务器和数据驱动)。

我在处理业务逻辑和表示层方面没有问题,但是当涉及到 UI 时,我需要根据表示数据和视图模型动态构建 UI。

TL; 博士

考虑到这一点,是否可以使用 Jetpack Compose 创建动态布局(不要与动态布局数据混淆)?

作为一个最小的例子,使用传统的ViewViewGroup这可以很容易地实现:

class DynamicViewActivity : AppCompatActivity() {
    
    private lateinit var root : LinearLayout

    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)

        // setup view group container
        root = LinearLayout(this)
        root.orientation = LinearLayout.VERTICAL
        root.layoutParams = LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT, 
            LinearLayout.LayoutParams.MATCH_PARENT)
        
        setContentView(root, LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT))
        
        // some lookup to create a dynamic layout
        val children : List<Pair<View, LinearLayout.LayoutParams>> = getChildren(someArgs)
        
        // add child views
        children.forEach { (view, params) -> root.addView(view, params) }
    }
    
    fun <T : View> addViewToRoot(view: T, params: LinearLayout.LayoutParams) {
        root.addView(view, params)
    }

    fun  removeFromRoot(viewTag : String) {
        root.findViewWithTag<View>(viewTag)?.let(root::removeView)
    }
}

您如何使用 Jetpack Compose 做同样的事情?

更新

按照@CommonsWare 的回答,我在 Compose 中实现了 UI。 由于我的实际代码有一个非常薄的 UI 层,所有侦听器和事件都使用单向和双向数据绑定,并且我的项目中已经解决了答案中的“未知”,因此交换 UI 非常容易。

话虽如此,我很快意识到像ScrollViewView::tooltipText这样的简单东西在 Compose 中还不存在。 与 xml 布局/资源相比,也没有简单的方法可以根据运行时配置(屏幕方向/屏幕桶大小等)进行布局。 这意味着对我来说,将数据绑定与所有丰富的View框架和库一起使用仍然是更好的解决方案。

期待 Compose 库的更新,也许会在未来的某个时候看看。

考虑到这一点,是否可以使用 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 -space 中,您可以拥有一个基于提供的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设置提示。

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


Compose 等价物将是这样的:

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

IOW,它将几乎相同。 主要区别是:

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

您的活动将是这样的:

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

...作为您的LinearLayout的替代品。

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


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

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

其中一些在基于View的 UI 和基于 Compose 的 UI 之间是相同的——例如 JSON 解析。 其中一些将大不相同,例如处理用户输入。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM