Compose Multiplatform结合MVI模式--初步尝试
写了个简单的界面,包含功能:
1,列表中动态增加行和删除行
2,根据列表中的数据生成 json。
包含的文件或类:
1,Main.kt, 程序入口,使用 Window 即是 desktop 端。
2,View.kt, 画界面,只展示数据,没有业务逻辑。
3,ViewModel.kt,与View“绑定”,给View提供数据,及响应View接收到的用户操作。
4,Repository.kt,模拟读取数据库。
5,ViewStatus.kt,可以理解为元数据,状态。
效果:
程序很简单,不多说,直接贴代码
1,Main.kt
/*** 入口*/
fun main() = application {Window(state = WindowState(), onCloseRequest = ::exitApplication, title = "记录转 JSON") {App()}
}
2,View.kt
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.VerticalScrollbar
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp/*** 画界面*/private val viewModel: ViewModel = ViewModel()@Composable
@Preview
fun App() {val dataList: MutableList<Param> = remember { viewModel.viewState.paramList }val options: MutableList<Pair<String, Boolean>> = mutableListOf()options.add(Pair("是", true))options.add(Pair("否", false))//byLazyColumn(dataList, options)byScrollBar(dataList, options)
}/*** 使用 LazyColumn,不会显示滚动条*/
@Composable
fun byLazyColumn(list: MutableList<Param>, drops: MutableList<Pair<String, Boolean>>) {MaterialTheme {//dataList.add(Param("参数项描述", "参数值", 0))//dataList.add(Param("", "", 0))//val stateVertical = rememberScrollState(0)LazyColumn(verticalArrangement = Arrangement.spacedBy(5.dp) // 行间距) {item {renderHead()}itemsIndexed(list) { idx, item ->renderItem(item, list, idx, drops)}item {renderTail()}}}
}/*** 有滚动条*/
@Composable
fun byScrollBar(list: MutableList<Param>, drops: MutableList<Pair<String, Boolean>>) {Column {renderHead()Row {Box(//必须要包在 Box 里面,滚动条才会显示出来modifier = Modifier.height(400.dp).fillMaxWidth()//.background(color = Color(180, 180, 180)).padding(10.dp)) {val stateVertical = rememberScrollState(0)Column(modifier = Modifier.height(400.dp).verticalScroll(stateVertical).fillMaxWidth().padding(end = 12.dp, bottom = 12.dp)) {list.forEachIndexed { idx, item ->renderItem(item, list, idx, drops)}}VerticalScrollbar(modifier = Modifier.align(Alignment.CenterEnd),adapter = rememberScrollbarAdapter(stateVertical))}}renderTail()}
}/*** 画一行*/
@Composable
fun renderItem(item: Param, list: MutableList<Param>, index: Int, drops: MutableList<Pair<String, Boolean>>) {val content = remember { mutableStateOf(item.content) }val isdtc = remember { mutableStateOf(item.isdtc) }val value = remember { mutableStateOf(TextFieldValue(if (item.value == null) "" else item.value.toString())) }Row {OutlinedTextField(value = content.value,modifier = Modifier.padding(horizontal = 20.dp).width(150.dp),onValueChange = {item.content = itcontent.value = it})OutlinedTextField(value = value.value,modifier = Modifier.padding(horizontal = 20.dp).width(150.dp),onValueChange = {try {item.value = it.text.toInt()value.value = it} catch (e: Exception) {item.value = nullvalue.value = TextFieldValue("")}})drops.forEach { option ->Row(Modifier.width(95.dp),verticalAlignment = Alignment.CenterVertically) {RadioButton(// 通过观察对象和index相同判断是否被选中selected = (isdtc.value == option.second),// 点击事件onClick = {isdtc.value = option.seconditem.isdtc = option.second})Text(text = option.first)}}// DropdownMenu(expanded = true, modifier = Modifier.padding(horizontal = 20.dp).width(150.dp), onDismissRequest = {}) {
// drops.forEach {
// DropdownMenuItem(onClick = {
// item.value = it.second
// }) {
// Text(text = it.first)
// }
// }
// }if (index == list.size - 1) {Button(modifier = Modifier.padding(horizontal = 20.dp), onClick = {//list.removeAt(index)val removeItemAction = ViewAction.RemoveItemAction()removeItemAction.target = itemviewModel.dispatch(removeItemAction)}) {Text("删除")}}}
}/*** 表头*/
@Composable
fun renderHead() {Row(horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) {Text(text = "参数项描述",modifier = Modifier.padding(horizontal = 20.dp).width(150.dp),textAlign = TextAlign.Center)Text(text = "参数值",modifier = Modifier.padding(horizontal = 20.dp).width(150.dp),textAlign = TextAlign.Center)Text(text = "是否故障",modifier = Modifier.padding(horizontal = 20.dp).width(150.dp),textAlign = TextAlign.Center)Button(modifier = Modifier.padding(horizontal = 20.dp), onClick = {//list.add(Param("", null, false))viewModel.dispatch(ViewAction.AddItemAction)}) {Text("增加")}}
}/*** 底部的按钮“生成json”和大文本框*/
@Composable
fun renderTail() {Row(horizontalArrangement = Arrangement.Center) {val state = remember {viewModel.viewState.result}Button(modifier = Modifier.padding(horizontal = 20.dp), onClick = {viewModel.dispatch(ViewAction.GenerateJsonAction)}) {Text("生成 JSON")}OutlinedTextField(value = state.value,onValueChange = { text -> state.value = text },modifier = Modifier.width(500.dp))}
}
3,ViewModel.kt
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.toMutableStateListclass ViewModel {private val repository = Repository()val viewState: ViewState = ViewState(repository.query().toMutableStateList(), mutableStateOf(""))private fun generateJson() {val json = StringBuilder("[")viewState.paramList.forEach {if (it.content.isNotEmpty()) {json.append("$it,")}}if (json.endsWith(",")) {json.delete(json.length - 1, json.length)}json.append("]")viewState.result.value = json.toString()}private fun addItem() {viewState.paramList.add(Param("", null, false))}private fun removeItem(item: Param?) {if (item != null) {viewState.paramList.remove(item)}}fun dispatch(action: ViewAction) {when(action) {ViewAction.GenerateJsonAction -> generateJson()ViewAction.AddItemAction -> addItem()else ->if (action is ViewAction.RemoveItemAction) {removeItem(action.target)}}}
}
4,Repository.kt
/*** 管理数据,或者处理业务逻辑*/
class Repository {fun query(): MutableList<Param>{val list: MutableList<Param> = mutableListOf()list.add(Param("", null, false))return list}
}
5,ViewStatus.kt
import androidx.compose.runtime.MutableState/*** 界面上的数据:一个列表,及根据列表生成的 json*/
data class ViewState(val paramList: MutableList<Param>, var result: MutableState<String>)/*** 界面上的操作定义*/
sealed class ViewAction {/** 增加一行 */object AddItemAction: ViewAction()/** 生成 json */object GenerateJsonAction: ViewAction()/** 删除一行 */class RemoveItemAction: ViewAction() {var target: Param? = null}
}/*** 列表中显示的数据类型*/
data class Param(var content: String, var value: Int?, var isdtc: Boolean) {override fun toString(): String {return if (value != null) "{\"content\":\"$content\", \"value\":$value, \"isdtc\":\"$isdtc\"}"else "{\"content\":\"$content\", \"isdtc\":\"$isdtc\"}"}
}
Compose Multiplatform结合MVI模式--初步尝试相关推荐
- Compose也能跨平台?Compose Multiplatform是啥?KMM又是什么?
现在的跨平台框架真是跟打了鸡血似的,跟生产队的驴一样玩命的更新啊,一会儿功夫就遍地开花,开发者尤其是Androiders们还能学得过来吗? Compose Multiplatform Compose ...
- 10个问题带你看懂 Compose Multiplatform 1.0
近日 JetBrains 正式发布了 Compose Multiplatform 1.0 版,这标志其在生产环境中使用的时机已经成熟.相信有不少人对它还不太熟悉,本文通过下面 10 个热门问题带大家认 ...
- Compose Multiplatform 正式官宣,与 Flutter 必有一战?
作者 | fundroid 来源 | AndroidPub 7月底 Compose for Android 1.0 刚刚发布,紧接着 8月4日 JetBrains 就宣布了 Compose Multi ...
- Compose Multiplatform 正式官宣,与 Flutter 迟早必有一战?
7月底 Compose for Android 1.0 刚刚发布,紧接着 8月4日 JetBrains 就宣布了 Compose Multiplatform 的最新进展,目前已进入 alpha 阶段. ...
- 腾讯SkillNet|NLU任务全能网络,对Pathways架构的初步尝试
卷友们好,我是rumor. 21年10月的时候,谷歌大佬Jeff Dean提出了下一代AI架构的Pathways概念[1],旨在通过一个大模型完成各种不同的任务.对于较早关注AI领域的同行们来说,这其 ...
- Compose Multiplatform 实战:联机五子棋
1. 认识 Compose Multiplatform Jetpack Compose 作为 Android 端的新一代UI开发工具,得益于 Kotlin 优秀的语法特性,代码写起来十分简洁,广受开发 ...
- 又一款跨平台开发框架 :Compose Multiplatform 1.0 发布!
点击"开发者技术前线",选择"星标" 让一部分开发者看到未来 作者 | 罗燕珊 来自 infoQ JetBrains 工程师 Sebastian Aigne ...
- 使用Compose实现基于MVI架构、retrofit2、支持 glance 小部件的TODO应用
前言 现在声明式 UI 已逐渐成为主流,在客户端上,已有成熟的 Flutter 和 SwiftUi ,而原生安卓上的声明式 UI 却在去年年底才姗姗来迟. 虽然 compose 姗姗来迟,但是关于它的 ...
- 4.2 手机模拟操作初步尝试(获取登录页面的源代码)
本节先来初步尝试手机模拟操作,为之后爬取微信朋友圈的实战演练做铺垫. 4.2.1 用Android Studio 连接夜神模拟器 要操控手机App,得先连接到手机.前面安装的夜神模拟器就是用来模拟An ...
最新文章
- 常州大学阿里云大数据学院举行“创新思维”课程答辩
- Android 5.0 SEAndroid下如何获得对一个内核节点的访问权限
- tableau应用实战案例(五十)-销售业绩的tableau可视化案例
- BugKuCTF WEB 本地包含
- 命令行切换到conda环境_Anaconda命令行常用操作
- 事实表和维度表是怎么造数据_从电商数据指标到电商数据中台
- 关于没有commit的死锁问题
- 工作中JS语法的小整理以及vue小知识的收纳
- 《python自动化》学习笔记:正则表达式基础知识
- HyperLogLog浅析
- 小程序解析短视频接口API开发文档
- 500个运营工具大全,速度收藏!!!
- Symbian 项目
- 机器学习笔记之概率图模型(五)马尔可夫随机场的结构表示
- fw313r路由器上网方式服务器无响应,迅捷(FAST)路由器fw313r手机设置上网方法
- python神经网络图像分类,图像分类卷积神经网络
- 智能温室监测系统解决方案
- 安装完固态硬盘后计算机里没显示,如何解决安装固态硬盘后系统看不到的问题[详细介绍]...
- 理解OAuth 2.0
- C语言为什么不执行数组下标的有效性检查