Mar*_*een 3 android android-layout android-jetpack-compose
背景
我目前正在研究创建布局的选项,在项目开发过程中,我希望可能将 UI 迁移到 Jetpack Compose 或发布后,具体取决于库的稳定性/灵活性。
该项目的一部分将使用服务器驱动的 UI。然而,UI 的变化是无法提前得知的,并且将是动态的(服务器和数据驱动)。
我在处理业务逻辑和表示层方面没有任何问题,但是当涉及到 UI 时,我需要根据表示数据和视图模型动态构建 UI。
长话短说
考虑到这一点,是否可以使用 Jetpack Compose 创建动态布局(不要与动态布局数据混淆)?
作为一个最小的例子,使用传统的View方法ViewGroup可以轻松实现:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
如何使用 Jetpack Compose 做同样的事情?
更新
根据 @CommonsWare 的回答,我在 Compose 中实现了 UI。由于我的实际代码有一个非常薄的 UI 层,所有侦听器和事件都使用单向和双向数据绑定,并且答案中的“未知”已在我的项目中解决,因此交换 UI 非常容易。
话虽如此,我很快意识到Compose 中尚不存在诸如ScrollView和 之类的简单事物。View::tooltipText与 xml 布局/资源相比,也没有简单的方法可以根据运行时配置(屏幕方向/屏幕桶大小等)进行布局。这对我来说意味着,使用数据绑定与所有丰富的View框架和库仍然是更好的解决方案。
期待 Compose 库更新,也许会在未来的某个时候关注。
Com*_*are 12
\n\n考虑到这一点,是否可以使用 Jetpack Compose 创建动态布局(不要与动态布局数据混淆)?
\n
当然。撰写是所有功能。您可以解析数据并根据该数据调用函数,无论该数据是“填充此预定义的 UI 结构”还是该数据是“定义 UI 结构”。
\n例如,假设您的服务器有一个返回以下 JSON 的端点:
\n[\n {\n "element": "label",\n "attributes": {\n // values omitted for brevity\n }\n },\n {\n "element": "field",\n "attributes": {\n // values omitted for brevity\n }\n },\n // additional elements omitted for brevity\n]\nRun Code Online (Sandbox Code Playgroud)\n您的工作是根据该 JSON 组装一个 UI。对于各种类型,元素label应该是固定文本,field元素应该是文本输入字段等等。该attributes对象包含因元素而异的详细信息。
所以,你解析一下。假设您最终得到一个List<UiElement>结果,其中UiElement是一个接口或抽象类或其他东西,其子类型基于受支持的元素(例如,LabelElement,FieldElement)。现在您的工作是基于此构建一个 UI List<UiElement>。
在-space 中,您可以拥有一个基于提供的View创建 的函数:ViewUiElement
fun buildView(element: UiElement) = when (element) {\n is LabelElement -> buildTextView(element)\n is FieldElement -> buildEditText(element)\n else -> TODO("add other element cases here")\n}\nRun Code Online (Sandbox Code Playgroud)\nbuildTextView()会组装 a TextView,无论是从布局膨胀还是调用构造函数。buildEditText()会组装一个EditText,无论是从布局膨胀还是调用构造函数。等等。这些函数中的每一个都负责从 中获取值attributes并用它们做一些有用的事情,例如在 a 中设置文本TextView或在 中设置提示EditText。
在问题的代码片段中,getChildren()您将迭代List<UiElement>并调用列表中的buildView()每个,而不是使用 -and-loop 方法UiElement,并将结果添加到您的LinearLayout.
等效的 Compose 是这样的:
\n@Composable\nfun buildNode(element: UiElement) {\n when (element) {\n is LabelElement -> buildTextNode(element)\n is FieldElement -> buildTextFieldNode(element)\n else -> TODO("add other element cases here")\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nIOW,它几乎是相同的。主要区别是:
\n@Composable上也需要)buildTextNode()buildTextFieldNode()buildTextNode()and中内容的细节buildTextFieldNode()会让人想起buildTextView()and buildEditText(),但基于可组合项你的活动应该是这样的:
\nColumn {\n uiElements.forEach { buildNode(it) }\n}\nRun Code Online (Sandbox Code Playgroud)\n...作为您的替代品LinearLayout。
(实际上,这两个示例都需要一个滚动容器,但我们在这里也将忽略它)
\n服务器定义的 UI 的所有复杂性都超出了代码示例的范围:
\nView例如,基于 - 的 UI 和基于 Compose 的 UI \xe2\x80\x94 JSON 解析之间的某些内容是相同的。其中一些会有很大不同,例如处理用户输入。
但“解析服务器响应并根据该响应创建 UI 元素”的一般方法、视图和可组合项同样能够应对挑战。特别是,在您的问题中的代码示例级别,视图和可组合项都可以处理您的高级场景。细节决定成败。
\n