你也能改主题 —— AMPod 主题编写入门

这份文档假设你完全没学过编程。只要会用文本编辑器改文字,就能照着改出自己的主题。
全文用一个词固定指代 XML 里的"一种东西":标签(尖括号包起来的名字,如 <Text>)。

怎么读这篇文章

先说清楚一件事:在 AMPod 里,一个"主题"其实就是一个文件夹,文件夹里有一张"图纸",叫 theme.xml。 App 启动时照着这张图纸把界面摆出来——哪里放文字、哪里放按钮、按钮长什么样,全都写在图纸上。所以"改主题"这件事,说到底就是用记事本打开这张图纸,改上面的文字。改错了也不会把手机弄坏,顶多是界面变丑或者某个地方不显示了,改回来就行。

这篇文章一共 6 个部分,每章只讲一件事:第 1 章先用生活里的例子讲明白"在屏幕上摆东西"是怎么回事(位置、大小、谁套着谁);第 2 章教你找到图纸、改图纸、看效果;第 3 章讲怎么让界面上的文字"自己会变"(比如歌名会跟着当前播放的歌换);第 4 章讲怎么少写重复的东西;第 5 章是一本"字典",把图纸能用的所有零件(标签)一个一个介绍给你,你不用从头读到尾,想知道某个零件怎么用,直接翻到它那一页就行;第 6 章讲图片怎么准备;最后两个附录,A 是所有可以填的"选项"速查表,B 是一个 30 分钟上手练习。

第 5 章里每个零件都写成一张统一的"卡片",卡片一共 6 栏,长得像这样(下面只是一个图例,演示每一栏是干什么的,真正的卡片内容在后续章节里):

### <中文名> `<标签>` —— 一句大白话用途
**长这样:** (ASCII 框图或"见某主题第N行")
**照抄能跑:** 一行最小 XML
**常用属性:** 表格(属性 | 怎么写 | 大白话作用)
**更多属性:** 一行带过 → 见附录 A
**小白坑:** 最易错的一处

读卡片的方法:先看"长这样",知道这个零件在屏幕上是什么效果;再看"照抄能跑",那一行 XML 你可以原样复制进自己的图纸,立刻能看到东西出现;然后看"常用属性"表,表里每一行是一个可以填的"选项"——比如下面图例里只示意的两行:

属性 怎么写 大白话作用
text text="你好" 写上要显示的文字
color color="#FF0000" 文字颜色,红绿蓝各两位
(这里只是举例怎么读表,<Text> 的完整属性细节见 5.1。)最后一栏"小白坑"提醒你新手最容易犯的一个错,抄代码前扫一眼能省很多折腾。

第 1 章 先搞懂"摆东西" —— 布局(Layout)

在正式改图纸之前,先把一件事想明白:界面就是把一堆东西摆到屏幕上。一块文字、一张封面图、一个按钮——每一个都得回答两个问题:摆在哪儿?多大?

在图纸里,回答这两个问题的东西叫 Layout(读音"雷奥特",意思是"布局",就是"摆放方案")。图纸上每个组件标签里面,都藏着一个小跟班 <Layout/> 标签,专门写它的摆放方案。你可以把组件想成一个相框:相框里装什么照片是组件自己的事,而相框钉在墙上哪个位置、相框多大,全由 <Layout/> 说了算。

1.1 屏幕就是一面墙:八个"图钉孔"

<Layout/> 能用的位置写法一共只有 8 个,别多也别少。想象屏幕是一个盒子,每个组件也是一个小盒子,被放进大盒子里。小盒子在大盒子里怎么定位,就靠这 8 个"图钉孔":

             top
              ↓
   left → ┌───────────────────┐ ← right
          │                   │
          │    ┌─────────┐    │
          │    │  组件   │    │
          │    │    ·    │    │
          │    └─────────┘    │
          │                   │
  bottom →└───────────────────┘
          ←───── width ─────→      height = 框自己多高

   · 是组件的中心点。
   left / right / top / bottom:决定这个框贴靠哪条边。
   centerX / centerY:让 · 对准整面墙的竖直中线 / 水平中线,用来居中。
图钉孔(真实写法) 大白话
left 靠左:小盒子的左边对齐到哪
right 靠右:小盒子的右边对齐到哪
top 靠上:小盒子的上边对齐到哪
bottom 靠下:小盒子的下边对齐到哪
centerX 水平居中:小盒子的中线对准哪条竖线
centerY 垂直居中:小盒子的中线对准哪条横线
width 自己多宽
height 自己多高

1.2 等号右边填什么:装着我的盒子、整个屏幕、别的组件、纯数字

图钉孔找到了,还得说清楚"对齐到哪儿"——也就是等号右边引号里填的东西。一共四类:

第一类:parent——"装着我的那个盒子"。 组件都是层层套放的,parent(读音"派恩特",意思是"家长、外面那层")就是指套着自己的最近那层:外面有收纳盒就指那个盒、被抽屉队装着就指那条抽屉,最外层没套东西时就指整个屏幕。比如:

<Layout left="parent.left+20" top="parent.top" right="parent.right-20" bottom="parent.bottom"/>

这一行是真的(出自 Simple 主题图纸):左边往里挪 20、右边往里挪 20、上下贴边——效果就是"铺满整块地方,但左右各留一条 20 宽的缝"。+20 就是"再往里挪 20",-20 就是"再退回来 20",跟算账一样。

第二类:screen——"整个屏幕,不管被几层盒子套着"。 screen(屏幕)和 parent 一样能写 .left .right .top .bottom .centerX .centerY,区别在于它永远指整个屏幕,不像 parent 会被最近的盒子改口。什么时候用它?想让一块东西顶天立地贴屏幕两边、不受盒子内缩影响的时候——比如播放页顶上的导航条、底部整条状态色带。真实例子(Simple 图纸第 58 行):

<Layout left="screen.left" top="screen.top" right="screen.right" bottom="parent.top+100"/>

翻译:左右贴屏幕、上边贴屏幕顶、下边再参照父盒子往下挪 100——一句话里 parent 和 screen 可以混着用。新手记一句就够:想跟着盒走用 parent,想钉死在屏幕上用 screen。

第三类:#它的名字——"挂在某个组件旁边"。 图纸上的组件都可以起个名字(写在其 id 属性里,类似给东西贴个名牌)。#cover.bottom+8 这种写法的意思是:"id 叫 cover 那个组件的底边,再往下 8"——就像"把说明牌挂在这张封面照片下面,留 8 的空隙"。真实样本:

<Layout centerY="#albumCover.centerY" left="#albumCover.right+10" width="180"/>

翻译:垂直方向跟封面图对齐在同一高度,水平方向贴在封面图右边缘再往右 10,自己宽 180。("跟封面并排站、中间留点缝的文字块",就是它。)

第四类:直接给数字。 width="80" 就是宽 80。数字是"点"(diǎn,屏幕上量长度的单位,约等于一个小米粒),手机上 80 点大概是指甲盖宽度。位置数字也可以是负数(比如 top="-12",意思"往上冒出去 12",常用来让东西故意压线);但 width 和 height 必须大于 0,写 0 或负数等于没写,东西会被忽略掉。

还有一个特殊词:auto(自动)。 width="auto" 意思是"我多宽由我的内容说了算"——比如一块文字,写"你好"就短,写"正在播放的歌曲名称很长"就长,auto 就是让它自己按字数伸缩,不用你操心。真实样本:

<Layout centerY="#buttonPlay.centerY" centerX="#buttonPlay.centerX" width="auto" height="auto"/>

翻译:这块东西(自动大小)的中心对准播放按钮的中心——就是"正好叠在播放按钮正中间的一小段字"。

1.3 谁盖着谁:zOrder(叠放次序)

两个东西摆到了同一块地方,谁在上面盖着谁?图纸用一个叫 zOrder(读音"Z-奥德尔",意思是"叠放次序")的数字决定:数字大的盖在数字小的上面,就像叠扑克牌。两个东西数字相同(或者都没写,没写就当作 0),就按图纸里写的先后——后写的盖在上面。所以想让某个东西永远不被盖住,给它写个大的 zOrder(比如 zOrder="10")就行。

1.4 一个完整的小例子

下面这两行合起来就是"一块文字,摆在哪、多大"的完整写法(照此格式写,放到真实图纸里就能用):

<Text id="title" text="正在播放" ...>
    <Layout left="parent.left+20" right="parent.right-20" centerY="parent.centerY" height="40"/>
</Text>

它在屏幕上长这样:

┌──────────────────────────────┐
│                              │  ← 整个屏幕 / 盒子
│      ┌────────────────────┐  │
│      │      正在播放      │  │  ← 这块卡片 height = 40
│      └────────────────────┘  │     left / right 各收 20
│                              │     centerY 让它上下居中
└──────────────────────────────┘

读图口诀:先找参照物(parent 装着我的盒 / screen 整个屏幕 / #名字 别的组件),再看偏移(+几、-几),最后看大小(width/height,或 auto)。三样凑齐,摆放就说得明明白白。

1.5 三种"打包摆法"先混个脸熟

东西一多,一个个算坐标会累死人。图纸还提供了三种"打包摆放"的容器(容器=能装别的组件的大盒子),先建立印象,这一章不用会写:

这三种的完整用法,分别在第 4 章和第 5.3 节里细讲,这里知道"有它们、大概是干嘛的"就够了。

第 2 章 主题就是一个文件夹 + 一张图纸

这一章解决三个问题:主题文件在电脑上的哪里、图纸 theme.xml 长什么骨架、改完之后怎么让 App 用上它。

2.1 先找到"主题"这个东西

打开项目的 AMPod/Themes/ 文件夹,你会看到一排子文件夹:Black、White、Classic、CoverFlow、Vinyl、Simple、Simple(White)、暗夜星空……每一个文件夹就是一个主题,文件夹名就是主题名。

随便点开一个(比如 Simple),里面只有两样东西:

所以"做一个自己的主题"的完整操作就是:把整个文件夹复制一份、改个名(比如复制 Simple 改成 MySimple),然后放心大胆地改新文件夹里的图纸和图——原来的主题一个字节都不会动,改坏了随时删掉重来。

2.2 图纸 theme.xml 的骨架:三大块

别看 theme.xml 有一千六百多行,其实反复出现的只是三大块"章节",每块管一类事:

第一块:<Common>——全主题共用的"字帖"。 开头几行原样长这样(出自 Simple 图纸第 4~6 行):

<Common>
    <Style value="dark"/>
    <Font id="titleFont" family="PingFangSC_Reguar" size="20"/>

<Style> 定的是整体明暗基调(dark = 深色),<Font> 是"字帖":给一种字体起个名字(id),写清楚字体和字号。之后图纸上任何一块文字只要报这个字帖的名字(fontRef="#titleFont"),就直接用上这套字体字号——就像公司统一公文模板,改一次模板,全公司的文件跟着变。想全局换字体、改字号,改这一块最省力。

第二块:<TabBar>——手机屏幕底部那排标签按钮。 Simple 图纸第 21~27 行原样:

<TabBar backgroundColor="#00000000" textColor="#a0a0a0" selectedTextColor="#f0f0f0" iconColor="#a0a0a0" selectedIconColor="#f0f0f0">
    <Medias icon="eTabMedias"/>
    <Playlists icon="eTabPlaylists"/>
    <Folders icon="eTabFolders"/>
    <Library icon="eTabLibrary"/>
    <More icon="eTabMore"/>
</TabBar>

<Medias>、<Playlists> 这些标签各代表底部一个标签页(媒体、歌单、文件夹、资料库、更多),icon= 后面写的是图标图片的文件名(不带 .png 后缀)。想换底部图标,把对应图片换成自己的就行。

第三块:<Page>——App 里每一屏各有一张图纸。 第 1 章讲的组件(<Text>、<Image>、<IconButton>……)全都写在 <Page> 里。每个 <Page> 有个 id 名字,对应 App 的一屏,比如:

<Page id="MiniPlayer" height="48" backgroundColor="#00000000" dynamicBackground="false" popupBarTransparent="true">

这就是屏幕底部那条"迷你播放条"(5.6 节会讲它的完整长相)。Albums 那一屏的图纸就是 <Page id="Albums" ...>、More 那一屏就是 <Page id="More" ...>。想改哪一屏,就找到 id 是它名字的那一段改。 顺带认脸:<Page id="More">("更多"屏)里列着"主题 / 设置 / 关于 / WiFi 传文件"这些入口,其中"主题"入口就是 2.4 节要用的切换页。

图纸上还会零星看到 <Actions>,它是工具条里的"一键操作"小组(比如批量删除、加入歌单),藏在某屏的工具条里面,不是独立大块,这里混个脸熟即可。

2.3 一个主题其实有 50 多张图纸:语言与尺寸"变体"

再仔细看一眼 Simple 文件夹,会发现 xml 文件不止一个——有 54 个!命名规律是:

所以每种语言有 6 个"档位":通用版 + 3 个手机尺寸版 + iPad 版 + Duo 版。语言 × 档位,凑出这 54 个文件。

App 怎么挑图纸?像点餐一样逐级退省:

  1. 先看有没有"你的语言 + 你的尺寸"那张——手机是英文、屏幕 375x667,就点 theme.en@375x667.xml;
  2. 没有这一档?退而点"你的语言"的通用款——theme.en.xml;
  3. 连语言款都没有?退到基础款 theme.xml(中文)。
这条规则对改主题最重要的影响:改一处,常要同步改多个文件。 比如你把主图纸 theme.xml 里一句话改了,可英文手机上显示的是 theme.en 系列图纸里的旧句子——那边纹丝不动;反过来中文用户可能走的是 theme.zh-Hans@375x667.xml,主图纸改了他也看不见。自己用哪几种语言、哪几档屏幕,就把那几个对应文件一起改,其余可以不管。

好消息:某个变体文件缺失不会弄坏界面——App 自动退回上一档,顶多文字变回中文。但如果某个变体文件存在、里面却写错了,那一档就真的白屏,不会替你退档。

2.4 怎么让 App 用上你的主题

三步:

  1. 复制一个主题文件夹,改成你喜欢的名字(如 MySimple),放回 AMPod/Themes/ 里、和官方主题们并排放好;重新编译安装运行 App 后,App 会自动把它认成一个新主题;
  2. 打开 App,进"更多"页 → 点"主题";
  3. 在列表里选中你的主题,界面立刻换成你的。
改坏了也不怕:随时切回别的主题,你的文件夹只是躺在硬盘里,不动别人的。

2.5 改完怎么验证生效

第 3 章 让文字"自己会变" —— 数据绑定与动作

前两章里,图纸上的东西都是"写死的":<Text> 里写"你好",屏幕上就一直显示"你好"。可播放界面不一样——歌名要跟着当前播放的歌换、进度条要一秒一秒往前走、封面图要换成这张歌自己的封面。这一章讲的就是怎么给组件"接根水管",让 App 里的实时数据自动流进来,以及另一半孪生问题:你点一下按钮,怎么让 App 去干活。

3.1 数据绑定 subscribe —— 给组件接根水管

组件默认是"死"的,要让它跟着播放状态自动变,就在它的标签上加一个 subscribe(读音"瑟斯凯布",意思是"订阅")属性。订阅的写法固定是一个等号加引号,引号里分成两半,中间用一个冒号隔开:

subscribe="要改组件的哪个部位:${数据的名字}"

冒号左边是"水管接到组件的哪个接口上"(显示的文字?图片?藏不藏起来?),冒号右边 :${...} 里是"数据的水源叫什么名字"。举几个 Simple 主题里的真实写法:

一个组件可以同时接好几根水管,用分号 ; 隔开(程序里就是按分号拆开一根根处理的)。比如黑胶主题里那张转盘,一行同时接了三根(Vinyl 主题真实写法):
subscribe="enable:${playback.state.playing};image:${media.artwork};value:${media.progress}"

意思是:正在播放才转动、封面用当前歌的、转到的位置跟着播放进度走。

两边的名字各有哪些

冒号左边——接在组件的哪个部位上(Simple 主题里全部用过的):

部位写法 大白话作用
text: 改显示的文字
image: 改显示的图片
hidden: 数据为真时藏起来
visible: 数据为真时才显示(和 hidden 相反)
select: 改按钮的"亮起/按下"状态(开关亮不亮)
enable: 改"能不能点"(为假时变灰、点不动)
value: 改数值——进度条走到哪、滑块在哪、数字显示几
冒号右边——数据水源的名字(${} 里填的这些,App 运行时一直在更新,所以叫"数据源";都是现成主题里实测在用、放心抄的):
数据名 大白话意思
media.title 当前歌的歌名
media.artist 当前歌的歌手
media.duration 当前歌的总时长
media.position 当前播到第几秒(进度数字)
media.progress 当前进度比例(给进度条、转盘这类用)
media.artwork 当前歌的封面图
media.favorited 当前歌收藏了没有
media.picker.select 在选择列表里"选中了这个条目"没有
playback.state.playing 是不是正在播放
playback.mode 循环模式(单曲/全部/不循环……)
playback.shuffle 随机播放开没开
audioFx.equalizer.enabled 均衡器开没开
transform.wifi.opened WiFi 传文件服务开没开
transform.server.url 传文件时手机浏览器要输的那串网址
这些名字的前缀(media. 是"当前这首歌"、playback. 是"播放状态"、audioFx. 是"音效"、transform. 是"WiFi 传输")就是数据分几路水管,你不用深究,照抄上表里的完整名字即可。

照抄能跑

一块跟着换歌自动变的歌名文字(仿 Simple 第 39~41 行,属性都是真的):

<Text id="songTitle" fontSize="20" textColor="#f0f0f0" subscribe="text:${media.title}">
    <Layout left="parent.left+20" right="parent.right-20" centerY="parent.centerY" height="40"/>
</Text>

小白坑

subscribe 写错了不报错,那块组件照常显示,只是"不跟着变"——这是全书最难查的一类错,因为界面看不出毛病。所以抄的时候盯紧三样:冒号必须是英文的 :(中文全角 : 程序不认识)、${ 和 } 要成对(少个 } 整条作废)、$ 紧贴 {,前面别多空格。最稳的办法是从上表里整段复制,别手敲。

3.2 动作命令 onClick —— 点一下喊一嗓子

水管管的是"数据流向屏幕",反方向——"你的点击传给 App"——靠按钮类标签上的 onClick(读音"昂克立克",意思是"被点击时")属性。它填的不是数据,而是一条命令:一个短横线连着的小写词,比如 onClick="playback-start"。你点下去,这串词就一层层往上传给 App 的总调度(App 里管这事的"服务台"叫 AppExecutor);服务台听懂哪个词,就去干对应的活——开始播放、切下一首、打开搜索……

注意:onClick 不是每个组件都有,只有按钮类的才有(<IconButton>、<ToggleButton>、<TextIconButton>、<SwitchButton>……第 5 章的卡片会逐个标清楚谁认什么)。

Simple 主题里服务台常听的命令,照功能分两堆:

播放控制类(都出自 Simple 图纸真实行):

命令 干什么
playback-start 开始播放(第 1151 行)
playback-pause 暂停(第 1152 行)
playback-forward 下一首(第 1153 行)
playback-backward 上一首(第 1150 行)
playback-switch-mode 切换循环模式:全部循环→单曲循环→不循环轮着来(第 1149 行)
playback-switch-shuffle 切换随机播放开/关(第 1155 行)
功能类:
命令 干什么
library-search 跳到搜索页(第 728 行)
media-playlist-create 新建歌单(第 218 行)
media-queue-open 打开播放队列(第 1084 行)
media-queue-clear 清空播放队列(第 1289 行)
media-favorite-toggle 收藏/取消收藏当前歌(第 1088 行)
id3tags-save 保存歌曲的标签信息(第 1057 行)
equalizer-switch-toggle 开/关均衡器(第 1465 行)
feedback-send-email 发反馈邮件(第 1631 行)
照抄能跑的一个例子——暂停时才露头的播放按钮(Simple 第 45~48 行的做法,水管和嗓子配合的典型:onClick 负责"点了喊播放",subscribe 负责"正在播放时把自己藏起来"):
<IconButton id="buttonPlay" imageNormal="ePlayS" onClick="playback-start"
    subscribe="hidden:${playback.state.playing}">
    <Layout centerX="parent.centerX" centerY="parent.centerY" width="auto" height="auto"/>
</IconButton>

(imageNormal="ePlayS" 里的 ePlayS 是这个主题自带的图标名——你换用自己主题里的图标名即可,别原样照抄到没有这张图的主题里。)

小白坑

命令词表远不止上面这十几个(还有 router-back 返回上一页、schema-medias-play-all 播放全部等等),但你不用背、也不用穷举——新手改主题时最常用的招是"把现成的按钮挪位置、改样子",原来的 onClick 是什么就沿用什么,这是最安全的。真要加新命令,先在现成主题图纸里搜同样的词,看人家挂在什么组件上,照着挂;挂在认不得这个命令的组件上,点了就石沉大海。命令后面能不能带参数、哪些组件认哪些命令,第 5 章各按钮卡片里会讲。

3.3 小结

subscribe 是把 App 的实时数据接进组件的水管(部位:${数据名},写错不报错、只是不变);onClick 是点按钮时喊给服务台的一嗓子(一条短横线小写词)。一进一出,界面就"活"了。

第 4 章 少写重复 —— Box / Stack / 模板(Include)

第 1 章末尾说过:东西一多,一个个算坐标会累死人。这一章把三种"打包摆法"里的两样——收纳盒(Box)和自动排队的抽屉(Stack)——完整讲给你,再介绍第三样复用利器:复印机(模板 / Include)。("翻页相册"Scroll 属于页面级组件,它的用法在 5.3 节单讲。)

4.1 Box —— 透明收纳盒

Box(意思是"盒子")是一个透明收纳盒:把一组组件装进去,盒里每个组件的 parent(第 1 章学的"外面那层")指的就是这个盒子本身。想挪动这一整组?只挪盒子,盒里全部跟着动,不用改里面的任何一行坐标。盒子还可以顺便化个妆:填色、圆角、描边、毛玻璃。

Simple 主题图纸里就有真的 Box,第 1069~1091 行原样如下(缩进略作收拾)。它是播放页那个能左右翻的"相册"的第一页,装着封面、频谱条、歌名、歌手和两个小按钮:

<Box>
    <Image id="albumCover" zOrder="2" defaultImage="eAlbumCover" borderColor="#ffffff20" borderWidth="0.5"
           cornerRadius="16" shadowColor="#000000" shadowOffset="{8,8}" shadowOpacity="0.2" shadowRadius="12"
           subscribe="image:${media.artwork}">
        <Layout top="parent.top+48" left="parent.left+32" right="parent.right-32" height="#albumCover.width@999"/>
    </Image>
    <SpectrumView id="spectrumView" zOrder="3" bins="80" gaps="1" gradientStartColor="#ffffff" gradientEndColor="#a0a0a0">
        <Layout left="parent.left+32" top="#albumCover.bottom" right="parent.right-32" height="120"/>
    </SpectrumView>
    <Text id="textTitle" zOrder="4" marquee="true" fontRef="#STHeitiSC-Medium-18" textColor="#e0e0e0" textAlignment="left" subscribe="text:${media.title}">
        <Layout left="parent.left+32" top="#spectrumView.bottom-16" right="#btnFavorite.left-32"/>
    </Text>
    <Text id="textArtist" zOrder="4" marquee="true" fontRef="#STHeitiSC-Medium-18" textColor="#e0e0e0" textAlignment="left" subscribe="text:${media.artist}">
        <Layout left="#textTitle.left" top="#textTitle.bottom" right="#textTitle.right"/>
    </Text>
    <IconButton id="btnQueue" zOrder="4" imageNormal="eQueue" onClick="media-queue-open">
        <Layout centerY="#textTitle.centerY" right="#albumCover.right" width="24" height="24"/>
    </IconButton>
    <ToggleButton id="btnFavorite" zOrder="4" imageSelected="eFavMark" imageUnselected="eFavUnmark"
        onClick="media-favorite-toggle" subscribe="select:${media.favorited}">
        <Layout centerY="#btnQueue.centerY" right="#btnQueue.left-32" width="24" height="24"/>
    </ToggleButton>
</Box>

逐处认一下重点:

Box 支持的属性一共 8 个(... 表示可写可不写):
属性 怎么写 大白话作用
id id="card" 贴名牌;写了别的组件才能挂它(如 #card.bottom)
zOrder zOrder="3" 叠放次序,1.3 学过
fillColor fillColor="#22000000" 盒底填的颜色。# 后 8 位:前 6 位颜色、后 2 位透明度(00 全透明、FF 一点不透)
cornerRadius cornerRadius="16" 圆角,数字越大角越圆
clip clip="true"(或 1) 超出盒子边的内容剪掉,不让它探出盒外
borderColor borderColor="#33ffffff" 描边(边框)的颜色;必须和下面那个一起写才生效
borderWidth borderWidth="0.5" 描边的粗细
blur blur="dark" 或 "light" 盒底垫一层毛玻璃(半透明磨砂质感,分深、浅两种)
要说明的是:现成主题里仅有的这些 Box 全是不带任何外观的透明盒(它们都是相册的页面)。带底色、圆角的 Box 暂无现成示例,下面这个是最小示意——按上表真实存在的属性拼的,可以照格式写,但用前务必先在主题副本上验证(2.1 教过怎么复制):
<Box id="card" fillColor="#22000000" cornerRadius="12" clip="true">
    <Layout left="parent.left+16" right="parent.right-16" top="parent.top+60" height="92"/>
    <!-- 盒里照常放组件,它们的 parent 就是这个盒 -->
</Box>

(一张左右各收 16、离顶 60、高 92 的半透明黑圆角卡片。)

小白坑

一旦组件被装进盒子,它 <Layout> 里的 parent 就从"屏幕"改口叫"盒子"了(想让盒里某样东西仍然贴着屏幕、不受盒子内缩影响,就改用 screen,见 1.2)。新手最常见的翻车:把原本按屏幕摆好的一组组件原样套进一个 Box 里,里面一个坐标都没动,位置却全变了——因为参照物换成了盒子的边。所以先定盒子的大小位置,再按"盒就是世界"写盒里的东西。另外,盒子自己尽量把大小说清楚(横着给 left+right 或 width,竖着给 top+bottom 或 height),完全靠内容撑开的盒子容易和你想的不一样。

4.2 Stack —— 自动排队的抽屉

Stack(意思是"一摞")就是第 1 章说的自动排队的抽屉:组件丢进去,抽屉按你定的方向(横排或竖排)照书写的先后顺序把它们排成一队、自动留缝——队里每个组件不用写任何坐标。Black、White、Classic、CoverFlow、Vinyl、Simple、暗夜星空这些主题里到处都是它。

真实样本——Simple 图纸第 1145~1156 行,播放界面最底下那一排 6 个按钮(循环模式、上一首、播放、暂停、下一首、随机):

<Stack id="stackControls" zOrder="2" direction="horizontal" distribute="equalSpacing" align="centerY">
    <Layout left="parent.left+32" right="parent.right-32" bottom="parent.bottom"/>
    <PlayModeButton imageNormal="eNormal" imageRepeatAll="eRepeatAll"
                    imageRepeatOne="eRepeat1" imageShuffle="eShuffle"
                    onClick="playback-switch-mode" subscribe="value:${playback.mode}"/>
    <IconButton imageNormal="eBackward" onClick="playback-backward"/>
    <IconButton imageNormal="ePlay" onClick="playback-start" subscribe="hidden:${playback.state.playing}"/>
    <IconButton imageNormal="ePause" onClick="playback-pause" subscribe="visible:${playback.state.playing}"/>
    <IconButton imageNormal="eForward" onClick="playback-forward"/>
    <ToggleButton imageSelected="eShuffleOn" imageUnselected="eShuffleOff"
        onClick="playback-switch-shuffle" subscribe="select:${playback.shuffle}"/>
</Stack>

看门道:只有抽屉自己写了 <Layout>(贴屏幕底边、左右各收 32),队里 6 个按钮一个坐标都没有。direction="horizontal" 是横排;distribute="equalSpacing" 把它们均匀铺满一整行;align="centerY" 让高矮不一的按钮在竖直方向对齐居中。想给这排按钮换个顺序?把两行 XML 挪个位置就行,不用重算任何坐标。

抽屉可用的属性:

属性 怎么写 大白话作用
direction direction="horizontal" 排队方向:horizontal 横排 / vertical 竖排(不写默认竖排)
distribute distribute="equalSpacing" 一整队怎么分空间,5 选 1,见下表
align align="centerY"(或 center) 交叉方向怎么对齐,见下表
spacing spacing="12" 相邻两个之间的缝有多宽(数字)
padding padding="8,12,8,12" 抽屉内侧四周再留一圈空:逗号隔开四个数,依次是左,上,右,下
distribute(分布)的 5 个值:
值 大白话
fill(默认,不写就是它) 从队头一个个往队尾码,每个保持自己的大小,缝按 spacing,富余空间全堆在队尾——"从抽屉一头开始摆东西"
fillEqually 整块地方平均分给每一个,大家一样大——"切格子"
fillProportionally 按各自身上内容的大小比例分,内容大的多占点地方
equalSpacing 头一个贴最左(或最上)、末一个贴最右(或最下),中间的缝全一样宽——工具栏"铺满一整行"首选
equalCentering 也首尾贴边,但保证每个的"中心点"隔得一样开;队里东西宽窄不一时看着更整齐
后四种要吃掉"富余空间"才显出效果,所以抽屉自己必须先占满一整块地(横排把 left+right 都写全,竖排把 top+bottom 都写全),否则缝会塌回去。

align(对齐)对照表:

align 竖排时(管左右) 横排时(管上下)
start(默认) 靠左 靠上
center(写 centerX/centerY 也认) 水平居中 垂直居中(最常用)
end 靠右 靠下

Stack 铁律(重点,背下来)

抽屉里的组件,关于 <Layout> 只有两种合法写法:

  1. 干脆不写 <Layout>——推荐,也是上面真实样本的做法:按钮多大由图片决定、文字多长由内容决定;
  2. 写,但只准写 width 和/或 height,值只能是数字(后面可跟 @数字 表示硬度,不跟也行)或 auto。例如 <Layout width="44" height="44"/>。
除此之外,在队里组件的 <Layout> 里写 left right top bottom centerX centerY 这些定位,或者写 #谁谁谁 这种引用——整屏直接加载失败、变成空白。记住那句话:排队归抽屉管,坐标轮不到你写。同理,抽屉里不能再装 Box、Stack 或模板(装了同样判违规、整屏挂掉)——要分组,在抽屉外面分。队里的组件倒是照常可以贴名牌 id,屏上别的地方能用 #它的id 挂它。

竖排抽屉暂无现成示例(现成主题里用到的 Stack 全是横排按钮行),下面是据真实属性拼的最小示意——一列文字加按钮排下来:

<Stack direction="vertical" spacing="6" align="center">
    <Layout left="parent.left+20" top="parent.top+40" width="200"/>
    <Text textValue="标题"/>
    <Text textValue="副标题"/>
    <IconButton imageNormal="ePlay" onClick="playback-start"/>
</Stack>

(一条宽 200、起点离顶 40 的竖抽屉;里面三样从上到下按书写顺序排,每个之间留 6 的缝,都水平居中。)

小白坑

用了 equalSpacing 之类的分布,却发现按钮全挤成一坨、缝没了?十有八九是抽屉自己没占地盘——横排只写了 bottom 没写 left/right,抽屉收到刚够装下按钮的宽度,没有富余空间可均分。给抽屉自己的 <Layout> 补上 left + right(横排)或 top + bottom(竖排)就好。

4.3 Include —— 复印机(模板)

改主题改到一定规模你会发现:好几屏都有一模一样的一条导航栏。一行行复印,改一处就得跑五个地方改五遍。引擎为此提供了"复印机":图纸开头用 <Define>(意思是"定义")画一段样板,之后哪一屏要用,就 <Include ref="样板名">(Include,意思是"纳入")复印一份出来。样板改一处,所有复印件跟着变;样板上还能留"填空" {{...}},每份复印时填不同的值。

先说现状:实测所有现成主题里 Include / Define 的使用次数是 0——引擎支持、也有专业文档(docs/theme-nesting-usage.md),但没有任何现成活例子。所以这一小节只给"据语法拼的最小示意",不是成品抄了就跑。新手先用 4.1 / 4.2 就够;决定上复印机时,务必先在主题副本上试(2.1 教过复制),每一屏都检查过再用到正式图纸上。

示意(结构、属性名都是引擎真实认的,内容是为讲解拼的):

<!-- ① 图纸开头、和 <Page> 平级的位置,先画样板 -->
<Defines>
    <Define id="navbar">
        <Text id="title" textValue="{{title}}">
            <Layout centerX="parent.centerX" top="parent.top+12"/>
        </Text>
    </Define>
</Defines>

<!-- ② 哪一屏要,就复印一次,顺便填空 -->
<Page id="Albums">
    <Include id="nav" ref="navbar" title="专辑">
        <Layout left="parent.left" top="parent.top" right="parent.right" height="56"/>
    </Include>
</Page>

逐处认一下:

规矩(破坏任何一条,和 Box/Stack 的错一样:整屏不加载):

4.4 三样配合起来用

一句话分工:收纳盒(Box)管"整块一起动"——一组永远一起动的东西装进盒,以后只挪盒;抽屉(Stack)管"同类的排一队"——一行按钮丢进去,队内一个坐标都不用写;复印机(Include)管"同一段印 N 份"——样板画一次,到处复印。它们还常常叠着用:外面 Box 定位置,盒里 Stack 排一排按钮;而一份 Include 样板里,本身就可以装着 Box 或 Stack。目的只有一个——少写重复、改一处全变。

第 5 章 组件卡片手册

本章是查典用的:53 张卡片按用途分成 6 节,每张卡都给你"长这样 / 照抄能跑 / 属性表 / 小白坑"四段。不用通读,用到哪个查哪个。

5.1 基础显示

本节 5 张卡:文字、文本框、图片、小图标、矩形板——都是屏幕上"纯显示"的零件,画什么看到什么。第 3 章教的水管(subscribe)它们里有三样能接(文字、图片、小图标),矩形板和文本框不接。每卡的"照抄能跑"都记得带上 <Layout/> 小跟班(第 1 章的老规矩:不写摆放,整屏不显示)。

文字 <Text> —— 在屏幕上显示一块字

长这样: 一行(或多行)字。播放页的歌名、歌手名,列表里的条目文字,全是它。真实例子见 Simple 图纸第 1078、1081 行——上下叠着的歌名和歌手两行字(第 4 章装盒那段里就出现过)。

照抄能跑: 屏幕正中一句写死的字(属性都是引擎认的;接了水管"跟着换歌自动变"的写法,第 3 章 3.1 已经给过):

<Text id="hello" fontSize="20" textColor="#ffffff" textValue="你好">
    <Layout centerX="parent.centerX" centerY="parent.centerY" width="auto" height="auto"/>
</Text>

常用属性:

属性 怎么写 大白话作用
textValue textValue="你好" 要显示的文字内容。真名是 textValue,不是 text(见下面小白坑)。写 textValue="context:title"(图纸第 61 行)表示"用这一屏自己的标题",见到照抄即可
fontSize fontSize="20" 字多大,数字越大字越大
textColor textColor="#f0f0f0" 字的颜色,# 后六位(第 4 章 Box 同款写法)
fontRef fontRef="#STHeitiSC-Medium-18" 报第 2 章 2.2 的"字帖"名,字体字号一步到位(图纸第 38、1078 行都这么用)
subscribe subscribe="text:${media.title}" 接水管,文字跟着当前播放的歌自己变(第 3 章)
textAlignment textAlignment="center" 字往哪边靠:left / center / right
marquee marquee="true" 字比给它的地方长时,像跑马灯一样横着滚(图纸第 60~62、1078 行)。写了它,下面的 multiline 就失效,二选一
multiline multiline="true" 允许换行、排成好几行(图纸第 1402 行:WiFi 传文件页的提示语)
更多属性: id zOrder fontFamily(不走字帖、直接写字体名) borderWidth borderColor cornerRadius contentInsets(文字四周的内边距,逗号隔开四个数:上,左,下,右,真实写法如 contentInsets="{0,4,0,12}") hiddenDelay → 详见附录 A

小白坑: 新手最常犯的错是把文字内容写成 text="你好"——引擎不认识这个写法,结果一个字都不显示、也不报错("怎么读这本书"的示例表和 1.4 里那个 text= 是讲解用的简写,从本节起当它不存在)。记住:写的是 textValue。

文本框 <TextView> —— 可多行、能点进去打字的文本框

⚠️ 先泼冷水:引擎支持这个标签,但实测所有现成主题里 0 次引用——Black、White、Classic、CoverFlow、Vinyl、Simple、暗夜星空没有任何一个用过 <TextView>,没有活例子。照抄后务必先在主题副本上验证效果(2.1 教过怎么复制),确认没问题再写进正式图纸。

长这样: 和 Text 一样显示字,但更"软"——可以多行,点进去还能编辑,像聊天软件里的输入框。

照抄能跑: 据引擎真实属性拼的最小示意——没有现成参照,用前先在副本验证:

<TextView id="note" fontSize="16" textColor="#ffffff" textValue="随便写点什么" contentInsets="{8,8,8,8}">
    <Layout left="parent.left+20" right="parent.right-20" top="parent.top+60" height="80"/>
</TextView>

常用属性:

属性 怎么写 大白话作用
textValue textValue="随便写点什么" 框里默认的文字
fontSize fontSize="16" 字多大
textColor textColor="#ffffff" 字的颜色
contentInsets contentInsets="{8,8,8,8}" 字和框边的内边距,逗号隔开四个数:上,左,下,右
更多属性: id zOrder fontFamily borderWidth borderColor cornerRadius → 详见附录 A

小白坑: 最大的坑是拿它当 Text 的平替。屏幕上绝大多数文字都该用 <Text>(全部主题验证过的老手);<TextView> 没有活例子,只在确实需要"能打字编辑"的场景才考虑,而且先副本验证。

图片 <Image> —— 一张图,播放页里通常是封面

长这样: 一块矩形图片。播放页中间的大封面、迷你播放条左边的小封面,都是这个标签。下面引图纸第 34~37 行(迷你条那张小封面,属性最精简):占位图 + 描边 + 圆角 + 接封面水管。

┌────────────────────┐
│                    │
│       封面图       │
│                    │
└────────────────────┘
   圆角 + 描边 + 接封面水管的一张矩形图

照抄能跑: 仿 Simple 第 34~37 行的简化版:

<Image id="albumCover" defaultImage="eAlbumCover" cornerRadius="8" subscribe="image:${media.artwork}">
    <Layout centerX="parent.centerX" top="parent.top+40" width="160" height="160"/>
</Image>

(defaultImage="eAlbumCover" 里是 Simple 主题文件夹里的图片名——在 Simple 里照抄没问题,挪去别的主题要换成你主题文件夹里有的图,图片命名规矩第 6 章细讲。)

常用属性:

属性 怎么写 大白话作用
subscribe subscribe="image:${media.artwork}" 接"当前歌封面"水管,换歌封面跟着换(第 3 章;图纸第 35 行)
defaultImage defaultImage="eAlbumCover" 没图(或水管没通)时显示的占位图;名字不带 .png
cornerRadius cornerRadius="8" 图片的圆角
borderColor borderColor="#ffffff20" 描边颜色;必须和 borderWidth 一起写才生效(第 4 章 Box 描边同规矩)
borderWidth borderWidth="0.5" 描边粗细
onClick onClick="..." 让图片能点,引号里填一条命令词(第 3 章"喊一嗓子")。引擎认这个属性,但现成主题里没人在 <Image> 上用过,新手先别碰;要用就沿用现成按钮的命令词
更多属性: id zOrder shadowColor shadowOffset shadowOpacity shadowRadius(图片四周投影四件套,完整真实写法见图纸第 1605 行的 appIcon) → 详见附录 A

小白坑: <Image> 没有 image= 这个属性(那是下面 Icon 的)——想写死一张图,属性名是 defaultImage;想让图跟着歌变,用 subscribe="image:..."。直接写 image="xxx" 会被引擎默默忽略,那块地方一片空白。

小图标 <Icon> —— 单个小符号图片

长这样: 一张不带任何装扮(没圆角、没描边、没投影)的小符号图:列表行尾的">"箭头、WiFi 开/关小标志。Simple 图纸第 1276 行(排队小图标)、第 1396~1400 行(WiFi 开/关)、第 1639 行(">"箭头)都是它。

照抄能跑: 仿图纸第 1639 行——设置类列表行尾常见的那枚">"箭头:

<Icon id="arrow" image="eIconRight">
    <Layout centerY="parent.centerY" right="parent.right-16" width="20" height="20"/>
</Icon>

常用属性(这个标签一共就 4 个属性,全列在这):

属性 怎么写 大白话作用
id id="arrow" 贴名牌,别的组件才能挂它(第 1 章)
zOrder zOrder="3" 叠放次序,1.3 学过(图纸第 1276 行就写了)
image image="eIconRight" 用哪张图(主题文件夹里的图片名,不带 .png)
subscribe subscribe="visible:${transform.wifi.opened}" 接水管决定露不露脸:图纸第 1396~1400 行用"WiFi 开没开"牵着一对图标——开机的图标和关机的图标各接一根,对上的自己现身(第 3 章)
更多属性: 没有了——<Icon> 一共就这 4 个属性,已全部列在上表。

小白坑: image= 里写了你主题文件夹里不存在的图片名,这个图标就整个不显示、也不报错。抄现成主题的行时,先确认那张图(如 eIconRight.png)在你自己的主题文件夹里也有——没有就把 png 拷进去,或换成你有的图标名。

矩形板 <Rectange> —— 铺在内容底下的背景板

注意:引擎把这个标签拼成 Rectange(比正规英文 Rectangle 少一个字母 l)。图纸里见到别奇怪,更别"顺手纠正"——写成 Rectangle 引擎就不认识了。

长这样: 一块能填色、铺图、带圆角描边、还能来点毛玻璃的矩形板,最常垫在文字图片底下当"底板"。Simple 图纸第 57~58 行:每个页面顶上的导航栏就是一块深色毛玻璃板;第 31~33 行:迷你播放页的底,填色 + 描边 + 圆角 + 裁剪 + 毛玻璃全用上。

┌──────────────────┐
│  一块垫底的板子  │
└──────────────────┘
   文字、图片摆在它上面

照抄能跑: 一块半透明黑、圆角 12 的卡片底板(据下表真实属性拼):

<Rectange id="card" fillColor="#22000000" cornerRadius="12">
    <Layout left="parent.left+16" right="parent.right-16" top="parent.top+60" height="92"/>
</Rectange>

常用属性:

属性 怎么写 大白话作用
fillColor fillColor="#22000000" 板子填什么色。# 后 8 位:前 6 位颜色、后 2 位透明度(第 4 章 Box 同款);不写就是全透明
cornerRadius cornerRadius="12" 圆角,数字越大角越圆
borderColor borderColor="#ffffff50" 描边颜色;和 borderWidth 一起写才生效
borderWidth borderWidth="0.5" 描边粗细
blurType blurType="dark" 板底下垫毛玻璃(半透明磨砂),dark / light 两种(图纸第 31、57 行的真实用法都是 dark)
fillImage fillImage="某图名" 不用纯色、改铺一张图当板面(名字不带 .png)
clipToBounds clipToBounds="true" 探出板子边的内容剪掉(图纸第 31 行真实用法)
更多属性: id zOrder imageStretch(铺图时怎么拉伸) shadowColor shadowOffset shadowOpacity shadowRadius(投影四件套,图纸第 57 行 navbar 的完整真实写法) → 详见附录 A

小白坑: 两个都易踩。其一,标签名是 Rectange 不是 Rectangle——引擎拼写如此,写"对"了反而整块被忽略。其二,忘想叠放次序——板子十有八九要垫在最底下,给它写 zOrder="0"(或不写当 0),要盖在它上面的文字图片写在后面或给大数字(1.3 的规则:数字大的在上)。反过来,一块不透明的板子写在后头,会把内容整个盖成一块纯色板。

5.2 按钮与交互

本节 11 张卡:从最普通的带字按钮,到图片按钮、开关、滑条、搜索框——都是"能跟手指打交道"的零件。第 3 章教的"喊一嗓子"(onClick)大部分卡都有,但有两个例外:拨动开关用 onStateOn/onStateOff、弹出菜单按钮用 command(卡片里会专门讲);而最后的搜索框是输入框,根本没有点击命令。"接水管"(subscribe)它们几乎全都支持。老规矩:每卡的"照抄能跑"都带着 <Layout/> 小跟班。

按钮 <Button> —— 一块带底色、写着字的方块

长这样: 一个圆角矩形色块,中间写着字,按下去颜色会变。Simple 图纸第 1484、1494 行(均衡器页的"重置 / 保存"两个按钮)就是它。

┌────────────────┐
│      保存      │
└────────────────┘
   底色(tintColor) + 圆角 + 居中的字

照抄能跑: 仿 Simple 第 1494 行格式、命令换成第 3 章的(都是真实属性):

<Button id="btnClear" cornerRadius="8" text="清空队列"
        fontRef="#STHeitiSC-Light-16" textColor="#ffffff"
        tintColor="#333333" tappedTintColor="#262626"
        onClick="media-queue-clear">
    <Layout centerX="parent.centerX" centerY="parent.centerY" width="120" height="36"/>
</Button>

常用属性:

属性 怎么写 大白话作用
text text="保存" 按钮上写的字。这个标签的文字属性就叫 text——和第 5.1 的 <Text> 用的 textValue 不一样,别混(图纸第 1486 行真实写法)
textColor textColor="#ffffff" 字的颜色
fontSize / fontRef fontSize="16" 或 fontRef="#STHeitiSC-Light-16" 字多大;或直接报第 2 章"字帖"名一步到位
tintColor tintColor="#333333" 按钮底色(正常时)
tappedTintColor tappedTintColor="#26b630" 手指按下去那一刻的底色(图纸第 1490 行真实用法)
cornerRadius cornerRadius="8" 圆角,数字越大角越圆
onClick onClick="media-queue-clear" 点它喊的命令(第 3 章)
subscribe subscribe="enable:${transform.wifi.opened}" 接水管;接 enable: 时数据为假按钮变灰点不动(第 3 章)
更多属性: id zOrder fontFamily(不走字帖直接写字体名) contentInsets(文字离按钮边的内边距) translucent(半透明) blurStyle(毛玻璃样式) → 详见附录 A

小白坑: 按钮的字是 text、纯文字组件的字是 textValue——两个标签相反,写串了不报错、只是字不显示。另外它的宽高不会跟着字自动伸缩(没有 auto 那套照顾),<Layout> 里把 width height 写够,字太长会被裁掉。

图片按钮 <IconButton> —— 用一张图当按钮

长这样: 没有底色方块,就是一张图(播放键、返回键、搜索键全是它),按下可以换一张图。Simple 图纸里它出现几十次,第 42 行(下一首)、45~46 行(播放)、70 行(搜索)都是。

照抄能跑: Simple 第 45~46 行原样(播放按钮,接了水管"正在播放时把自己藏起来"):

<IconButton id="buttonPlay" imageNormal="ePlayS" imageHighlighted="" zOrder="2" onClick="playback-start"
    subscribe="hidden:${playback.state.playing}">
    <Layout centerX="parent.centerX" centerY="parent.centerY" width="auto" height="auto"/>
</IconButton>

常用属性:

属性 怎么写 大白话作用
imageNormal imageNormal="ePlayS" 平时显示哪张图(主题文件夹里的图名,不带 .png)
imageHighlighted imageHighlighted="ePlayP" 手指按下去那一刻换的图;写 imageHighlighted=""(空引号,第 45 行真实写法)表示按下不换图
onClick onClick="playback-start" 点它喊的命令(第 3 章)
subscribe subscribe="hidden:${playback.state.playing}" 接水管换图/管显隐——播放、暂停两个键互相"你出现我就藏起来"就是这么做的(第 2、3 章)
更多属性: 没有了——<IconButton> 一共就这 6 个:id zOrder 也已在上面出现(id 贴名牌、zOrder 叠放次序,第 1 章学过),已全部列完。

小白坑: 它没有 text 属性,图上想加字是加不上去的——要"图 + 字"一起,用下面的 TextIconButton。还有:imageNormal 写了你主题文件夹里没有的图名,按钮整个不显示也不报错(和 5.1 的 Icon 同坑,第 6 章讲怎么备图)。

图文按钮 <TextIconButton> —— 图旁配着字的按钮

长这样: 一张小图标加一段文字并排,比如"▶ 播放全部"。Simple 图纸第 74~86 行(播放页顶上的"播放全部"按钮)是活例子。

照抄能跑: 仿 Simple 第 74~84 行格式,命令沿用第 3 章小坑里提过的真实词 schema-medias-play-all(图纸第 84 行原样):

<TextIconButton id="btnPlayAll" style="0" spacing="6"
            iconNormal="eIconPlayAll" text="播放全部"
            textColor="#e0e0e0" highlightedTextColor="#888888"
            fontRef="#STHeitiSC-Medium-15" contentInsets="{0,4,0,12}"
            onClick="schema-medias-play-all">
    <Layout left="parent.left+16" bottom="#navbar.bottom-10"/>
</TextIconButton>

常用属性:

属性 怎么写 大白话作用
iconNormal iconNormal="eIconPlayAll" 平时显示的图标(图名不带 .png)
iconHighlighted iconHighlighted="eIconPlayAllP" 按下时换的图标
text text="播放全部" 图标旁边写的字(和 Button 一样叫 text,不是 textValue)
textColor textColor="#e0e0e0" 字的颜色
spacing spacing="6" 图标和文字之间留多宽的缝(图纸第 76 行有真实写法 spacing="0")
onClick onClick="schema-medias-play-all" 点它喊的命令(第 3 章)
style style="0" 排版样式编号;现成主题全用 0,新手照抄即可
subscribe subscribe="select:${media.favorited}" 接水管管显隐/状态(第 3 章)
更多属性: id zOrder fontFamily fontSize fontRef highlightedTextColor(按下时字的颜色,图纸第 80 行真实用法) backgroundColor borderColor borderWidth cornerRadius contentInsets(字的内边距,真实写法如 contentInsets="{0,4,0,12}") → 详见附录 A

小白坑: 尺寸全靠图和字自己撑,<Layout> 里别用 width="auto" 又指望它精确——先给 spacing 和 contentInsets 留够余地。改字时记住这里也是 text,别顺手写成 textValue。

浮动按钮 <FloatingButton> —— 飘在内容上层、会吸边的圆钮

长这样: 一个圆形的悬浮按钮,盖在页面内容上面,常常靠屏幕边站着;松手后它会"吸"到离它最近的边上(像 iOS 的悬浮球)。Simple 图纸第 148~151 行(专辑列表右下那颗"定位当前歌曲"的圆钮)是活例子。

照抄能跑: Simple 第 148~151 行原样(media-locate-current 是图纸里的真实命令,照抄没问题):

<FloatingButton id="fbLocate" zOrder="99" imageNormal="eLocate" onClick="media-locate-current"
    cornerRadius="16" borderColor="#ffffff50" borderWidth="0.5" blur="dark" snapMargin="24"
    shadowColor="#ffffff" shadowOffset="{0,2}" shadowOpacity="0.15" shadowRadius="6">
    <Layout right="parent.right-24" bottom="parent.bottom-160" width="32" height="32"/>
</FloatingButton>

常用属性:

属性 怎么写 大白话作用
imageNormal imageNormal="eLocate" 圆钮中间的图
imageHighlighted imageHighlighted="eLocateP" 按下时换的图
onClick onClick="media-locate-current" 点它喊的命令(第 3 章)
fillColor fillColor="#22000000" 圆底的填充色(# 后 8 位那套写法,第 4 章 Box 同款)
cornerRadius cornerRadius="16" 圆角;写边长的一半(32 的钮写 16)就是正圆
snapMargin snapMargin="24" 吸边时离屏幕边留 24 的缝(图纸第 149 行真实用法)
blur blur="dark" 圆底垫毛玻璃(dark/light,和第 4 章 Box 的 blur 同款)
更多属性: id zOrder(图纸给它写 99,保证盖一切) borderColor borderWidth(描边,一起写才生效) shadowColor shadowOffset shadowOpacity shadowRadius(投影四件套,图纸第 150 行完整真实写法) subscribe → 详见附录 A

小白坑: 它靠 <Layout> 定位在屏幕角落,又常盖在所有东西上面——zOrder 记得给个大数(图纸第 148 行就用 99);给 0 的话会被页面内容埋住"凭空消失"。

菜单按钮 <MenuButton> —— 点一下弹出一张菜单

长这样: 外表是个图片按钮,点下去从底部弹出一张菜单让你挑一项。Simple 图纸第 87~93 行(排序按钮)、第 1115 行(歌词页的"歌词/封面"按钮)都是它。

照抄能跑: 仿 Simple 第 87~93 行(command title icon 三个写在身上的子标签 <Menu>/<Action> 里,图纸原样):

<MenuButton id="btnSort" imageNormal="eSortBy" zOrder="3">
    <Layout centerY="#btnPlayAll.centerY" centerX="#searchBtn.centerX"/>
    <Menu title="按以下方式排序">
        <Action command="sort-by-filename" title="文件名" icon="eSortByFileName"/>
        <Action command="sort-by-title" title="标题" icon="eSortByTitle"/>
    </Menu>
</MenuButton>

常用属性:

属性 怎么写 大白话作用
imageNormal imageNormal="eSortBy" 按钮平时显示的图
imageHighlighted imageHighlighted="eSortByP" 按下时换的图
command <Action command="sort-by-filename"/> 注意:它没有 onClick。点按钮弹菜单、挑中哪一项就喊哪一条命令——命令词写在菜单项 <Action> 的 command 里(第 3 章"喊一嗓子"的规矩不变,只是属性换了名字;上面示意的 sort-by-filename 等就是图纸第 90~92 行的真实词)
title title="文件名" 菜单和每一行的显示文字(同样写在 <Menu>/<Action> 上)
icon icon="eSortByFileName" 每一行左边的小图标(写在 <Action> 上)
subscribe subscribe="visible:${playback.state.playing}" 接水管管显隐(第 3 章)
更多属性: id zOrder → 详见附录 A

小白坑: 想当然给它写 onClick= ——引擎不认,点了没反应。记住这一家只有 command。子项 <Action> 里的命令词也必须是服务台认识的(抄现成主题里的,别编)。

多选按钮 <SelectButton> —— 全选/半选/没选三态的勾选钮

长这样: 列表批量操作时行首/栏头那个圆形勾选标记:没选空心、选中实心、"选了一部分"是半实心——三种状态三张图。Simple 图纸第 442~445 行(选择歌曲页顶上的"全选"钮)是活例子。

照抄能跑: 仿 Simple 第 442~443 行:

<SelectButton id="btnSelect" zOrder="3"
    imageNoSelect="eSelectNo" imagePartialSelect="eSelectPartial" imageAllSelect="eSelectAll"
    onClick="media-select-toggle" subscribe="value:${media.picker.select}">
    <Layout centerY="#library.centerY" right="#btnCheckMedias.left-16" width="auto" height="auto"/>
</SelectButton>

常用属性(这个标签一共就 8 个,全列在这):

属性 怎么写 大白话作用
imageNoSelect imageNoSelect="eSelectNo" "没选"状态显示的图
imagePartialSelect imagePartialSelect="eSelectPartial" "选了一部分"状态的图
imageAllSelect imageAllSelect="eSelectAll" "全选"状态的图(图名不带 .png)
onClick onClick="media-select-toggle" 点它喊的命令(图纸第 443 行真实用法)
subscribe subscribe="value:${media.picker.select}" 接"选中状态"水管,三张图自动换哪张(第 3 章的 media.picker.select)
id / zOrder id="btnSelect" zOrder="3" 名牌 / 叠放次序(第 1 章)
更多属性: 没有了——上面 8 个就是它的全部,已全部列完。

小白坑: 它是批量选择页专用的零件,三张图少写一张,对应状态就会显示空白;图名也必须是你主题文件夹里真有的图。一般新手只换图、不改逻辑。

拨动开关 <SwitchButton> —— 像 iOS 设置里那种开关

长这样: 一个胶囊形轨道、上面一颗圆滑块,拨过去轨道变色——就是系统"设置"里那种开关。Simple 图纸第 1391~1394 行(WiFi 传文件页的开关)是活例子。

照抄能跑: 仿 Simple 第 1391~1394 行的格式,命令换成第 3 章真实的均衡器开关 equalizer-switch-toggle:

<SwitchButton id="switchEQ" onColor="#cccccc" offColor="#444444" thumbColor="#eeeeee"
    onStateOn="equalizer-switch-toggle" onStateOff="equalizer-switch-toggle"
    subscribe="enable:${audioFx.equalizer.enabled}">
    <Layout right="parent.right-24" centerY="parent.centerY"/>
</SwitchButton>

常用属性:

属性 怎么写 大白话作用
onColor onColor="#cccccc" 开状态时轨道的颜色
offColor offColor="#444444" 关状态时轨道的颜色
thumbColor thumbColor="#eeeeee" 那颗圆滑块的颜色
onStateOn onStateOn="equalizer-switch-toggle" 拨到"开"时喊的命令
onStateOff onStateOff="equalizer-switch-toggle" 拨到"关"时喊的命令
subscribe subscribe="enable:${transform.wifi.opened}" 接水管;第 1393 行真实用法是"WiFi 服务状态牵着的开关联动"(第 3 章)
更多属性: id zOrder → 详见附录 A

小白坑: 全节唯一没有 onClick 的按钮——两个方向的命令分开写成 onStateOn / onStateOff,给它写 onClick 不生效。开关本身的状态一般由页面用水管牵(如第 3 章的 audioFx.equalizer.enabled),新手主要改三个颜色。

切换按钮 <ToggleButton> —— 亮/灭两态的按钮

长这样: 一张图、两个状态:亮了是一张图、灭了是另一张(收藏的小星星、随机的骰子都是它)。Simple 图纸第 1087~1088 行(收藏星)、第 1154~1155 行(随机播放)是活例子。

照抄能跑: Simple 第 1087~1088 行原样:

<ToggleButton id="btnFavorite" zOrder="4" imageSelected="eFavMark" imageUnselected="eFavUnmark"
    onClick="media-favorite-toggle" subscribe="select:${media.favorited}">
    <Layout centerY="#btnQueue.centerY" right="#btnQueue.left-32" width="24" height="24"/>
</ToggleButton>

常用属性(这个标签一共就 7 个,全列在这):

属性 怎么写 大白话作用
imageSelected imageSelected="eFavMark" "亮着"(选中)状态显示的图
imageUnselected imageUnselected="eFavUnmark" "灭着"状态显示的图(图名不带 .png)
onClick onClick="media-favorite-toggle" 点它喊的命令(第 3 章)
subscribe subscribe="select:${media.favorited}" 接"当前歌收藏没"的水管,亮灭自己跟着变(第 3 章)
id / zOrder id="btnFavorite" zOrder="4" 名牌 / 叠放次序(第 1 章)
更多属性: 没有了——上面 7 个就是全部(subscribe 已在表中),已全部列完。

小白坑: 水管接的是 select:(按钮亮灭),别接成 text:(它没有字)。两张图都得是你主题文件夹里存在的图,缺哪张对应状态就空白。

循环模式按钮 <PlayModeButton> —— 专用切换"顺序/循环"的三态按钮

长这样: 长得像图片按钮,但专门伺候播放模式:顺序播放、全部循环、单曲循环,各配一张图,点一下换一种、图标跟着换。Simple 图纸第 1147~1149 行(播放页那排控制键最左边那颗)是活例子。

照抄能跑: 仿 Simple 第 1147~1149 行(属性都用下表清单里的):

<PlayModeButton imageNormal="eNormal" imageRepeatAll="eRepeatAll" imageRepeatOne="eRepeat1"
    onClick="playback-switch-mode" subscribe="value:${playback.mode}">
    <Layout centerX="parent.centerX" centerY="parent.centerY"/>
</PlayModeButton>

常用属性(这个标签一共就 8 个,全列在这):

属性 怎么写 大白话作用
imageNormal imageNormal="eNormal" 顺序播放时显示的图
imageRepeatAll imageRepeatAll="eRepeatAll" 全部循环时的图
imageRepeatOne imageRepeatOne="eRepeat1" 单曲循环时的图(图名不带 .png)
onClick onClick="playback-switch-mode" 点它喊"切换循环模式"(第 3 章)
subscribe subscribe="value:${playback.mode}" 接"当前循环模式"水管,三张图自动换哪张(第 3 章)
id / zOrder id="btnMode" zOrder="2" 名牌 / 叠放次序(第 1 章)
更多属性: 没有了——上面 8 个就是全部,已全部列完。

小白坑: 三态图缺一张,对应模式下按钮显示空白。顺带说明:图纸里偶尔还能见到它身上多写一个 imageShuffle=(Simple 第 1148 行有),但经核对解析引擎并不读取这个属性——写了也无效果,附录 A 不收它,新手别指望用第四张图。

滑条 <Slider> —— 能拖的进度条 / 音量条

长这样: 一条轨道,上面一颗可拖动的圆点;已走过的段落和没走的常涂成两种颜色,两端还能各摆一个小图标。Simple 图纸第 1128~1134 行(播放页拖进度那条)是活例子。

最小图 ──────●───────── 最大图
     已走色   ↑圆点    未走色

照抄能跑: Simple 第 1128~1134 行原样(三个回调挂的命令都是图纸真实词,属"进度条拖动"三件套,照抄即可):

<Slider id="sliderProgress" zOrder="1" thumbColor="#00000000" minimumTrackColor="#e0e0e0" continuous="true"
        maximumTrackColor="#606060"
        onSlidingStart="playback-seek-sliding-start"
        onSlidingValueChanged="playback-seek-sliding-value-changed"
        onSlidingFinished="playback-seek-sliding-finished"
        subscribe="value:${media.progress}">
    <Layout centerX="parent.centerX" bottom="#stackControls.top-40" left="parent.left+32" right="parent.right-32"/>
</Slider>

常用属性:

属性 怎么写 大白话作用
minimumTrackColor minimumTrackColor="#e0e0e0" 已走过那段轨道的颜色
maximumTrackColor maximumTrackColor="#606060" 还没走那段的颜色
thumbColor thumbColor="#00000000" 拖动圆点的颜色(写全透明就是不画圆点)
thumbImage thumbImage="eThumb" 用一张图当圆点
minimumValueImage / maximumValueImage 各写一个图名 轨道左右两端的小图标(如音量条两端的喇叭)
vertical vertical="true" 竖着摆的滑条(默认横着)
subscribe subscribe="value:${media.progress}" 接水管,圆点位置跟着播放进度走(第 3 章)
continuous continuous="true" 拖动过程中就实时汇报(第 1128 行真实用法)
更多属性: id zOrder onSlidingStart(开始拖时喊的命令) onSlidingValueChanged(拖动中喊的) onSlidingFinished(松手时喊的)——注意引擎只有这三个回调名,常见的叫法 onValueChange 在这里不存在,写错不报错、只是拖了没反应 → 详见附录 A

小白坑: 轨道两端的小图标属性是 minimumValueImage / maximumValueImage(带 Value),颜色是 minimumTrackColor / maximumTrackColor(带 Track)——两组名字很像,写串了不报错,只是那个部位"没化妆"而已。

搜索框 <SearchBar> —— 页面顶上的输入搜索框

长这样: 一个圆角输入条,左边带放大镜小图标、里面灰字写着提示语("搜索"),点进去能打字。它是输入框不是按钮——没有 onClick,搜索逻辑由页面自己处理,新手一般只改样式。Simple 图纸第 169~180 行(媒体页顶部搜索框)是活例子。

照抄能跑: 仿 Simple 第 169~180 行(属性都在清单内;placeholder 的灰字提示是清单内属性、为讲解填的文案):

<SearchBar id="searchBar" zOrder="3"
    backgroundColor="#24242550"
    placeholder="搜索"
    placeholderTextColor="#e0e0e0"
    textColor="#ffffff"
    searchIcon="eSearchS1"
    searchIconOffset="4"
    borderColor="#ffffff50"
    borderWidth="0.5"
    cornerRadius="12">
    <Layout left="parent.left+16" right="parent.right-32" bottom="#navbar.bottom-10" height="36"/>
</SearchBar>

常用属性:

属性 怎么写 大白话作用
placeholder placeholder="搜索" 没打字时显示在框里的灰色提示
placeholderTextColor placeholderTextColor="#e0e0e0" 提示语的颜色
textColor textColor="#ffffff" 你打进去的字的颜色
searchIcon searchIcon="eSearchS1" 放大镜图标用哪张图(图名不带 .png)
searchIconOffset searchIconOffset="4" 放大镜往右挪 4(第 176 行真实用法)
backgroundColor backgroundColor="#24242550" 框的底颜色(# 后 8 位那套,第 4 章 Box 同款)
cornerRadius cornerRadius="12" 圆角
borderColor / borderWidth borderColor="#ffffff50" + borderWidth="0.5" 描边,一起写才生效
更多属性: id zOrder fontRef(报字帖名) fontName(直接写字体名;图纸第 173 行真实用法) fontSize → 详见附录 A

小白坑: 别拿它当按钮使——它不认 onClick/subscribe,写了也没用;打字搜内容、按回车出结果这套事,是那一屏的页面(<Page>)自动接管的,图纸里只管它的长相。

5.3 容器与布局

本节 5 张卡。前三张 Box、Stack、Include 第 4 章已完整讲过(收纳盒、抽屉、复印机的比喻和全例都在那),这里只压缩成速查卡——"这标签到底能写哪些属性"一张表看全,讲透的部分回指 4.1 / 4.2 / 4.3;后两张 Scroll、Toolbar 是新面孔,完整展开。一条通用纪律先说在前头:水管(subscribe)不接容器——写在 Box / Stack / Scroll 上会被引擎无视(还会记一行日志);工具条 Toolbar 是个例外,它接水管的写法跟别人不一样,见它的卡。老规矩:每卡"照抄能跑"都带 <Layout/> 小跟班。

收纳盒 <Box> —— 一组永远一起动的东西(速查)

长这样: 透明收纳盒——第 4 章 4.1 全讲过:装进去的组件互相以盒为 parent,挪盒整组跟着挪;顺便还能填色、圆角、描边、毛玻璃。活例子是 Simple 图纸第 1069~1091 行(播放页相册的一页,4.1 开头贴的就是它原文)。

照抄能跑: 与 4.1 的最小示意同一份(按下表真实属性拼;现成主题里 Box 全是相册里的透明页盒,带底色的先在副本验证):

<Box id="card" fillColor="#22000000" cornerRadius="12" clip="true">
    <Layout left="parent.left+16" right="parent.right-16" top="parent.top+60" height="92"/>
    <!-- 盒里照常放组件,它们的 parent 就是这个盒 -->
</Box>

常用属性(这个标签一共就 8 个属性,全列在这):

属性 怎么写 大白话作用
id id="card" 贴名牌,别的组件才能挂它(如 #card.bottom)
zOrder zOrder="3" 叠放次序(1.3)
fillColor fillColor="#22000000" 盒底填色;# 后 8 位,最后 2 位透明度;不写全透明
cornerRadius cornerRadius="12" 圆角
clip clip="true"(或 1) 探出盒边的内容剪掉
borderColor borderColor="#ffffff50" 描边颜色;和 borderWidth 一起写才生效
borderWidth borderWidth="0.5" 描边粗细
blur blur="dark" 或 "light" 盒底毛玻璃,深浅两种
更多属性: 没有了——<Box> 一共就这 8 个属性,已全部列在上表。

小白坑: 进盒之后 parent 改口指盒子、不再指屏幕,整组原样套盒坐标全变(4.1 的大坑)。速查再补一条新手容易手滑的:水管别接在盒上——subscribe 写在 <Box> 上会被无视,想让一整组东西一起显隐,水管接到组里每个组件自己身上。

抽屉 <Stack> —— 一队同类自动排队(速查)

长这样: 自动排队的抽屉——第 4 章 4.2 全讲过:丢进去的组件按书写顺序排成一队,队内一个坐标都不用写。活例子是 Simple 图纸第 1145~1156 行(播放界面底上一排 6 个按钮,4.2 贴的就是原文)。

照抄能跑: 从 4.2 真实样本砍到两个按钮(全是最小尺寸、照格式能跑):

<Stack id="row" direction="horizontal" distribute="equalSpacing" align="centerY">
    <Layout left="parent.left+32" right="parent.right-32" bottom="parent.bottom"/>
    <IconButton imageNormal="eBackward" onClick="playback-backward"/>
    <IconButton imageNormal="eForward" onClick="playback-forward"/>
</Stack>

常用属性(这个标签一共就 7 个属性,全列在这):

属性 怎么写 大白话作用
id id="row" 贴名牌
zOrder zOrder="2" 叠放次序
direction direction="horizontal" 横排 / vertical 竖排;不写默认竖排
distribute distribute="equalSpacing" 整队怎么分空间,5 选 1(每个值的大白话见 4.2 的表)
align align="centerY" 交叉方向对齐:start / center / end,写 centerX centerY 也认(对照表在 4.2)
spacing spacing="12" 相邻两个之间的缝宽
padding padding="8,12,8,12" 内侧四周再留一圈空,逗号四个数:左,上,右,下
更多属性: 没有了——<Stack> 一共就这 7 个属性,已全部列在上表。

小白坑: 铁律必须再背一遍(4.2):队里组件的 <Layout> 要么干脆不写,写就只准写 width / height,值只能是数字(可带 @硬度)或 auto。 写 left top centerX 这些定位、写 #谁谁谁 引用、或者往抽屉里再装 Box/Stack/Include——任一犯规,整屏加载失败变空白。另一个常见翻车:equalSpacing 之类没效果、按钮挤成一坨,多半是抽屉自己没占地盘(横排要把 left+right 写全,4.2 小白坑)。

翻页相册 <Scroll> —— 几屏叠一起,一次翻一页

长这样: 一个左右(或上下)翻页的相册,一屏只露一页,底下通常有一排"第几页"的小圆点。播放页能翻过来的"封面页、歌词页"就是它——Simple 图纸第 1067 行原样(盒里装着 4.1 那个收纳盒):

┌──────────────────────────┐
│                          │
│        当前这一页        │  ← 整页是一个 Box
│                          │     左右一滑换页
│          · o ·           │  ← 页码小圆点
│                          │
└──────────────────────────┘

照抄能跑: 仿 Simple 第 1067 行的真实写法(zOrder direction showPageControl pageDotColor pageDotActiveColor 五个属性照第 1067 行原样),两页各装一个盒、盒里一句字:

<Scroll id="pages" zOrder="1" direction="horizontal" showPageControl="true"
        pageDotColor="#888888" pageDotActiveColor="#ffffff">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Box>
        <Text textValue="第一页" fontSize="20" textColor="#ffffff">
            <Layout centerX="parent.centerX" centerY="parent.centerY" width="auto" height="auto"/>
        </Text>
    </Box>
    <Box>
        <Text textValue="第二页" fontSize="20" textColor="#ffffff">
            <Layout centerX="parent.centerX" centerY="parent.centerY" width="auto" height="auto"/>
        </Text>
    </Box>
</Scroll>

常用属性:

属性 怎么写 大白话作用
direction direction="horizontal" 翻页方向:horizontal 左右翻 / vertical 上下翻;不写默认左右(和抽屉的默认正好相反)
showPageControl showPageControl="true" 显示页码小圆点(图纸第 1067 行真实用法)
pageDotColor pageDotColor="#888888" 没被选中的小圆点颜色(第 1067 行)
pageDotActiveColor pageDotActiveColor="#ffffff" 当前页那个小圆点的颜色(第 1067 行)
更多属性: id zOrder pageDotPosition(小圆点位置:top / bottom / left / right,不写引擎自己摆——此属性引擎认,但全部现成主题 0 处用过,写了先副本验证) fillColor cornerRadius clip borderColor borderWidth blur(装扮六件与上面的 Box 卡同款,第 4.1 表直接对照) → 详见附录 A

小白坑: 最大的规矩:相册的每一页必须是一个 <Box>。往 <Scroll> 里直接摆 Text、Image 这类散组件(没装盒),整屏直接加载失败——引擎只收 <Box> 当页,没有通融(第 1 章 1.5 说过"一页=一个盒",这里是引擎强制的硬纪律)。空相册(一个盒都没有)不报错,但就是一片空白。还有:它的 direction 默认和抽屉相反——抽屉不写默认竖、相册不写默认横,照抄时把 direction 明写出来最稳。

工具条 <Toolbar> —— 一长条圆角描边条,上面站一排命令按钮

长这样: 一条横躺的圆角描边背景条,里面均匀站着一排按钮。Simple 图纸第 530~537 行是活例子——多选歌曲后从底部冒出来的那条操作栏(删除文件、加入列表、移动到文件夹):

┌──────────────────────────┐
│    [图]   [图]   [图]    │  ← 圆角 + 描边的一长条背景
└──────────────────────────┘     按钮在子标签 <Actions> 里的 <Item>

照抄能跑: Simple 第 530~537 行原样(注意它的结构:条自己只管外观,按钮装在 <Actions> 里):

<Toolbar id="toolbar" zOrder="99" borderWidth="0.5" borderColor="#ffffff50" cornerRadius="14">
    <Layout left="parent.left+20" right="parent.right-20" bottom="parent.bottom" height="50"/>
    <Actions>
        <Item image="eTBDeleteFile" text="" onClick="medias-delete-files"/>
        <Item image="eTBAdd2List" text="" onClick="medias-add-to-playlist"/>
        <Item image="eTBMove2Folder" text="" onClick="medias-move-to-folder"/>
    </Actions>
</Toolbar>

常用属性:

属性 怎么写 大白话作用
id id="toolbar" 贴名牌
zOrder zOrder="99" 叠放次序——它常浮在最上层,现成主题给的就是 99(第 530 行)
cornerRadius cornerRadius="14" 这条的圆角(第 530 行)
borderWidth borderWidth="0.5" 描边粗细
borderColor borderColor="#ffffff50" 描边颜色;和 borderWidth 一起写才生效(第 4 章老规矩)
subscriptions subscriptions="hidden:${...}" 工具条接水管专用写法:属性名是带 s 的 subscriptions(第 530~537 行没用到它,属引擎认、无现成例子——要用时先副本验证)
更多属性: 没有了——条自己一共就这 6 个:上面 5 个加 id/zOrder 已列全,它的 <Layout> 必须写(不写整屏不加载)。

小白坑: 两个。其一,按钮不是 Toolbar 的属性:image text onClick 长在条里的子标签 <Actions> 下一个个 <Item> 上——上面照抄示例里 <Item .../> 那三行才是按钮本体,改按钮的字、图和命令都在 <Item> 上改,别往 <Toolbar> 标签上写。其二,水管写法独一份:别的组件都写 subscribe=,唯独工具条只认 subscriptions=(带 s)——写成 subscribe 连警告都没有、静默无效;新手先不用它,真要"根据状态藏整条"时按上表写并先在副本验证。

复印机 <Include> —— 抄样板印一份(速查,现成主题无活例)

⚠️ 现状回指 4.3:实测所有现成主题 Include / Define 使用次数为 0——引擎支持、专业文档齐全(docs/theme-nesting-usage.md),但没有任何活例子。新手先用 Box/Stack 就够;决定上复印机,务必先照 2.1 复制主题副本、逐屏验证。本节不再重讲原理,只给属性速查。

长这样: 复印机——第 4 章 4.3 全讲过:<Defines> 里用 <Define id="样板名"> 画样板,哪屏要就在哪屏 <Include ref="样板名" id="复印件名"> 印一份;样板上留的 {{空}} 在引用时逐个填值。

照抄能跑: 与 4.3 示意同一份(据语法拼,非成品,先副本验证):

<!-- 图纸开头先画样板 -->
<Defines>
    <Define id="navbar">
        <Text textValue="{{title}}">
            <Layout centerX="parent.centerX" top="parent.top+12" width="auto" height="auto"/>
        </Text>
    </Define>
</Defines>

<!-- 哪一屏要,复印一次并填空 -->
<Include id="nav" ref="navbar" title="专辑">
    <Layout left="parent.left" top="parent.top" right="parent.right" height="56"/>
</Include>

常用属性(复印机自己的固定属性只有下面 3 个):

属性 怎么写 大白话作用
ref ref="navbar" 复印哪份样板(<Define> 的名字);拼错直接报错挂整屏
id id="nav" 必需,这次复印件的名字;复印件内部组件从此叫 nav/内部名(4.3 讲过)
zOrder zOrder="2" 这份复印件的叠放次序
更多属性: 除了这 3 个,<Include> 标签上再写的任何属性都不算自己的配置,而是给样板填空的参数——样板里 {{title}} 这样一个空,引用时写 title="要填的值" 补上;哪些空能填、叫什么名,全看那份 <Define> 自己留了什么(所以没有固定清单)。填空只准进"显示类"属性,onClick / subscribe 里不许用(规矩明细见 4.3)。

小白坑: id 或 ref 缺一个、样板名拼错、留的空没填齐——都是报错挂掉整屏,没有"半显示"的余地。另外别忘了 4.3 的排他条款:抽屉(Stack)队里不许放 Include(和 Box/Stack 同罪,整屏挂掉)。

5.4 播放与视觉

本节 10 张卡,分两堆:前四个(Visual、SpectrumView、CoverFlow、VinylTurntable)是播放页的"主体展示"——可视化画面、频谱跳条、封面流、黑胶转盘;后六个是歌词家族(LyricShow、KaraokeShow、LyricDisplay、LyricLineShow)加两个功能标签(CountdownView 定时关机列表、PlaybackUiIndicator 顶部三态切换条)。水管(subscribe)大多接的是"该显示时才显示"的显隐牵线,用到的 playback.ui.visualShowing、playback.ui.lyricShowing 这几个数据名第 3 章表里没列,但都是现成图纸实测在用的,照抄即可。有个例外先说在前头:CoverFlow 没有水管。老规矩:每卡"照抄能跑"都带 <Layout/> 小跟班。

音频可视化 <Visual> —— 一块随音乐起舞的动效画面

长这样: 一块矩形区域,播放时里面随音乐实时跳动画面(Milkdrop 等渲染效果;用哪种效果是 App 设置里选的,不归图纸管)。Black 图纸第 1253~1258 行是活例子——摆在封面那块地方、圆角描边带投影,只在"可视化"界面现身。

┌────────────────────┐
│   ~ 跳动的光影 ~   │  ← 画面跟着音乐实时动
│   ~ 随音乐起舞 ~   │     图纸只管它摆哪、长什么样
└────────────────────┘

照抄能跑: 仿 Black 第 1253~1258 行(描边、圆角、投影、水管全是原样真实值):

<Visual id="visual" zOrder="3" cornerRadius="12"
        borderColor="#ffffff25" borderWidth="0.5"
        shadowColor="#00000080" shadowOffset="{8,8}" shadowOpacity="0.2" shadowRadius="12"
        subscribe="visible:${playback.ui.visualShowing}">
    <Layout left="parent.left+32" top="parent.top+60" right="parent.right-32" height="160"/>
</Visual>

常用属性:

属性 怎么写 大白话作用
subscribe subscribe="visible:${playback.ui.visualShowing}" 整块水管,唯一要小心的属性。Black 图纸实际用两种:visible:${playback.ui.visualShowing}(可视化界面才显示)和 hidden:${playback.ui.mainShowing}(封面界面时藏起来),照抄即可
cornerRadius cornerRadius="12" 圆角
borderColor / borderWidth borderColor="#ffffff25" + borderWidth="0.5" 描边,一起写才生效(第 4 章老规矩)
shadowColor / shadowOffset / shadowOpacity / shadowRadius shadowColor="#00000080" shadowOffset="{8,8}" shadowOpacity="0.2" shadowRadius="12" 投影四件套(图纸第 1254 行完整真实写法)
更多属性: id zOrder fullscreenIcon(引擎解析这个属性,但全部现成主题 0 处用过、现版本也没有消费它的代码——写了目前无效果,新手别指望它) → 详见附录 A

小白坑: 水管接错方向,要么可视化永远不露脸,要么一直盖在封面歌词上面。两种真实接法(visible:visualShowing / hidden:mainShowing)从 Black 图纸整段抄,别自创数据名。

频谱条 <SpectrumView> —— 一排跟着音量跳动的小柱子

长这样: 一排高矮不齐的竖条,随音乐节奏跳动。Simple 图纸第 1092~1094 行是活例子(第 4 章 4.1 装盒那段里它就是盒里的 spectrumView)。

▂ ▅ ▃ ▇ ▄ ▅ ▂ ▆ ▃  <- 柱子数=bins  柱间距=gaps

照抄能跑: Simple 第 1092~1094 行原样(值全是真实值):

<SpectrumView id="spectrumView" zOrder="3" bins="80" gaps="1"
    gradientStartColor="#ffffff" gradientEndColor="#cdcdcd">
    <Layout left="parent.left+32" top="#albumCover.bottom" right="parent.right-32" height="100"/>
</SpectrumView>

常用属性(这个标签自己的外观属性全在这里了):

属性 怎么写 大白话作用
bins bins="80" 柱子根数,不写默认 80
gaps gaps="1" 柱子之间的缝宽,不写默认 1
gradientStartColor gradientStartColor="#ffffff" 渐变起点色(柱子一头)
gradientEndColor gradientEndColor="#cdcdcd" 渐变终点色(柱子另一头)
id / zOrder id="spectrumView" zOrder="3" 名牌 / 叠放次序(第 1 章)
更多属性: 只剩一个 subscribe——引擎认它,但全部现成主题里 SpectrumView 没有一处接过水管,照抄示例别多加;真要接,先在副本验证 → 详见附录 A

小白坑: bins="0"、gaps="0" 会被引擎当成没写(回落到默认 80 和 1)——写 0 不等于"零根柱、零缝"。另外它靠 <Layout> 的 height 决定柱子能长多高,高度忘给就看不到东西。

封面流 <CoverFlow> —— 横着滑着选封面的架子

长这样: 一排封面横向排开、中间那张最大,左右一滑换一张——老 iPod 那个经典效果。CoverFlow 主题图纸第 1153~1160 行是活例子(这个组件就住在 CoverFlow 主题里,Simple 没有)。

┌──┐  ┌────────┐  ┌──┐
│小│  │大 封 面│  │小│   ← 中间最大,两边小且往后斜
└──┘  └────────┘  └──┘   手指左右一滑换一张
          ▒ 倒影 ▒       ← reflectionGap 控制倒影留的缝

照抄能跑: 仿 CoverFlow 主题第 1153~1160 行格式(itemSize、placeholder 等值全照图纸原样;cAlbumCover 是 CoverFlow 主题文件夹里的图,用到别的主题要换成你有的图名):

<CoverFlow id="albumCover" zOrder="2"
    itemSize="{240,240}"
    itemBorderWidth="5" itemBorderColor="#cccccc20"
    reflectionGap="4" placeholder="cAlbumCover">
    <Layout left="parent.left+32" top="#textArtist.bottom+20" right="parent.right-32" height="260"/>
</CoverFlow>

常用属性:

属性 怎么写 大白话作用
itemSize itemSize="{240,240}" 每张封面多大:花括号里宽,高两个数(图纸第 1155 行真实写法)。第 1 章说过花括号是"两个数打包",这里就是宽高成对
placeholder placeholder="cAlbumCover" 没封面时顶上的占位图(图名不带 .png)
itemBorderColor / itemBorderWidth itemBorderColor="#cccccc20" + itemBorderWidth="5" 每张封面的描边,一起写才生效(图纸第 1156~1157 行真实值)
reflectionGap reflectionGap="4" 封面和它水下倒影之间留的缝(图纸第 1158 行)
id / zOrder id="albumCover" zOrder="2" 名牌 / 叠放次序
更多属性: 没有了——上面 7 个就是 <CoverFlow> 的全部。注意它没有 subscribe,别给它接水管(见小白坑)。

小白坑: 本节唯一没有水管(subscribe)的标签——封面跟着滑动的数据由页面内部喂给它,图纸里写了 subscribe 会被引擎无视。想把它摆好,关键是 <Layout> 的 height 给够(示例 260):架子比 itemSize 的 240 矮的话,倒影和描边会被切掉。

黑胶唱机 <VinylTurntable> —— 会转的唱片加一支唱臂

长这样: 一张圆形黑胶盘,盘心贴着当前歌的封面,右上方一支唱臂;正在播放时盘面跟着进度转。Vinyl 主题图纸第 1152~1153 行是活例子(这个组件住在 Vinyl 主题,Simple 没有)。

        ╲ 唱臂
    + - - ● - - +
   ( ●  封面 ●   )   <- 圆盘随播放转动
    + - - - - - +      唱臂搭在盘边

照抄能跑: Vinyl 第 1152~1153 行原样(wArt、wTonearm 是 Vinyl 主题文件夹里的图名,搬到别的主题要换成你有的图;三根水管的含义第 3 章 3.1 讲过——正在播放才转、盘心用当前封面、转到哪跟进度走):

<VinylTurntable id="albumCover" zOrder="2"
    albumImage="wArt" tonearmImage="wTonearm"
    subscribe="enable:${playback.state.playing};image:${media.artwork};value:${media.progress}">
    <Layout top="#textArtist.bottom+20" centerX="parent.centerX" width="300" height="300"/>
</VinylTurntable>

常用属性:

属性 怎么写 大白话作用
albumImage albumImage="wArt" 盘心那张封面占位图(图名不带 .png;实际播放时被水管里的当前封面顶替)
tonearmImage tonearmImage="wTonearm" 唱臂用哪张图
subscribe subscribe="enable:...;image:...;value:..." 三根水管一起接(分号隔开,第 3 章),照上例整串抄
id / zOrder id="albumCover" zOrder="2" 名牌 / 叠放次序
更多属性: recordImage(黑胶盘本身的图——引擎认,但全部现成主题 0 处用过:盘面纹路是程序画好的,不靠这张图,要改盘面颜色用下面说的 Disc 子标签);盘面细节(纹路颜色、圆心点、盘边高光等一整套)长在子标签 <Disc> 上——不是转盘自己的属性,样式清单见 Vinyl 图纸第 1154 行往后原样照抄,明细不在本卡 → 详见附录 A

小白坑: 三根水管一根都不能少:少 enable: 暂停了盘还在转,少 image: 盘心永远是你的占位图,少 value: 转盘不跟进度走。图名也得是你主题文件夹里真有的图——Vinyl 的图都带 w 前缀,原样抄进别的主题会因为缺图变成"破盘"。改这个组件,新手主要就是换两张图、挪位置。

歌词一行 <LyricShow> —— 跟着唱词换的单行小歌词

长这样: 一行的歌词条,只显示正在唱的当前一句。CoverFlow 主题图纸第 1165~1167 行是活例子(摆在频谱底下那行小字;它只在部分变体图纸出现,基础款 CoverFlow/theme.xml 同位置用的是下面的 LyricLineShow)。

照抄能跑: 仿 CoverFlow 第 1165~1167 行(normalColor/highlightColor/cursorFontRef/textAlignment 四个值照图纸原样;水管那行是 Black 变体第 1391~1393 行的真实写法,作用"歌词界面时它自己藏起来"——在本页永远显示的话删掉那行也行):

<LyricShow id="lyricShow" zOrder="2"
    normalColor="#a0a0a0" highlightColor="#e0e0e0"
    cursorFontRef="#STHeitiSC-Medium-16" textAlignment="center"
    subscribe="hidden:${playback.ui.lyricShowing}">
    <Layout left="parent.left+16" top="#spectrumView.bottom" right="parent.right-16" height="auto"/>
</LyricShow>

常用属性:

属性 怎么写 大白话作用
normalColor normalColor="#a0a0a0" 普通行字色
highlightColor highlightColor="#e0e0e0" 当前正在唱那一行的字色(图纸第 1165 行真实值)
textAlignment textAlignment="center" 行内怎么靠:left / center / right
normalFontRef normalFontRef="#STHeitiSC-Medium-16" 普通行的字帖(第 2 章 2.2);直接指定也可用 normalFontSize / normalFontFamily
cursorFontRef cursorFontRef="#STHeitiSC-Medium-16" "光标"(当前行)字体的字帖(图纸第 1166 行真实用法)
subscribe subscribe="hidden:${playback.ui.lyricShowing}" 接显隐水管,整行跟着播放界面切换藏/露(Black 变体第 1393 行真实写法)
更多属性: id zOrder multiline(true/1 允许多行;现成主题 0 处用过,先副本验证) normalFontSize normalFontFamily cursorFontSize cursorFontFamily cursorColor(引擎不读这个属性——当前行颜色归 highlightColor 管,别指望 cursorColor) → 详见附录 A

小白坑: 歌词家族四个标签长得像、别拿错:LyricShow 是单行小歌词;要整屏滚动的列表用 LyricDisplay;要逐字染色的大字用 KaraokeShow。它们属性名前缀不同(normalColor 都认识,highlightColor 只有 LyricShow 读),抄卡片别抄串。

卡拉OK歌词 <KaraokeShow> —— 逐字点亮的歌词

长这样: 大号歌词,一个字一个字跟着演唱进度点亮,当前行背后常垫一条半透明圆角高亮。Vinyl 主题图纸第 1341 行是活例子。

已唱的字亮 ▍正在唱的字▍ 还没唱的字
(当前行背后垫一块半透明圆角条 = selectColor 三件套)

照抄能跑: Vinyl 第 1341 行砍到一行(PingFangTC-Semibold、两个字号、三组颜色值全部照图纸原样;fontSize 按屏幕大小各变体在 18~30 之间挑,见各尺寸文件第 1289~1297 行):

<KaraokeShow id="karaoke" zOrder="1"
    fontFamily="PingFangTC-Semibold" fontSize="22"
    normalColor="#a0a0a0" activeColor="#ffffff"
    selectColor="#ffffff" selectColorAlpha="0.2" selectCornerRadius="12">
    <Layout left="parent.left+20" right="parent.right-20" centerY="parent.centerY"/>
</KaraokeShow>

常用属性:

属性 怎么写 大白话作用
fontFamily fontFamily="PingFangTC-Semibold" 字体名,直接写(图纸第 1341 行真实值);也可用 fontRef 报字帖名(引擎认,现成主题无人用)
fontSize fontSize="22" 字多大
normalColor normalColor="#a0a0a0" 还没唱到的字的颜色
activeColor activeColor="#ffffff" 已经唱到的字的颜色
selectColor selectColor="#ffffff" 当前行背后那条高亮的颜色
selectColorAlpha selectColorAlpha="0.2" 高亮的透明度(0 全透、1 不透)
selectCornerRadius selectCornerRadius="12" 高亮条的圆角
translatedFontSize translatedFontSize="16" 翻译行(歌词下面那行小字)的字大小(Vinyl 第 1341 行真实用法)
更多属性: id zOrder fontRef subscribe(引擎都认,但现成主题的 KaraokeShow 没有一处用过,先副本验证) → 详见附录 A

小白坑: 三个颜色各管各的:normalColor / activeColor 管字(未唱/已唱),selectColor 管字背后那块半透明垫条——把它当"已唱字色"改,字不会变、只有背景条变。fontFamily 必须是系统里真存在的字体名,抄示例里的 PingFangTC-Semibold 最稳。

歌词列表 <LyricDisplay> —— 整屏滚动的歌词表

长这样: 一整列歌词,当前行高亮放大、跟着自动滚,带翻译行;点某一行还能跳过去唱。Black 图纸第 1286~1299 行(id 叫 multilineLyric,"歌词"界面那块)是活例子。

  上一行歌词(灰)
▶ 当前行歌词(亮、稍大)  <- 自动滚到中部
  下一行歌词(灰)
  翻译行(更小的灰字)

照抄能跑: 仿 Black 第 1286~1299 行(全部属性名和值照图纸原样;id 和 <Layout> 位置可按你的页面改):

<LyricDisplay id="multilineLyric" zOrder="4" textAlignment="left"
    normalColor="#b0b0b0" activeColor="#ffffff"
    translationNormalColor="#b0b0b0" translationActiveColor="#ffffff"
    normalFontRef="#STHeitiSC-Medium-18" activeFontRef="#STHeitiSC-Medium-18"
    translationFontRef="#STHeitiSC-Medium-16"
    selectColor="#ffffff" selectColorAlpha="0.2" selectCornerRadius="12"
    subscribe="visible:${playback.ui.lyricShowing}">
    <Layout left="parent.left+32" top="#indicator.bottom+20" right="parent.right-32" bottom="#stackFunctions.top-32"/>
</LyricDisplay>

常用属性(属性多,颜色字体的成套搭配看下一行说明):

属性 怎么写 大白话作用
textAlignment textAlignment="left" 每行怎么靠:left / center / right(图纸第 1288 行真实值)
normalColor / activeColor normalColor="#b0b0b0" activeColor="#ffffff" 普通行 / 当前行的字色
normalFontRef / activeFontRef normalFontRef="#STHeitiSC-Medium-18" 普通行 / 当前行的字帖(两套常给不同字号,当前行显得大)
translationNormalColor / translationActiveColor 各写一个颜色 翻译行(下面那行小字)的普通 / 当前字色
translationFontRef translationFontRef="#STHeitiSC-Medium-16" 翻译行的字帖
selectColor / selectColorAlpha / selectCornerRadius selectColor="#ffffff" selectColorAlpha="0.2" selectCornerRadius="12" 当前行背后高亮条:颜色 / 透明度 / 圆角(三件套一起写,图纸第 1296~1298 行真实值)
subscribe subscribe="visible:${playback.ui.lyricShowing}" 歌词界面才露脸(图纸第 1299 行真实写法)
更多属性: id zOrder;不走字帖、直接指定字体的 normalFontName normalFontSize activeFontName activeFontSize translationFontName translationFontSize → 详见附录 A

小白坑: 它有 normalFontSize / normalFontName——和 LyricShow 同前缀但别和 5.1 <Text> 的 textValue 家串门:这里没有"写死的歌词文字"可填,内容全靠页面从歌词文件喂进来,图纸只管两套餐色(正文一套、翻译一套)× 两种状态(普通 / 当前)。少配一套,另一套会显示成默认色,看着像"改了没生效"。

歌词单行 <LyricLineShow> —— 只摆一句歌词的横排

长这样: 一行歌词(当前句),可带一行小字的翻译,没有列表滚动。CoverFlow 主题图纸第 1167~1176 行是活例子(基础款 theme.xml 里封面页频谱下方那行歌词就是它)。

照抄能跑: 仿 CoverFlow 第 1167~1176 行(属性名和值全部照图纸原样,水管是变体里真实用过的显隐写法,不需要可删):

<LyricLineShow id="lyricShow" zOrder="2" textAlignment="left"
    normalColor="#a0a0a0" activeColor="#ffffff" translationColor="#ffffff"
    normalFontRef="#STHeitiSC-Medium-16" translationFontRef="#STHeitiSC-Medium-14"
    subscribe="hidden:${playback.ui.lyricShowing}">
    <Layout left="parent.left+32" top="#spectrumView.bottom" right="parent.right-32" height="auto"/>
</LyricLineShow>

常用属性:

属性 怎么写 大白话作用
textAlignment textAlignment="left" 怎么靠:left / center / right(图纸第 1169 行真实值)
normalColor normalColor="#a0a0a0" 普通字色
activeColor activeColor="#ffffff" 当前句的字色(图纸第 1171 行真实值)
translationColor translationColor="#ffffff" 翻译行的字色(图纸第 1172 行真实值)
normalFontRef / translationFontRef normalFontRef="#STHeitiSC-Medium-16" 正文 / 翻译的字帖(图纸第 1173~1174 行真实用法)
subscribe subscribe="hidden:${playback.ui.lyricShowing}" 接显隐水管(变体图纸真实用法)
更多属性: id zOrder normalFontName normalFontSize translationFontName translationFontSize(不走字帖、直接写字体名和字号) → 详见附录 A

小白坑: 翻译行只有一个颜色 translationColor——它不是 LyricDisplay,没有"翻译的当前行颜色"(translationActiveColor 写在这里不生效)。单行组件 <Layout> 给 height="auto" 最省心(图纸第 1175 行真实写法),写死高度容易把字切半。

定时关机列表 <CountdownView> —— 倒计时页那一列选项

长这样: "定时关机"页面里一整列选项("15 分钟 / 30 分钟 / 播完当前歌曲…"),每行左边标题、下面一行副标题、右边一个勾选图。Simple 图纸第 1361~1373 行是活例子。注意它的结构:标签自己只管五件事,字色、背景那些外观全写在里面的 <CellStyle> 子标签上。

┌────────────────────┐
│15 分钟            ✓│  ← 标题(左) + 勾选图(右)
│  到点自动关机      │  ← 副标题
│30 分钟            ✓│
└────────────────────┘

照抄能跑: Simple 第 1361~1373 行原样(值全部照图纸,连 <CellStyle> 子标签一起抄):

<CountdownView id="timer" zOrder="0" insets="{58,20,80,20}" cellHeight="58" lineSpacing="8">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <CellStyle
        titleFontName="Heiti SC" titleFontSize="14" titleTextColor="#cdcdcd"
        subtitleFontName="Menlo" subtitleFontSize="12" subtitleTextColor="#a0a0a0"
        accessoryImage="eChecked" highlightedBackgroundColor="#ffffff10"/>
</CountdownView>

常用属性(分两处写——先认清在哪一层):

属性 写在哪 大白话作用
id / zOrder 标签自己 名牌 / 叠放次序(第 1 章)
insets 标签自己 整列四周留的空,花括号四个数(图纸第 1361 行真实写法 {58,20,80,20})
cellHeight 标签自己 每一行多高(图纸第 1361 行 cellHeight="58")
lineSpacing 标签自己 行与行之间的缝(图纸第 1361 行 lineSpacing="8")
titleTextColor <CellStyle> 里 每行标题的字色(图纸第 1366 行)
titleFontName / titleFontSize <CellStyle> 里 标题的字体和字号(图纸第 1364~1365 行;也可用 titleFontRef 报字帖)
subtitleTextColor <CellStyle> 里 副标题的字色(图纸第 1369 行)
subtitleFontName / subtitleFontSize <CellStyle> 里 副标题的字体和字号(图纸第 1367~1368 行;也可用 subtitleFontRef)
accessoryImage <CellStyle> 里 行尾勾选图(图名不带 .png,图纸第 1370 行 eChecked)
separatorColor / separatorWeight <CellStyle> 里 行间分隔线的颜色和粗细(引擎认,Simple 未用;见更多属性)
更多属性: 以下全部写在 <CellStyle> 子标签里,是列表外观细调项,改主题一般照抄现成即可——标题:titleFontRef titleHighlightedTextColor(按下时) titleSelectedTextColor(选中时);副标题:subtitleFontRef subtitleHighlightedTextColor subtitleSelectedTextColor;整行底色:backgroundColor highlightedBackgroundColor(Simple 第 1371 行真实用过) selectedBackgroundColor;分隔线:separatorColor separatorWeight → 详见附录 A

小白坑: 最大的坑是站错层:把 titleTextColor= 这类外观属性直接写在 <CountdownView> 标签上——引擎只在自己的标签上读 id zOrder insets cellHeight lineSpacing 这五个,其余全在 <CellStyle> 里读;写错层不报错、只是"改了没反应"。抄的时候把整段(含 <CellStyle ... />)一起抄,改值只改值、不动结构。

播放界面切换条 <PlaybackUiIndicator> —— 顶上"封面·视觉化·歌词"三格开关

长这样: 播放界面顶部居中的一小条,三格字(点哪格切到哪格),格子之间可以垫一枚分隔小图。Black 图纸第 1234~1237 行是活例子。

  封面 │ 视觉化 │ 歌词    <- 当前界面那格是亮色

照抄能跑: Black 第 1234~1237 行原样(playback-switch-ui 是真实命令——点某格就切到对应界面,第 3 章"喊一嗓子"的规矩,只是这个组件喊的出口名叫 valueChanged;三格文字内容随语言变体不同,中文版主题里写的是 textMain="封面" textVis="视觉化" textLyric="歌词"):

<PlaybackUiIndicator id="indicator" zOrder="100"
    textMain="COVER" textVis="VISUAL" textLyric="LYRIC"
    fontRef="#STHeitiSC-Medium-16"
    titleColor="#b0b0b0" titleSelectedTextColor="#ffffff"
    dividerImage="sIconDivider"
    valueChanged="playback-switch-ui">
    <Layout top="parent.top+10" centerX="parent.centerX"/>
</PlaybackUiIndicator>

常用属性:

属性 怎么写 大白话作用
textMain / textLyric / textVis textMain="封面" textLyric="歌词" textVis="视觉化" 三格各自的字(图纸第 1234 行真实用法)
titleColor titleColor="#b0b0b0" 没被选中格子的字色
titleSelectedTextColor titleSelectedTextColor="#ffffff" 当前界面那格(亮着)的字色
fontRef fontRef="#STHeitiSC-Medium-16" 三格共用的字帖(图纸第 1235 行真实写法);也可用 titleFontName / titleFontSize 直接指定
dividerImage dividerImage="sIconDivider" 格子之间的分隔图(图名不带 .png,图纸第 1235 行)
valueChanged valueChanged="playback-switch-ui" 点击切换时喊的命令——它没有 onClick,切换出口就叫 valueChanged(图纸第 1236 行真实值)
更多属性: id zOrder(图纸给 100,保证在最上层不被盖) titleFontName titleFontSize subscribe(引擎认,现成主题无人用,先副本验证) → 详见附录 A

小白坑: 两个。其一,想给它写 onClick——不认,点了不切换;它的命令属性只有 valueChanged,而且值固定就抄 "playback-switch-ui"(切到哪格由引擎自己带过去,不用你填参数)。其二,文字属性都带前缀:textMain / textLyric / textVis,颜色是 titleColor / titleSelectedTextColor——写成裸的 text= / color= 都没反应。

5.5 均衡器

本节 4 张卡,拼出"音效-均衡器"那一页。均衡器就是调高低音:整首歌整体调高调低叫"总增益",把低音、中音、高音分开调叫"分频段"。四个零件各管一块:EQPreamp 是一根总增益竖滑杆、EQBand10 是一排 10 根分频段推子(页面主体)、EQSelectBox 是选预设(流行、摇滚那种)的下拉框、EQMeterChart 是显示各频段当前高低的曲线图。这一节有个和前几节都不同的关键点:这四样都不接水管、不写命令——滑杆的数值、预设列表、曲线画法全是 App 内部自动喂好的,图纸只管"摆哪、长啥样、穿什么图"。页面顶上的开关、保存、重置用的是普通的 ToggleButton / Button(5.2 教过),喊的命令词真实存在于图纸:equalizer-switch-toggle、equalizer-preset-save、equalizer-preset-reset(CoverFlow 图纸第 1568、1604、1594 行)。四张卡的活例子全部出自 CoverFlow 图纸第 1560~1659 行的均衡器页——这是均衡器组件用得最全的主题;Simple、Black、Vinyl 的同款页面写法几乎一样。老规矩:每卡"照抄能跑"都带 <Layout/> 小跟班。

总增益滑杆 <EQPreamp> —— 一根杆把整首歌调高调低

长这样: 一根竖滑杆,上面一个可上下拖的滑块,旁边配一行小字标签(如 "Preamp")。拖高整首歌变响、拖低变轻,CoverFlow 图纸旁边的刻度字是 +15 到 -15。活例子在 CoverFlow 图纸第 1608~1619 行——摆在均衡器页最左边那根。

 +15 ┤
     ▉     <- 滑块(thumbImage)
 0dB ┤  Preamp  <- 标签文字(labelText)
     │     <- 滑槽(trackMinImage / trackMaxImage)
 -15 ┤

照抄能跑: CoverFlow 第 1608~1619 行原样(cThumb、cTrackMin 是 CoverFlow 主题文件夹里的图名,搬到别的主题要换成你有的图;旁边 +15 / 0dB / -15 三行刻度字是三个独立 <Text>,图纸第 1621~1632 行,见小白坑):

<EQPreamp id="preamp" zOrder="2"
    thumbImage="cThumb" thumbStretch="{0,0}"
    trackMinImage="cTrackMin" trackMaxImage="cTrackMin" trackStretch="{16,0}"
    fontRef="#STHeitiSC-Light-12" labelText="Preamp" labelTextColor="#efefef">
    <Layout left="parent.left+32" top="#save.bottom+32" width="40" height="360"/>
</EQPreamp>

常用属性:

属性 怎么写 大白话作用
thumbImage thumbImage="cThumb" 滑块(拖着走的小疙瘩)用哪张图,图名不带 .png(图纸第 1610 行)
trackMinImage / trackMaxImage trackMinImage="cTrackMin" trackMaxImage="cTrackMin" 滑槽两段的图:滑块已推过的段 / 没推到的段;图纸第 1613~1614 行两段用了同一张,看不出分段也行
labelText labelText="Preamp" 滑杆旁边的标签文字(图纸第 1616 行);不写时程序默认显示 "PREAMP"
labelTextColor labelTextColor="#efefef" 标签文字的颜色(图纸第 1617 行)
fontRef fontRef="#STHeitiSC-Light-12" 标签的字帖(第 2 章 2.2);也可直接写 fontFamily + fontSize
thumbStretch / trackStretch thumbStretch="{0,0}" trackStretch="{16,0}" 图比滑块/滑槽位置大时怎么拉伸:花括号里两个数是"左右保留、上下保留"的边距(图纸第 1611、1612 行真实写法);照抄现成值即可
更多属性: id zOrder(名牌 / 叠放次序,第 1 章);thumbColor trackMinColor trackMaxColor——不配图的"纯色版"着色属性,引擎认、真生效(滑块色 / 滑槽已推段色 / 未推段色),但四个现成主题全部用图版、0 处用色版,想试先副本验证 → 详见附录 A

小白坑: 三个。其一,刻度字不归它管——+15 / 0dB / -15 是另外三个 <Text> 摆在滑杆旁边的(图纸第 1621~1632 行,每个用 left="#preamp.right+16" 贴着滑杆右边,1.2 学的"参照别的组件"),想改刻度去动那三个 Text,别在 EQPreamp 上找属性。其二,<Layout> 的宽高要写实数——它是竖杆,图纸给 width="40" height="360"(第 1618 行);高度不给够,杆就拖不出行程。其三,labelText 的名字是 labelText,不是 5.1 <Text> 的 textValue——写串了标签位置会空着。

十段推子 <EQBand10> —— 一排 10 根竖推子,均衡器的主体

长这样: 一排 10 根竖滑杆并排,每根管一个频段(从低音到高音),各推各的高矮;每根底下有程序自动印的频率标注(32、64、125、250、500、1K、2K、4K、8K、16K)。活例子在 CoverFlow 图纸第 1634~1646 行——摆在总增益滑杆右边那一排。

  ▉  ▃  ▂  ▅  ▇  ▂  ▄  ▃  ▅  ▂   <- 10 根推子,各管一个频段
 32 64 125 250 500 1K 2K 4K 8K 16K  <- 频率标注(文字程序自带)

照抄能跑: CoverFlow 第 1634~1646 行原样(subscribe="" 是图纸真实写法——空串、不起作用,含义见小白坑;图名同上一页,换主题要换你有的图):

<EQBand10 id="bands" zOrder="3"
    thumbImage="cThumb" thumbTappedImage="cThumb" thumbStretch="{0,0}"
    trackMinImage="cTrackMin" trackMaxImage="cTrackMin" trackStretch="{16,0}"
    fontRef="#STHeitiSC-Light-12" textColor="#efefef" subscribe="">
    <Layout left="#mid.right+16" top="#preamp.top" right="parent.right-32" height="#preamp.height"/>
</EQBand10>

常用属性:

属性 怎么写 大白话作用
thumbImage thumbImage="cThumb" 滑块常态的图(图纸第 1636 行)
thumbTappedImage thumbTappedImage="cThumb" 手指按住时换的图;图纸第 1637 行常态和按下用了同一张,想"按下变色"就备两张图
trackMinImage / trackMaxImage 各一张图名 滑槽两段(已推 / 未推)的图(图纸第 1639~1640 行)
thumbStretch / trackStretch trackStretch="{16,0}" 拉伸边距,花括号两个数,同 EQPreamp(图纸第 1638、1641 行)
fontRef fontRef="#STHeitiSC-Light-12" 10 个频率标注的字帖;也可直接 fontFamily + fontSize
textColor textColor="#efefef" 频率标注的文字颜色(图纸第 1643 行)
id / zOrder id="bands" zOrder="3" 名牌 / 叠放次序(第 1 章)
更多属性: 只剩 subscribe——引擎读进模型但没有任何代码消费它,图纸样例写的也是空串 subscribe=""(第 1644 行);10 段的数值是均衡器内部自动喂的,给它接水管没有效果,照抄时保留或删除都行,别自创数据名 → 详见附录 A

小白坑: 两个。其一,它没有颜色版属性——trackMinColor / trackMaxColor / thumbColor 在 EQPreamp 上引擎认(上一卡刚说),写到 EQBand10 头上就是空气:这个标签的滑槽只有图版。从 EQPreamp 那行抄过来记得砍掉颜色属性。其二,它的频率标注(32…16K)是程序印死的,图纸里没有属性能改这些字,只能改字色字号。另外看它的 <Layout>:top="#preamp.top"、height="#preamp.height" 全在参照左边那根总增益杆(1.2 的"参照别的组件")——排对齐靠这个,照抄现成写法最稳。

预设下拉框 <EQSelectBox> —— 选"流行 / 摇滚 / 古典"的框

长这样: 一条显示当前预设名的小框,右边带个下拉箭头;点开展开一列预设选项,选一个收回去。选项内容(有哪些预设)是 App 自带的,图纸不写、也没法写。活例子在 CoverFlow 图纸第 1571~1586 行——摆在均衡器页顶上。

[ 流行          ▾ ]  <- 框: titleTextColor 字 + titleBackgroundColor 底 + arrowImage 箭头
 +-----------------+
 | 摇滚            |  <- 展开的选项: optionTextColor / optionBackgroundColor
 | 古典            |  <- 选项间的细线: optionSeparatorColor + Weight
 +-----------------+

照抄能跑: CoverFlow 第 1571~1586 行,只删了一行(图纸第 1579 行还写了 arrowTintColor="#303030"——引擎不读这个属性,整份代码里没有它,删掉毫无影响、留着也不生效,见小白坑;其余值原样):

<EQSelectBox id="dropdown" zOrder="2"
    cornerRadius="8" borderWidth="0.5" borderColor="#ffffff50"
    titleBackgroundColor="#cdcdcd" titleTextColor="#101010"
    arrowImage="cIconDropdown" arrowRightOffset="10"
    optionBackgroundColor="#cdcdcd" optionTextColor="#101010"
    optionSeparatorWeight="0.5" optionSeparatorColor="#00000020"
    fontRef="#STHeitiSC-Light-15">
    <Layout left="parent.left+32" centerY="#reset.centerY" right="#reset.left-20" height="32"/>
</EQSelectBox>

常用属性:

属性 怎么写 大白话作用
titleTextColor / titleBackgroundColor titleTextColor="#101010" titleBackgroundColor="#cdcdcd" 框上文字的颜色 / 框本身的底色(图纸第 1575~1576 行;"title"就是收起时看到的那条)
arrowImage arrowImage="cIconDropdown" 右边的下拉箭头图(图名不带 .png,图纸第 1577 行)
arrowRightOffset arrowRightOffset="10" 箭头离框右边留多少空(图纸第 1578 行)
optionTextColor / optionBackgroundColor 各一个颜色 点开列表里每项的字色 / 项底色(图纸第 1580~1581 行)
optionSeparatorColor / optionSeparatorWeight optionSeparatorColor="#00000020" optionSeparatorWeight="0.5" 选项之间分隔线的颜色和粗细(图纸第 1582~1583 行真实值)
cornerRadius / borderWidth / borderColor cornerRadius="8" borderWidth="0.5" borderColor="#ffffff50" 框的圆角 / 描边粗细 / 描边颜色(图纸第 1572~1574 行;描边粗细和颜色一起写才生效,第 4 章老规矩)
fontRef fontRef="#STHeitiSC-Light-15" 文字的字帖(图纸第 1584 行);也可直接 fontFamily + fontSize
更多属性: 投影四件套 shadowColor shadowOffset shadowOpacity shadowRadius(引擎认,四个现成主题 0 处用过,要加先副本验证) → 详见附录 A

小白坑: 两个。其一,选项不用写、也写不了——预设名是 App 自带的,这个标签没有任何"填选项"的属性;图纸只管它长什么样。其二,CoverFlow 图纸第 1579 行那个 arrowTintColor 是纸面上的假属性——引擎从不读它,改箭头颜色只能换 arrowImage 那张图;你照抄时把此行带上然后指望它调箭头色,就会撞上"改了没反应"。

电平曲线图 <EQMeterChart> —— 底下显示各频段高低的图表

长这样: 一块带格线的图表:底上一条条横向格线,中间一条折线连着 10 个频段当前的高矮,图上一个可横拖的半透明宽指示条(屏幕窄、10 根推子摆不下时,拖它带动推子那排左右滚动)。活例子在 CoverFlow 图纸第 1647~1659 行——均衡器页最底下那块。

┌────────────────────────┐
│─ ─ ─ ─ ─ ─ ─ ─ ─ ─     │
│   ╱╲    ╱╲╱╲           │
│░░░░▓▓░░░░░░░           │
└────────────────────────┘
   横格线 gridLine* / 波形折线 waveformLine* / 半透明指示条 thumb*

照抄能跑: CoverFlow 第 1647~1659 行原样(值全部照图纸;<Layout> 里 top="#bands.bottom+20" 摆在推子排下方 20 的位置):

<EQMeterChart id="chart" zOrder="2"
    backgroundColor="#00000020"
    gridLineColor="#90909050" gridLineWidth="0.5" numberOfGridAxisY="10"
    waveformLineColor="#ffffff" waveformLineWidth="1"
    thumbColor="#ffffff20" thumbIndicatorColor="#cccccc" thumbWidth="160"
    textColor="#cccccc">
    <Layout left="parent.left+16" top="#bands.bottom+20" right="parent.right-16" height="100"/>
</EQMeterChart>

(图纸第 1647~1659 行没写 textColor,程序默认用 #cccccc;上面这行是按默认值显式写出来,方便你改色,删掉也一模一样。)

常用属性:

属性 怎么写 大白话作用
backgroundColor backgroundColor="#00000020" 图表底色(图纸第 1649 行;# 后 8 位、末两位是透明度,第 4 章同款)
gridLineColor / gridLineWidth gridLineColor="#90909050" gridLineWidth="0.5" 格线的颜色和粗细(图纸第 1650~1651 行)
numberOfGridAxisY numberOfGridAxisY="10" 横向格线画几条、把高度均分成几档(图纸第 1656 行);不写或写 0 默认按 10 条
waveformLineColor / waveformLineWidth waveformLineColor="#ffffff" waveformLineWidth="1" 那条跟着增益起伏的折线的颜色和粗细(图纸第 1652~1653 行)
thumbColor thumbColor="#ffffff20" 宽指示条的填充色(图纸第 1655 行,用了近乎全透的白)
thumbIndicatorColor thumbIndicatorColor="#cccccc" 指示条上那条指示线的颜色(图纸第 1657 行)
thumbWidth thumbWidth="160" 指示条的宽度(图纸第 1654 行);不写默认 200
更多属性: id zOrder;textColor——图底每个频段下面的 dB 小字(如 "0.0",程序自带内容)的颜色,引擎认、图纸靠默认值 #cccccc,想改再写 → 详见附录 A

小白坑: 它是四张卡里最"纯显示"的一个:数值曲线、dB 小字全是程序喂的,图纸里没有 subscribe、也没有命令属性,别去给它接水管或加 onClick。<Layout> 的 height 给够(图纸给 100,第 1658 行)——图表被压扁就看不出曲线起伏了。颜色值同样走 # 后 6 位或 8 位的老规矩(第 4 章),写成 4 位缩写(如 #cccc)引擎不认。

5.6 列表与页面级

这一节的 18 张卡都是一整屏或一大块整列表的组件:App 的主要界面——歌曲列表、资料库菜单、设置页、主题选择页——全由这一族的标签撑起来。它们和前面几节的卡有个大不同:标签自己身上几乎没属性(大多只有 id zOrder insets 三个,少数多两个 cellHeight lineSpacing,个别多几个颜色/图属性),真正决定"每一行长什么样"的内容全部写在子标签里——<Table> + <Cell>(列表行样式)、<Grid>(网格样式)、<Items>(菜单项)、<Settings>(设置分组和开关)、<EmptyView>(空页提示)、<ContextMenu>(长按菜单)。这些子标签本书没逐一开卡,所以这一节的正确用法是:整段抄现成主题的写法,只改值、不动结构;想大改样式,就对照每张卡"更多属性"里给的行号,把子标签一起看懂再动手。

每张卡的"常用属性"表只列标签自己身上真正生效的几项(已逐个核对引擎源码),子标签里的样式属性一概不往标签身上搬——这是 5.4 定时关机、5.5 均衡器反复强调的老教训,在这一族里最容易翻车,也最要紧。

行号出处说明:本节除注明外全部取自 Simple 主题的中文版图纸 Simple/theme.zh-Hans.xml(图纸骨架和默认的 theme.xml 一致,只是文字是中文,更好认)。<Layout/> 小跟班张张都带,规矩同 1.4。

歌曲清单 <MediaView> —— App 最主要的那一列列歌

长这样: "媒体"页、专辑详情、搜索结果、最近播放里那一大列歌:每行一个小封面 + 歌名 + 歌手 + 时长 + 一个"更多"小图。标签自己只有 id zOrder insets 三个属性;每行怎么摆、长按弹什么菜单、列表空了显示什么,全在子标签 <Table> <EmptyView> <ContextMenu> 里。Simple 图纸第 104~144 行是活例子——这个标签在 Simple 里一共出现 4 次(第 104、292、360、819 行),写法几乎一样。

┌────────────────────────┐
│ ▣ 歌名           3:45 ⋯│
│ ▣ 另一首歌       4:02 ⋯│
│ ▣ 再一首         2:10 ⋯│
└────────────────────────┘
   每行:小封面(▣) + 标题 + 时长 + "更多"(⋯)

照抄能跑: Simple 第 104~119 行(子标签的其余部分——空页提示、长按菜单——在第 121~143 行,用到时连同这段一起整段抄,别自己发明):

<MediaView id="list" zOrder="1" insets="{112,16,120,28}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <Cell
        artworkImage="eAlbumCoverS"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleSelectedTextColor="#ffffff"
        accessoryImage="eIconMoreAction"
        selectedBackgroundColor="#ffffff10"
        highlightedBackgroundColor="#ffffff10"/>
    </Table>
</MediaView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章);别页的 Layout 可以 #list.xxx 参照它
zOrder zOrder="1" 叠放次序(第 1 章);图纸惯例给 1,让导航条、按钮盖在它上面
insets insets="{112,16,120,28}" 花括号四个数={上,左,下,右},列表内容在自己地盘里四周留的空(图纸第 104 行真实值:上面留 112 是给顶部导航和工具行让位)
更多属性: 就这三个,没有更多。每行行样式在 <Table> 的 <Cell> 里(上面片段可见全部写法);空列表提示在 <EmptyView>、长按菜单在 <ContextMenu>(第 121~143 行)。改整屏样式照着这页抄:Simple/theme.zh-Hans.xml:104。

小白坑: 想改"每行的字色、行高",跑到 <MediaView> 标签身上写 titleTextColor=、cellHeight=——不报错,也毫无反应,引擎在这层只读那三个属性。行样式一律去 <Cell> 里改。另外注意 <Layout/> 铺满整屏和 insets 留空是两回事:Layout 管"列表占屏幕哪块",insets 管"这块里面四周再让出多少";第一行被顶部导航压住了,调的是 insets 第一个数。

字母索引条 <IndexingView> —— 屏幕右边那根 A-Z

长这样: 贴在屏幕右边缘的一竖列 A~Z 小字母,手指按住会浮出一个深色气泡显示当前按到的字母。它和左边的列表是分开的一对:列表负责按字母分区,它只负责"跳"。Simple 图纸第 145~147 行是活例子,凡是带字母排序的列表页(歌曲、专辑、歌手、流派、文件夹、各选择页)右边都摆了这么一根。

                 ┌─┐
   歌曲列表…     │A│
                 │B│
                 │C│
                 │…│
                 │Z│
                 └─┘
   一竖列 A~Z 贴屏幕右边缘,手指按住浮出气泡显示当前字母

照抄能跑: Simple 第 145~147 行原样(width="24" 是条的宽度,上下各留了空当,别让它顶到导航条):

<IndexingView id="indexView" zOrder="9" textColor="#cccccc" touchedBackgroundColor="#000000">
    <Layout right="parent.right" top="parent.top+100"  bottom="parent.bottom-120" width="24"/>
</IndexingView>

常用属性:

属性 怎么写 大白话作用
id id="indexView" 名牌(第 1 章)
zOrder zOrder="9" 叠放次序;图纸给 9,保证盖在列表之上
textColor textColor="#cccccc" 字母的字色(图纸第 145 行真实值)
touchedBackgroundColor touchedBackgroundColor="#000000" 按下时浮出气泡的底色(图纸第 145 行)
touchedBackgroundAlpha touchedBackgroundAlpha="0.5" 气泡底色的透明度,0~1 的小数;引擎认,现成主题没写过,不填就是默认
更多属性: 就这五个,没有更多——字母内容是程序按列表数据自动生成的,图纸里没有任何属性能改"显示哪些字母"。出处:Simple/theme.zh-Hans.xml:145。

小白坑: 它和左边的列表是两个组件:给列表页加索引,得单独再摆一根 <IndexingView>,<MediaView> 身上没有任何"启用索引"的开关。位置全靠 <Layout> 说了算——图纸用 right="parent.right" 贴右边缘、width="24" 定宽;想让字母变小变淡,只有 textColor 能改颜色,字母大小没有属性可调。

选歌清单 <MediaPickerView> —— 批量选歌那屏

长这样: 点"播放全部"旁边的批量按钮后出现的那整屏清单:每行前面一个勾选圈,选完用底部工具条一把操作(删除 / 加入列表 / 移动到文件夹)。Simple 图纸第 459~474 行(MediasPicker 页)是活例子,第 511 行(MediasPicker2 页)同款。这个标签自身比 MediaView 多两个属性:cellHeight lineSpacing 直接写在标签上;勾选圈的图、行文字样式在子标签 <Cell> 里。

┌──────────────────────────┐
│ ○ ▣ 歌名 — 歌手     3:45 │
│ ● ▣ 歌名 — 歌手     4:02 │
└──────────────────────────┘
   每行前面一个勾选圈 ○;选中的行换成实心 ●

照抄能跑: Simple 第 459~474 行原样:

<MediaPickerView id="list" zOrder="1" insets="{110,16,120,24}" cellHeight="56" lineSpacing="8">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
        <Cell
        artworkImage="eAlbumCoverS"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleSelectedTextColor="ffffff"
        imageChecked="eSelected"
        imageUnchecked="eUnselected"
        highlightedBackgroundColor="#ffffff10"/>
</MediaPickerView>

常用属性:

属性 怎么写 大白话作用
id / zOrder id="list" zOrder="1" 名牌 / 叠放次序(第 1 章)
insets insets="{110,16,120,24}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 459 行真实值)
cellHeight cellHeight="56" 每行多高(图纸第 459 行)——注意它写在本标签上,和 MediaView 把行高藏进 <Table> 不同
lineSpacing lineSpacing="8" 行与行之间的缝(图纸第 459 行)
更多属性: 就这五个。行样式、勾选圈图(imageChecked imageUnchecked)全在 <Cell> 子标签里——上面片段就是全部写法。出处:Simple/theme.zh-Hans.xml:459。

小白坑: 两个。其一,想换勾选圈图,去 <Cell> 里改 imageChecked / imageUnchecked,写到 <MediaPickerView> 头上没反应。其二,行高行缝在这里是标签自身的属性(上面刚抄的那行就有),别被前面 MediaView 的坑带反了——同一族里"藏在哪一层"每个标签都不一样,拿不准就照本卡片段改值。

文件夹列表 <FolderView> —— "文件夹"页那屏

长这样: 底部"文件夹"页打开某个文件夹后看到的整屏:上半段子文件夹、下半段音频文件,两个区各自一行行列表。Simple 图纸第 596~644 行(FolderMedias 页)是活例子。标签自己只有 id zOrder insets 三个;内容分成 <FolderSection> 和 <MediaSection> 两个子标签,各有各的 <Table> <Cell> <ContextMenu>。

┌────────────────────────┐
│ ▸ 摇滚收藏            ›│
│ ▸ 现场录音            ›│
│ ▣ 夜航曲 — 歌手       ⋯│
└────────────────────────┘
   上半 ▸ 子文件夹(FolderSection) / 下半 ▣ 文件(MediaSection)

照抄能跑: Simple 第 596~614 行(文件夹区整段原样;文件区 <MediaSection> 在第 615~643 行、结构同款,整页抄时两段都要带上):

<FolderView id="list" zOrder="1" insets="{102,0,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <FolderSection insets="{0,16,10,0}">
        <Table cellHeight="56" lineSpacing="8">
            <Cell
            coverImage="eFolderCover"
            titleFontName="Heiti SC"
            titleFontSize="14"
            titleTextColor="#cdcdcd"
            titleSelectedTextColor="#ffffff"
            subtitleFontName="Heiti SC"
            subtitleFontSize="12"
            subtitleTextColor="#808080"
            subtitleSelectedTextColor="ffffff"
            accessoryImage="eIconRight"
            selectedBackgroundColor="#ffffff10"
            highlightedBackgroundColor="ffffff10"/>
        </Table>
    </FolderSection>
    <!-- 第 615~643 行还有 <MediaSection>(文件区):一套 <Table><Cell> 加一个 <ContextMenu> -->
</FolderView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{102,0,120,0}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 596 行真实值)
更多属性: 就这三个。两个区 <FolderSection> / <MediaSection> 里各自的表样、行样式、长按菜单,见 Simple/theme.zh-Hans.xml:596 整页。

小白坑: 这页的外观分两层管:整屏四周的留空在 <FolderView> 的 insets,两个区各自还能再留一圈自己的 insets(图纸第 598、615 行 <FolderSection insets="{0,16,10,0}">)——发现"文件夹区离屏幕边太近",先分清该调外面大的还是区里小的。改行样式同样是下到 <Table><Cell> 里改,标签身上写颜色属性全是空气。

选文件夹 <FolderPickerView> —— "移动到文件夹"那屏

长这样: 长按某首歌选"移动到文件夹"后出现的整屏:第一行是"返回上一级",往下一个个子文件夹,每行带勾选圈,选完点右上角确认。Simple 图纸第 674~701 行(FolderPicker 页)是活例子。标签自己只有 id zOrder insets 三个;行样式在子标签 <Table> 里,而且里面是两个 <Cell>:<Cell0> 专门管"返回上一级"那一行,<Cell> 管普通文件夹行。

┌──────────────────────────┐
│ ↩ 返回上一级             │
│ ○ ▸ 摇滚收藏             │
│ ● ▸ 华语精选             │
└──────────────────────────┘
   第一行 ↩ 返回上一级;其余每行一个文件夹,选中的打 ●

照抄能跑: Simple 第 674~701 行原样(<Cell0> 管"返回上一级"行、<Cell> 管普通文件夹行,两段都在片段里):

<FolderPickerView id="list" zOrder="1" insets="{58,16,120,0}" >
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <!-- 返回上一级Cell -->
        <Cell0
        coverImage="eFolderUp"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleHighlightedTextColor="#ffffff"
        highlightedBackgroundColor="#ffffff10"/>
        <!-- 文件夹Cell -->
        <Cell
        coverImage="eFolderCover"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleHighlightedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleHighlightedTextColor="ffffff"
        imageChecked="eSelected"
        imageUnchecked="eUnselected"
        selectedBackgroundColor="#ffffff10"
        highlightedBackgroundColor="#ffffff10"/>
    </Table>
</FolderPickerView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,16,120,0}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 674 行真实值)
更多属性: 就这三个。行样式在 <Table> 里分两套(<Cell0> 顶行 / <Cell> 普通行),见 Simple/theme.zh-Hans.xml:674 整页。

小白坑: 这页有两套行样式:改普通文件夹的行改 <Cell>,改第一行"返回上一级"要改 <Cell0>——只改一个,另一边纹丝不动,别以为是 bug。行高行缝在这里写在 <Table> 上(cellHeight="56" lineSpacing="8",第 676 行),和上一卡 MediaPickerView 写在标签自身又不一样——再次提醒:这一族"属性藏在哪一层"每个标签各自为政,永远以现成片段为准。

列表清单 <PlaylistView> —— "播放列表"页那一列列表

长这样: 底部"播放列表"页的整屏:一行一个你建的列表(我的最爱、睡前轻音乐……),每行一张列表封面 + 名称 + 歌曲数 + "更多"小图。Simple 图纸第 221~243 行(Playlists 页)是活例子。标签自己只有 id zOrder insets 三个;行样式在 <Table><Cell> 里,长按菜单(重命名 / 清空 / 删除)在 <ContextMenu> 里。

┌────────────────────────┐
│ ▣ 我的最爱     128 首 ⋯│
│ ▣ 睡前轻音乐    32 首 ⋯│
└────────────────────────┘
   每行:列表封面 ▣ + 名称 + 歌曲数 + 更多 ⋯

照抄能跑: Simple 第 221~237 行(第 238~242 行还有一个 <ContextMenu>,整页抄时一起带上):

<PlaylistView id="list" zOrder="1" insets="{58,16,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <Cell
        coverImage="ePlaylistCover"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        accessoryImage="eIconMoreAction"
        highlightedBackgroundColor="ffffff10"
        selectedBackgroundColor="#ffffff10"
        titleSelectedTextColor="#ffffff"
        subtitleSelectedTextColor="ffffff"/>
    </Table>
</PlaylistView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,16,120,0}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 221 行真实值)
更多属性: 就这三个。行样式、长按菜单见 Simple/theme.zh-Hans.xml:221 整页。

小白坑: 老规矩:行样式、行高全在 <Table><Cell> 里改,标签身上只有那三个属性。顺带认一个图纸里的小怪相:上面第 233 行 highlightedBackgroundColor="ffffff10"、第 236 行 subtitleSelectedTextColor="ffffff" 开头没写 #——引擎对带不带 # 都认(# 只是可选前缀),所以这两处照样生效,不是 bug;但为了自己不改错,建议统一写成带 # 的形式。

选列表弹层 <PlaylistPickerView> —— "添加到列表"那屏

长这样: 选中歌后点"加入列表"弹出的整屏:一行一个列表,点一下就加进去。Simple 图纸第 408~421 行(PlaylistPicker 页)是活例子。标签自身有五个属性(多 cellHeight lineSpacing,直接写在标签上);行样式在子标签 <Cell> 里。

┌──────────────────────────┐
│ ▣ 我的最爱                │
│ ▣ 睡前轻音乐              │
└──────────────────────────┘
   点其中一行,就把当前歌加入那个列表

照抄能跑: Simple 第 408~421 行原样:

<PlaylistPickerView id="list" zOrder="1" insets="{58,16,120,16}" cellHeight="56" lineSpacing="8">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Cell
    coverImage="ePlaylistCover"
    titleFontName="Heiti SC"
    titleFontSize="14"
    titleTextColor="#cdcdcd"
    titleSelectedTextColor="#ffffff"
    subtitleFontName="Heiti SC"
    subtitleFontSize="12"
    subtitleTextColor="#808080"
    subtitleSelectedTextColor="#ffffff"
    highlightedBackgroundColor="#ffffff10"/>
</PlaylistPickerView>

常用属性:

属性 怎么写 大白话作用
id / zOrder id="list" zOrder="1" 名牌 / 叠放次序(第 1 章)
insets insets="{58,16,120,16}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 408 行真实值)
cellHeight cellHeight="56" 每行多高(写在标签自己身上,图纸第 408 行)
lineSpacing lineSpacing="8" 行与行的缝(写在标签自己身上,图纸第 408 行)
更多属性: 就这五个。行样式全在 <Cell> 子标签里——上面片段就是全部写法。出处:Simple/theme.zh-Hans.xml:408。

小白坑: 行高行缝是标签自身的,字色字图是 <Cell> 的——改错了地方就是"改了没反应"。另外注意它没有 <Table> 包装:<Cell> 直接站在 <PlaylistPickerView> 里面(上面片段第 3 行就是 <Cell>),和 MediaView 那套多一层 <Table> 的结构不同,整段抄的时候别多删也别多补这一层。

播放队列 <QueueView> —— 接下来要放的那一列

长这样: 播放页点队列图标进入的"播放队列"整屏:一行一首排队的歌,每行右边一个叉,点叉移除,长按可拖动改顺序。Simple 图纸第 1297~1313 行(MediaQueue 页)是活例子。标签自身五个属性(cellHeight lineSpacing 写在标签上);行样式在子标签 <Cell> 里。

┌──────────────────────────┐
│ 1. 夜航曲 — 歌手      ✕  │
│ 2. 晨光 — 歌手        ✕  │
└──────────────────────────┘
   每行:序号 + 曲名 + 歌手 + 移除叉 ✕;长按行可拖动排序

照抄能跑: Simple 第 1297~1313 行原样:

<QueueView id="list" zOrder="1" insets="{48,16,80,10}" cellHeight="36" lineSpacing="0.5">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
        <Cell
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        titleHighlightedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleSelectedTextColor="#ffffff"
        subtitleHighlightedTextColor="#ffffff"
        accessoryImage="eCloseX"
        selectedBackgroundColor="#ffffff10"
        highlightedBackgroundColor="#ffffff10"/>
</QueueView>

常用属性:

属性 怎么写 大白话作用
id / zOrder id="list" zOrder="1" 名牌 / 叠放次序(第 1 章)
insets insets="{48,16,80,10}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 1297 行真实值)
cellHeight cellHeight="36" 每行多高(写在标签自己身上;图纸给 36,比一般列表页的 56 矮,队列看得更多)
lineSpacing lineSpacing="0.5" 行与行的缝(写在标签自己身上;图纸给 1.5,几乎不留缝)
更多属性: 就这五个。行样式在 <Cell> 里——注意它没有封面图属性,队列行只有文字和右边的叉(accessoryImage="eCloseX",图纸第 1310 行)。出处:Simple/theme.zh-Hans.xml:1297。

小白坑: 行里的"歌名 + 队列位置 + 移除叉"都是程序自带的,图纸里没有属性往队列里"加一首歌"或"改叉的形状以外的图标逻辑"——它只管这一屏长什么样。行高行缝写在标签身上(本卡片段第 1 行就有),字色去 <Cell> 里改——和选列表弹层同一套分层,别站错层。

歌手清单 <ArtistsView> —— 歌手那一列

长这样: 资料库点"歌手"进去的整屏:一行一个歌手,每行一张圆头像 + 歌手名 + 歌曲数 + 右箭头。Simple 图纸第 875~891 行(Artists 页)是活例子。标签自己只有 id zOrder insets 三个;行样式在 <Table><Cell> 里。

┌──────────────────────────┐
│ ◉ 朴树      12 首       › │
│ ◉ 周杰伦    48 首       › │
└──────────────────────────┘
   每行:头像 ◉ + 歌手名 + 歌曲数 + 箭头 ›

照抄能跑: Simple 第 875~891 行原样:

<ArtistsView id="list" zOrder="1" insets="{58,16,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <Cell
        coverImage="eArtistCover"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleSelectedTextColor="ffffff"
        accessoryImage="eIconRight"
        highlightedBackgroundColor="#ffffff10"/>
    </Table>
</ArtistsView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,16,120,0}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 875 行真实值)
更多属性: 就这三个。行样式见上面片段;引擎其实还认一个 <Grid> 子标签(网格摆法),但所有现成主题都用 <Table>,想试网格先副本验证。出处:Simple/theme.zh-Hans.xml:875。

小白坑: 和歌曲清单一个脾气:标签自身没几个属性,头像、字色、行高全在 <Table><Cell> 里改。注意 <Layout> 里 right="parent.right-28"——右边特意留 28,是给那根 A-Z 索引条让位的(下一卡讲),列表页配索引条时两边要这样配合。

专辑网格 <AlbumsView> —— 专辑那一屏格子

长这样: 资料库点"专辑"进去的整屏:两列网格,每格一张封面 + 专辑名 + 歌手。Simple 图纸第 182~201 行(Albums 页)是活例子。标签自己只有 id zOrder insets 三个;"两列、格子多大、字什么色"全在子标签 <Grid> 里。

┌──────────┬──────────┐
│   封面   │   封面   │
│  专辑名  │  专辑名  │
└──────────┴──────────┘
   两列并排的格子,每格:封面 + 专辑名 + 歌手

照抄能跑: Simple 第 182~200 行原样:

<AlbumsView id="list" zOrder="1" insets="{112,16,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <Grid columns="2" lineSpacing="16" itemSpacing="16">
        <Cell
        coverImage="eAlbumCoverS"
        coverCornerRadius="8"
        coverBorderWidth="0.5"
        coverBorderColor="#ffffff50"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleTextAlignment="left"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#a0a0a0"
        highlightedBackgroundColor="#ffffff10"
        selectedBackgroundColor="#ffffff10"
        titleSelectedTextColor="#ffffff"/>
    </Grid>
</AlbumsView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{112,16,120,0}" 网格内容四周留的空,花括号四个数={上,左,下,右}(图纸第 182 行真实值)
更多属性: 就这三个。列数 columns、格间距 itemSpacing、行间距 lineSpacing 写在 <Grid> 子标签上(图纸第 184 行真实值 columns="2" lineSpacing="16" itemSpacing="16");格子的封面、圆角、字色在 <Grid> 里的 <Cell>。出处:Simple/theme.zh-Hans.xml:182。

小白坑: 专辑页是网格不是列表:子标签叫 <Grid> 不叫 <Table>,想改"一行摆几个"就改 <Grid> 的 columns——这个属性在 <MediaView> 那族列表里是没有的。另外封面圆角、描边写在 <Cell> 上(coverCornerRadius coverBorderColor 等,图纸第 187~189 行),不写到标签身上。

流派清单 <GenresView> —— 流派那一列

长这样: 资料库点"流派"进去的整屏:一行一个流派(摇滚、古典、电子……),每行一张封面 + 流派名 + 歌曲数 + 右箭头。Simple 图纸第 912~928 行(Genres 页)是活例子。标签自己只有 id zOrder insets 三个;行样式在 <Table><Cell> 里。

┌──────────────────────────┐
│ ▣ 摇滚        56 首      │  <- 每行: 封面+流派名+数量
│ ▣ 古典        23 首      │
└──────────────────────────┘

照抄能跑: Simple 第 912~928 行原样(和歌手页结构完全同款,只是占位图换成流派封面 eGenreCover):

<GenresView id="list" zOrder="1" insets="{58,16,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right-28" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <Cell
        coverImage="eGenreCover"
        titleFontName="Heiti SC"
        titleFontSize="14"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        subtitleFontName="Heiti SC"
        subtitleFontSize="12"
        subtitleTextColor="#808080"
        subtitleSelectedTextColor="ffffff"
        accessoryImage="eIconRight"
        highlightedBackgroundColor="#ffffff10"/>
    </Table>
</GenresView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,16,120,0}" 列表内容四周留的空,花括号四个数={上,左,下,右}(图纸第 912 行真实值)
更多属性: 就这三个。行样式见上面片段;引擎同样备了 <Grid> 网格摆法,但所有现成主题都是 <Table>,想试先副本验证。出处:Simple/theme.zh-Hans.xml:912。

小白坑: 它每行的封面用的是流派封面图 coverImage="eGenreCover"(第 916 行)——这是"没有该流派封面时的占位图",真正的图由程序自动去找;图纸里写别的图名只会换占位图,不会"指定所有流派都用这张图"。行样式站层老规矩:标签身上只认那三个。

资料库菜单 <LibraryView> —— "资料库"页那张入口清单

长这样: 底部"资料库"页的整屏:一行一个入口——最近播放、歌手、专辑、流派、我的最爱、文件夹。Simple 图纸第 731~755 行(Library 页)是活例子。标签自己只有 id zOrder insets 三个;入口内容写在子标签 <Items> 里(有哪些项、每项的图和文字),行样式写在 <Table><CellStyle> 里。

┌────────────────────────┐
│ 🕐 最近播放           ›│
│ 🎤 歌手               ›│
│ 💿 专辑               ›│
└────────────────────────┘
   一行一个入口,内容写在子标签 <Items> 里

照抄能跑: Simple 第 731~755 行原样(第 744 行 borderColor="ffffff10" 没带 # 也照样生效,带不带都认,见小白坑):

<LibraryView id="menu" zOrder="1" insets="{58,20,34,20}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <CellStyle
        imageOffset="4"
        imageCornerRadius="6"
        titleFontName="Heiti SC"
        titleFontSize="15"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff"
        accessoryImage="eIconRight"
        highlightedBackgroundColor="#ffffff10"
        borderWidth="0.5"
        borderColor="ffffff10"
        cornerRadius="6"/>
    </Table>
    <Items>
        <Recents icon="eRecents" title="最近播放"/>
        <Artists icon="eArtists" title="歌手"/>
        <Albums icon="eAlbums" title="专辑"/>
        <Genres icon="eGenres" title="流派"/>
        <Favorites icon="eFavorites" title="我的最爱"/>
        <Folders icon="eFolders" title="文件夹"/>
    </Items>
</LibraryView>

常用属性:

属性 怎么写 大白话作用
id id="menu" 名牌(第 1 章);图纸习惯给菜单页起名 menu
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,20,34,20}" 清单内容四周留的空,花括号四个数={上,左,下,右}(图纸第 731 行真实值)
更多属性: 就这三个。想改入口的增删和文字,动 <Items> 里那一排(每张卡就是一个入口标签:Recents / Artists / Albums / Genres / Favorites / Folders);想改行长相,动 <Table><CellStyle>。出处:Simple/theme.zh-Hans.xml:731。

小白坑: 这一族里它和下一卡 MoreView 是内容写在图纸上的:入口项就在 <Items> 里摆着,加一行入口=在 <Items> 里加一个对应标签、配上 icon= 和 title=;而前面那些列表(歌、专辑、歌手……)的内容是 App 数据自动灌进来的,图纸里找不到"某一首歌"。别拿错改法:改 <Items> 是增删入口,改 titleTextColor 是调外观,两处不在同一层。

更多菜单 <MoreView> —— "更多"页那张入口清单

长这样: 底部"更多"页的整屏:一行一个入口——媒体上传、歌词上传、主题、设置、关于。Simple 图纸第 766~789 行(More 页)是活例子。和资料库菜单完全同款结构:标签自己只有 id zOrder insets;内容在 <Items> 里,行样式在 <Table><CellStyle> 里。

┌──────────────────────────┐
│ ⇄ 媒体上传             ›  │  <- <Items> 里的一项
│ ✎ 歌词上传             ›  │     一行一个入口
│ ◑ 主题                 ›  │
│ ⚙ 设置                 ›  │
└──────────────────────────┘

照抄能跑: Simple 第 766~789 行原样:

<MoreView id="menu" zOrder="1" insets="{58,20,34,20}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Table cellHeight="56" lineSpacing="8">
        <CellStyle
        imageOffset="4"
        imageCornerRadius="6"
        titleFontName="Heiti SC"
        titleFontSize="15"
        titleTextColor="#cdcdcd"
        titleSelectedTextColor="#ffffff10"
        accessoryImage="eIconRight"
        highlightedBackgroundColor="#ffffff10"
        borderWidth="0.5"
        borderColor="ffffff10"
        cornerRadius="6"/>
    </Table>
    <Items>
        <FileTransfer icon="eMoreFileTransfer" title="媒体上传"/>
        <LyricTransfer icon="eMoreLyricTransfer" title="歌词上传"/>
        <Themes icon="eMoreThemes" title="主题"/>
        <Settings icon="eMoreSettings" title="设置"/>
        <About icon="eMoreAbout" title="关于"/>
    </Items>
</MoreView>

常用属性:

属性 怎么写 大白话作用
id id="menu" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,20,34,20}" 清单内容四周留的空,花括号四个数={上,左,下,右}(图纸第 766 行真实值)
更多属性: 就这三个。入口增删改 <Items>(FileTransfer / LyricTransfer / Themes / Settings / About),行外观改 <Table><CellStyle>。出处:Simple/theme.zh-Hans.xml:766。

小白坑: 抄上面这段时留意一处可疑值:第 775 行选中字色写成了 titleSelectedTextColor="#ffffff10"——# 后 8 位、末两位 10 是透明度,等于近乎全透的白,选中时字几乎看不见,疑似把上一行 #ffffff10 误照了过来,建议改回 #ffffff。另外第 779 行 borderColor="ffffff10" 没带 #——引擎带不带都认,照样生效,不是 bug。这页真正的坑还是"别原样抄可疑值",改自己主题时顺手修正它。

搜索整屏 <LibrarySearchView> —— 资料库搜索的结果页

长这样: 资料库页点搜索后整屏:顶上搜索框(SearchBar,5.2 讲过),下面按"歌曲 / 歌手 / 专辑 / 列表"分区显示结果,每个区一个标题 + 一列结果。Simple 图纸第 961~1039 行(LibrarySearch 页)是活例子。标签自己只有 id zOrder insets 三个;四个结果区是四个子标签 <MediaSection> <ArtistSection> <AlbumSection> <PlaylistSection>,每区里再各嵌一套 <Table><Cell>。

┌────────────────────────┐
│   [ 🔍 输入关键词 ]    │
│── 歌曲                 │
│ ▣ 结果…          3:45 ⋯│
│── 歌手                 │
└────────────────────────┘
   搜索框(SearchBar) + 分区结果(MediaSection / ArtistSection…)

照抄能跑: Simple 第 961~977 行(歌曲区整段原样;图纸里后面还依次排着歌手、专辑、列表三个区,第 991~1038 行,结构同款,整页抄时四区都要带上):

<LibrarySearchView id="list" zOrder="1" insets="{112,0,120,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <MediaSection insets="{0,16,0,16}" titleTextColor="cccccc">
        <Table cellHeight="56" lineSpacing="8">
            <Cell
            artworkImage="eAlbumCoverS"
            titleFontName="Heiti SC"
            titleFontSize="14"
            titleTextColor="#cdcdcd"
            titleSelectedTextColor="#ffffff"
            subtitleFontName="Heiti SC"
            subtitleFontSize="12"
            subtitleTextColor="#808080"
            subtitleSelectedTextColor="#ffffff"
            accessoryImage="eIconMoreAction"
            highlightedBackgroundColor="#ffffff10"/>
        </Table>
    </MediaSection>
    <!-- 第 978~989 行该区还有一个 <ContextMenu>;其后 <ArtistSection>/<AlbumSection>/<PlaylistSection> 三区同款 -->
</LibrarySearchView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{112,0,120,0}" 结果内容四周留的空,花括号四个数={上,左,下,右}(图纸第 961 行真实值:上面留 112 给搜索框让位)
更多属性: 就这三个。四个区各自的标题字色(titleTextColor)和区留白(insets)写在区标签上(图纸第 963 行),行样式在区里的 <Cell>。完整四区见 Simple/theme.zh-Hans.xml:961。

小白坑: 三个。其一,"结果有几条、是什么"全由程序搜出来灌进去,图纸里找不到任何一行写"显示某某歌曲"。其二,想改区标题颜色,去 <MediaSection> 等区标签上改 titleTextColor=——它是区标签的属性,不是本标签的。其三,顶部搜索框不归它管——那是独立的 <SearchBar>(同页第 948~960 行),改搜索框外观去动那个标签。

设置整屏 <SettingsView> —— "设置"页那列开关

长这样: 更多页点"设置"进去的整屏:分组标题(播放 / 音效 / 歌词)+ 一组组行:行左边标题、右边一个开关或一个箭头。Simple 图纸第 1331~1350 行(Settings 页)是活例子。标签自己只有 id zOrder insets 三个;设置项内容全部写在图纸上:子标签 <Settings> 里装一个个 <Group>,<Group> 里再装 <Switcher>(开关行)、<SettingEntry>(点进去别的页的行)等。

┌──────────────────────────┐
│ 播放                     │  <- <Group title="播放">
│ 淡入淡出           [开]  │  <- <Switcher key="crossfading">
│ 摇一摇换歌         [关]  │
└──────────────────────────┘

照抄能跑: Simple 第 1331~1342 行("播放"组整段原样;图纸里"音效 / 歌词"两组在第 1343~1348 行,同款写法,整页抄时都要带上):

<SettingsView id="list" zOrder="1" insets="{58,0,80,0}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Settings>
        <Group insets="{0,20,0,20}" title="播放" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#909090">
            <Switcher key="crossfading" title="淡入淡出" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" imageOn="eSwitchOn" imageOff="eSwitchOff" cellHeight="58" separatorColor="#ffffff10"/>
            <Switcher key="shake-to-switch" title="摇一摇换歌" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" imageOn="eSwitchOn" imageOff="eSwitchOff" cellHeight="58" separatorColor="#ffffff10"/>
            <SleepMode titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" doubleLine="true"
                subtitleFontName="Menlo" subtitleFontSize="12" subtitleTextColor="#a0a0a0" cellHeight="60" separatorColor="#ffffff10" highlightedBackgroundColor="#ffffff10" accessoryImage="eIconRight"/>
            <Switcher key="keep-screen-on" title="播放界面保持屏幕常亮" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" imageOn="eSwitchOn" imageOff="eSwitchOff" cellHeight="58" separatorColor="#ffffff10"/>
            <Switcher key="remember-playback" title="记忆播放位置" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" imageOn="eSwitchOn" imageOff="eSwitchOff" cellHeight="58" separatorColor="#ffffff10"/>
            <Switcher key="resume-on-startup" title="启动时恢复播放" titleFontName="Heiti SC" titleFontSize="15" titleTextColor="#cdcdcd" imageOn="eSwitchOn" imageOff="eSwitchOff" cellHeight="58" separatorColor="#ffffff10"/>
        </Group>
        <!-- 第 1343~1348 行还有"音效"组(含 SettingEntry)和"歌词"组(含 Switcher),同款写法 -->
    </Settings>
</SettingsView>

常用属性:

属性 怎么写 大白话作用
id id="list" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,0,80,0}" 设置内容四周留的空,花括号四个数={上,左,下,右}(图纸第 1331 行真实值)
更多属性: 就这三个。每组的组名和字色写在 <Group> 上(title= titleTextColor=),每行的开关图、行高、分隔线写在 <Switcher> / <SleepMode> / <SettingEntry> 等行标签上——都在 <Settings> 里面。出处:Simple/theme.zh-Hans.xml:1331。

小白坑: 这个页最容易被新手"想当然":看到"淡入淡出"这行想把它删掉,是在 <Settings> 里删掉对应的 <Switcher> 那一行;看到开关想换图,是改那行的 imageOn / imageOff——全都发生在子标签层,<SettingsView> 自己身上什么都没有。反过来,key="crossfading" 这种是接线名(对应 App 内部哪条设置),删行可以,改 key 会把行接错线,别乱动。

可视化选择 <VisualizerSelectView> —— 动效二选一的那屏

长这样: 播放页切到视觉化后点选择按钮出现的整屏:一行一种可视化效果(经典、Milkdrop……),选中行右边打个勾。Simple 主题没有这页;活例子在 CoverFlow 中文版图纸第 1468~1482 行(VisualizerSelect 页)。标签自己只有 id zOrder insets 三个;行样式在 <Table><CellStyle> 里。

┌────────────────────────┐
│ 经典                  ✓│
│ Milkdrop               │
│ 频谱                   │
└────────────────────────┘
   一行一种动效,当前款右边打勾

照抄能跑: CoverFlow 第 1468~1482 行原样(cChecked 是 CoverFlow 主题文件夹里的图名,搬去别的主题要换成你有的勾图):

<VisualizerSelectView id="menu" zOrder="1" insets="{0,0,0,0}">
    <Layout left="parent.left+16" top="#navbar.bottom+10" right="parent.right-16" bottom="parent.bottom"/>
    <Table cellHeight="60" lineSpacing="0.5">
        <CellStyle
        titleFontName="Heiti SC"
        titleFontSize="15"
        titleTextColor="#ffffff"
        accessoryImage="cChecked"
        separatorWeight="0.5"
        separatorColor="#ffffff20"
        highlightedBackgroundColor="#ffffff20"
        visualBorderWidth="0.5"
        visualBorderColor="cccccc50"
        visualCornerRadius="4"/>
    </Table>
</VisualizerSelectView>

常用属性:

属性 怎么写 大白话作用
id id="menu" 名牌(第 1 章)
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{0,0,0,0}" 内容四周留的空,花括号四个数={上,左,下,右}(图纸第 1468 行给了全 0——留空全靠 <Layout> 里的 +16 -16)
更多属性: 就这三个。行高行缝在 <Table> 上(图纸第 1470 行 cellHeight="60" lineSpacing="0.5");字色、勾图、分隔线,还有"每行左侧预览小窗"的描边圆角(visualBorderWidth visualBorderColor visualCornerRadius)在 <CellStyle> 里。出处:CoverFlow/theme.zh-Hans.xml:1468。

小白坑: 可选的动效有哪些由 App 自己决定,图纸里没有"加一款可视化"的属性;这一屏只管列表长相。<Layout> 里 top="#navbar.bottom+10" 是参照本页导航条摆的(第 1469 行)——抄去别的页时记得参照名要对得上,不然这条摆放会失效。

主题选择网格 <ThemeListView> —— 换主题那屏缩略图

长这样: 更多页点"主题"进去的整屏:三列网格,每格一张主题预览缩略图 + 主题名,当前主题打个勾。Simple 图纸第 1574~1587 行(Themes 页)是活例子。标签自己只有 id zOrder insets 三个;网格和格子样式在 <Grid><CellStyle> 里。

┌─────┬─────┬─────┐
│ 缩略 │ 缩略 │ 缩略 │  <- 每格: 主题预览图+名称
│ 图A  │ 图B  │ 图C  │     三列网格
│ 名称 │ 名称✓│ 名称 │  <- 当前主题右上打勾
└─────┴─────┴─────┘

照抄能跑: Simple 第 1574~1587 行原样(snapshot="Snapshot" 指各主题文件夹里那张预览图的文件名,不用你管;eChecked 是勾图):

<ThemeListView id="themeList" zOrder="1" insets="{58,16,80,16}">
    <Layout left="parent.left" top="parent.top" right="parent.right" bottom="parent.bottom"/>
    <Grid columns="3" lineSpacing="10" itemSpacing="10">
        <CellStyle
        snapshot="Snapshot"
        titleFontName="Heiti SC"
        titleFontSize="15"
        titleTextColor="#ffffff"
        titleTextAlignment="left"
        maskColor="#00000080"
        imageChecked="eChecked"
        highlightedBackgroundColor="#202020"/>
    </Grid>
</ThemeListView>

常用属性:

属性 怎么写 大白话作用
id id="themeList" 名牌(第 1 章);本页别处用 #themeList 参照它
zOrder zOrder="1" 叠放次序;铺底用 1
insets insets="{58,16,80,16}" 网格内容四周留的空,花括号四个数={上,左,下,右}(图纸第 1574 行真实值)
更多属性: 就这三个。列数 / 格间距在 <Grid> 上(图纸第 1576 行 columns="3" lineSpacing="10" itemSpacing="10");名字字号、图上的黑色半透明遮罩 maskColor、选中勾图 imageChecked 在 <CellStyle> 里。出处:Simple/theme.zh-Hans.xml:1574。

小白坑: 主题清单的内容(有哪些主题可选)由 App 扫 Themes 文件夹自动列出来,图纸里没有"加一个主题"的属性;想让自己的主题出现在这屏里,做法是第 2 章讲的"复制文件夹改名",而不是改图纸。缩略图也不用你在图纸里指定路径——snapshot="Snapshot" 是约定名,每个主题文件夹自备一张。

元数据编辑 <ID3TagView> —— 编辑标签那一整屏

长这样: 长按一首歌点"编辑元数据"进去的整屏:上面一张大封面(可点换),下面一行行可点的字段(标题、歌手、专辑……),点进去弹出键盘改字。Simple 图纸第 1060~1062 行(ID3TagEdit 页)是活例子。这一族的异类:没有子标签,字段行全由程序生成,但标签自身反而有七个属性可写(比别的成员多)。

┌────────────────────────┐
│     ┌────────┐         │
│     │ 封面   │ ✎       │
│     └────────┘         │
│ 标题   夜航曲         ›│
│ 歌手   歌手名         ›│
└────────────────────────┘
   大封面(✎=换封面) + 一行行字段(标题/歌手…),值用 textColor

照抄能跑: Simple 第 1060~1062 行原样(本页没有写 insets,留空全靠 <Layout> 的 +32 -32):

<ID3TagView id="list" zOrder="1" titleColor="#cdcdcd" textColor="#ffffff" borderColor="#ffffff50" coverDefault="eNoCover" coverOpIcon="eCoverEdit">
    <Layout left="parent.left+32" top="parent.top+50" right="parent.right-32" bottom="parent.bottom"/>
</ID3TagView>

常用属性:

属性 怎么写 大白话作用
id / zOrder id="list" zOrder="1" 名牌 / 叠放次序(第 1 章)
titleColor titleColor="#cdcdcd" 每行左边"字段名"(标题 / 歌手…)的字色(图纸第 1060 行)
textColor textColor="#ffffff" 每行右边"当前值"的字色(图纸第 1060 行)
borderColor borderColor="#ffffff50" 行与行之间分隔线的颜色(图纸第 1060 行;8 位色末两位 50 是透明度)
coverDefault coverDefault="eNoCover" 这首歌没有封面时垫的占位图(图名不带 .png,图纸第 1060 行)
coverOpIcon coverOpIcon="eCoverEdit" 封面角上"换封面"操作的小图标(图纸第 1060 行)
更多属性: 七个属性全部在本卡表里,它没有子标签,也没有 insets(引擎在这层不读它)——四周留空全靠 <Layout>。出处:Simple/theme.zh-Hans.xml:1060。

小白坑: 两个。其一,想改"有哪些字段"——改不了,字段行是程序按 ID3 标签种类固定生成的,图纸只负责配色配图;保存、关闭按钮也不归它管(那是本页顶部的两个 IconButton,图纸第 1052~1058 行,命令词 id3tags-save)。其二,它是唯一没有 insets 的这一族成员——给别的卡养成的"insets 三件套"习惯到这里要收一收,写了也不生效,留空写进 <Layout> 里。

第 6 章 图片怎么准备

前面每章的卡片里反复出现过 ePlay、eAlbumCover 这样的图片名。这一章把图片这件事讲全:放哪、怎么起名、图纸里怎么引用、格式选什么、以及新手最容易踩的几个坑。

6.1 图片放哪

第 2 章说过,一个主题就是一个收纳盒(文件夹)。图片就放进这个盒子里,和图纸(theme.xml)同一层。比如 Simple 主题里的播放按钮,完整路径是 AMPod/Themes/Simple/ePlay@3x.png。

引擎装载一个主题时,只在这个盒子里找图。图片放到别处(桌面、盒子里再套的子文件夹)都不算数——图纸里名字写对了也永远找不到。

6.2 名字怎么起

一张图的全名长这样:eFavMark@3x.png。拆开是四段:

段 例 是什么
前缀 e 小写字母 e。AMPod 现成主题的零件图几乎都以它开头(可记成"图元"的 e,唯一例外下面讲),照这个传统来,别"创新"换别的字母
意思 FavMark 这张图是干什么的,驼峰拼法(每个单词首字母大写,连成一串;"收藏标记")
清晰度 @3x iOS 的屏幕清晰度档位,见下
格式 .png 图片格式,见 6.4
@2x / @3x 是什么: iOS 的屏幕有"粗"有"细"(老屏一个像素点大、新视网膜屏一个像素点小得多),同一张图按精细度出成不同"档位"是 iOS 的老传统,@2x、@3x 就是两档标记。在这套主题引擎里,图纸写基名后引擎自动补档位找图:先找 @3x 那张,没有再找 @2x(顺序固定,跟设备型号无关)。所以新手只要出一档、出 @3x 就够——Simple 的 110 张图实测全部只带一个档位后缀(86 张 @3x、24 张 @2x),没有一张同时出两档,也没有一张不带后缀。

"全都以 e 开头"有没有例外? 有一个:Snapshot@2x.png——主题选择页的缩略图(5.6 主题清单卡说过它是约定名)。它是给"整个盒子"拍的照片,不是界面零件图,所以不遵守 e 前缀。除此之外的图,照 e 前缀走就对了。

意思那段怎么拼没有硬性规定: 有完整短语(eContextMoveToFolder,长按菜单里"移到文件夹"的图),也有缩写(eLrcSearch)。拿不准就翻现成主题的图,见样学样。

6.3 图纸(XML)里怎么引用

一句话规矩:只写基名——不带 .png,也不带 @2x/@3x。 下面每条都是 Simple 图纸里逐字能查到的真实写法:

引用写法 用在哪
icon="eAlbums" 底部标签页的图标(图纸第 750 行)
imageNormal="ePlay" 按钮的常态图(5.2 IconButton,图纸第 1151 行)
image="eIconRight" 单独摆的一张图(5.1 Icon,图纸第 1639 行)
defaultImage="eAlbumCover" 图片占位——封面还没来时先垫它(5.1 Image,图纸第 1070 行)
coverOpIcon="eCoverEdit" 封面角上"换封面"的小图标(5.6 ID3TagView,图纸第 1060 行)
imageChecked="eChecked" 网格里格子选中时的勾图(5.6 Grid,图纸第 1584 行)
规矩的反面:引擎其实"宽容"——imageNormal="ePlay.png"、imageNormal="ePlay@3x" 这类带后缀的写法,它兜一圈也能找回来。但别依赖这份宽容:现成图纸几千行全是基名写法(上面表格就是证据),跟着写最稳;而有些档位写法是真坑,比如图纸写 ePlay@2x、盒子里却只有 @3x 图——引擎指定档位时就不再退档,结果图不显示、也不报错(和 5.1 Icon 卡的小白坑同一个)。矩形板铺图用的 fillImage="某图名" 也一样写基名。

一个特殊引用: 图纸里偶尔见到 defaultImage="context:app.icon" 这样的写法——冒号开头的 context: 不是文件名,别去文件夹里找这个 png。它走的是第 3 章讲的"水管":数据由程序当场递过来(这里是 App 自己的图标),跟磁盘上的图片文件没关系。

6.4 格式与透明

6.5 加自己的图、改现成的图

加一张新图,两步:

  1. 按 6.2 的规矩做好图,比如 eMyButton@3x.png,放进你的主题文件夹,和 theme.xml 同层;
  2. 在图纸里按 6.3 的规矩引用基名,比如 <IconButton imageNormal="eMyButton" ...>。
改一张现成图: 把你做好的新图同名覆盖进主题文件夹里那张(建议先把原图拷一份留备份)。文件名对上就生效,图纸一个字都不用改。

6.6 小白坑汇总

附录 A 全部属性速查

第 5 章每张卡片只细讲了最高频的几个属性,其余"更多属性"一行带过、或拆进了子标签。这里把它们全部汇总成一张表,按属性名字母顺序排(不区分大小写),供你记不准某个属性属于谁、想快速查含义时翻。

怎么读这张表:


A

属性 出现在 一句话大白话
accessoryImage (Cell) 歌曲/队列等列表行、定时关机 CellStyle、资料库与更多菜单 CellStyle、可视化选择 CellStyle 每行最右边那枚小图:">"箭头、"⋯"更多、"✕"移除、"✓"勾
activeColor LyricDisplay、LyricLineShow、KaraokeShow 当前那一行歌词 / 已经唱到的字的颜色
activeFontName LyricDisplay 当前行的字体名(不走字帖,直接写)
activeFontRef LyricDisplay 当前行的字帖名
activeFontSize LyricDisplay 当前行的字号
albumImage VinylTurntable 黑胶盘心那张图(占位封面,播放时被水管里的真封面顶替)
align Stack 交叉方向对齐:start 靠头 / center 居中 / end 靠尾,写 centerX、centerY 也认
arrowImage EQSelectBox 预设框右边的下拉箭头图
arrowRightOffset EQSelectBox 箭头离框右边留多少空
arrowTintColor EQSelectBox ⚠ 引擎不读——改箭头颜色只能换 arrowImage 那张图
artworkImage (Cell) 歌曲类列表行、选歌清单 Cell、搜索结果区 Cell 每行左边小封面的占位图(真封面由程序喂)
B
属性 出现在 一句话大白话
backgroundColor Page 屏底、TabBar 底、SearchBar 框底、EQMeterChart 图表底、列表 (Cell) 整行底色 底色的通用名字,到处都有
bins SpectrumView 频谱柱子的根数(不写默认 80;写 0 当没写)
blur Box、FloatingButton、Scroll(六件与 Box 同款) 底下垫毛玻璃:dark 深 / light 浅
blurStyle Button 毛玻璃样式(引擎读;本书无展开示例,照抄现成)
blurType Rectange 矩形板的毛玻璃类型:dark / light
bottom (Layout) 下边对齐到哪(1.1 的图钉孔)
borderColor Box、Rectange、Image、Text、TextView、TextIconButton、FloatingButton、Visual、SearchBar、Toolbar、EQSelectBox、ID3TagView(这里是行间分隔线色)、Scroll、(Cell) 行描边 描边颜色;必须和 borderWidth 一起写才生效
borderWidth 同 borderColor 那一大串 描边粗细
C
属性 出现在 一句话大白话
cellHeight CountdownView、选歌清单、选列表弹层、播放队列(这四个写标签自身)、各列表的 <Table>、设置页 <Switcher>/<SleepMode> 行 每一行多高
centerX (Layout) 水平居中:中线对准哪条竖线
centerY (Layout) 垂直居中:中线对准哪条横线
clip Box、Scroll 探出盒边的内容剪掉
clipToBounds Rectange 矩形板版的"剪掉探出内容"(板子家叫这个名字)
color —— ⚠ 只在"怎么读这本书"的图例里当示意用;真实组件没有这个属性,文字颜色是 textColor
columns (Grid) 网格一行摆几列
command (子标签 Action) MenuButton 的菜单项 挑中该项喊的命令——MenuButton 一家没有 onClick,只有它
contentInsets Text、TextView、Button、TextIconButton 文字四周的内边距,写法如 {0,4,0,12}(上,左,下,右)
continuous Slider 拖动过程中就实时汇报(配 onSlidingValueChanged 用)
coverBorderColor (Cell) 专辑网格格 缩略图的描边颜色
coverCornerRadius (Cell) 专辑网格格 缩略图的圆角
coverDefault ID3TagView 这首歌没封面时垫的占位图
coverImage (Cell) 专辑/歌手/流派/列表/文件夹各行 行左边那张图的占位图名
coverOpIcon ID3TagView 大封面角上"换封面"的小图标
cursorColor LyricShow ⚠ 引擎不读——当前行颜色归 highlightColor 管
cursorFontFamily LyricShow "当前行"的字体名(直接写)
cursorFontRef LyricShow "当前行"(光标行)的字帖名
cursorFontSize LyricShow "当前行"的字号
D
属性 出现在 一句话大白话
defaultImage Image 没图(水管没通)时的占位图名——<Image> 写死图只能靠它
direction Stack(不写默认竖排)、Scroll(不写默认横翻) 排队 / 翻页的方向:horizontal / vertical;两家默认相反,明写最稳
distribute Stack 整队怎么分空间:fill、fillEqually、fillProportionally、equalSpacing、equalCentering(表见 4.2)
dividerImage PlaybackUiIndicator 顶上三格之间垫的分隔小图
doubleLine (子标签 SleepMode) 设置页行 该行占两行排(标题+副标题),值照抄现成
dynamicBackground Page 该屏背景跟不跟着封面动态变化;图纸大量用(false 居多),值照抄现成
F
属性 出现在 一句话大白话
family Font(<Common> 字帖里) 字帖用的字体名(2.2)
fillColor Box、Rectange、FloatingButton、Scroll 底填充色;# 后 8 位,末 2 位是透明度,不写全透明
fillImage Rectange 不用纯色、改铺一张图当板面(基名)
fontFamily Text、TextView、Button、TextIconButton、KaraokeShow、EQPreamp、EQBand10、EQSelectBox 不走字帖、直接写字体名
fontName SearchBar SearchBar 家"直接写字体名"的叫法(同上面 fontFamily 的作用)
fontRef 几乎所有带字的:Text、Button、TextIconButton、SearchBar、LyricShow(带前缀 cursorFontRef 等)、LyricDisplay、PlaybackUiIndicator、EQPreamp、EQBand10、EQSelectBox、(Cell/CellStyle 带前缀 titleFontRef/subtitleFontRef)、Font 起的名被 # 引用 报第 2 章"字帖"名,字体字号一步到位
fontSize Text、Button、TextIconButton、SearchBar、KaraokeShow、EQ 各滑杆(配 fontFamily)、(Cell/CellStyle 带前缀 titleFontSize/subtitleFontSize) 字多大,数字越大字越大
fullscreenIcon Visual ⚠ 引擎解析但没人用它——写了目前无效果,别指望
G
属性 出现在 一句话大白话
gaps SpectrumView 频谱柱子之间的缝宽(不写默认 1;写 0 当没写)
gradientEndColor SpectrumView 柱子渐变的终点色
gradientStartColor SpectrumView 柱子渐变的起点色
gridLineColor EQMeterChart 图表横格线的颜色
gridLineWidth EQMeterChart 格线粗细
H
属性 出现在 一句话大白话
height (Layout) 自己多高(必须>0,可 auto 或数字@硬度);<Page id="MiniPlayer" height="48"> 里定迷你条整屏高 高度
hiddenDelay Text 5.1 卡列出的细调项;本书未展开,照抄现成即可
highlightColor LyricShow 正在唱那一行的字色(只有 LyricShow 读这个名)
highlightedBackgroundColor (Cell/CellStyle) 各列表行、定时关机、设置行 手指按住那一行时垫的底色
highlightedTextColor TextIconButton 按下那一刻字的颜色
I
属性 出现在 一句话大白话
icon TabBar 的每个标签页、(子标签 Items) 资料库/更多的入口项、(子标签 Action) 菜单项 那个位置用哪张图(基名,不带 .png/@3x,见 6.3)
iconColor TabBar 图标平时的颜色
iconHighlighted TextIconButton 按下时换的图标
iconNormal TextIconButton 平时的图标
id 几乎每个组件标签(Layout 里没有它);Font、Define 也用 id 起名 贴名牌,别的组件才能用 #名字 挂它(1.2)
image Icon 的自身图、(子标签 Item) Toolbar 按钮图 ⚠ 但 <Image> 标签没有这个属性——写死图用 defaultImage,随歌换图用 subscribe
imageAllSelect SelectButton "全选"状态的图
imageChecked (Cell) 选歌/选文件夹行的勾选圈、(CellStyle) ThemeListView 选中主题的勾 勾上时显示的图
imageCornerRadius (子标签 CellStyle) 资料库/更多菜单行 行左边图标的圆角
imageHighlighted IconButton、FloatingButton、MenuButton 按下那一刻换的图;写空引号 "" 表示按下不换
imageNoSelect SelectButton "没选"状态的图
imageOff (子标签 Switcher) 设置页开关行 开关拨到"关"时的图
imageOffset (子标签 CellStyle) 资料库/更多菜单行 行左边图标的内缩距离
imageOn (子标签 Switcher) 设置页开关行 开关拨到"开"时的图
imagePartialSelect SelectButton "选了一部分"状态的图
imageRepeatAll PlayModeButton 全部循环模式的图
imageRepeatOne PlayModeButton 单曲循环模式的图
imageSelected ToggleButton "亮着"(选中)状态的图
imageShuffle PlayModeButton ⚠ 引擎不读——图纸里虽有写它,写了无效果,别指望第四张图
imageStretch Rectange 铺图时怎么拉伸;本书未展开,照抄现成
imageUnchecked (Cell) 选歌/选文件夹行 没勾时显示的图
imageUnselected ToggleButton "灭着"状态的图
insets CountdownView、歌曲清单/选歌/文件夹/歌单/歌手/专辑/流派/资料库/更多/搜索/设置/主题网格(标签自身)、区标签 FolderSection/MediaSection 等、(子标签 Group) 内容在自家地盘四周让出的空,花括号四个数={上,左,下,右}——⚠ ID3TagView 例外,不读它
itemBorderColor CoverFlow 每张封面的描边颜色(和 Width 一起写才生效)
itemBorderWidth CoverFlow 每张封面的描边粗细
itemSize CoverFlow 每张封面多大,花括号{宽,高}
itemSpacing (Grid) 网格格子之间的缝
K
属性 出现在 一句话大白话
key (子标签 Switcher) 设置页开关行 这行接的是 App 里哪条设置(如 crossfading)——接线名,别乱改
L
属性 出现在 一句话大白话
labelText EQPreamp 滑杆旁边的标签文字(不写程序默认 "PREAMP")
labelTextColor EQPreamp 标签文字的颜色
left (Layout) 左边对齐到哪
lineSpacing CountdownView、选歌清单、选列表弹层、播放队列(标签自身)、各列表 <Table>、<Grid> 行与行之间的缝
M
属性 出现在 一句话大白话
marquee Text 字太长时像跑马灯横滚(写了它 multiline 失效)
maskColor (子标签 CellStyle) ThemeListView 主题缩略图上垫的黑色半透明遮罩
maximumTrackColor Slider 没走过那段轨道的颜色
maximumValueImage Slider 轨道右端的小图标
minimumTrackColor Slider 已走过那段轨道的颜色
minimumValueImage Slider 轨道左端的小图标
multiline Text(允许多行);LyricShow 也认但现成主题 0 处用过 允许换行排成多行
N
属性 出现在 一句话大白话
normalColor LyricShow、LyricDisplay、KaraokeShow 普通行 / 还没唱到的字的颜色
normalFontFamily LyricShow 普通行的字体名(直接写)
normalFontName LyricDisplay、LyricLineShow 普通行的字体名(直接写)
normalFontRef LyricDisplay、LyricLineShow 普通行的字帖名
normalFontSize LyricShow、LyricDisplay、LyricLineShow 普通行的字号
numberOfGridAxisY EQMeterChart 横格线画几条(不写或 0 按 10 条)
O
属性 出现在 一句话大白话
onClick Button、IconButton、TextIconButton、FloatingButton、SelectButton、ToggleButton、PlayModeButton、Image(引擎认、无实例)、(子标签 Item) Toolbar 按钮 点一下喊给服务台的命令(第 3 章)——⚠ MenuButton、SwitchButton、PlaybackUiIndicator、SearchBar 都不认它
onSlidingFinished Slider 松手时喊的命令
onSlidingStart Slider 开始拖时喊的命令
onSlidingValueChanged Slider 拖动过程中喊的命令
onStateOff SwitchButton 拨到"关"时喊的命令
onStateOn SwitchButton 拨到"开"时喊的命令
onValueChange —— ⚠ 引擎里根本没有——滑条回调只有上面 onSliding 三个
optionBackgroundColor EQSelectBox 点开列表每项的底色
optionSeparatorColor EQSelectBox 选项间分隔线颜色
optionSeparatorWeight EQSelectBox 选项间分隔线粗细
optionTextColor EQSelectBox 点开列表每项的字色
P
属性 出现在 一句话大白话
padding Stack 抽屉内侧四周留一圈空,逗号四个数=左,上,右,下
pageDotActiveColor Scroll 当前页那颗小圆点的颜色
pageDotColor Scroll 没选中小圆点的颜色
pageDotPosition Scroll 小圆点摆哪:top/bottom/left/right(引擎认、现成主题 0 处用过,先副本验证)
placeholder SearchBar=框里的灰提示文字;CoverFlow=没封面时垫的占位图名 同名两义,按所在组件理解
placeholderTextColor SearchBar 提示语的颜色
popupBarTransparent Page(MiniPlayer 那屏) 图纸真实用法;含义本书未展开,照抄现成值
R
属性 出现在 一句话大白话
recordImage VinylTurntable 黑胶盘本身的图(引擎认、现成主题 0 处用过——盘面纹路是程序画的)
ref Include 复印哪份样板(<Define> 的名字);拼错挂整屏
reflectionGap CoverFlow 封面和它水下倒影之间留的缝
right (Layout) 右边对齐到哪
S
属性 出现在 一句话大白话
searchIcon SearchBar 放大镜用哪张图
searchIconOffset SearchBar 放大镜往右挪多少
selectColor KaraokeShow、LyricDisplay 当前行背后高亮条的颜色
selectColorAlpha KaraokeShow、LyricDisplay 高亮条透明度(0 全透、1 不透)
selectCornerRadius KaraokeShow、LyricDisplay 高亮条的圆角
selectedBackgroundColor (Cell/CellStyle) 选中那一行的底色
selectedIconColor TabBar 当前页图标的颜色
selectedTextColor TabBar 当前页标签字的颜色
separatorColor (Cell/CellStyle) 定时关机、可视化选择、(子标签 Switcher/SleepMode) 设置行 行间分隔线颜色
separatorWeight 同上 分隔线粗细
shadowColor Image、Rectange、FloatingButton、Visual、EQSelectBox(引擎认、0 处用过) 投影四件套之一:影子颜色
shadowOffset 同上 影子往哪偏,花括号{x,y}
shadowOpacity 同上 影子浓淡(0~1 小数)
shadowRadius 同上 影子晕开多大
showPageControl Scroll 显不显示页码小圆点
size Font(<Common> 字帖里) 字帖的字号(2.2)
snapshot (子标签 CellStyle) ThemeListView 缩略图的约定文件名(每个主题文件夹自备一张)
spacing Stack(队里相邻的缝)、TextIconButton(图和字之间的缝) 留多宽的距离
style TextIconButton 排版样式编号;现成主题全用 0,照抄即可
subscribe 绝大多数显示/按钮组件(第 3 章的水管) 部位:${数据名},接实时数据
subscriptions Toolbar 工具条接水管的专用写法(带 s);写成 subscribe 静默无效
subtitleFontName (Cell/CellStyle) 各列表行、定时关机 第二行小字(副标题)的字体名
subtitleFontRef (子标签 CellStyle) 定时关机 副标题的字帖名
subtitleFontSize (Cell/CellStyle) 各列表行、定时关机 副标题的字号
subtitleHighlightedTextColor (Cell) 选文件夹行、播放队列行 按住时副标题的字色
subtitleSelectedTextColor (Cell/CellStyle) 各列表行 选中时副标题的字色
subtitleTextColor (Cell/CellStyle) 各列表行、定时关机 副标题的字色
T
属性 出现在 一句话大白话
text Button 按钮上的字、TextIconButton 图标旁的字、(子标签 Item) Toolbar 按钮字 ⚠ <Text> 标签没有它(那家用 textValue)——两处相反,写串不报错只是字不显示
textAlignment Text、LyricShow、LyricDisplay、LyricLineShow 字往哪边靠:left / center / right
textColor TabBar、Text、TextView、Button、TextIconButton、SearchBar(打的字)、EQBand10(频率标注)、EQMeterChart(dB 小字)、IndexingView(字母)、ID3TagView(行值) 字的颜色,# 后 6 位(或 8 位带透明度)
textLyric PlaybackUiIndicator 三格中"歌词"格的字
textMain PlaybackUiIndicator 三格中"封面"格的字
textValue Text、TextView 要显示的文字内容——这才是 <Text> 家的真名
textVis PlaybackUiIndicator 三格中"视觉化"格的字
thumbColor Slider(圆点色)、EQPreamp(纯色版滑块色,认但现成主题全用图版)、EQMeterChart(宽指示条填充色) "滑块/圆点"的颜色
thumbImage Slider(用图当圆点)、EQPreamp、EQBand10(滑块图) "滑块/圆点"用哪张图
thumbIndicatorColor EQMeterChart 指示条上那条指示线的颜色
thumbStretch EQPreamp、EQBand10 滑块图怎么拉伸:花括号两个数(左右保留,上下保留)
thumbTappedImage EQBand10 手指按住滑块时换的图
thumbWidth EQMeterChart 宽指示条的宽度(不写默认 200)
title (子标签 Menu/Action) 菜单标题和每行字、(子标签 Group/SettingEntry 等) 设置行显示文字、(子标签 Items) 资料库/更多入口的字 菜单项、设置行、入口的显示文字
titleBackgroundColor EQSelectBox 框(收起时那条)的底色
titleColor PlaybackUiIndicator(没选中格的字色)、ID3TagView(行"字段名"的字色) 标题类的字色
titleFontName (Cell/CellStyle) 各列表行、定时关机、(子标签 Group/Switcher) 设置组与行 行标题的字体名
titleFontRef (子标签 CellStyle) 定时关机 行标题的字帖名
titleFontSize (Cell/CellStyle) 各列表行、定时关机、(子标签 Group/Switcher) 行标题的字号
titleHighlightedTextColor (Cell) 选文件夹行、(CellStyle) 按住时标题的字色
titleSelectedTextColor (Cell/CellStyle) 各列表行 选中时标题的字色
titleTextAlignment (Cell) 专辑网格格、(子标签 CellStyle) ThemeListView 标题在格子里怎么靠
titleTextColor (Cell/CellStyle) 各列表行、定时关机、(子标签 Group/Switcher) 设置行、(区标签 MediaSection 等) 搜索结果区的区标题 行/组标题的字色
tonearmImage VinylTurntable 唱臂用哪张图
top (Layout) 上边对齐到哪
trackMaxColor EQPreamp 滑槽"没推到"段的纯色(⚠ 写到 EQBand10 上不生效)
trackMaxImage EQPreamp、EQBand10 滑槽"没推到"段的图
trackMinColor EQPreamp 滑槽"已推过"段的纯色(⚠ 写到 EQBand10 上不生效)
trackMinImage EQPreamp、EQBand10 滑槽"已推过"段的图
trackStretch EQPreamp、EQBand10 滑槽图怎么拉伸:花括号两个数
translatedFontSize KaraokeShow 翻译行(歌词下面那行小字)的字号
translationActiveColor LyricDisplay 翻译行"当前句"的字色
translationColor LyricLineShow 翻译行的字色(单行家只有一个,没有"当前句"之分)
translationFontName LyricDisplay、LyricLineShow 翻译行的字体名
translationFontRef LyricDisplay、LyricLineShow 翻译行的字帖名
translationFontSize LyricDisplay、LyricLineShow 翻译行的字号
translationNormalColor LyricDisplay 翻译行"普通句"的字色
translucent Button 半透明效果;本书未展开,照抄现成
touchedBackgroundAlpha IndexingView 按下气泡底色的透明度(0~1,引擎认、现成主题没写过)
touchedBackgroundColor IndexingView 按下 A-Z 时浮出气泡的底色
V
属性 出现在 一句话大白话
value Common 里的 <Style>(value="dark") 全主题明暗基调
valueChanged PlaybackUiIndicator 点格子切换界面时喊的命令——它没有 onClick,值固定抄 "playback-switch-ui"
vertical Slider 竖着摆的滑条(默认横着)
visualBorderColor (子标签 CellStyle) 可视化选择 行左侧预览小窗的描边颜色
visualBorderWidth 同上 预览小窗的描边粗细
visualCornerRadius 同上 预览小窗的圆角
W
属性 出现在 一句话大白话
width (Layout) 自己多宽;必须大于 0,可写 auto 让内容撑开,数字后还可跟 @硬度(1.2 学过)
Z
属性 出现在 一句话大白话
zOrder 几乎每个组件标签(Layout 里没有它) 叠放次序,数字大的盖在上面(1.3);浮在最上的常写 99/100

附录 B 30 分钟改出一个看得见的效果

这一节带你走完全程:亲手改三处,在 App 里看到变化。三处改动是:播放页封面变大、歌名换颜色、底部第一个标签换图标。照着做,每步都给确切文件名、行号和属性。

你需要:一台 Mac(或能打开主题文件夹的设备)、一个文本编辑器(系统自带的"文本编辑"即可,别用会加格式的 Word)、一个能编译安装 App 的环境——零基础的话,这一步请平时帮你装 App 的人搭把手。

三处改动一览(下文逐步细讲):

改哪 文件和行号(MySimple/theme.zh-Hans.xml) 从什么 改成什么 你会看到
播放页封面 第 1070~1073 行的 <Image id="albumCover"> 的 <Layout> left/right 里的 32 12 封面明显变宽(高度自动跟上)
播放页歌名 第 1078 行的 <Text id="textTitle"> textColor="#e0e0e0" textColor="#ff5252" 歌名变红
底部第一个标签 第 22 行的 <Medias icon="eTabMedias"/> eTabMedias eAlbums "媒体"那格换了另一张图标

B.1 备份一份 Simple 主题

  1. 在 Finder 里打开项目的 AMPod/Themes/ 文件夹。
  2. 把 Simple 整个文件夹复制一份,重命名为 MySimple(名字随你喜欢,下面都用 MySimple 举例)。
改的全是 MySimple 里的文件,原来的 Simple 一个字节不动;改坏了随时把 MySimple 删掉重来(2.1 讲过的道理)。

B.2 第一次改:把播放页的封面变大

  1. 用文本编辑器打开 AMPod/Themes/MySimple/theme.zh-Hans.xml。
为什么不是 theme.xml?2.3 讲过"点餐"规则:手机语言是简体中文时,App 点的是 theme.zh-Hans 系列这张,theme.xml 只是兜底款。想双保险,就两个文件做一样的改动(它们骨架相同)。本章练习先只动这一两个,正式改主题要 54 个变体同步改,2.3 说过。
  1. 找到播放那一屏:搜 <Page id="Playback">,在 theme.zh-Hans.xml 第 1066 行。
  2. 在这一屏里找封面:搜 id="albumCover",会找到第 1070~1075 行这块 <Image>(注意:图纸里 albumCover 这个名字出现好几处——迷你条 34 行也有一枚,别改错;本页认准 1070 行往下的,它在 <Scroll> 相册的第一个 <Box> 里)。它的 <Layout>(第 1073 行)原样是:
<Layout top="parent.top+48" left="parent.left+32" right="parent.right-32" height="#albumCover.width@999"/>
  1. 把 left="parent.left+32" 和 right="parent.right-32" 里的 32 都改成 12,改完后整行是:
<Layout top="parent.top+48" left="parent.left+12" right="parent.right-12" height="#albumCover.width@999"/>

效果:封面左右各少让 20,整张宽出 40;height="#albumCover.width@999" 那句是第 4 章讲过的"正方形跟宽度走",高度自己跟上,不用你动。保存文件。

B.3 第二次改:换个歌名颜色

  1. 还在 theme.zh-Hans.xml,搜 id="textTitle",找到第 1078 行那一块——它是播放页的歌名,身上绑着 subscribe="text:${media.title}"(第 3 章的水管)。
  2. 把它身上的 textColor="#e0e0e0" 改成 textColor="#ff5252"(醒目的红;# 后六位红绿蓝各两位,想换别的色自己调)。
  3. 保存。注意:图纸里 textColor="#e0e0e0" 出现很多次,别用批量替换——一定要按 id="textTitle" 搜到那一行再改,不然会把别的屏的字一起染了。

B.4 第三次改:换底部第一个标签的图标

  1. 搜 eTabMedias,在 theme.zh-Hans.xml 第 22 行:<Medias icon="eTabMedias"/>——底部标签栏"媒体"那格用的图。
  2. 把它改成 icon="eAlbums":MySimple 文件夹里就躺着 eAlbums@3x.png,名字照 6.3 的规矩不带 .png、不带 @3x。
  3. 保存。想换别的,在 MySimple 文件夹里看还有哪张 e* 图顺眼,就填它的基名。

B.5 在 App 里切到 MySimple,看效果

(改完文件要先像 2.4 那样把 MySimple 放进工程、重新编译安装,App 里才会认得这个新主题。)

  1. 打开 App → 底部"更多" → 点"主题" → 在网格里选中 MySimple。
  2. 随便播一首歌,点屏幕底部的迷你播放条,展开完整播放页,逐条对照:
- 封面是不是变宽变大了? - 歌名是不是变红了? - 屏幕底部第一个标签的图标,是不是从"媒体"换成了"专辑"那张图?

三处都变了——你已经第一次亲手改出一个主题了。只有部分生效?说明那几处改动你漏了某一步,或改到了别的文件——回 B.2~B.4 逐个对一遍。

下面每条排错前先记住:本附录的行号以刚复制好、还一个字没改过的 MySimple 为准;你每改一行,后面的行号就可能挪一两行,所以每次都是"先搜、再改"最保险。

B.6 没看到效果?排错三步

B.7 下一步

三处改动跑通之后,回到第 5 章,挑一两张卡片里"照抄能跑"的片段,塞进 MySimple 某个 <Page> 里玩:加一个 e* 图名前先确认文件夹里有那张图(5.1 Icon 的小白坑),改完一次只看一屏(2.5 的节奏)。等玩顺手了,再按 2.3 的规则把正式改动同步到全部语言、尺寸变体文件,把你的主题变成"正经主题"。