在Jetpack Compose中潜水

大家好 在离开周末之前,我们必须与您分享另一份特别为Android开发人员的学生准备的翻译 高级课程



尝试用于Android应用程序的新UI框架


在过去的几年中,参与许多移动项目,我不得不使用各种技术,例如Android,ReactNative和Flutter。 从ReactNative切换回经典Android,让我百感交集。 返回Kotlin的过程很顺利,但是我真的很想念React UI框架。 组成用户界面的小型可重用组件非常棒,并提供了更大的灵活性和开发速度。

回到经典的Android,我需要担心保持View层次结构尽可能统一。 因此,很难真正致力于组件方法。 这使复制粘贴更具吸引力,从而导致更复杂且受支持较少的代码。 最终,我们避免尝试使用可以改善用户体验的用户界面。


Android揭示了Jetpack Compose。 插图:伊曼纽尔·巴吉拉(Emanuel Bagilla)

Jetpack进行救援


因此,在观看Google I / O 2019大会上的Android新增功能之后,我立即开始处理Compose并尝试了解有关它的更多信息。 Compose是Kotlin完全开发的反应式用户界面工具包。 Compose看起来与现有的用户界面框架(例如React,Litho或Flutter)非常相似。

Android UI框架的当前结构自2008年以来就存在,并且随着时间的推移变得越来越复杂,很难维护。 Jetpack Compose的目标是从一开始就考虑现代组件的哲学。 该框架在编写时考虑了以下主要目标:

  • 与平台版本不一致:由于Compose不依赖于新的Android版本,因此可以快速修复错误。
  • 较小的技术堆栈:创建用户界面时,框架不会强制您使用“视图”或“片段”。 所有元素都是组件,可以自由组合在一起。
  • 透明的状态管理和事件处理:在大型应用程序中需要解决的最重要和最复杂的事情之一是在用户界面中处理数据流和状态。 Compose阐明了由谁负责状态以及如何处理事件,这与React的处理方式类似。
  • 编写更少的代码:在Android中编写用户界面通常需要大量代码,尤其是在创建更复杂的布局(例如,使用RecyclerView)时。 Compose旨在极大地简化创建用户界面的方式。

这使得创建隔离的和可重复使用的组件更加容易,从而使创建带有现有元素的新屏幕变得更加容易。 作为开发人员,帮助您专注于创建方便的用户界面,而不是试图控制View层次结构并驯服View和Fragment。

使用Compose的一个简单应用程序:Hello World


让我们看看带有Jetpack Compose的简单Hello World应用程序的代码。

class ComposeActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper { MyApp() } } } @Composable fun MyApp() { MaterialTheme { Text(text = "Hello world!", style = +themeTextStyle { h3 }) } } } 

onCreate方法中onCreate我们通过调用setContent设置应用程序的内容。 这是一种初始化复合窗口小部件树并将其包装在FrameLayout

为了使其工作,我们需要将应用程序包装在CraneWrapperMaterialThemeCraneWrapper负责配置ContextFocusManagerTextInputService 。 需要使用MaterialTheme来提供小部件的颜色,样式和字体。 考虑到这一点,我们可以添加Text组件,它将以某种样式在屏幕上显示我们的文本。

州介绍


管理数据流和状态可能是一项艰巨的任务。 为了说明使用Compose多么容易,让我们创建一个简单的计数器应用程序。
为了处理状态,Jetpack Compose使用了其他现代UI框架的思想,例如Flutter和React。 有一个单向的和反应性的数据流,可导致您的小部件进行更新或“重建”。

 @Composable fun MyApp() { MaterialTheme { Counter() } } @Composable fun Counter() { val amount = +state { 0 } Column { Text(text = "Counter demo") Button(text = "Add", onClick = { amount.value++ }) Button(text = "Subtract", onClick = { amount.value-- }) Text(text = "Clicks: ${amount.value}") } } 


在上面的示例中,我们添加了“添加”和“减去”按钮,以及显示当前点击次数的标签。 如您在下面的示例中看到的那样,更新“金额”状态,状态发生变化时,会智能地重新排列小部件。


启动演示应用程序

amount状态用+state { 0 }初始化。 为了弄清楚什么是巫术,我抓取了源代码。 这是我的观点,尽管我仍然不确定我是否完全了解所有内容。

state {...}创建一个Effect< State < T < code>>Effect类是一个模糊类,其中包含在合成上下文中按位置运行的可执行代码块。 State类包含一个具有Model类型的值,从本质上讲该值是可观察的。 +运算符是一个临时运算符,可从Effect解析State

自定义状态模型


除了使用+state {}创建单个值模型外,我们还可以使用@Model注释创建自定义模型。 我们可以通过将计数器应用程序划分为较小的小部件,并将模型传递给其他已更新并显示此模型状态的小部件来改进计数器应用程序。

 @Model class CounterModel { var counter: Int = 0 var header = "Counter demo" fun add() { counter++ } fun subtract() { counter-- } } 


使用@Model批注,Compose Compiler插件使模型中的所有变量均可见,从而可用于重新排列小部件。 让我们更新小部件以使用CounterModel

 @Composable fun Counter(counterModel: CounterModel) { Column { CounterHeader(counterModel) AddSubtractButtons(counterModel) CounterLabel(counterModel) } } @Composable fun CounterHeader(counterModel: CounterModel) { Text(text = counterModel.header) } @Composable fun AddSubtractButtons(counterModel: CounterModel) { Button( text = "Add", onClick = { counterModel.add() }) Button( text = "Subtract", onClick = { counterModel.subtract() }) } @Composable fun CounterLabel(counterModel: CounterModel) { Text(text = "Clicks: ${counterModel.counter}") } 


微调器应用程序所包含的唯一窗口小部件现已拆分为几个较小的可组合窗口小部件。 CounterModel传递给各种小部件,以显示模型的状态,或使用add()subtract()函数更改模型的状态。

没有更多的看法


重要的是要了解,Jetpack Compose窗口小部件不会在后台使用视图或片段,这些只是在画布上绘制的功能。 Compose Compiler插件使用@Composable批注处理所有功能,并自动更新UI层次结构。

例如, Divider小部件由包含DrawFillRect小部件的Padding小部件组成。 查看DrawFillRect的源代码,很明显,他直接在画布上绘制线条。 所有其他小部件都以相同的方式实现。

 @Composable private fun DrawFillRect(brush: Brush) { Draw { canvas, parentSize -> val paint = Paint() brush.applyBrush(paint) canvas.drawRect(parentSize.toRect(), paint) } } 


在Divider小部件内使用的DrawFillRect的源代码。
如果我们通过启动Google的一个示例应用程序来查看Layout Inspector,我们将清楚地看到,使用Compose启动Android应用程序时,没有ViewViewGroups 。 我们看到一个包含在代码中创建的CraneWrapperFrameLayout ,从那里在屏幕上显示Compose UI层次结构。


布局检查器检查Jetpack Compose。

没有视图也意味着Jetpack Compose无法使用当前可用的视图,例如android.widget.Button ,并且必须从头开始创建所有小部件。 例如,如果您使用相同的方法查看Flutter,您会发现这是一项艰巨的工作。 这是Jetpack Compose在准备投入生产之前需要时间的原因之一。

所有元素都是小部件。


就像Flutter一样,在Compose中,所有元素都是小部件。 将更复杂的小部件分解为职责明确的基本小部件。 因此,即使填充,分隔符等也是小部件。 例如,如果要在按钮周围添加缩进,只需将其包装在padding小部件中:

 Padding(padding = 16.dp) { Button(text = "Say hello", onClick = { ... }) } 


将代码连接到用户界面


将Kotlin代码与UI小部件结合起来非常容易。 例如,如果要显示重复或取决于某些条件的用户界面。 因此,您可以轻松显示名称列表,如下所示。

 Column { listOf("John", "Julia", "Alice", "Mark").forEach { Text(text = it) } } 


这是一个非常强大的功能,但是您必须注意不要在用户界面级别上编写过多的逻辑。

与您的Android应用程序兼容


设计Compose是为了使您可以将其添加到现有应用程序中,并将UI的某些部分逐步转移到新框架中。 上面的示例将Jetpack Compose UI添加到一个活动中。 您还可以使用GenerateView批注将Compose小部件嵌入现有的XML布局中:

 @Composable @GenerateView fun Greeting(name: String) { /* … */ } //  -  layout.xml <GreetingView app:name=”John” /> 

结论


我对Compose感到很满意,因为它减少了我为Android开发时所遭受的不断增长的痛苦。 它有助于变得更加灵活,专注于创建方便的用户界面,明确的职责也有助于避免错误。

我认为Compose还有很长的路要走,不超过一两年就可以投入生产。 但是,我认为现在是查看Jetpack Compose的好时机。 创作者正在积极寻求反馈,在这个阶段您仍然可以进行更改。 所有评论将有助于改进此新框架。

阅读我的Try Jetpack今天撰写文章,了解如何连接Compose pre-alpha。 另外,我认为用Google I / O观看有关声明性接口模板视频将非常有趣。
我期待何时可以在真正的Android应用程序中使用Compose!

仅此而已。 我们期待您的评论,祝您周末愉快!

Source: https://habr.com/ru/post/zh-CN458106/


All Articles