一, 概念
由于 Compose 是声明式工具集,因此更新它的唯一方法是通过新参数调用同一可组合项。
这些参数是界面状态的表现形式。每当状态更新时,都会发生重组。
这是官方的解释,那么要刷新ui就只能通过管理state的形式,通过状态变更重组组件,其实就是响应式编程
响应式编程就是数据发生了变化,对应的界面就重新绘制,类似react,vue和flutter
传统的界面写法是命令式编程,都是主动告诉界面view变更
二, 示例
这里是官方示例,需要在Composable中可组合项中的状态remember和mutableStateOf
mutableStateOf会创建可观察的 MutableState<T>,value如有任何更改,系统会安排重组读取value的所有可组合函数
remember将值存储起来,当界面发生了重新绘制,就会读之前存储的值
当onValueChange变化时,Text数据就会更新
@Composable
fun HelloContent() {
Column(modifier = Modifier.padding(16.dp)) {
var name by remember { mutableStateOf("") }
if (name.isNotEmpty()) {
Text(
text = "Hello, $name!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.h5
)
}
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") }
)
}
}
三, 实践
实际项目中使用的时候,数据变更往往是由网络或者其他模块事件下驱动,所以我们创建Store管理这些状态, 通过对数据的更新,来变更ui
object Store {
var logs = mutableStateOf(emptyList<Message>()) //必须采用emptyList.否则数据可能不更新
val visible = mutableStateOf(true)
val device = Device()
private val bufferedImage: BufferedImage? = null
val camera1bufferedImage = mutableStateOf(bufferedImage)
val camera2bufferedImage = mutableStateOf(bufferedImage)
val camera3bufferedImage = mutableStateOf(bufferedImage)
init {
for (i in 0..99999) {
pushLogToUI { Message(Math.random().toString()) }
}
}
}
画边框和虚线
采用box,设置边框
Column(Modifier.padding(top = 5.dp).wrapContentWidth()) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(top = 5.dp)
.size(Dp(mvPreviewWidth.toFloat()), Dp(mvPreviewHeight.toFloat()))
.border(1.dp, color = Color.Gray),
) {
drawline(true)
}
}
采用Canvas基于坐标画虚线
@Composable
fun drawline(offsetStart: (Size) -> Offset, offsetEnd: (Size) -> Offset) {
Canvas(modifier = Modifier.fillMaxSize()) {
//绘制直线
drawLine(
start = offsetStart.invoke(size),
end = offsetEnd.invoke(size),
color = Color.Black,
strokeWidth = 1F, //设置直线宽度
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) //设置为虚线
)
}
}
效果如下所示:
显示图片
这里是获取摄像头的每一帧BufferedImage,并将BufferedImage转换为Image支持的Bitmap,bufferedImage.toComposeImageBitmap()
bufferedImage.value?.let {
Image(
contentDescription = "",
contentScale = ContentScale.Crop,
bitmap = it.toComposeImageBitmap(),
modifier = Modifier.fillMaxSize()
)
}
效果如下:
列表展示
起初对state理解不够深刻,@Composable里面使用mutableStateOf如何去驱动数据,数据怎么传进来,如下:
@Composable
fun messageList(more: Boolean, onClick: () -> Unit) {
val messages = remember { mutableStateOf(emptyList<LogItem>()) }
}
其实remember必须在@Composable下使用,而mutableStateOf可以抽取到如Store,viewModel中,直接对mutableStateOf数据做修改,数据就可以变更了
列表中展示数据,对网络请求的数据刷新也是如此
@Composable
fun messageList(more: Boolean, onClick: () -> Unit) {
val messages = remember { Store.logs }
val state = rememberLazyListState()
Box(Modifier.fillMaxSize()) {
LazyColumn(Modifier.border(1.dp, color = Color.Gray).fillMaxSize().padding(10.dp), state) {
items(messages.value.size) { x ->
Text(messages.value[x].getMessage(), fontSize = 12.sp, color = messages.value[x].getColor())
Spacer(modifier = Modifier.height(5.dp))
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
adapter = rememberScrollbarAdapter(scrollState = state)
)
}
}
通过对Store.logs添加数据
inline fun pushLogToUI(noinline log: () -> LogItem) {
val logItem = log.invoke()
Store.logs.value += listOf(logItem)
}
仓库地址: github.com/luxiao0314/…