Kotlin Compose 终结toDo项目 点击可以编辑修改todo
Posted 安果移不动
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin Compose 终结toDo项目 点击可以编辑修改todo相关的知识,希望对你有一定的参考价值。
接
Kotlin Compose 完善toDo项目 Surface 渲染背景 与阴影_安果移不动的博客-CSDN博客
TodoComponents.kt
package com.anguomob.jecpack.activity.compose.todo.one
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.*
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anguomob.jecpack.activity.compose.todo.bean.ToDoIcon
import com.anguomob.jecpack.activity.compose.todo.bean.TodoItem
//当item选中的时候 会弹出一个输入框 用于编辑选中ToDoItem的信息
@Composable
fun TodoItemInLineEditor(
item: TodoItem,
onEditItemChange: (TodoItem) -> Unit,
onEditDone: () -> Unit,
onRemoveItem: () -> Unit,
)
TodoItemInput(
text = item.task,
onTextChange = onEditItemChange(item.copy(task = it)) ,
icon = item.icon,
onIconChange = onEditItemChange(item.copy(icon = it)) ,
iconsVisible = true,
submit = onEditDone,
buttonSlot =
//保存和删除两个图标
Row()
val shrinkButtons = Modifier.widthIn(20.dp)
TextButton(onClick = onEditDone, modifier = shrinkButtons)
//文字的emoji表情
Text(
text = "\\uD83D\\uDCBE",
textAlign = TextAlign.End,
)
TextButton(onClick = onRemoveItem, modifier = shrinkButtons)
//文字的emoji表情
Text(
text = "删除",
textAlign = TextAlign.End,
)
)
@Composable
fun ToDoItemInputBackground(
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit,
elevatioon: Boolean
)
//帧动画形式展示Surface底部的阴影
val animatedElevation by animateDpAsState(
targetValue = if (elevatioon) 1.dp else 0.dp,
TweenSpec(300)
)
Surface(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.05f),
shape = RectangleShape,
elevation = animatedElevation
)
Row(modifier = modifier.animateContentSize(animationSpec = TweenSpec(300)))
content()
//输入框
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun TodoInputText(
text: String,
onTextChanged: (String) -> Unit,
onImeAction: () -> Unit,
modifier: Modifier = Modifier,
)
val keyboardController = LocalSoftwareKeyboardController.current
TextField(
value = text,
onValueChange = onTextChanged,
modifier = modifier,
colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Transparent),
maxLines = 1,
//配置软键盘
keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone =
onImeAction()
//点击完成之后 隐藏键盘
keyboardController?.hide()
)
)
@Composable()
fun TodoEditButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
enable: Boolean = true
)
TextButton(
onClick = onClick,
shape = CircleShape,
colors = ButtonDefaults.buttonColors(),
modifier = modifier,
enabled = enable
)
Text(text)
//顶部布局
@Composable
fun TodoItemEntryInput(onItemComplete: (TodoItem) -> Unit)
val (text, setText) = remember
mutableStateOf("")
val (icon, setIcon) = remember
mutableStateOf(ToDoIcon.Default)
//icon是否可一件取决于文本是否有内容
val iconsVisible = text.isNotBlank()
//点击add
val submit =
onItemComplete(TodoItem(text, icon))
setText("")
setIcon(ToDoIcon.Default)
TodoItemInput(
text = text,
onTextChange = setText,
icon = icon,
onIconChange = setIcon,
iconsVisible = iconsVisible,
submit = submit,
buttonSlot =
TodoEditButton(
onClick = submit,
text = "添加",
enable = text.isNotBlank()
)
)
//输入框 加其他
@Composable
fun TodoItemInput(
text: String,
onTextChange: (String) -> Unit,
icon: ToDoIcon,
onIconChange: (ToDoIcon) -> Unit,
iconsVisible: Boolean,
submit: () -> Unit,
buttonSlot: @Composable () -> Unit
)
Column
Row(
Modifier
.padding(horizontal = 16.dp)
.padding(top = 16.dp)
)
TodoInputText(
text = text,
modifier = Modifier
.weight(1f)
.padding(end = 8.dp),
onTextChanged = onTextChange,
onImeAction = submit
)
Spacer(Modifier.width(8.dp))
Box(modifier = Modifier.align(Alignment.CenterVertically))
buttonSlot()
AnimatedIconRow(
visible = iconsVisible,
icon = icon,
onIconChange = onIconChange,
modifier = Modifier.padding(8.dp)
)
//一排图标 根据文本框是否有内容 自动弹起收缩
@Composable
fun AnimatedIconRow(
modifier: Modifier = Modifier,
visible: Boolean,
icon: ToDoIcon,
onIconChange: (ToDoIcon) -> Unit
)
val enter = remember
fadeIn(animationSpec = TweenSpec(300, easing = FastOutLinearInEasing))
val exit = remember
fadeOut(animationSpec = TweenSpec(100, easing = FastOutSlowInEasing))
//最小高度16dp
AnimatedVisibility(visible = visible, enter = enter, exit = exit, modifier = modifier)
IconRow(icon = icon, onIconChange = onIconChange)
@Composable
fun IconRow(icon: ToDoIcon, onIconChange: (ToDoIcon) -> Unit, modifier: Modifier = Modifier)
Row(modifier = modifier)
for (todoIcon in ToDoIcon.values())
SelectableIconButton(
icon = todoIcon.imageVector,
iconContentDescription = todoIcon.contentDescription,
onIconSelect = onIconChange(todoIcon) ,
isSelected = todoIcon == icon
)
@Composable
fun SelectableIconButton(
icon: ImageVector,
iconContentDescription: Int,
onIconSelect: () -> Unit,
isSelected: Boolean,
modifier: Modifier = Modifier
)
//图标选中和未选中 颜色不一样
val tint = if (isSelected)
MaterialTheme.colors.primary
else
MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
val enter = remember
fadeIn(animationSpec = TweenSpec(300, easing = FastOutLinearInEasing))
val exit = remember
fadeOut(animationSpec = TweenSpec(100, easing = FastOutSlowInEasing))
TextButton(onClick = onIconSelect, modifier = modifier)
Column
Icon(
imageVector = icon,
tint = tint,
contentDescription = stringResource(id = iconContentDescription)
)
//最小高度16dp
AnimatedVisibility(visible = isSelected, enter = enter, exit = exit)
Box(
Modifier
.padding(top = 3.dp)
.width(icon.defaultWidth)
.height(1.dp)
.background(tint)
)
TodoScreen.kt
package com.anguomob.jecpack.activity.compose.todo.one
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.anguomob.jecpack.activity.compose.todo.bean.TodoItem
import generateRandomTodoItem
import kotlin.random.Random
@Composable
fun TodoScreen(
items: List<TodoItem>,
currentlyEditing: TodoItem?,
onAddItem: (TodoItem) -> Unit,
onRemove: (TodoItem) -> Unit,
onStartEdit: (TodoItem) -> Unit,
onEditItemChange: (TodoItem) -> Unit,
onEditDone: () -> Unit,
)
Column()
val enableTopSection = currentlyEditing == null
//输入框 外加一个灰色背景
ToDoItemInputBackground(content =
if (enableTopSection)
TodoItemEntryInput(onItemComplete = onAddItem)
else
Text(
text = "编辑条目",
style = MaterialTheme.typography.h6,
textAlign = TextAlign.Center,
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(16.dp)
.fillMaxWidth()
)
, elevatioon = true)
LazyColumn(
modifier = Modifier
.weight(1f)
)
items(items) todo ->
if (currentlyEditing?.id == todo.id)
TodoItemInLineEditor(
item = currentlyEditing,
onEditItemChange = onEditItemChange,
onEditDone = onEditDone,
onRemoveItem = onRemove(todo)
)
TodoRow(todo, modifier = Modifier.fillParentMaxWidth(),onItemClick=onStartEdit)
Button(
onClick =
onAddItem(generateRandomTodoItem())
,
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
)
Text("新建TODO")
@Composable
fun TodoRow(todo: TodoItem, modifier: Modifier = Modifier, onItemClick: (TodoItem) -> Unit)
Row(
modifier = modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.clickable onItemClick(todo) ,
//子元素水平均匀分发
horizontalArrangement = Arrangement.SpaceBetween
)
Text(todo.task)
val iconAlpha = remember(todo.id)
randomTint()
Icon(
imageVector = todo.icon.imageVector,
tint = LocalContentColor.current.copy(alpha = iconAlpha),
contentDescription = stringResource(id = todo.icon.contentDescription)
)
private fun randomTint(): Float
val number = Random.nextFloat().coerceIn(0.3f, 0.9f)
return number
数据bean
TodoItem.kt
package com.anguomob.jecpack.activity.compose.todo.bean
import android.support.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.graphics.vector.ImageVector
import com.anguomob.jecpack.R
import java.util.*
data class TodoItem(
val task: String,
val icon: ToDoIcon = ToDoIcon.Default,
val id: UUID = UUID.randomUUID()
)
enum class ToDoIcon(val imageVector: ImageVector, @StringRes val contentDescription: Int)
Square(Icons.Default.CropSquare, R.string.expand),
Done(Icons.Default.Done, R.string.done),
Event(Icons.Default.Event, R.string.event),
PrivacyTip(Icons.Default.PrivacyTip, R.string.privacy),
Trash(Icons.Default.RestoreFromTrash, R.string.restore),
Default(Square.imageVector, Square.contentDescription)
viewMoel
package com.anguomob.jecpack.activity.compose.todo.viewmodel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.anguomob.jecpack.activity.compose.todo.bean.TodoItem
class TodoViewModel : ViewModel()
//TODO 集合 只读
var todoItems = mutableStateListOf<TodoItem>()
private set
//当前正在编辑的TodoItem的索引位置
private var currentEditPosition by mutableStateOf(-1)
val currentEditItem: TodoItem?
get() = todoItems.getOrNull(currentEditPosition)
fun addItem(item: TodoItem)
todoItems.add(item)
fun removeItem(item: TodoItem)
todoItems.remove(item)
onEditDone()
fun onEditDone()
currentEditPosition = -1
//当传入TodoItem列表中的条目被选中的时候,传入该对象,获取它在列表中的索引位置
fun onEditItemSelected(item: TodoItem)
currentEditPosition = todoItems.indexOf(item)
//todoItem编辑完成,重新给集合中的tood Item赋值 id属性不能修改,进行校验
fun onEditItemChange(item: TodoItem)
todoItems.getOrNull(currentEditPosition)?.let
todoItems[currentEditPosition] = item
点击条目变成上方出现一个条目可以对其进行更新 删除 保存
之前的新加逻辑依然存在
生成随机的测试数据
import com.anguomob.jecpack.activity.compose.todo.bean.ToDoIcon
import com.anguomob.jecpack.activity.compose.todo.bean.TodoItem
//
// Created by Administrator on 2022/6/20.
//
fun generateRandomTodoItem(): TodoItem
val message = listOf(
"Learn compose",
"Learn state",
"Build dynamic UIs",
"Learn Unidirectional Data Flow",
"Integrate LiveData",
"Integrate ViewModel",
"Remember to saveState!",
"Build stateless composables",
"Use state from stateless composables",
).random()
val icon = ToDoIcon.values().random()
return TodoItem(message, icon)
以上是关于Kotlin Compose 终结toDo项目 点击可以编辑修改todo的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin Compose Todo小项目 删除添加项目 ,认识状态提升
Kotlin Jetpack Compose remember 给Icon添加透明度
Kotlin jetpack compose Tab的渲染 AnimatedVisibility的使用
Kotlin jetpack compose 文本输入框ExitText/TextField remember 居然可以传两个参数