欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > Android Compose入门和基本使用

Android Compose入门和基本使用

2025/4/7 22:37:16 来源:https://blog.csdn.net/ChenYiRan123456/article/details/147026101  浏览:    关键词:Android Compose入门和基本使用

文章目录

    • 一、Jetpack Compose 介绍
      • Jetpack Compose是什么
      • Composable 函数
      • 命令式和声明式UI
      • 组合和继承
    • 二、状态管理
      • 什么是状态
      • State
      • remember
      • 状态提升
    • 三、自定义布局
      • Layout Modifier
      • Layout Composable
      • 固有特性测量
        • 使用内置组件固有特性测量
        • 自定义固有特性测量
    • 四、项目中使用Jetpack Compose
    • Compose与View相互操作
      • View中使用Compose
      • Compose中使用View
      • Compose和View的关系
    • Jetpack Compose使用对比
      • 相关资料链接:
    • 五、总结

一、Jetpack Compose 介绍

Jetpack Compose是什么

Jetpack Compose是用于构建原生Android界面的新工具包,是一种声明式UI框架。

Composable 函数

Compose声明式UI的基础是Composable函数,通过定义一组接收数据而渲染界面元素的可组合函数来构建界面。换句话说,加上了Composeable函数,我们的函数就会变成一个UI。

举个例子:

Geeting函数接收一个String,并渲染出一个显示问候信息的Text

@Composable
fun Greeting(name: String) {Text("Hello, $name")
}

运行效果:

在这里插入图片描述

对于需要渲染成界面的函数,称之为可组合函数,有几个特点

  • 此函数带有@Composable注解,表面他是一个可组合函数,所有组合函数都必须要带上这个注解
  • 可组合函数需要在其他可组合函数的作用域内被调用
  • 为了与普通函数区分,要求组合函数首字母大写
  • 带上@Preview注解的Composeable函数,点击desgin选项,可以实时预览样式,不过函数不能带参数,不然预览不了

命令式和声明式UI

命令式UI:通过一系列明确的指令来控制用户界面的行为。需要手动指定每一步操作,以确保用户界面达到预期的状态。View视图体系就属于命令式编程,比如先获取到一个View的对象引用,再通过它的setXXX()方法去改变这个View的属性,以此来更改UI状态。手动操控视图比较容易遗漏,比如一条数据再多个地方呈现,很有可能会忘记去更新这个视图。

声明式UI:只需要负责声明描述UI,而不需要手动更新。我们只需要在描述一个页面的时候附带各个控件的状态,然后当有任何状态发生改变时,页面会自动刷新。可能会有疑问,那每次更新一个控件,都会刷新整个页面吗,其实不是,Compose采用了相似的优化策略,也就是重组。刷新页面时,只会更新那些状态发生改变的控件,那些状态没有发生改变的会跳过执行。

举个例子

在这里插入图片描述

当布局里面的文字发生变化的时候,怎么更新

左边命令式的写法:

在这里插入图片描述

右边声明式写法:不需要手动更新,页面会跟随数据的变化自动更新,只需要在初始化的时候加上by mutableStateOf,当text的值发生改变的时候,会自动更新页面。(属性委托)

在这里插入图片描述

这个时候会发现,数据和界面进行关联,界面跟着数据自动更新,不就是DataBinding吗,但是DataBinding只能更新界面上元素的值,而Compose不仅可以更新界面上的任何元素,不只是元素的值,还可以更新界面元素结构。

在这里插入图片描述

showImage发生变化时,界面自动改变,而在View中通常是用visibility进行视觉的隐藏,而这对UI的性能是有提升的,因为对visibility的设置会重新渲染布局,requestLayout

组合和继承

组合优先于继承,这是面向对象编程中的设计原则,比如:在View的体系中,View.java有超过3万行代码是比较臃肿的,比较臃肿的父类,会造成子类视图功能的不合理。举个例子,在Android View中常用的Button控件,为了让Button具备显示文字的功能,继承了TextView,但是也有很多不适合按钮的功能也被继承下来,后续随着TextView自身功能的迭代,Button可能引入更多的不必要功能。

而Jetpack Compose则是函数的方式,相互之间没有继承关系,Compose中的Button

Button(onClick = { /* 处理点击事件 */ },modifier = Modifier.wrapContentSize()
) {Text("I'm a button")
}

这样就将一个 Button 组件和一个 Text 组件组合起来,Button 本身的作用就是提供点击事件,Text 提供文本作用的,形成了一个带有文本的按钮。设计模式的角度来讲,各个组件职责更单一。

二、状态管理

什么是状态

状态是可以变化的任何值。在Compose函数中,如果数据状态没有发生改变,则UI永远不会自行改变。在Compose中,每个组件都是一个被@Composable修饰的函数,其状态就是函数的参数。因为Composeable是个函数,只能通过传参,当参数不变,则函数的输出就不会变,唯一的参数决定唯一输出。反言之,如果要让界面发生变化,则需要改变界面的状态,然后 Composable 响应这种变化。

举个例子:

一个简单的计数器,有个显示计数的空间,一个增加的按钮,每点击一次,则计数器加1,一个减少的按钮,每点击一次,计数器减一

View中状态

假设我们按照之前View的体系,可以这样写

class MainActivity : AppCompatActivity() {// ...override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// ...binding.incrementBtn.setOnClickListener {binding.tvCounter.text = "${Integer.valueOf(binding.tvCounter.text.toString()) + 1 }"}binding.decrementBtn.setOnClickListener {binding.tvCounter.text = "${Integer.valueOf(binding.tvCounter.text.toString()) - 1 }"}}
}

但是上面的计算逻辑和UI的耦合度比较高,可以再优化一下,就变成

class MainActivity : AppCompatActivity() {// ...private var counter: Int = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// ...binding.incrementBtn.setOnClickListener {counter++updateCounter()}binding.decrementBtn.setOnClickListener {counter--updateCounter()}}private fun updateCounter() {binding.tvCounter.text = "$counter"}
}

这个主要改动在于,新增了counter变量用于计数,其实属于一种"状态提升",原本TextView的内部状态,上升到了Activity中,这样假设更好了UI,但是计数的逻辑还是可以复用。

还可以再优化,比如现在的计算逻辑在Activity中,无法在其他页面进行复用,进一步使用MVVM结构进行改造。引入ViewModel,将状态从Activity中提升到了ViewModel

class CounterViewModel: ViewModel() {private var _counter  = MutableLiveData(0)val counter: LiveData<Int> =_counterfun incrementCounter() {_counter.value = _counter.value!! + 1}fun decrementCounter() {_counter.value = _counter.value!! - 1}
}class MainActivity : AppCompatActivity() {private val viewModel: CounterViewModel by viewModels<CounterViewModel>()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding.incrementBtn.setOnClickListener {viewModel.incrementCounter()}binding.decrementBtn.setOnClickListener {viewModel.decrementCounter()}viewModel.counter.observe(this){ count->binding.tvCounter.text = count}}
}

有Jetpack库使用经验还是比较熟悉上面的代码的,将状态提升到了ViewModel,使用LiveData包装起来,在Activity中监听状态的变化,从而自动刷新UI。

而在Compose中实现这样一个计数器。

@Composable
fun CounterPage() {Column(horizontalAlignment = Alignment.CenterHorizontally) {var counter = 0Text(text = "$counter")Button(onClick = { counter++ }) {Text(text = "increment")}Button(onClick = { counter-- }) {Text(text = "decrement")}}
}

在这里插入图片描述

这个时候无论怎么点击,Text显示的值还是为0,我们只是单纯的计数count,但是函数并没有被重新调用,刷新UI,所以Text显示的值没有发生变化。

在上面传统View中,我们改变状态,需要主动调用updateCounter方法去刷新UI,后面经过改造,我们把状态提升到了ViewModel,这样使用LiveData包装后,需要在Activity中监听状态的变化,才能对状态的变化进行响应。

Compose的刷新UI逻辑是:状态发生变化,触发了重组,函数被重新调用,由于参数发生了变化,函数输出改变了,最终渲染出的画面才发生变化。

State

在传统View中,需要使用LiveData将状态变量包装成一个可观察类型的对象。Compose也提供了可观察的状态类型,可变状态类型MutableState和不可变状态类型State。我们只需要使用State/MutableState将状态变量包装起来,这样就可以触发重组,方便的是在声明式UI框架中,不需要我们显示注册监听状态变化,框架自动实现了订阅关系。

@Composable
fun CounterPage() {Column(horizontalAlignment = Alignment.CenterHorizontally) {val counter: MutableState<Int> = mutableStateOf(0)Log.d("cyr", "counter text --> ${counter.value}")Text(text = "${counter.value}")Button(onClick = {Log.d("cyr", "increment button click ")counter.value++}) {Text(text = "increment")}Button(onClick = {Log.d("cyr", "decrement button click ")counter.value--}) {Text(text = "decrement")}}
}

打印日志

D  counter text --> 0
D  increment button click             
D  counter text --> 0
D  decrement button click 
D  counter text --> 0
D  increment button click 
D  counter text --> 0
D  increment button click 
D  counter text --> 0
D  decrement button click 
D  counter text --> 0
D  decrement button click 
D  counter text --> 0

这里使用了mutableStateOf()方法初始化了一个MutableState类型的状态变量,并传入默认值0,使用的时候通过调用counter.value。

通过日志发现,点击按钮Text(text = “${counter.value}”)会重新执行,也就是发生了重组,但是执行的时候参数没有改变,依然是0,要解决这个问题,则需要使用Compose的一个函数,remember

在这里插入图片描述

remember

remember函数的源码

/*** Remember the value produced by [calculation]. [calculation] will only be evaluated during the composition.* Recomposition will always return the value produced by composition.*/
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T = currentComposer.cache(false, calculation)

可以将remember看作是为函数提供单个对象的存储空间,其作用就是对其包裹起来的变量值进行缓存,后续发生重组过程中,不会重新初始化,而是直接从缓存中读取。

在这里插入图片描述

使用remember向自身添加内存,然后在内存中存储MutableStateOf,以创建MutableState< String >,对Value进行任何更改都会自动重组用于读取此状态的所有可组合函数。

在这里插入图片描述

@Composable
fun CounterPage() {Column(horizontalAlignment = Alignment.CenterHorizontally) {val counter: MutableState<Int> = remember { mutableStateOf(0) }Text(text = "${counter.value}")Button(onClick = {counter.value++}) {Text(text = "increment")}Button(onClick = {counter.value--}) {Text(text = "decrement")}}
}

再次运行,正常加减

状态提升

我们希望事件始终向上流动,而状态始终向下流动,状态提升,做法就是内部状态移除,以参数的形式传入。以及需要回调给调用方的事件,也以参数形式传入。

改造一下:

@Composable
fun CounterPage(counter: Int, onIncrement: () -> Unit, onDecrement: () -> Unit) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Text(text = "$counter")Button(onClick = {onIncrement()}) {Text(text = "increment")}Button(onClick = {onDecrement()}) {Text(text = "decrement")}}
}

这样CounterPage函数变成一个无状态函数,有助于单向数据流打造(确保数据只能从一个方向流动:从父组件流向子组件)

三、自定义布局

布局阶段用来对视图树中每个LayoutNode进行宽高尺寸测量并完成位置摆放。

在Compose中,每个LayoutNode都会根据来自父LayoutNode的布局约束进行自我测量(类似传统View的MeasureSpec)。布局约束中包含了父LayoutNode允许子LayoutNode的最大宽高和最小宽高,当父LayoutNode希望子LayoutNode测量的宽高为某个具体值时,约束中的最大宽高和最小宽高就是相同的。LayoutNode不允许被多次测量,在Compose中多次测量会抛异常,有些需求场景需要多次测量LayoutNode,Compose提供了固有特性测量。

Layout Modifier

使用Modifier.layout()手动控制元素的测量和布局。通常layout修饰符的使用方法

fun Modifier.customLayoutModifier(...)
= Modifier.layout { measurable, constraints ->...
})

使用layout修饰符的时候,传入的回调Lambda包含两个参数:measurable,constraints

measureable:提供api完成测量与布局的过程

constraints:子元素的测量约束,包裹宽度和高度的最大值和最小值

例子:

在这里插入图片描述

fun Modifier.firstBaselineToTop(firstBaselineToTop: Dp
) = layout { measurable, constraints ->//测量元素val placeable = measurable.measure(constraints)//测量之后,获取元素的基线值val firstBaseLine = placeable[FirstBaseline]//元素新的Y坐标 = 新基线值 - 旧基线值val placeableY = firstBaselineToTop.roundToPx() - firstBaseLineval height = placeable.height + placeableYlayout(placeable.width, height) {//设置元素的位置placeable.placeRelative(0, placeableY)}
}@Composable
fun TextWithPaddingToBaseLine(){Test2Theme {Text(text = "Hi there!",Modifier.firstBaselineToTop(24.dp).background(Color.Red))}
}

Layout Composable

LayoutModifier会将当前元素的所有子元素视为整体进行统一的测量和布局,更多适用于统一处理的场景。当我们需要测量布局中每一个子组件,类似于自定义ViewGroup,需要使用Layout Composable。

@Composable
fun CustomLayout(modifier: Modifier = Modifier,// custom layout attributes content: @Composable () -> Unit
) {Layout(modifier = modifier,content = content) { measurables, constraints ->// measure and position children given constraints logic here}
}

modifier:由外部传入的修饰符,会决定该UI元素的constraints

content:在content中声明所有子元素信息

例子:

@Composable
fun MyOwnColumn(modifier: Modifier = Modifier,content: @Composable () -> Unit
) {Layout(modifier = modifier,content = content) { measurables, constraints ->val placeables = measurables.map { measurable ->measurable.measure(constraints)}var yPosition = 0layout(constraints.maxWidth, constraints.maxHeight) {placeables.forEach { placeable ->// placeRelative 会根据当前 layoutDirection 自动调整子元素的位置(从左至右或从右至左)placeable.placeRelative(x = 0, y = yPosition)yPosition += placeable.height}}}
}@Composable
fun BodyContent(modifier: Modifier = Modifier) {MyOwnColumn(modifier.padding(8.dp)) {Text("MyOwnColumn")Text("places items")Text("vertically.")Text("We've done it by hand!")}
}

固有特性测量

Compose的每一个组件不允许多次进行测量的,多次测量在运行时会抛异常,但是有些情况需要进行多次测量,比如有种场景,希望中间分割线高度与两边文案的高度保持相等。

在这里插入图片描述

采取的方案可以预先获取到两边文案组件高度信息,然后计算两边高度的最大值,即可确定当前父组件的高度值,只需让分割线的高度占满整个父组件即可。

使用内置组件固有特性测量
@Composable
fun Greeting() {MyOwnRow(modifier = Modifier.height(IntrinsicSize.Min)) {   //使用了自定义的固有测量属性,不然Divider高度占满屏幕Text(text = "Hello Kotlin",fontSize = 10.sp)Divider(color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp)//.layoutId("Divider"))Text(text = "Hello Android",fontSize = 18.sp)}}

在这里插入图片描述

自定义固有特性测量

需要额外重写measurePolicy中的固有特性测量Intrinsic系列方法。

在这里插入图片描述

当使用Modifier.width(IntrinsicSize. Max)时,在测量阶段便会调用maxIntrinsicWidth方法,以此类推。

Row组件同样的固有特性效果。因为我们的需求场景只使用了Modifier.height(IntrinsicSize. Min),所以仅重写minIntrinsicHeight方法就可以了。

@Composable
fun Greeting() {MyOwnRow(modifier = Modifier.height(IntrinsicSize.Min)) {   //使用了自定义的固有测量属性Text(text = "Hello Kotlin",fontSize = 10.sp    //字体大小不一样,高度不一样)Divider(color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp)//.layoutId("Divider"))Text(text = "Hello Android",fontSize = 18.sp    //字体大小不一样,高度不一样)}}@Composable
fun MyOwnRow(modifier: Modifier = Modifier, content: @Composable () -> Unit) {Layout(modifier = modifier, content = content, measurePolicy = object : MeasurePolicy {override fun MeasureScope.measure(measurables: List<Measurable>,constraints: Constraints): MeasureResult {val placeables = measurables.map { measurable ->//给控件设置layoutId可以直接找到控件,自定义测量规则//measurable.layoutId=="Divider"measurable.measure(constraints)}var positionX = 0return layout(constraints.maxWidth, constraints.maxHeight) {placeables.forEach { placeable ->placeable.placeRelative(positionX, 0)positionX += placeable.width}}}override fun IntrinsicMeasureScope.minIntrinsicHeight(measurables: List<IntrinsicMeasurable>,width: Int): Int {var maxHeight = 0measurables.forEach {//找出最大的高度并赋值给maxHeight//遍历每个子组件,计算其在最大宽度下的最小固有高度,并确保最终的最大高度至少为当前的最大高度maxHeight = it.minIntrinsicHeight(width).coerceAtLeast(maxHeight)}return maxHeight}})
}

固有特性测量的本质就是允许父组件预先获取到每个子组件宽高信息后,影响自身在测量阶段获取到的constraints宽高信息,从而间接影响子组件的测量过程。在上面的例子中我们通过预先测量文案子组件的高度,从而确定了父组件在测量时获取到的constraints高度信息,并根据这个高度指定了分割线高度。

四、项目中使用Jetpack Compose

1.检查AGP版本,确保升级是新版本,最低4.2

2.检查Kotlin版本,具体可查看

https://developer.android.com/jetpack/androidx/releases/compose-kotlin

例如:kotlin版本是1.9.0对应compose Compiler版本是1.5.0/1.5.1

3.修改配置信息

3.1 启用compose

    buildFeatures {compose true}

3.2 配置Compose Compiler版本

composeOptions {kotlinCompilerExtensionVersion '1.5.0'
}

kotlin版本对应

3.3 确保JVM版本为Java 8

3.4 添加依赖

dependencies {// Integration with activitiesimplementation 'androidx.activity:activity-compose:版本号'// Compose Material Designimplementation 'androidx.compose.material:material:版本号 '// Animationsimplementation 'androidx.compose.animation:animation:版本号'  implementation 'androidx.compose.ui:ui:版本号'implementation 'androidx.compose.ui:ui-graphics:版本号'implementation 'androidx.compose.ui:ui-tooling-preview:版本号'// Integration with ViewModelsimplementation 'androidx.lifecycle:lifecycle-viewmodel-compose:版本号'// UI TestsandroidTestImplementation 'androidx.compose.ui:ui-test-junit4:版本号'
}

Compose与View相互操作

View中使用Compose

使用ComposeView作为桥梁

xml文件中加入ComposeView

例如:

<?xml version="1.0" encoding="utf-8"?>
<com.example.test.nestedscroll.MyNestedScrollViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:id="@+id/nestedscrollview"android:layout_height="match_parent"android:orientation="vertical"><androidx.appcompat.widget.LinearLayoutCompatandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/linearLayoutCompat"android:orientation="vertical">//使用composeView<androidx.compose.ui.platform.ComposeViewandroid:id="@+id/compose_image_view"android:layout_width="wrap_content"android:layout_height="wrap_content"/><LinearLayoutandroid:id="@+id/tablayout_viewpager"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.google.android.material.tabs.TabLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/tablayout"/><androidx.viewpager2.widget.ViewPager2android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/viewpager_view"/></LinearLayout></androidx.appcompat.widget.LinearLayoutCompat></com.example.test.nestedscroll.MyNestedScrollView>
//代码中,先根据 id 查找出来,再 setContent 即可:
val imageView = findViewById<ComposeView>(R.id.compose_image_view)
imageView.setContent {ComposeImageView()
}
@Composable
fun ComposeImageView(){Image(painter = painterResource(R.drawable.ic_test1),contentDescription = null,contentScale = ContentScale.Crop,modifier = Modifier.fillMaxSize().height(300.dp))
}

动态添加

val linearLayoutCompat =findViewById<LinearLayoutCompat>(R.id.linearLayoutCompat)
linearLayoutCompat.addView(ComposeView(this@MainActivity3).apply {setContent {ComposeImageView()}
},0)

直接在 ComposeView 中添加view视图,会抛出 Cannot add views to ComposeView; only Compose content is supported 错误

//1
<androidx.compose.ui.platform.ComposeViewandroid:id="@+id/tablayout_viewpager"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.google.android.material.tabs.TabLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/tablayout"/><androidx.viewpager2.widget.ViewPager2android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/viewpager_view"/></androidx.compose.ui.platform.ComposeView>//2
val composeView = ComposeView(context)
composeView.addView(TextView(context)) 

Compose中使用View

1.有少数View暂时没有Compose版本,比如WebView,MapView

2.之前有写好的UI不想动,想直接拿过来用

Compose中使用AndroidView

例如:

@Composable
fun MyTextView(text: String) {AndroidView(fatory = { context ->TextView(context).apply {setText(text)}},update = { view -> view.setText(text)})
}

这个桥梁是AndroidView

@Composable
@UiComposable
fun <T : View> AndroidView(factory: (Context) -> T,modifier: Modifier = Modifier,update: (T) -> Unit = NoOpUpdate
) {AndroidView(factory = factory,modifier = modifier,update = update,onRelease = NoOpUpdate)
}

factory接收一个Context参数,用来构建一个View,update方法是一个callback,inflate之后会执行,读取状态值变化之后,也会被执行。

例如:

@Composable
fun MyComposeWithButton() {// 定义一个可变状态变量var buttonText by remember { mutableStateOf("Click Me") }Box(modifier = Modifier.fillMaxSize()) {// 使用 AndroidView 来嵌入 ButtonAndroidView(factory = { context ->Button(context).apply {setText(buttonText)setOnClickListener {buttonText = "Clicked"}}},modifier = Modifier.fillMaxSize(),update = { button ->// 当 buttonText 发生变化时,更新 Button 的文本button.text = buttonText})// 添加一个按钮,点击时重置文本Button(onClick = { buttonText = "Reset Text" },modifier = Modifier.align(alignment = androidx.compose.ui.Alignment.BottomCenter)) {androidx.compose.material3.Text("Reset Text")}}
}

Compose和View的关系

窗口视图架构

Activity的具体实现类是PhoneWindow,在Activity执行attach的时候,会创建一个PhoneWindow对象。PhoneWindow作为装载根视图DecordView的顶级容器,Activity通过setContentView实际上是调用PhoneWindow来创建DecordView,并解析xml布局加载到DecordView的contentView部分。

在这里插入图片描述

Compose调用setContent其实就是往ContentView里面放布局

通过android studio的LayoutInspector看到ComposeActivity的布局结构

在这里插入图片描述

·

public fun ComponentActivity.setContent(parent: CompositionContext? = null,content: @Composable () -> Unit
) {//decorView 的第一个子View如果是 ComposeView 直接用,如果不是就创建一个ComposeView ,然后添加到跟布局val existingComposeView = window.decorView.findViewById<ViewGroup>(android.R.id.content).getChildAt(0) as? ComposeViewif (existingComposeView != null) with(existingComposeView) {...setContent(content)} else ComposeView(this).apply {...setContent(content)...setOwners()setContentView(this, DefaultActivityContentLayoutParams)}
}

decorView 的第一个子View如果不是ComposeView就创建一个 ,然后添加到跟布局。而ComposeView 里又通过 Wrapper_android.kt 创建了一个 AndroidComposeView。AndroidComposeView 是继承ViewGroup。dispatchDraw方法也是通过Canvas画布进行绘制。

在这里插入图片描述

我们点开任何一个Compose组件函数,一系列调用最终都会到了Layout.kt的Layout()方法,Layout() 核心是调用ReusableComposeNode ()方法。这里有个参数 factory,factory 是一个构造器函数, factory 被调用就会创建一个LayoutNote,定义的布局属性modifier也在这里设置给了LayoutNode。每个组件最终都是一个LayoutNote

AndroidComposeView中有个root节点,而每个ComposeView的组件内部其实是一个LayoutNode,会添加到Root节点中,形成一个组件树

Wrapper.android.kt

具体测量源码分析参考文章链接

https://juejin.cn/post/6981805443219718151

Jetpack Compose使用对比

官网示例:https://zhuanlan.zhihu.com/p/386826633

相关资料链接:

xml对应可替换Compose的View

https://www.jetpackcompose.app/What-is-the-equivalent-of-RecyclerView-in-Jetpack-Compose

compose对应kotlin版本

https://developer.android.com/jetpack/androidx/releases/compose-kotlin

官方文档

https://developer.android.com/develop/ui/compose/documentation?hl=zh-cn

五、总结

Compose重新定义了Android UI的开发方式,提升了开发效率。

  • 声明式UI,不需要手动刷新数据
  • 取消XML,完成消除了混合写法的(XML+Java、kotlin)的局限性
  • 兼容性,大多数的Jetpack库(如ViewModel,LiveData等)以及Kotlin协程都适用于Compose,也能和现有的View体系共存,一个View项目中引入Compose
  • 更简单的动画和触摸事件Api,Compose提供了很多可以直接用的Material组件
  • 简化代码的数量,比如RecyclerView需要的Adapter适配器,使用LazyColumn,减少一些bug

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词