Android Jetpack之Compose入门(一)

最近项目中使用到了Compose,为此重新温习一下Compose相关的知识。

学习后,长时间不使用忘记的很快,为此记录学习的Compose的入门各种问题。

一问一答的方式。

为什么要使用compose ?compose有哪些好处?

基于view的工具包已经存在了十多年,设备的功能更加强,用户对设备的期望越来越高。基于google的调研判断 大家需要一个现代框架的现代的工具包。而且能够充分使用到kotlin的强大功能

googel想更加快速提供更多的功能和改进。

Jetpack compose 是一个声明式的未捆绑工具包

compose 直观、易用、功能更强大。

compose 开发更快速,代码更少

compose 方法参数使用数据驱动UI,界面就会保持同步

怎么在项目中使用Compoes?

有两种方式,

  • 一个是针对新建的项目。
  • 一个是对旧的项目添加Compose支持。
方式一:创建支持 Compose 的新应用

使用Android Studio 提供新项目模板,

  1. 使用Android Studio ,请从菜单栏中依次选择 File > New > New Project。
  2. 在 Select a Project Template 窗口中,选择 Empty Activity,然后点击 Next。
方式二:现有应用设置 Compose
第一步:修改 build.gradle.kts (Module: app)
复制代码
android {
    // ... 其他配置保持不变 ...

    buildFeatures {
        // 1. 开启 Compose 功能
        compose = true
    }

    composeOptions {
        // 2. 指定 Compose 编译器版本 (需要与你的 Kotlin 版本匹配)
        // 你当前使用 kotlin-stdlib:1.9.0,建议使用如下版本:
        kotlinCompilerExtensionVersion = "1.5.1" 
    }
}

dependencies {
    // ... 原有依赖保持不变 ...

    // 3. 添加 Compose 核心依赖
    val composeBom = platform("androidx.compose:compose-bom:2024.02.00") // 使用 BOM 管理版本
    implementation(composeBom)
    androidTestImplementation(composeBom)

    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3") // 或者使用 material (Material 2)
    
    // 这里的 activity-compose 版本需要 1.5.0+,你的 context 中 activity 是 1.5.0,最好升级一下
    implementation("androidx.activity:activity-compose:1.8.2") 

    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")
}

修改后点击 "Sync Now" 同步项目。

第二步:修改 MainActivity.kt

将原来的 XML 布局加载方式 (setContentView(R.layout.activity_main)) 替换为 Compose 的 setContent。

复制代码
// 1. 继承改为 ComponentActivity (AppCompatActivity 也可以,但 ComponentActivity 更轻量且原生支持 Compose)
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        
        // 2. 使用 setContent 替代 setContentView
        setContent {
            // 这里是 Compose 的入口
            // 通常会包裹在一个 Theme 中,例如 MyApplicationTheme { ... }
            Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                Greeting(
                    name = "Android",
                    modifier = Modifier.padding(innerPadding)
                )
            }
        }
    }
}

// 3. 定义一个 Composable 函数
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

// 4. 添加预览
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    Greeting("Android")
}

模板代码setContent 是是怎么如何理解?如何使用?

复制代码
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
//        enableEdgeToEdge()
        // 注意这里使用的是 闭包,{}  而非  ()
        setContent {
            MyApplicationTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    MessageCard("tom", Modifier.padding(innerPadding))
                }
            }
        }
    }
}

@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name welcome compose world!",
        modifier = modifier.width(200.dp).height(30.dp)
            .clickable(onClick = {
                Log.d("MainActivity", "MessageCard: $name")
            }),

        maxLines = 1,
        fontSize = 20.sp,
        color = Color.Blue,
    )
}


@Preview()
@Composable
fun MessageCardPreview() {
    Text(
        text = "Hello manman welcome compose world!",
    )
}
MyApplicationTheme是什么?

对应android view的主题样式

Scaffold是什么
复制代码
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            // 直接使用 Compose组件
            Greeting(
                name = "Compose",
                modifier = Modifier
                    .padding(innerPadding)
                    .background(color = Color.Yellow)
            )
        // 通过 Scaffold 的包装使用组件
//      Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
//                Greeting(
//                    name = "Compose",
//                    modifier = Modifier.padding(innerPadding)
//                )
//            }
        }
    }

先对比效果: 注意添加 enableEdgeToEdge() ,不添加两者没有区别。

enableEdgeToEdge() 是一个辅助函数,它能让您的应用内容显示在系统栏(即屏幕顶部的状态栏和底部的导航栏)后面,从而实现所谓的"边到边"效果。

这会给用户带来更加沉浸式的现代化界面体验。

调用这个函数后,它会自动将系统栏设置为半透明,并处理好窗口边衬区(Window Insets),确保您的布局能够正确响应系统栏的存在,避免重要的UI元素被遮挡。

不使用 Scaffold

使用Scaffold

对比效果看出,不使用显示的文字都覆盖到系统状态栏上面了

Scaffold (脚手架)组件,是 Compose Material 3 库提供的一个高级布局组件。提供了一个标准的屏幕结构,让你可以轻松地组合常见的 UI 元素,

顶部栏底部栏浮动按钮

可以把它想象成一个房子的框架,它已经为你预留好了放窗户(TopAppBar)、门(BottomAppBar)、阳台(FloatingActionButton)和主要居住空间(Content)的位置。

完整体的页面

代码

复制代码
 @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            var presses by remember { mutableIntStateOf(0) }
            // 通过 Scaffold 的包装使用组件
            Scaffold(
                // 1、放置顶部栏
                topBar = {
                    TopAppBar(
                        title = {
                            Text("My app")
                        },
                        colors = TopAppBarDefaults.topAppBarColors(
                            containerColor = MaterialTheme.colorScheme.primaryContainer,
                            titleContentColor = MaterialTheme.colorScheme.primary,
                        )
                    )
                },
                // 2、放置底部栏
                bottomBar = {
                    BottomAppBar(
                        containerColor = MaterialTheme.colorScheme.primaryContainer,
                        contentColor = MaterialTheme.colorScheme.primary
                    ) {
                        Text(
                            modifier = Modifier.fillMaxWidth(),
                            textAlign = TextAlign.Center,
                            text = "Bottom bar"
                        )
                    }
                },
                // 3、放置浮窗操作按钮
                floatingActionButton = {
                    FloatingActionButton(onClick = { presses++ }) {
                        Icon(Icons.Default.Add, contentDescription = "Add")
                    }
                },

                modifier = Modifier.fillMaxSize()
            ) { innerPadding ->
                Greeting(
                    name = "Compose  $presses",
                    modifier = Modifier.padding(innerPadding)
                )
            }
        }
    }

@Preview() 怎么无法预览

复制代码
@Preview()
@Composable
fun MessageCardPreview(name: String) {
    Text(
        text = "Hello $name welcome compose world!",
    )
}

// 方式一:创建一个新的、专门用于预览的无参函数
// 方式二:创建有参的 带有默认值的 预览函数
@Preview(showBackground = true) // 推荐加上 showBackground = true,方便查看
@Composable
fun MessageCardPreview(name: String = "compose") {
    Text(
        text = "Hello $name welcome compose world!",
    )
}

带有参数的 Composable 函数无法直接生成预览。

预览功能需要一个无参的 Composable 函数来调用,因为它不知道该为 name: String 这个参数传入什么值。

Compose方法中入参一定要带 Modifier吗?

复制代码
@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name welcome compose world!",
        maxLines = 1,
        fontSize = 20.sp,
        color = Color.Blue,
    )
}

将 Modifier 作为参数,它允许调用者(使用你组件的地方)从外部自定义该组件的外观、布局和行为,而无需修改组件的内部代码。

  • 有 Modifier:你的 Greeting 组件可以在任何地方使用。调用者可以决定它的大小、位置、背景、边距、点击事件等。
  • 组件的内部逻辑(显示什么,比如 "Hello $name!")与其外部样式(它在布局中的位置、大小、装饰)是分开的。
  • 几乎所有的官方 Material Design 组件(Text, Button, Column, Scaffold 等)都接受 Modifier 参数,保持一致性会让你的代码库更易于理解和维护。

Text怎么设置宽高?和view不太一样

复制代码
@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name welcome compose world!",
        modifier = modifier.width(200.dp).height(30.dp)
            .clickable(onClick = {
                Log.d("MainActivity", "MessageCard: $name")
            }),

        maxLines = 1,
        fontSize = 20.sp,
        color = Color.Blue,
    )
}

理解使用 Modifier

Modifier 是 Jetpack Compose 中用于装饰和改变可组合项的构建块,它是一个不可变的有序元素集合,允许你控制组件的布局属性、行为和外观。

主要用途

  1. 布局控制
    1. 设置尺寸:fillMaxSize()、fillMaxWidth()、width()、height()
    2. 设置间距:padding()
    3. 设置权重:weight()
  2. 视觉效果
    1. 背景颜色:background(Color.Red)
    2. 边框:border()
    3. 圆角:clip(RoundedCornerShape(8.dp))
  3. 交互处理
    1. 点击事件:clickable {}
    2. 触摸反馈:combinedClickable()

Compose 中的三个基本标准布局元素是 ColumnRowBox 可组合项。

ColumnRowBox 类似于传统 Android 开发中的 垂直LinearLayout 、水平LinearLayout 和 FrameLayout

为什么Compose 中的状态保存一定要用 remember?

以官方Demo 为例:

1、直接使用 var expanded = false 变量设置不同的值不会使 Compose 将其检测为状态更改,

因此不会产生任何效果

原因:

更改此变量不会触发重组的原因是Compose 并未跟踪此更改。此外,每次调用 Greeting 时,都会将该变量重置为 false。

复制代码
Compose 应用通过调用可组合函数将数据转换为界面。如果您的数据发生变化,Compose 会使用新数据重新执行这些函数,
从而创建更新后的界面,此过程称为重组。Compose 还会查看各个可组合项需要哪些数据,
以便只需重组数据发生了变化的组件,而避免重组未受影响的组件。

2、如需向可组合项添加内部状态,可以使用 mutableStateOf 函数,该函数可让 Compose 重组读取该 State 的函数。

不能只是将 mutableStateOf 分配给可组合项中的某个变量。

val expanded = mutableStateOf(false) ,同样 不会产生任何效果

3、重组后保留状态,请使用 remember 记住可变状态。

remember 可以起到保护作用,防止状态在重组时被重置。

如果从屏幕的不同部分调用同一可组合项,则会创建不同的界面元素,且每个元素都会拥有自己的状态版本。

可以将内部状态视为类中的私有变量

复制代码
 @Composable
    fun Greeting() {
        // 1、
        // var expanded = false
  
        // 2、
 
        //  val expanded = mutableStateOf(false)

        // 3、
        val expanded = remember { mutableStateOf(false) }

        val extraPadding = if (expanded.value) 48.dp else 0.dp
        Surface(color = Color.Yellow, modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
            Row(modifier = Modifier.padding(24.dp)) {
                Column(modifier = Modifier
                    .weight(1f)
                    .padding(bottom = extraPadding)) {
                    Text(text = "Hello")
                    Text(text = "World")
                }
                ElevatedButton(
                    onClick = { expanded.value = !expanded.value }
                ) {
                    Text(if (expanded.value) "Show less" else "Show more")
                }
            }
        }
    }
相关推荐
普通网友1 小时前
Android Jetpack从入门到精通,干货满满
android·android jetpack
子云心1 小时前
Android Jetpack 系列(七)App Startup 启动优化
android·android jetpack·jetpack·initializer·startup·appstartup
嫩嫩的猿1 小时前
android jetpack compose Model对象更新变量 UI不更新、不刷新问题
android·ui·android jetpack
普通网友1 小时前
Android Jetpack 之 LifeCycle 组件_android 自定义view lifecycle
android·gitee·android jetpack
_codemonster2 小时前
数据库字符集编码问题
android·数据库·oracle
Pika8 小时前
深入浅出Compose HitTest 机制
android·android jetpack
资深web全栈开发8 小时前
CoI - 组合优于继承:解耦的艺术
android·java·开发语言
冬奇Lab9 小时前
WMS进阶:多窗口模式与显示管理深度解析
android·源码阅读