用 Claude Code 把 MasterGo 设计稿转成 Android 布局,我前几次得到的结果都不太能看:列表被生成了一坨静态重复布局,XML 里硬编码着 android:text="张三",多样式的信息流只认出一种 item——每次改生成结果,花的时间比自己手写布局还多。
坑踩得多了才意识到,这些问题不是随机的。根因在于设计稿是静态视觉快照,AI 没法从稿子里猜出列表语义和动态数据。逐个案例去修补不可持续,得把规则前置:设计侧的命名规范、截图与 DSL 的阅读顺序、写进 CLAUDE.md 和 Skill 的生成与自检规则。
这篇文章把这套体系完整整理出来:基础问题覆盖边界识别和动态数据,进阶问题覆盖多 ViewType、嵌套 RV 与截图和 DSL 的阅读顺序。文中的命名规范、Prompt 模板、Skill 文件都可以直接拿去用。
所有问题的本质是同一个矛盾:MasterGo 设计稿是“静态视觉快照”,而 Android 布局是“动态语义结构”。
| 问题类别 | 具体表现 | 根因 |
|---|---|---|
| RV 边界识别不准 | 把列表生成为静态重复布局,或漏掉滚动区域 | 设计稿只画了3~5条item,AI无法判断“这是列表还是固定布局” |
| 动态数据丢失 | XML中硬编码 android:text="张三" | 设计稿文字/图片全是占位符,AI默认照抄 |
| 多ViewType遗漏 | 只识别出一种item样式,其余被忽略或误判为独立区块 | 同一列表中不同样式的item在设计稿中视觉差异大,AI未关联 |
| 嵌套RV展平 | 内层横向列表被当成外层一部分,或变成静态LinearLayout | AI未理解“item内部还包含可滚动容器”的层级语义 |
| DSL与截图矛盾 | 生成的结构与视觉不符 | 单一输入源信息不完整,缺少交叉验证机制 |
在 MasterGo 中建立统一的语义化命名体系,让 MCP 导出的 DSL 自带结构信息:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
? 推荐命名体系:
[RV/Outer] 首页信息流 → 外层纵向RecyclerView ├─ [RV-Item/Banner] 横幅卡片 → ViewType: BANNER ├─ [RV-Item/Product] 商品卡片 → ViewType: PRODUCT ├─ [RV-Item/Ad] 广告卡片 → ViewType: AD ├─ [RV-Item/Recommend] 推荐容器 │ └─ [RV/Inner] 横向推荐列表 → 嵌套横向RecyclerView │ ├─ [RV-Item] 推荐卡片A │ ├─ [RV-Item] 推荐卡片B │ └─ [RV-Item] 推荐卡片C └─ [RV-Item/Section] 分组标题 → ViewType: SECTION
[Fixed] 顶部搜索栏 → 固定Header,不随列表滚动 [Fixed] 底部TabBar → 固定Footer |
命名规则速查:
| 前缀 | 含义 | 示例 |
|---|---|---|
| [RV/Outer] | 外层RecyclerView | [RV/Outer] 商品列表 |
| [RV/Inner] | 嵌套RecyclerView | [RV/Inner] 横向推荐 |
| [RV-Item] | 单类型item | [RV-Item] 商品卡片 |
| [RV-Item/变体名] | 多ViewType item | [RV-Item/Banner] |
| [Fixed] | 固定区域 | [Fixed] 底部导航 |
| [Scroll] | ScrollView(非列表) | [Scroll] 协议文本 |
设计师花5分钟做好命名,开发侧能省2小时调试。MasterGo MCP导出的DSL会完整保留图层名,这是最可靠的语义信号。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
┌──────────────────────────────────────────────────────┐ │ 双通道阅读流程 │ │ │ │ Phase 1: 截图 → 宏观语义判断 │ │ ┌────────────────────────────────────────────────┐ │ │ │ • 区域划分:哪些是固定/滚动/嵌套 │ │ │ │ • 滚动判断:被屏幕边缘截断 → 可滚动 │ │ │ │ • 重复模式:视觉上重复的卡片 → 列表语义 │ │ │ │ • 嵌套检测:item内部有横向排列子元素 → 嵌套RV │ │ │ │ • 多类型:同一区域卡片结构明显不同 → 多ViewType │ │ │ │ │ │ │ │ 输出:结构分析表 │ │ │ │ ?? 暂停,等用户确认 │ │ │ └──────────────────────┬─────────────────────────┘ │ │ ▼ │ │ Phase 2: MCP DSL → 精确数值提取 │ │ ┌────────────────────────────────────────────────┐ │ │ │ • 精确尺寸/颜色/字体/间距 │ │ │ │ • 图层嵌套层级(父子关系) │ │ │ │ • Auto Layout方向和间距 │ │ │ │ • 与Phase 1结论交叉对比 │ │ │ │ │ │ │ │ 矛盾处理规则: │ │ │ │ • 截图显示滚动 + DSL未超出 → 仍按滚动处理 │ │ │ │ (设计稿可能只展示部分数据) │ │ │ │ • DSL中≥3个相同结构兄弟节点 → 强制识别为RV │ │ │ │ • DSL中节点被clip裁切 → 等同于"被截断" │ │ │ │ │ │ │ │ 输出:最终结构确认表 │ │ │ │ ?? 暂停,等用户确认 │ │ │ └──────────────────────┬─────────────────────────┘ │ │ ▼ │ │ Phase 3: 代码生成(基于确认后的结构+DSL数据) │ └──────────────────────────────────────────────────────┘ |
| 能力维度 | 截图能做(DSL不能) | DSL能做(截图不能) |
|---|---|---|
| 滚动判断 | ? 内容被屏幕截断 → 可滚动 | ? 无法感知视觉裁切 |
| 重复语义 | ? 视觉上“看起来是一组” | ? 只有节点树,无语义 |
| 整体层次 | ? 一眼看出固定/滚动/嵌套 | ? 需要逐层解析 |
| 精确数值 | ? 像素级估算误差大 | ? 精确px/hex/dp |
| 图层关系 | ? 遮挡/分组难以判断 | ? 明确的父子嵌套 |
| 样式参数 | ? 圆角/阴影/字重难辨 | ? 完整样式属性 |
截图回答“是什么”(语义),DSL回答“是多少”(精确)。两者互补,不能互相替代。
识别规则(写入 CLAUDE.md / Skill):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
## RecyclerView 边界判定规则
满足以下任一条件 → 生成 RecyclerView(而非静态布局): 1. 同父容器下 ≥3 个结构完全相同的兄弟节点 2. 图层名称包含 [RV]、list、recycler、列表、卡片流 3. 内容被父容器 clip 裁切(DSL中overflow=hidden且子节点超出) 4. 截图中内容被屏幕边缘截断
滚动方向判定: - 重复节点纵向排列 → LinearLayoutManager(VERTICAL) - 重复节点横向排列 → LinearLayoutManager(HORIZONTAL) - 重复节点网格排列 → GridLayoutManager(spanCount=N) - 截断在水平方向 → 横向 - 截断在垂直方向 → 纵向 |
Prompt 模板(Phase 1 截图分析时使用):
|
1 2 3 4 5 6 7 8 9 |
请观察这张设计稿截图,完成以下分析(不写代码):
1. 哪些区域的内容被屏幕边缘截断?→ 标记为可滚动,标注截断方向 2. 哪些区域存在视觉上重复的卡片/行?→ 标记为列表 3. 哪些元素始终可见(顶栏/底栏)?→ 标记为固定 4. 某个重复单元内部是否还包含横向排列的子元素?→ 标记为嵌套RV
输出格式: | 区域 | 类型(RV/Fixed/Scroll) | 滚动方向 | Item类型数 | 含嵌套RV | 备注 | |
XML规范(写入 CLAUDE.md):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
## 动态数据绑定规范
1. 所有运行时动态赋值的文本: ? tools:text="示例商品名称" ? android:text="张三"
2. 所有运行时动态加载的图片: ? tools:src="@drawable/placeholder" ? android:src="@mipmap/avatar_zhangsan"
3. 条件显示/隐藏的View: ? android:visibility="gone" + tools:visibility="visible"
4. 每个需要数据绑定的View必须有明确id: ? android:id="@+id/tv_title" 命名规范:类型前缀_语义名(tv_title, iv_avatar, rv_list, btn_submit)
5. 提供数据模型时,字段与设计稿文本一一对应: ```json { "title": "string, 最多2行", "price": "double, 显示为¥{price}" } ``` |
设计侧:用 [RV-Item/变体名] 命名区分不同类型
Prompt 侧:附带 ViewType 映射表
|
1 2 3 4 5 6 7 8 9 10 |
## 多ViewType声明
[RV/Outer] 首页信息流 → RecyclerView(VERTICAL)
| ViewType | 图层名 | 布局文件 | 数据模型 | 出现规律 | |----------|--------|---------|---------|---------| | TYPE_BANNER(0) | [RV-Item/Banner] | item_banner.xml | BannerData | 每10个出现1次 | | TYPE_PRODUCT(1) | [RV-Item/Product] | item_product.xml | ProductData | 主要数据 | | TYPE_AD(2) | [RV-Item/Ad] | item_ad.xml | AdData | 每5个插入1个 | | TYPE_SECTION(3) | [RV-Item/Section] | item_section.xml | SectionData | 分组时出现 | |
代码生成规范(写入 Skill):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
## 多ViewType生成规范
数据层:sealed class 作为基类 sealed class FeedItem { data class Banner(...) : FeedItem() data class Product(...) : FeedItem() ... }
Adapter: - 继承 ListAdapter<FeedItem, RecyclerView.ViewHolder> - getItemViewType(): when(item) 返回类型常量 - onCreateViewHolder(): 按viewType inflate不同布局 - onBindViewHolder(): when(holder) 分发绑定
DiffUtil: - areItemsTheSame: 按类型+id判断 - areContentsTheSame: data class自动equals
自检: - [ ] when分支覆盖所有ViewType? - [ ] 每个ViewHolder只引用自己的ViewBinding? - [ ] DiffUtil能区分不同类型? |
设计侧:[RV-Item] 内部嵌套 [RV/Inner]
布局生成规范:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<!-- item_recommend_row.xml:外层item --> <LinearLayout orientation="vertical"> <TextView android:id="@+id/tv_section_title" tools:text="猜你喜欢" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_inner" android:layout_width="match_parent" android:layout_height="wrap_content" tools:layoutManager="LinearLayoutManager" tools:orientation="horizontal" tools:listitem="@layout/item_recommend_card" /> </LinearLayout> |
Kotlin生成规范(写入 Skill):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
## 嵌套RV生成规范
外层Adapter的对应ViewHolder中: class RecommendRowHolder(binding: ItemRecommendRowBinding) : RecyclerView.ViewHolder(binding.root) { private val innerAdapter = RecommendCardAdapter() init { binding.rvInner.apply { layoutManager = LinearLayoutManager(context, HORIZONTAL, false) adapter = innerAdapter setHasFixedSize(true) isNestedScrollingEnabled = false } } fun bind(data: RecommendRowData) { binding.tvSectionTitle.text = data.title innerAdapter.submitList(data.cards) } }
性能优化(必须生成): - 多个内层RV使用相同item类型 → 共享RecycledViewPool - 内层RV设置 isNestedScrollingEnabled = false - 嵌套深度限制:最多3层,超过提醒用户简化 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# CLAUDE.md
## 项目信息 - 语言:Kotlin - 布局:XML + ViewBinding - 图片加载:Glide - 列表:RecyclerView + ListAdapter + DiffUtil
## MasterGo 转码规则 1. 优先使用 MCP DSL,截图作为语义补充 2. 阅读顺序:截图(宏观) → DSL(精确) → 交叉验证 3. ≥3个相同结构兄弟节点 → RecyclerView 4. 被clip裁切/屏幕截断 → 可滚动 5. [RV-Item/变体名] → 多ViewType 6. [RV-Item]内含[RV/Inner] → 嵌套RV 7. 动态文本一律用 tools:text,禁止硬编码 8. 尺寸换算:设计稿375px宽 → 360dp 9. 颜色提取到colors.xml,不内联 10. 输出路径:以用户输入的地址为准,不自行推断落盘位置
## 生成顺序(每步暂停确认) 结构分析 → XML布局 → 数据模型 → Adapter → 页面初始化 → 自检 |
生成文件落盘位置:以用户输入的地址为准。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
## 输出路径规则
优先级(从高到低): 1. 用户明确指定的路径(命令参数 / 对话中给出)→ 严格使用,不改动 2. 用户未指定时 → 先询问用户,确认后再生成,不自行选择默认目录
落盘结构: - 单平台:直接在用户指定路径下生成标准 res/ 结构 <用户路径>/res/layout/ 页面 + item 布局 <用户路径>/res/drawable/ shape 背景 <用户路径>/res/raw/ SVG 图标 <用户路径>/res/values/ colors.xml / dimens.xml / strings.xml - 多平台:在用户指定路径下先建平台文件夹(android/、ios/ …), 各平台产物放入对应文件夹
禁止行为: - 禁止把文件写到用户未确认的位置 - 禁止生成后不告知最终落盘路径 - 生成完成后必须输出:本次写入的完整目录路径 + 文件清单 |
原因:布局产物最终要并入 Android 工程的 app/src/main/res/, 落盘位置不对会造成“生成了但找不到”的问题。以用户输入为唯一事实来源, 生成完毕必须回显完整路径供用户确认。
.claude/skills/design-to-android/SKILL.md:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
--- name: design-to-android description: | 将 MasterGo 设计稿转换为 Android XML + Kotlin 代码。 支持:多ViewType、嵌套RecyclerView、动态数据绑定。 当用户提供设计稿链接或截图并要求生成Android布局时触发。 ---
# MasterGo → Android 布局转换
## 前置条件 - MasterGo MCP 已配置 - 用户提供:设计稿链接(带layerId)+ 可选截图
## 执行流程
### Phase 1: 视觉语义分析(截图优先) 输入:设计稿截图 任务:宏观结构判断 输出:区域划分表(类型/滚动方向/Item类型数/嵌套关系) ?? 暂停,等用户确认
### Phase 2: DSL精确提取(MCP结构化) 输入:MasterGo MCP getDsl 任务:精确数值提取 + 层级验证 交叉验证规则: - 截图显示滚动 + DSL未超出 → 仍按滚动处理 - DSL中≥3个相同结构兄弟节点 → 强制RV - DSL中节点被clip裁切 → 可滚动 - 同名前缀不同后缀 → 多ViewType - [RV]内嵌套[RV] → 嵌套列表 输出:最终结构确认表 ?? 暂停,等用户确认
### Phase 3: 代码生成 输入:确认后的结构 + DSL数据 生成顺序: 1. 主布局(activity/fragment) 2. 每个item布局(多ViewType则多个) 3. 嵌套的内层item布局 4. 数据模型(sealed class / data class) 5. Adapter(含多ViewType分发 + 嵌套RV初始化) 6. ViewHolder(每种类型一个) 7. 页面初始化代码
### Phase 4: 自检 - [ ] RV边界正确?(无静态重复布局) - [ ] 多ViewType全部覆盖? - [ ] 嵌套RV独立Adapter + 性能优化? - [ ] 所有动态文本用tools:? - [ ] 滚动方向正确? - [ ] 所有动态View有id? - [ ] 颜色提取到colors.xml?
## 识别规则速查 | 信号 | 判定 | |------|------| | ≥3个相同结构兄弟节点 | RecyclerView | | 图层名含[RV] | RecyclerView | | 内容被clip裁切 | 可滚动 | | 截图内容被屏幕截断 | 可滚动 | | [RV-Item/A] + [RV-Item/B] | 多ViewType | | [RV-Item]内含[RV/Inner] | 嵌套RV | | 横向重复+超出宽度 | 横向RV | |
.claude/commands/d2a.md:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
根据以下设计稿生成 Android 布局代码:
设计稿链接:$ARGUMENTS
输出路径:以我在对话中指定的地址为准;若我未指定,先问我,确认后再生成。
请严格按照 design-to-android skill 的流程执行: 1. Phase 1: 先读截图做结构分析,输出分析表,等我确认 2. Phase 2: 再用MCP获取DSL,交叉验证,输出确认表,等我确认 3. Phase 3: 分步生成代码(XML → Model → Adapter → 初始化) 4. Phase 4: 自检报告
重点关注: - 被截断的区域 → RecyclerView - 重复结构的不同变体 → 多ViewType - item内部的横向列表 → 嵌套RV - 所有动态数据 → tools:属性 |
使用方式:/d2a https://mastergo.com/file/xxx?layer_id=yyy
|
1 |
claude mcp add mastergo -- npx -y @mastergo/magic-mcp --token=YOUR_MG_TOKEN |
Token获取:MasterGo → 个人设置 → 开发者 → Personal Access Token MCP走结构化DSL通道,远比截图识别准确,务必优先使用。
这套体系不是万能 钥匙,效果建立在几个前提上,套用前先对照自己的情况:
一句话:本文针对标准列表/信息流类页面(RecyclerView 为主、多 ViewType、嵌套列表)最有效,离这个画像越远,准确率预期就要打得越低。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
设计侧 ────────────────────────────────────────────── ? 列表区域命名加 [RV/Outer] 或 [RV] 前缀 ? Item组件命名 [RV-Item] 或 [RV-Item/变体名] ? 嵌套列表标注 [RV/Inner] ? 固定区域标注 [Fixed] ? 设计稿只画2~3个重复item(不要画太多增加干扰)
Prompt 侧 ──────────────────────────────────────────── ? 先给截图做Phase 1结构分析 ? 附带区域标注表(哪里是列表/方向/数据源) ? 附带数据模型JSON Schema ? 多ViewType附带映射表 ? 嵌套RV明确声明层级关系和滚动方向
CLAUDE.md / Skill 侧 ──────────────────────────────── ? 写入RV识别规则(≥3重复/clip裁切/[RV]命名) ? 写入多ViewType生成规范(sealed class + when分发) ? 写入嵌套RV生成规范(独立Adapter + 性能优化) ? 写入tools:属性规范 ? 写入截图→DSL双通道阅读流程 ? 写入分步生成+暂停确认流程 ? 写入自检清单
生成流程 ──────────────────────────────────────────── ? 确认输出路径(以用户输入的地址为准,未指定则先询问) ? Phase 1: 截图结构分析 → 人工确认 ? Phase 2: DSL精确提取+交叉验证 → 人工确认 ? Phase 3: XML布局 → 数据模型 → Adapter → 初始化 ? Phase 4: 自检报告 ? 生成完毕回显:完整落盘路径 + 文件清单 |
不要指望 AI 从设计稿里“猜”出所有语义。截图负责语义,DSL 负责数值,命名负责身份,Skill 负责方法,四者结合起来,设计稿转码的准确率才有保障。