بنقرة واحدة
compose-migration
将Android View系统界面重构为Jetpack Compose框架。当用户要求将某个界面改为Compose、重构界面、或提到Compose迁移时调用此技能。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
将Android View系统界面重构为Jetpack Compose框架。当用户要求将某个界面改为Compose、重构界面、或提到Compose迁移时调用此技能。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
通过对比 entities、schema JSON 和迁移方案,诊断并修复 Android Room 数据库在 entity 或 database 变更后的 schema 校验崩溃;增加数据库版本号,添加重命名/新增/删除列的迁移,并验证生成的 Room 代码。
优化Markdown文件中的ASCII流程图,使其更适合手机显示。将宽大的ASCII边框图转换为紧凑的树形结构。当用户要求修复流程图在手机上的显示问题时调用。
分析 Compose 界面是否使用了项目中可复用的组件和主题系统。适用于检查 Compose 屏幕是否符合项目规范或需要重构以使用共享组件。
检查备份系统中选择器、备份逻辑、恢复逻辑、备份信息显示之间的数据一致性。当用户问备份对不对、备份选漏了没、恢复和备份匹配吗、备份信息准不准时调用。
Legado Plus 模块学习讲解器。当用户想学习某个模块、询问模块功能、请求讲解代码架构时调用。提供生动详尽的模块讲解,包括架构图、代码走读、实践任务。
Critical Android code review for Payoo Android app. Focuses on high-impact issues - naming conventions, memory leaks, UIState patterns, business logic placement, lifecycle management, and MVI/MVVM pattern violations. Use when reviewing Kotlin files, pull requests, or checking ViewModels, Activities, Fragments, UseCases, and Repositories.
| name | compose-migration |
| description | 将Android View系统界面重构为Jetpack Compose框架。当用户要求将某个界面改为Compose、重构界面、或提到Compose迁移时调用此技能。 |
本技能提供将传统View系统(XML布局 + RecyclerView + Adapter)重构为Jetpack Compose的完整流程。
需要收集的信息:
├── Activity/Fragment文件路径
├── XML布局文件路径
├── Adapter文件路径(如果有列表)
├── ViewModel文件路径
├── 是否有菜单
├── 是否有对话框
└── 是否有搜索功能
# 检查build.gradle是否有Compose依赖
grep -n "compose" build.gradle
grep -n "compose" libs.versions.toml
# 检查是否已有Compose主题
find . -name "*Theme*.kt" | head -5
文件命名规范:XxxScreen.kt
/**
* XXX界面 - Jetpack Compose实现
*/
package io.legado.app.ui.xxx
// 基础导入
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun XxxScreen(
viewModel: XxxViewModel = viewModel(),
onBackClick: () -> Unit
) {
// 状态管理
val uiState by viewModel.uiState.collectAsState()
// 页面骨架
Scaffold(
topBar = {
TopAppBar(
title = { Text("标题") },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, "返回")
}
},
actions = {
// 菜单按钮
}
)
}
) { paddingValues ->
// 主内容
Column(modifier = Modifier.padding(paddingValues)) {
// UI内容
}
}
}
class XxxActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// 初始化主题
initTheme()
super.onCreate(savedInstanceState)
// 使用setContent替代setContentView
setContent {
XxxContent(
onBackClick = { finish() }
)
}
}
}
ViewModel改造:
class XxxViewModel : BaseViewModel() {
// 将普通属性改为StateFlow
private val _uiState = MutableStateFlow<XxxUIState>(XxxUIState.Loading)
val uiState: StateFlow<XxxUIState> = _uiState.asStateFlow()
// 如果有开关类状态,也要用StateFlow
private val _isEnabled = MutableStateFlow(AppConfig.isEnabled)
val isEnabled: StateFlow<Boolean> = _isEnabled.asStateFlow()
fun setEnabled(enabled: Boolean) {
AppConfig.isEnabled = enabled
_isEnabled.value = enabled // 更新StateFlow
}
}
UIState定义:
sealed class XxxUIState {
object Loading : XxxUIState()
data class Success(val data: List<Item>) : XxxUIState()
data class Error(val message: String) : XxxUIState()
object Empty : XxxUIState()
}
RecyclerView → LazyColumn:
// ❌ 旧代码:RecyclerView + Adapter
recyclerView.adapter = MyAdapter(items)
// ✅ 新代码:LazyColumn
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 8.dp)
) {
items(items, key = { it.id }) { item ->
ItemCard(item = item)
}
}
列表项组件:
@Composable
private fun ItemCard(item: Item) {
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp),
shape = RoundedCornerShape(12.dp)
) {
Column(modifier = Modifier.padding(12.dp)) {
Text(text = item.title, style = MaterialTheme.typography.titleMedium)
Text(text = item.subtitle, style = MaterialTheme.typography.bodySmall)
}
}
}
onCreateOptionsMenu → DropdownMenu:
// ❌ 旧代码
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.xxx, menu)
return true
}
// ✅ 新代码
var showMenu by remember { mutableStateOf(false) }
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, "更多")
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
text = { Text("选项1") },
onClick = { /* 处理点击 */ }
)
}
AlertDialog.Builder → AlertDialog组件:
// ❌ 旧代码
AlertDialog.Builder(this)
.setTitle("标题")
.setMessage("内容")
.setPositiveButton("确定") { _, _ -> }
.show()
// ✅ 新代码
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text("标题") },
text = { Text("内容") },
confirmButton = {
TextButton(onClick = { showDialog = false }) {
Text("确定")
}
}
)
}
SearchView → OutlinedTextField:
// ❌ 旧代码
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.search(newText)
return true
}
})
// ✅ 新代码
var searchQuery by remember { mutableStateOf("") }
OutlinedTextField(
value = searchQuery,
onValueChange = {
searchQuery = it
viewModel.search(it.ifBlank { null })
},
placeholder = { Text("搜索...") },
leadingIcon = { Icon(Icons.Default.Search, null) }
)
重构完成后删除:
需要删除的文件:
├── res/layout/activity_xxx.xml # Activity布局
├── res/layout/item_xxx.xml # 列表项布局
├── res/menu/xxx.xml # 菜单文件(可选保留)
└── XxxAdapter.kt # RecyclerView适配器
| 检查项 | 命令/方法 |
|---|---|
| Activity使用setContent | grep "setContent" XxxActivity.kt |
| 无XML布局文件 | find . -name "activity_xxx.xml" |
| 无Adapter文件 | find . -name "*Adapter*.kt" |
| 组件有@Composable | grep "@Composable" XxxScreen.kt |
| 列表用LazyColumn | grep "LazyColumn" XxxScreen.kt |
| 状态用collectAsState | grep "collectAsState" XxxScreen.kt |
| 编译通过 | ./gradlew compileDebugKotlin |
// ❌ 错误:直接调用suspend函数
onClick = { viewModel.clearAll() }
// ✅ 正确:在协程中调用
val coroutineScope = rememberCoroutineScope()
onClick = {
coroutineScope.launch {
viewModel.clearAll()
}
}
// ❌ 错误:普通函数获取,不会更新
val isEnabled = viewModel.isEnabled()
// ✅ 正确:使用collectAsState
val isEnabled by viewModel.isEnabled.collectAsState()
// 在Composable中获取Context
val context = LocalContext.current
// 获取Application
val application = context.applicationContext as Application
// 字符串资源
stringResource(R.string.xxx)
// 颜色资源
colorResource(R.color.xxx)
// 尺寸资源
dimensionResource(R.dimen.xxx)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun XxxScreen(
viewModel: XxxViewModel = viewModel(),
onBackClick: () -> Unit
) {
// 状态
val uiState by viewModel.uiState.collectAsState()
val items by viewModel.items.collectAsState()
// 本地状态
var showSearch by remember { mutableStateOf(false) }
var searchQuery by remember { mutableStateOf("") }
var showMenu by remember { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
// 副作用
LaunchedEffect(searchQuery) {
viewModel.search(searchQuery.ifBlank { null })
}
// 页面骨架
Scaffold(
topBar = {
TopAppBar(
title = { Text("标题") },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, "返回")
}
},
actions = {
IconButton(onClick = { showSearch = !showSearch }) {
Icon(Icons.Default.Search, "搜索")
}
Box {
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, "更多")
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
text = { Text("选项") },
onClick = { showMenu = false }
)
}
}
}
)
}
) { paddingValues ->
Column(Modifier.padding(paddingValues)) {
// 搜索框
AnimatedVisibility(visible = showSearch) {
OutlinedTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
modifier = Modifier.fillMaxWidth().padding(16.dp),
placeholder = { Text("搜索...") }
)
}
// 内容区域
when (val state = uiState) {
is XxxUIState.Loading -> {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator()
}
}
is XxxUIState.Success -> {
LazyColumn(Modifier.fillMaxSize()) {
items(state.data, key = { it.id }) { item ->
ItemCard(item)
}
}
}
is XxxUIState.Empty -> {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("暂无数据")
}
}
is XxxUIState.Error -> {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(state.message, color = MaterialTheme.colorScheme.error)
}
}
}
}
}
}
重构完成后,提供以下信息:
文件变更清单
代码对比
验证结果