夜雨聆风学习资料网

ARTICLE · 1062928

保姆级学习开发安卓手机软件实战(四)--完善Device设备类,启动开关及列表改卡片

保姆级学习开发安卓手机软件实战(四)--完善Device设备类,启动开关及列表改卡片
通信协议基本已出,我们就继续吧~~~

把占位的 List<String> 换成真正的 List<Device>,列表改成卡片 + 启用开关。

那首先先建立设备类吧。

一、建 data/model 包

展开到 app > kotlin+java > com > example > jstz1

右键 jstz1 → New → Package→输入data.model再回车(直接创建两级文件夹)

右键 model → New → Kotlin Class/File→ 输入 Device → 选 File → 回车

再右键 model → New → Kotlin Class/File → 输入 ProtocolCode → 选 File → 回车

这里是创建了两个文件,一个文件放设备模型,后一个是放协议码(也就是字节对应内容)

首先是Device 文件内容:

data class Device(    val devId: String,            // 设备码    val enabled: Boolean = true,  // 是否启用    val online: Boolean = false   // 连接状态)data class DeviceState(    val devId: String,             // 关联键    val process: Process,          // 当前流程)

其次是ProtocolCode 文件内容:

enum class Process(val code: Intval label: String) {    STOP(0"停止"),    DONE(10"完成"),    MANUAL(20"手动"),    UNKNOWN(-1"未知");    companion object {        /** 通信层用:字节码 → 枚举 */        funfromCode(code: Int): Process =            entries.firstOrNull { it.code == code } ?: UNKNOWN       /** 通信层用:中文label → 枚举 */       funfromLabel(label: String): Process =           entries.firstOrNull { it.label == label } ?: UNKNOWN           }}

这两个文件其实就是根据通信协议提取出来的,我只贴出了部分,以供后续流程,大家这里其实就是找到自己软件的数据对象。

例如,如果这个软件是登记人员,那这里数据对象就是人,有性别、年龄、住址等属性,那如果很多信息,需要分别使用,就可以像我一样,分成两个大类。然后第二个文件就是枚举,例如,性别有男女之分。

然后就回到之前昨天我们创建的DeviceListViewModel.kt,将之前占位的地方改成我们真实的设备类。

因为有两个类,这里改成两个了:

val devicesList<Device> = emptyList(),val devicesStatesMap<StringDeviceState> = emptyMap(),

下面加载的地方也要改:

 private funloadMockDevices() {        _uiState.value = _uiState.value.copy(            devices = listOf(                Device( devId="aaaa0001", enable = true,  onLine = true),                Device( devId="aaaa0002", enable = false,  onLine = true),                Device( devId="aaaa0003", enable = true,  onLine = false)            ),            devicesStates = mapOf(               "aaaa0001" to DeviceState(                   devId = "aaaa0001",                   process = Process.DONE               ),                "aaaa0002" to DeviceState(                    devId = "aaaa0002",                    process = Process.COLLECT_GAS                ),                "aaaa0003" to DeviceState(                    devId = "aaaa0003",                    process = Process.STOP                ),            )        )    }

二、Screen 改成卡片样式

先添加引用:

import com.example.jstz1.data.model.AlarmCodeimport com.example.jstz1.data.model.Deviceimport com.example.jstz1.data.model.DeviceStateimport com.example.jstz1.data.model.Processimport androidx.compose.material3.Cardimport androidx.compose.material3.CardDefaultsimport androidx.compose.material3.Switchimport androidx.compose.ui.draw.alphaimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.shape.CircleShapeimport com.example.jstz1.ui.theme.Errorimport com.example.jstz1.ui.theme.Successimport com.example.jstz1.ui.theme.Warning

修改数据在界面上的内容:

 state.devices.forEach { device ->                    DeviceCard(                        device = device,                        state = state.devicesStates[device.devId]                    )                    Spacer(modifier = Modifier.height(12.dp))                }

然后完善DeviceCard(里面有些属性如果报错,你们自己删掉就成)方法:

@Composableprivate funDeviceCard(    device: Device,    state: DeviceState?) {    Card(        modifier = Modifier            .fillMaxWidth()            .alpha(if (device.enable) 1f else 0.4f),     // 禁用整卡半透明        colors = CardDefaults.cardColors(            containerColor = MaterialTheme.colorScheme.surface        )    ) {        Row(            modifier = Modifier                .fillMaxWidth()                .padding(16.dp),            verticalAlignment = Alignment.CenterVertically        ) {            // ---- 左:状态圆点 ----            Box(                modifier = Modifier                    .size(10.dp)                    .background(                        color = if (device.onLine) Success                        else Warning,                        shape = CircleShape                    )            )            Spacer(modifier = Modifier.width(12.dp))            // ---- 中:编号 + 状态信息(占满中间剩余空间) ----            Column(modifier = Modifier.weight(1f)) {                Row(verticalAlignment = Alignment.CenterVertically) {                    Text(                        text = device.devId,                        style = MaterialTheme.typography.bodyLarge,                        color = MaterialTheme.colorScheme.onSurface                    )                }                Text(                    text = if (device.onLine) "在线" else "离线",                    style = MaterialTheme.typography.bodySmall,                    color = MaterialTheme.colorScheme.onSurfaceVariant                )                // state 不为空才显示真空度+流程(通信没连上时显示"未知")                state?.let {//**`.let { ... }`**:Kotlin 标准扩展函数                    Text(                        text = "%s".format(it.process.label),                        style = MaterialTheme.typography.bodySmall,                        color = MaterialTheme.colorScheme.onSurfaceVariant                    )                }            }            // ---- 右:启用开关 ----            Switch(                checked = device.enable,                onCheckedChange = { /* TODO: 后续实现 */ }            )        }    }}
这是现在运行的效果了:

然后我们把最后那个开关代码完善下:

因为要更新数据模型的值,但我们之前设置过,只能内部修改,所以需要在DeviceListViewModel.kt中添加修改启动状态的函数:

funtoggleEnabled(devId: String) {        _uiState.value = _uiState.value.copy(            devices = _uiState.value.devices.map { device ->                if (device.devId == devId) device.copy(enable = !device.enable)                else device            }        )    }

同时在界面上要用到vm来访问(回调):

再次运行:

点击第一个开关:

因为我这边设备比较多,所以打算改成卡片的,2*2的网格。

三、修改设备卡片容器为网格(每行两列)

导入:

import androidx.compose.foundation.lazy.grid.GridCellsimport androidx.compose.foundation.lazy.grid.LazyVerticalGridimport androidx.compose.foundation.lazy.grid.items

然后在设备显示位置修改如下:

if (state.devices.isEmpty()) {    Text(        text = "还没有设备",        style = MaterialTheme.typography.bodyMedium,        color = MaterialTheme.colorScheme.onSurfaceVariant    )else {    LazyVerticalGrid(        columns = GridCells.Fixed(2),        // ← 2 列        modifier = Modifier.weight(1f),      // ← 占满"标题下、按钮上"的全部剩余高度        verticalArrangement = Arrangement.spacedBy(12.dp),        horizontalArrangement = Arrangement.spacedBy(12.dp)    ) {        items(            items = state.devices,            key = { it.devId }               // ← 稳定 key,Compose 知道每张卡的"身份证"        ) { device ->            DeviceCard(                device = device,                state = state.devicesStates[device.devId],                onToggle = { vm.toggleEnabled(device.devId) }            )        }    }}

然后记得多加几台设备哦,就能看到效果:

鼠标可以上下滑动,查看下面的设备卡。

下节,把数据进行保存吧,本地存储,重启不丢。

相关学习资料