公司当前项目是使用Figma UI设计工具,以这个为例。
其他设计工具例如蓝湖等如果有MCP,使用同理。
Figma当前是有MCP供对外使用的。配置教程见Figma官网。
一、编辑skill
看完之前的文章大概就了解了如何创建一个skill。
在skills文件夹创建一个子文件夹,子文件夹名字是figma_ui_to_xml,然后在figma_ui_to_xml内创建一个Markdown文件,文件名是固定的SKILL.md。
编辑name和description,为了让skill被调用,我在description加上了粘贴Figma MCP链接时一定有的一段文案Implement this Figma design.。下面是Figma MCP粘贴链接的内容:
Implement this Figma design.
@https://www.figma.com/design/Lod2RUqzBCaISqN4/FOXX-UI-library?node-id=43080-44506&m=dev
下面是name和description
---
name: figma_ui_to_xml
description: Implement this Figma design。将Figma MCP链接转换为Android原生XML。
---
在SKILL.md中根据项目的实际情况编辑提示词,把所有的可以参考的文件,例如抽取的style,颜色等都写进去,下面是我删减自己项目敏感信息后的提示词,可以根据自己的项目修改:
---
name: figma_ui_to_xml
description: Implement this Figma design。将 Figma MCP 节点链接转换为 Android 原生 XML(根布局 ConstraintLayout),并严格遵循项目规范:AppCompat、snake_case id、dp/sp、strings/colors 资源优先、复用现有 drawable/style、完整 constraints。
---
# 将 Figma MCP 链接转换为 Android 原生 XML
## 何时使用
当用户提供 **Figma MCP 原始数据链接 / 节点链接**,需要输出可直接落地的 **Android 原生 XML 布局**(并尽量复用项目既有资源)时启用本 Skill。
## 输入
- Figma MCP 原始数据链接 / 节点链接(必须)
-(可选)项目资源约束信息:现有 `styles_buttons.xml`、`styles_textviews.xml`、已有 `drawable`、字符串/颜色命名规则
## 严格流程(每次都按此执行)
1. **提取设计数据(必须完整)**
- 用 Figma MCP 把节点的:结构层级、尺寸/位置、颜色/变量、文本内容、字体/字号、间距、圆角/描边/阴影等全部提取出来
- 如有疑点,用 screenshot 辅助核对(只用于校验,不替代结构数据)
2. **元素映射到 Android View(固定规则)**
- 根布局:**ConstraintLayout**,宽高 `match_parent`
- 将每个 Figma 元素映射为原生控件(优先 AppCompat):
- 文本 → `AppCompatTextView`
- 图片/图标 → `AppCompatImageView`
- 按钮 → `AppCompatButton`(优先套用公共 style)
- 容器/卡片 → `ConstraintLayout` / `FrameLayout`(按需要,但根必须是 ConstraintLayout)
3. **生成 XML(严格按项目规范)**
- View 尽量用 **AppCompat**
- 所有 `id` 用 **snake_case**(例如 `@+id/tv_title`)
- 尺寸用 `dp`,字号用 `sp`
- **constraints 必须写完整**:避免运行时落到 `(0,0)`;不要只写单边约束
- 文本:
- 优先放 `res/values/strings.xml`
- 若确认找不到对应 key:允许在 XML 写死文案(但要保持可替换性,避免拼接难维护)
- 颜色:
- 优先用 `res/values/colors.xml`
- 没有就写死 `#RRGGBB`
- 字体映射(按约定):
- Regular / Light / Medium → `roboto_regular` / `roboto_light` / `roboto_medium`
- **所有文本控件统一加**:`android:lineSpacingExtra="3dp"`(忽略 letterSpacing)
4. **优先复用项目已有资源(避免重复造轮子)**
- `res/drawable` 已有背景/圆角/边框:能引用就直接引用
- Button:优先套用 `res/values/styles_buttons.xml` 公共样式
- TextView:优先套用 `res/values/styles_textviews.xml` 公共样式
5. **输出**
- 输出最终 Android XML 布局代码(可直接粘贴使用)
- 如需补充 `strings/colors/drawable`:仅在末尾给出“建议新增项”(不要过度发散)
## 输出格式要求
- 先给:`res/layout/xxx.xml` 完整内容
- 再给(可选):**建议新增项**
- `res/values/strings.xml`:新增 key/value(仅缺失项)
- `res/values/colors.xml`:新增颜色(仅缺失项)
- `res/drawable/*.xml`:仅当确实无可复用资源时才建议新增,并说明用途(圆角/描边/阴影等)
## 质量检查清单(生成后自检)
- [ ] 根布局是 ConstraintLayout 且 match_parent
- [ ] 每个控件的约束完整(至少水平 + 垂直都可定位)
- [ ] id 全部 snake_case、命名语义清晰(tv_/iv_/btn_/cl_ 前缀可按项目习惯)
- [ ] dp/sp 使用正确(无裸数字)
- [ ] TextView 都有 `lineSpacingExtra="3dp"`
- [ ] TextView 优先 套用 `res/values/styles_textviews.xml` 公共样式
- [ ] Button:优先套用 `res/values/styles_buttons.xml` 公共样式
- [ ] strings/colors 优先资源化,且“建议新增项”不多不少
- [ ] 优先复用了 styles/drawable(没有重复创建同款背景)
## 示例(用户会这样问)
**输入:**
- “Implement this Figma design。@<link>”
**你要输出:**
1) `activity_xxx.xml` / `layout_xxx.xml` 的完整 ConstraintLayout XML
2)(如需要)`strings.xml / colors.xml / drawable` 的最小新增建议清单
二、实际使用
我安装了Figma.app程序,开启了本地MCP服务(见下图)。
使用起来比较简单,从Figma MCP粘贴链接,回车即可,会自动使用上面的skill。
使用high模型可能时间比较长,需要等待一会儿。
例如下面的UI设计稿:
Codex实现的代码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/card_container"
android:layout_width="0dp"
android:layout_height="182dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="98dp"
android:background="@drawable/item_card_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_time"
style="@style/txt_light_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="14dp"
android:text="2024-11-05 16:30:48"
android:textColor="@color/color_second_txt"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_status"
style="@style/txt_light_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
android:text="Completed"
android:textColor="@color/color_status_pending"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_plan_icon"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_marginStart="16dp"
android:layout_marginTop="44dp"
android:background="@drawable/bg_topup_icon_bg"
android:padding="10dp"
app:srcCompat="@drawable/ic_topup_plan"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
style="@style/txt_regular_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:text="2GB Top-up (30-day)"
android:textColor="@color/color_default_txt"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/tv_price"
app:layout_constraintStart_toEndOf="@id/iv_plan_icon"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_price"
style="@style/txt_medium_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="US$9.99"
android:textColor="@color/color_default_txt"
android:textSize="14sp"
app:layout_constraintBaseline_toBaselineOf="@id/tv_title"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_desc"
style="@style/txt_light_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="8dp"
android:maxLines="2"
android:text="2GB cellular data usage within 30 days."
android:textColor="@color/color_second_txt"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@id/tv_quantity"
app:layout_constraintStart_toEndOf="@id/iv_plan_icon"
app:layout_constraintTop_toBottomOf="@id/tv_title"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_quantity"
style="@style/txt_light_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="x1"
android:textColor="@color/color_second_txt"
android:textSize="10sp"
app:layout_constraintBaseline_toBaselineOf="@id/tv_desc"
app:layout_constraintEnd_toEndOf="parent"/>
<LinearLayout
android:id="@+id/ll_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="113dp"
android:layout_marginEnd="16dp"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_total_label"
style="@style/txt_light_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1 Item(s), Total:"
android:textColor="@color/color_txt_secondary"
android:textSize="12sp"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_total_value"
style="@style/txt_light_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="US$9.99"
android:textColor="@color/color_default_txt"
android:textSize="14sp"/>
</LinearLayout>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_refund"
style="@style/bt_primary_light_bg_primary_text"
android:layout_width="104dp"
android:layout_height="28dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
android:gravity="center"
android:text="Refund"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/ll_total"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
实际生成的xml,可用度已经相当高了,复用项目中已有的drawable、style、color、string等,写法也是参照了项目中的其他布局,最后自己微调一下即可。
如果要生成Compose代码其实同理,一通百通,写好skill即可。
三、AI的局限性
1、读取的图片文件有时候不正确
因为图片资源也是包含多图层的,有时候下载下来的不对,需要手动调整。另外很多时候下载的png等格式图片我们需要压缩后使用,这一点也需要手动处理。
2、文案的处理
涉及到多语言的话,新增的文案是不能让AI直接放入strings.xml,项目中可能会有一个翻译表,需要先放入翻译表中翻译后,再通过python写入项目。
3、颜色的处理
涉及到日夜间模式等,UI一般会有多份,例如日间UI、夜间UI等,新增的色值一般是日夜间色成对出现的,这样除非直接给出多份UI,不然AI是不知道如何适配日夜间色的。
4、UI上复杂的滑动事件
复杂的滑动、动画等,静态UI体现不出来,所以生成代码肯定会有所欠缺,但可以通过多次对话弥补一部分,实在不行只能自己动手写了。
5、不都是完全符合skill的要求
每次返回不都是完全符合skill的要求,需要调整skill,或者二次对话让AI纠错。
四、总结
虽然AI还有一些问题,但目前已经解决了90%的问题,剩余的工作我们自己调整一下即可。这在很大程度上解放了我们繁琐的写UI的工作,轻松不少。