Jetpack Compose 中的基础知识

1,151 阅读6分钟

本文是根据官方文档学习 作笔记记录

目录

  • Composable functions (可组合函数)
  • Layouts(文本,cloumn,图片,布置布局)
  • Material Design (Color(颜色),Typography(排版),Shape(形状),启用深色主题)
  • Lists and animations(列表和动画)
  • 领会的一些使用技巧

Jetpack Compose 是 Google 推出的 用来构建 Android 用户界面的工具包,采用的是声明式的方法。(如果你接触过 Flutter ,会有似曾相识的感觉。)对于本文的学习,如果你有 Kotlin 的开发基础, 一点Android 应用开发的知识,会轻松一点。

Composable functions

Compose 可组合函数是 Jetpack Compose 的基本构建块,用于定义UI 组件。

下面是一个简单示例 Greeting widget ,接收 String 参数 并通过 Text widget 显示出来。

// ...
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) 
        setContent {
            Greeting("AndroidDeveloper")
        }
    }
}

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

关于此函数,有几点需要注意。

  • @Composable 注解。这个注解告诉编译器,这个函数的目的是将数据转换为用户界面。
  • 带有此注解的函数也被称为可组合函数,简称可组合函数。这些函数是 Compose 中用户界面的构件。添加此注解非常简单快捷,可帮助您将用户界面组织成一个可重复使用的元素库
  • 此函数接收数据, 可组合函数可接收参数,用于界面描述。
  • 此函数不返回任何内容 (但它会返回 Unit ),声明式命名组件,用于描述屏幕状态而非构建界面widget。

在 Android Studio 预览你的函数

@Preview 注解 可帮助你实时预览可组合函数,无需构建应用程序并安装在Android 设备或者浏览器。 该注释必须用在不接受参数的可组合函数,因此无法直接预览 Greeting 的功能,需要创建一个 DefaultPreview ,传入参数去调用 Greeting

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

@Preview(showBackground = true,widthDp = 360, heightDp = 360)
@Composable
fun DefaultPreview() {
    Greeting(" Developer!")
}

Untitled.png

  • @Preview 注解中各项配置可设置,供你预览效果。

Untitled 1.png

Layouts

在绘制页面时,UI元素是分层的, 元素包裹在其他元素中,(各种Layout,View 的嵌套),而在Compose 中,我们可以通过其他组合函数调用可组合函数来构建UI层次结构。

到这里,我们已经知道 Compose 可组合函数 和 预览的用法了,接下来 是学会如何使用布局去完善效果。

这里我和官方文档一样 ,使用消息列表 这个样例来学习。

添加多个文本

class SecondActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MessageCard(Message("Android", "Jetpack Compose"))
        }
    }
}

data class Message(val author: String, val body: String)

@Composable
fun MessageCard(msg: Message) {
    Text(text = msg.author)
    Text(text = msg.body)
}

Untitled 2.png 从上图效果,我们创建的两个 Text 元素,是会重叠在一起,原因是我们未提供有关如何排列文本元素的信息。

使用 Column

Column 函数 可以垂直排列 其布局内元素。 你也可以使用 RowBox 去实现想要的效果。

 @Composable
fun MessageCard(msg: Message) {
    Column() {
        Text(text = msg.author)
        Text(text = msg.body)
    }
}

Untitled 3.png

Untitled 4.png

添加Image

@Composable
fun MessageCard(msg: Message) {
    Row() {
        Image(painter = painterResource(id = R.drawable.cat), contentDescription = "Contact profile picture")
        Column() {
            Text(text = msg.author)
            Text(text = msg.body)

        }
    }
}

预览效果

Untitled 5.png 现在看到的效果, 图片太大了, 间距也没有。这时候就需要就需要 Modifier ,点进Row 的源码可以看到默认参数是有 Modifier ,它的用途类似 将Android Layout 中的配置属性,做了一个集合,详细的各位可以看源码 或者文档, 我这里就不细说了。

Untitled 6.png

@Composable
fun MessageCard(msg: Message) {
    Row() {
        Image(
            painter = painterResource(id = R.drawable.cat),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column() {
            Text(text = msg.author)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = msg.body)
        }

    }
}

Untitled 7.png 这个效果比之前的好点了, Modifier 可玩性很高,更多功能各位可以自己去发掘。

Material Design

Compose 当然也是支持 Material Design 原则的, 它许多页面元素都原生支持 Material Design ,这里我使用了 项目中创建的Material 主题 和 Surface 来修改了 MessageCard 的外观 。

Material Design 是围绕 ColorTypographyShape 这三大要素构建的。下面的代码将围绕这三大元素修改。

  • 预览效果 & 主题文件位置

Untitled 8.png

Untitled 9.png

Untitled 10.png

Color

使用 MaterialTheme.colors ,可用封装在主题的颜色来设置效果。

@Composable
fun MessageCard(msg: Message) {
    Row() {
        Image(
            painter = painterResource(id = R.drawable.cat),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                **.border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)**
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column() {
            Text(text = msg.author, **color = MaterialTheme.colors.secondaryVariant**)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = msg.body)
        }

    }
}
  • 预览效果

Untitled 11.png

Typography

MaterialTheme.typography.* 这里用在 text的 style 中

Untitled 12.png

Shape

这块是我作为UI仔最喜欢的地方了,(整点花里胡哨的) 兄弟们,你看看这效果,就一句代码 !!就一句!! 不用写shape.xml , 不用盲猜了, 还能预览 !! 赶紧上号更新呀!

这里设置了shape 的大小(有三种尺寸选择,我们大部分的需求都能满足), 阴影加了1dp ,对了 ,各位发现没 ,直接是1.dp


@Composable
fun MessageCard(msg: Message) {
    Row() {
        Image(
            painter = painterResource(id = R.drawable.cat),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column() {
            Text(
                text = msg.author,
                color = MaterialTheme.colors.secondary,
                style = MaterialTheme.typography.subtitle2
            )
            Spacer(modifier = Modifier.height(4.dp))
            **Surface(shape = MaterialTheme.shapes.medium, elevation = 1.dp)** {
                Text(text = msg.body,modifier = Modifier.padding(all = 4.dp) , style = MaterialTheme.typography.body2)
            }
        }

    }

Untitled 13.png

深色主题

因为Jetpack Compose 默认支持 Material Design ,所以系统会自动适应深色背景

添加新的预览注解并启用夜间模式。

@Preview(name = "Light Mode")
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    showBackground = true,
    name = "Dark Mode"
)
@Preview(showBackground = true)
@Composable
fun PreviewMessageCard() {
    MySootheTheme {
        Surface {
            MessageCard(
                msg = Message("Lexi", "Hey, Look at this cute cat, it's so so lovely!")
            )
        }
    }
}

Untitled 14.png

这里浅色和深色主题的颜色选项是在 IDE 生成 的 Theme.kt 中定义的。

Lists and animations

来到我们常用的列表和动画了, 跟着官方文档学完后,我觉得这代码写起来真的变轻松了🥺 。

创建消息列表

这里的子项items 接收 传递 过来的参数list,lambda 省略了 遍历list这块代码 ,这里的message 是自定义命名的,LazyColumn 顾名思义只加载屏幕上显示的内容,对于长列表布局会更高效。

Untitled 15.png

保存状态 & 添加动画

到这里,消息列表的雏形就有了 ,在这块内容要做的是把一些过长的内容收起来,加上动画效果,把跟踪消息是否展开储存为本地界面状态。 需要用到 **remember**mutableStateOf 函数 。

**remember** 可将本地状态存储在内存中,并把跟踪的值的变化传递给 mutableStateOf。这个值的更新,系统会自动重新绘制使用此状态的可组合项(及其子项),这叫重组,可以看下链接的官方解释。

通过使用 Compose 的状态 API(如 remember 和 mutableStateOf),系统会在状态发生任何变化时自动更新界面。

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(id = R.drawable.cat),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        **var isExtended by remember { mutableStateOf(false) }**
        Column(**Modifier.clickable { isExtended = !isExtended }**) {
            Text(
                text = msg.author,
                color = MaterialTheme.colors.secondary,
                style = MaterialTheme.typography.subtitle2
            )
            Spacer(modifier = Modifier.height(4.dp))
            Surface(shape = MaterialTheme.shapes.medium, elevation = 1.dp) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    style = MaterialTheme.typography.body2,
                    **maxLines = if (isExtended) Int.MAX_VALUE else 1**
                )
            }
        }

    }
}

  • 预览效果
untitled1.gif

根据 isExpanded 跟踪的状态,修改展开和 收起来消息的背景颜色 (使用 MaterialTheme.colors 的两种颜色)。 animateContentSize 消息容器的动画效果。

@Composable
fun MessageCard(msg: Message) {
    Row(modifier = Modifier.padding(all = 8.dp)) {
        Image(
            painter = painterResource(id = R.drawable.cat),
            contentDescription = null,
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .border(1.5.dp, MaterialTheme.colors.secondary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        var isExpanded by remember { mutableStateOf(false) }
        v**al surfaceColor by animateColorAsState(
            if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface,
        )**
        Column(Modifier.clickable { isExpanded = !isExpanded }) {
            Text(
                text = msg.author,
                color = MaterialTheme.colors.secondary,
                style = MaterialTheme.typography.subtitle2
            )
            Spacer(modifier = Modifier.height(4.dp))
            Surface(shape = MaterialTheme.shapes.medium, **color = surfaceColor**, elevation = 1.dp) {
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp),
                    style = MaterialTheme.typography.body2,
                    maxLines = if (isExpanded) Int.MAX_VALUE else 1
                )
            }
        }

    }
}
  • 最终加上 背景色的动态效果

untitled.gif

以下是您目前为止所学的内容,所有内容只需不到 100 行代码!:

  • 定义可组合函数
  • 在可组合项中添加不同的元素
  • 使用布局可组合项构建界面组件
  • 使用修饰符扩展可组合项 Modifiers
  • 创建高效列表
  • 跟踪状态以及修改状态
  • 在可组合项上添加用户互动
  • 在展开消息时显示动画效果

改进的技巧,发现了比我讲得好的博主,我这里就不写了,分享给大家。

改进Jetpack Compose 的 12 个使用技巧