Compose你入门吧

27 阅读2分钟

前传

这就是一最最最入门级文章(无图片),开发环境需提前部署,还得先学Android基础知识,如果想学习的深还得专门看专门的教程,自己玩玩就算了

这篇文章学完后基本上你能掌握Compose的%65,真的只是写着好玩

Compose是什么?

Compose是Android官方新推出的一篇ui工具包,旨在提升ui编写速度,并使用声明式编程范式

Compose 一部曲 可组合项

Compose使用一个带Composable注解的大写首字母函数名作为基本ui单元,名叫 可组合项

@Composable
fun Home() { /* Compose UI */ } 

放点ui?

setContent {
    Home() // 显示Home可组合项
}
@Composable
fun Home() {
    Text(text = "Hi Compose")
}

Compose 第二部曲 状态

状态总得来说应用于事件更新

比如这里有一个按钮,点击一下就将i加1

@Composable
fun Home() {
    var i = 0
    Button(onClick = { i++ }) {
       Text(text = "Click Me Add 1, Count : $i")
    }
}

可是你只要运行点击一下,就会发现并不会加1,为什么? 界面毕竟是死的,i加是加了可Text没反应过来,这时我们在i这里套一个mutableStateOf函数,mutableStateOf就会帮我们重新运行这个可组合项 但是运行到第一行,i就已经初始化为0了,那么这时我们再套一个remember让它帮我们恢复就完美解决了

var i by remember { mutableStateOf(0) }

这便是最Compose最6b的状态,反过头看View的findViewById和setText,是不是好用多了? 所以说万物更新皆为状态,但是呢,状态也不能放进内部可组合项

@Composable
fun MyScreen() {
    Button(onClick = {
        var count by remember { mutableStateOf(0) }  // 每次重组重新创建
        count++  // 不会保存
    }) {
        Text("点击")
    }
}

Compose第三部曲 Material Design & 布局

Material Design也是Compose的灵魂所在,接下来就让我们看看吧

布局

布局分为 Column Row Box 也就是 垂直布局 水平布局 重叠布局 默认是Box重叠布局,比如我这里有个图片,我们将它裁剪成了圆(抱歉没有图片)

Image(
    painter = painterResource(id = R.drawable.avatar), //图片资源
    contentDescription = "头像", // 无障碍模式介绍
    modifier = Modifier
        .size(50.dp)
        .clip(CircleShape),  // 裁剪为圆形
    contentScale = ContentScale.Crop  // 裁剪填充
)

接下来,我们利用布局来做一个联系人列表

Row { //水平布局
    Image(
        painter = painterResource(id = R.drawable.avatar), //图片资源
        contentDescription = "头像",
        modifier = Modifier
            .size(50.dp)
            .clip(CircleShape),  // 裁剪为圆形
        contentScale = ContentScale.Crop  // 裁剪填充
    )        
    Column( // 垂直布局
        modifier = Modifier
            .padding(start = 8.dp) // 开头填充8dp
            .align(Alignment.CenterVertically) // 垂直居中对齐
    ) {
        Text(text = "Erqi")
        Text(text = "Pupils write Android", style = MaterialTheme.typography.bodyMedium) // 中等大标题
    }
}

Material Design单向数据流组件

单向数据流是什么?

Compose比较推崇使用单向数据流,从具体操作来看叫状态提升,就是将数据和事件交给外面的父容器管,这里我们不用容器保存,直接存储

@Composable
fun MyCounter(
    count: Int,  // 状态接收
    onIncrement: () -> Unit  // 事件回调
) {
    Button(onClick = onIncrement) {
        Text("Click Me Add1, Count : $coubt")
    }
}

最后然后在setContent里传递参数,不用多说

单向数据流组件

单向数据流组件一个典型的例子就是输入框了,运行后可以自己康康

var email by remember { mutableStateOf("") }
OutlinedTextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("email") },
    leadingIcon = { Icon(Icons.Default.Email, null) },  // 前导图标
    modifier = Modifier.fillMaxWidth()
)

Modifier修饰符

modifier用来修饰组件,属于Compose灵魂其二,几乎适用于所有组件,以下是几个小例子

  • fillMaxWidth 将组件的宽度设置成最大
  • fillMaxHeight 将组件的高度设置成最大
  • fillMaxSize 将组件的大小设置成最大
  • padding 设置内间距
  • clickable 设置点击事件
  • horizontalScroll(rememberScrollState()) 水平滚动
  • verticalScroll(rememberScrollState()) 垂直滚动

当然,这里有个坑,Modifier是链式调用,不同的顺序会有不同的效果

Modifier // 背景在内层
    .padding(16.dp)
    .background(Color.Red) 
Modifier // 背景在外层
    .background(Color.Red)
    .padding(16.dp) 

更多请见:developer.android.com/jetpack/com…

Compose五部曲 ViewModel容器集成

ViewModelJetpack库生命周期较长的容器,要使用这个容器需提前导入依赖 我们先写一个继承ViewModelMyViewModel

import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
    var count = 0 // 这是我们的状态
        private set  // 外部不能直接修改
    
    fun increment() { // 这是事件
        count++
    }
}

然后在Compose里使用

setContent {
    val myViewModel : MyViewModel = viewModel()
    MyScreen(myViewModel)
}

@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
    Column {
        Text("计数:${viewModel.count}") // 拎出值
        Button(onClick = { viewModel.increment() }) {
            Text("点击")
        }
    }
}

那么这就是ViewModel最基本的用法了,不过,我们还有StateFlow容器,配合ViewModel使用

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class MyViewModel : ViewModel() {
    // 1. 创建可变StateFlow(下划线开头是惯例)
    private val _count = MutableStateFlow(0)
    
    // 2. 对外暴露不可变的StateFlow
    val count: StateFlow<Int> = _count
    
    // 3. 修改数据
    fun increment() {
        _count.value++  // 直接改value
    }
}

Compose用法

@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
    // 4. collectAsState() 把StateFlow转成Compose状态
    val count by viewModel.count.collectAsState()
    
    Text("数值:$count")
    Button(onClick = { viewModel.increment() }) {
        Text("增加")
    }
}

总结:MutableStateFlow 存数据,StateFlow 读数据,.collectAsState() 用数据

后传

后什么转,自己再去学这些,你就相当于完全掌握了

  • 动画API
  • 页面导航
  • 副作用
  • Material Design的主题