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 = it
                content.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 = null
                    value.value = TextFieldValue("")
                }
            })

        drops.forEach { option ->
            Row(
                Modifier.width(95.dp),
                verticalAlignment = Alignment.CenterVertically
            ) {
                RadioButton(
                    //	通过观察对象和index相同判断是否被选中
                    selected = (isdtc.value == option.second),
                    //	点击事件
                    onClick = {
                        isdtc.value = option.second
                        item.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 = item
                viewModel.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.toMutableStateList

class 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\"}"
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值