Android compose 可见性动画未执行问题修复

接着修改待办事项demo, 动画有问题, 导致初始不显示数据,其实数据库是有数据的。原代码如下:

Kotlin 复制代码
package com.example.testcompose1

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.testcompose1.data.TodoEntity
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Collections.rotate

@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun TodoListScreen(
    viewModel: TodoViewModel,  // 获取ViewModel实例。 在同一个activity作用域中是单例。
    settingsViewModel: SettingsViewModel,
    onNavigateToDetail: (Int) -> Unit = {}
) {
//    val screenWidth = LocalConfiguration.current.screenWidthDp.dp.value
    val configuration = LocalConfiguration.current
    val density = LocalDensity.current
    val screenWidthPx = with(density) { configuration.screenWidthDp.dp.toPx() }
    val offsetX = -(screenWidthPx * 3).toInt()  // 从屏幕左侧3倍宽度外滑入

    var showInfiniteList by remember { mutableStateOf(false) }
    if (showInfiniteList) {
        // 显示无限滚动列表,并提供一个返回按钮
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text("无限滚动列表") },
                    navigationIcon = {
                        IconButton(onClick = { showInfiniteList = false }) {
                            Icon(Icons.Default.ArrowBack, contentDescription = "返回")
                        }
                    }
                )
            }
        ) { innerPadding ->
            // 给 InfiniteListPage 添加内边距
            Box(modifier = Modifier.padding(innerPadding)) {
                InfiniteListPage()
            }
        }
    } else { // 显示原待办事项列表
        // 使用 remember 和 mutableStateOf 保存输入框的文本
        var text by remember { mutableStateOf("") }
        // 使用 mutableStateListOf 保存待办项列表
//    val todoItems = remember { mutableStateListOf<String>() }
        // 将 StateFlow 转换为 Compose 可观察的 State
//        val todoItems by viewModel.todoItems.collectAsState()

        val todos by viewModel.todos.collectAsState()

        // 获取协程作用域,用于延迟删除
        val scope = rememberCoroutineScope()
        // 管理每个项的可见性,初始为 true,新添加的项先设为 false,然后立即设为 true
        val itemVisibility = remember { mutableStateMapOf<Int, Boolean>() } // key改为用id

        // 同步 itemVisibility 与 todoItems:为新增项添加初始 false,并在下一帧设为 true
        LaunchedEffect(todos) {
            todos.forEach { todo ->
                if (!itemVisibility.containsKey(todo.id)) {
                    // 新项:初始不可见
                    itemVisibility[todo.id] = false
                    // 等待一帧,然后设为可见,触发进入动画
                    launch {
                        delay(50) // 短暂延迟,确保重组
                        itemVisibility[todo.id] = true
                    }
                }
            }
            // 清理已删除的项
//            itemVisibility.keys.retainAll(todoItems.toSet())
            val currentIds = todos.map { it.id }.toSet()
            itemVisibility.keys.retainAll(currentIds)
        }

        Column(modifier = Modifier.padding(16.dp)) {
            ThemeSwitch(settingsViewModel)  // 添加开关
            Spacer(modifier = Modifier.height(8.dp))
            // 文本输入框
            TextField(
                value = text,
                onValueChange = { text = it }, // 反向绑定,视图变化--> 数据变化
                label = { Text("输入待办事项") },
                colors = TextFieldDefaults.colors(
                    focusedContainerColor = MaterialTheme.colorScheme.surface, // 获得焦点时的背景色
                    unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant, // 失去焦点时,输入框背景色
                    focusedIndicatorColor = MaterialTheme.colorScheme.primary, // 输入框底部下划线的颜色。
                    unfocusedIndicatorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
                ),
                modifier = Modifier.fillMaxWidth()
            )
            // 添加按钮
            Button(
                onClick = {
                    viewModel.addTodo(text)
                    text = ""
                },
                shape = MaterialTheme.shapes.small,  // 使用主题形状
                colors = ButtonDefaults.buttonColors(
                    containerColor = MaterialTheme.colorScheme.primary, // 容器背景色,按钮底色
                    contentColor = MaterialTheme.colorScheme.onPrimary // 内容颜色,按钮上文字 / 图标的颜色
                ),
                modifier = Modifier.padding(top = 8.dp)
            ) {
                Text("添加")
            }

            // 显示待办列表
            Spacer(modifier = Modifier.height(16.dp))
            Text("待办列表", style = MaterialTheme.typography.titleMedium)
            LazyColumn {
                items(items = todos
                ,key = { it.id }) //  使用唯一 id 作为 key,确保动画正确识别
                { todo ->
                    val visible = itemVisibility[todo.id] ?: true
                    // 为每个项添加动画。 AnimatedVisibility没起作用
                    AnimatedVisibility(
                        visible = visible,
                        enter = fadeIn(animationSpec = tween(1500, easing = FastOutSlowInEasing)) +
                                slideInHorizontally(
                                    initialOffsetX = { -3000 },  // 固定大偏移量,从左侧 3000 像素外滑入
                                    animationSpec = tween(1500, easing = FastOutSlowInEasing)
                                ) +
                                scaleIn(
                                    initialScale = 0.1f,
                                    animationSpec = tween(1500, easing = FastOutSlowInEasing)
                                ),
                        exit = fadeOut(animationSpec = tween(500)) +
                                slideOutHorizontally(targetOffsetX = { 200 }
                                    , animationSpec = tween(500))
                    ) {
                        // SideEffect 是一个专门用于执行副作用的可组合函数。它的主要作用是在每次 重组(recomposition) 时,安全地执行那些不直接影响 UI、但需要与外部系统交互的操作(例如日志记录、埋点、更新非 Compose 管理的状态等)。
                        SideEffect {
                            println("Item ${todo.title} 显示动画执行")
                        }
                        TodoItemRow(todo = todo
                            , onDelete = {
                                // 触发删除动画
                                itemVisibility[todo.id] = false
                                scope.launch {
                                    delay(500)
//                                    viewModel.removeItem(item)
//                                    deletingItems = deletingItems - item
                                    viewModel.deleteTodo(todo)
                                    // 清理状态由 LaunchedEffect 的 retainAll 负责
                                }
                             }
                               ,onToggle = {
                                viewModel.toggleComplete(todo)  // 切换完成状态
                            }
                            , onClick = { onNavigateToDetail(todo.id) }// 点击跳转
                        )
                    }
                }
            }
        }
    }
}

@Composable
fun TodoItemRow(
                todo: TodoEntity
                , onDelete: () -> Unit  // 添加删除回调,删除逻辑放在上层。即把回调传给里面的按钮。
                ,onToggle: () -> Unit
    , onClick: () -> Unit
    , modifier: Modifier = Modifier
) {
    Card(
        modifier = modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp)
            .clickable { onClick() }, // 现在 modifier 应该会叠加动画修饰符
        elevation = CardDefaults.cardElevation(
            defaultElevation = 2.dp // 这里传你要的默认高度
        ),
        shape = MaterialTheme.shapes.medium,  // 使用主题形状
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surface
        )
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxWidth()
                .padding(vertical = 8.dp),
            horizontalArrangement = Arrangement.SpaceBetween // 横向布局子元素两端对齐,剩余空白空间平均分配到子元素之间
        ) {
            // 新增Checkbox,切换事项是否已完成的状态
            Checkbox(
                checked = todo.isCompleted,
                onCheckedChange = { onToggle() }
            )
            Text(text = todo.title
                ,style = MaterialTheme.typography.bodyLarge,
                color = MaterialTheme.colorScheme.onSurface
                ,textDecoration = if (todo.isCompleted) TextDecoration.LineThrough else null // LineThrough是中划线
            )
            IconButton(onClick = onDelete) {
                Icon(Icons.Default.Delete, contentDescription = "删除"
                    , tint = MaterialTheme.colorScheme.error)
            }
        }
    }
}

// 主题切换开关
@Composable
fun ThemeSwitch(settingsViewModel: SettingsViewModel) {
    val isDarkTheme by settingsViewModel.isDarkTheme.collectAsState()
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(MaterialTheme.shapes.medium)
            .background(MaterialTheme.colorScheme.surface)
            .padding(horizontal = 16.dp, vertical = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            text = "深色模式",
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurface
        )
        Switch(
            checked = isDarkTheme,
            onCheckedChange = { settingsViewModel.toggleDarkMode() }
        )
    }
}

// 为了允许手动切换深色/浅色模式,在应用中保存用户的选择,并在主题中读取. 后面改用DataStore保存
//object ThemeManager {
//    var isDarkTheme by mutableStateOf(false)
//        private set
//
//    fun toggleTheme() { // 切换是否为深色主题
//        isDarkTheme = !isDarkTheme
//    }
//}

修改后:

Kotlin 复制代码
package com.example.testcompose1

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.testcompose1.data.TodoEntity
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Collections.rotate

@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun TodoListScreen(
    viewModel: TodoViewModel,  // 获取ViewModel实例。 在同一个activity作用域中是单例。
    settingsViewModel: SettingsViewModel,
    onNavigateToDetail: (Int) -> Unit = {}
) {
//    val screenWidth = LocalConfiguration.current.screenWidthDp.dp.value
    val configuration = LocalConfiguration.current
    val density = LocalDensity.current
    val screenWidthPx = with(density) { configuration.screenWidthDp.dp.toPx() }
    val offsetX = -(screenWidthPx * 3).toInt()  // 从屏幕左侧3倍宽度外滑入

    var showInfiniteList by remember { mutableStateOf(false) }
    if (showInfiniteList) {
        // 显示无限滚动列表,并提供一个返回按钮
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text("无限滚动列表") },
                    navigationIcon = {
                        IconButton(onClick = { showInfiniteList = false }) {
                            Icon(Icons.Default.ArrowBack, contentDescription = "返回")
                        }
                    }
                )
            }
        ) { innerPadding ->
            // 给 InfiniteListPage 添加内边距
            Box(modifier = Modifier.padding(innerPadding)) {
                InfiniteListPage()
            }
        }
    } else { // 显示原待办事项列表
        // 使用 remember 和 mutableStateOf 保存输入框的文本
        var text by remember { mutableStateOf("") }
        // 使用 mutableStateListOf 保存待办项列表
//    val todoItems = remember { mutableStateListOf<String>() }
        // 将 StateFlow 转换为 Compose 可观察的 State
//        val todoItems by viewModel.todoItems.collectAsState()

        val todos by viewModel.todos.collectAsState()

        // 获取协程作用域,用于延迟删除
        val scope = rememberCoroutineScope()
        // 管理每项的 AnimatedVisibility;新 id 先 false 再 true 以触发进入动画
        val itemVisibility = remember { mutableStateMapOf<Int, Boolean>() }

        // 必须用 collect 持续监听,不能用 LaunchedEffect(todos):Room 每次发射新 List 都会让
        // LaunchedEffect 重启并取消子协程,导致 delay(50) 里「设为可见」永远跑不完,界面一直空白。
        LaunchedEffect(Unit) {
            viewModel.todos.collect { current ->
                current.forEach { todo ->
                    if (!itemVisibility.containsKey(todo.id)) {
                        itemVisibility[todo.id] = false
                        launch {
                            delay(50)
                            itemVisibility[todo.id] = true
                        }
                    }
                }
                itemVisibility.keys.retainAll(current.map { it.id }.toSet())
            }
        }

        Column(modifier = Modifier.padding(16.dp)) {
            ThemeSwitch(settingsViewModel)  // 添加开关
            Spacer(modifier = Modifier.height(8.dp))
            // 文本输入框
            TextField(
                value = text,
                onValueChange = { text = it }, // 反向绑定,视图变化--> 数据变化
                label = { Text("输入待办事项") },
                colors = TextFieldDefaults.colors(
                    focusedContainerColor = MaterialTheme.colorScheme.surface, // 获得焦点时的背景色
                    unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant, // 失去焦点时,输入框背景色
                    focusedIndicatorColor = MaterialTheme.colorScheme.primary, // 输入框底部下划线的颜色。
                    unfocusedIndicatorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
                ),
                modifier = Modifier.fillMaxWidth()
            )
            // 添加按钮
            Button(
                onClick = {
                    viewModel.addTodo(text)
                    text = ""
                },
                shape = MaterialTheme.shapes.small,  // 使用主题形状
                colors = ButtonDefaults.buttonColors(
                    containerColor = MaterialTheme.colorScheme.primary, // 容器背景色,按钮底色
                    contentColor = MaterialTheme.colorScheme.onPrimary // 内容颜色,按钮上文字 / 图标的颜色
                ),
                modifier = Modifier.padding(top = 8.dp)
            ) {
                Text("添加")
            }

            // 显示待办列表
            Spacer(modifier = Modifier.height(16.dp))
            Text("待办列表", style = MaterialTheme.typography.titleMedium)
            LazyColumn {
                items(items = todos
                ,key = { it.id }) //  使用唯一 id 作为 key,确保动画正确识别
                { todo ->
                    val visible = itemVisibility[todo.id] ?: true
                    // 为每个项添加动画。 AnimatedVisibility没起作用
                    AnimatedVisibility(
                        visible = visible,
                        enter = fadeIn(animationSpec = tween(1500, easing = FastOutSlowInEasing)) +
                                slideInHorizontally(
                                    initialOffsetX = { -3000 },  // 固定大偏移量,从左侧 3000 像素外滑入
                                    animationSpec = tween(1500, easing = FastOutSlowInEasing)
                                ) +
                                scaleIn(
                                    initialScale = 0.1f,
                                    animationSpec = tween(1500, easing = FastOutSlowInEasing)
                                ),
                        exit = fadeOut(animationSpec = tween(500)) +
                                slideOutHorizontally(targetOffsetX = { 200 }
                                    , animationSpec = tween(500))
                    ) {
                        // SideEffect 是一个专门用于执行副作用的可组合函数。它的主要作用是在每次 重组(recomposition) 时,安全地执行那些不直接影响 UI、但需要与外部系统交互的操作(例如日志记录、埋点、更新非 Compose 管理的状态等)。
                        SideEffect {
                            println("Item ${todo.title} 显示动画执行")
                        }
                        TodoItemRow(todo = todo
                            , onDelete = {
                                // 触发删除动画
                                itemVisibility[todo.id] = false
                                scope.launch {
                                    delay(500)
//                                    viewModel.removeItem(item)
//                                    deletingItems = deletingItems - item
                                    viewModel.deleteTodo(todo)
                                    // 清理状态由 LaunchedEffect 的 retainAll 负责
                                }
                             }
                               ,onToggle = {
                                viewModel.toggleComplete(todo)  // 切换完成状态
                            }
                            , onClick = { onNavigateToDetail(todo.id) }// 点击跳转
                        )
                    }
                }
            }
        }
    }
}

@Composable
fun TodoItemRow(
                todo: TodoEntity
                , onDelete: () -> Unit  // 添加删除回调,删除逻辑放在上层。即把回调传给里面的按钮。
                ,onToggle: () -> Unit
    , onClick: () -> Unit
    , modifier: Modifier = Modifier
) {
    Card(
        modifier = modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp)
            .clickable { onClick() }, // 现在 modifier 应该会叠加动画修饰符
        elevation = CardDefaults.cardElevation(
            defaultElevation = 2.dp // 这里传你要的默认高度
        ),
        shape = MaterialTheme.shapes.medium,  // 使用主题形状
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surface
        )
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier
                .fillMaxWidth()
                .padding(vertical = 8.dp),
            horizontalArrangement = Arrangement.SpaceBetween // 横向布局子元素两端对齐,剩余空白空间平均分配到子元素之间
        ) {
            // 新增Checkbox,切换事项是否已完成的状态
            Checkbox(
                checked = todo.isCompleted,
                onCheckedChange = { onToggle() }
            )
            Text(text = todo.title
                ,style = MaterialTheme.typography.bodyLarge,
                color = MaterialTheme.colorScheme.onSurface
                ,textDecoration = if (todo.isCompleted) TextDecoration.LineThrough else null // LineThrough是中划线
            )
            IconButton(onClick = onDelete) {
                Icon(Icons.Default.Delete, contentDescription = "删除"
                    , tint = MaterialTheme.colorScheme.error)
            }
        }
    }
}

// 主题切换开关
@Composable
fun ThemeSwitch(settingsViewModel: SettingsViewModel) {
    val isDarkTheme by settingsViewModel.isDarkTheme.collectAsState()
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(MaterialTheme.shapes.medium)
            .background(MaterialTheme.colorScheme.surface)
            .padding(horizontal = 16.dp, vertical = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            text = "深色模式",
            style = MaterialTheme.typography.bodyLarge,
            color = MaterialTheme.colorScheme.onSurface
        )
        Switch(
            checked = isDarkTheme,
            onCheckedChange = { settingsViewModel.toggleDarkMode() }
        )
    }
}

// 为了允许手动切换深色/浅色模式,在应用中保存用户的选择,并在主题中读取. 后面改用DataStore保存
//object ThemeManager {
//    var isDarkTheme by mutableStateOf(false)
//        private set
//
//    fun toggleTheme() { // 切换是否为深色主题
//        isDarkTheme = !isDarkTheme
//    }
//}

关键改动:

没看出原因,借助的cursor修改的,cursor挺强大的,还分析处出问题原因。注释说了,必须用 collect 持续监听,不能用 LaunchedEffect(todos):Room 每次发射新 List 都会让 LaunchedEffect 重启并取消子协程,导致 delay(50) 里「设为可见」永远跑不完,界面一直空白。 ok.

相关推荐
取码网3 小时前
2025最新口红机防篡改版本源码
android·java·javascript
2501_915918413 小时前
iOS 混淆流程 提升 IPA 分析难度 实现 IPA 深度加固
android·ios·小程序·https·uni-app·iphone·webview
4311媒体网4 小时前
织梦CMS二开实战:打造智能化的审核定时发布功能
android
亿坊电商4 小时前
亿坊·零售连锁门店管理系统|全渠道一体化-独立部署_提升经营管理效率!
android·零售
TO_WebNow4 小时前
tp5部署宝塔遇到的问题
android·ide·php·android studio
2501_915909065 小时前
React Native 上架 App Store:项目运行与审核构建的流程
android·ios·小程序·https·uni-app·iphone·webview
jinanwuhuaguo5 小时前
最新更新版本,OpenClaw v2026.4.2 深度解读剖析:Task Flow 重磅回归与安全架构的全面硬化
android·开发语言·人工智能·回归·kotlin·安全架构·openclaw
匆忙拥挤repeat5 小时前
Android Compose 状态:核心api,状态恢复,状态提升,状态容器
android
千里马学框架6 小时前
干货分享:车载音频audio调试开发之dumpsys CarAudioService剖析
android·音视频·面试题·audio·系统开发·车载audio·framework工程师