MAML教程

来自MIUI Theme Wiki
跳转至: 导航搜索

目录

MORE & MAML 概述

  • MAML引擎脚本语言
MIUI Application Markup Language for MORE (MIUI MORE引擎应用标记语言)


  • MORE渲染引擎
Markup Oriented Rendering Engine (基于标记语言的渲染引擎)


  • 概述
最初用于百变锁屏,使用xml用特定的语法描述锁屏界面。后来不断增强功能,逐步演化成一套接近通用的界面描述语言和图形渲染引擎,在一定需求下可用于开发风格多变的用户界面。可方便地通过更换皮肤改变界面风格、动画甚至交互方式。
MAML语言和Android的界面描述xml 类似. 所不同的是Android描述的是静态界面,对界面元素的更改依赖java代码。MAML描述的是静态界面+动态属性,UI在时间线上按一定的帧率不断刷新,UI显示根据元素属性的变量表达式的计算结果实时更新。MAML语言和运行时引擎已经从锁屏中独立出来作为MIUI内置的通用框架,除了显示时间日期等,还支持查询标准Content Provider来获取各种信息如天气。显示图片文本等各种元素,各种动画,滑动点击等界面交互控件,适于实现展示信息或有简单交互操作的界面。比如时钟、天气小部件、闹钟响铃界面。
框架支持动态帧率,不必按照固定帧率不停渲染,在没有动画和更新的时侯停止渲染,此时仅占用极少资源,对于缓慢变化的动画使用低帧率渲染,高动态的动画开始后立即调整到高帧率全速渲染。全速渲染时全屏帧率基本可以达到60帧。合理使用可以既炫酷又不费电。


  • MORE引擎适用哪些类型的界面开发
 1. 展示性的信息,如图片,文字 (不适合列表信息和大段文本,不适合用户输入)
 2. 需要有丰富的界面动画及交互动画。
 3. 较简单的交互,拖拽、点击 (但不限于拖拽点击,通过xml代码可以实现部分类似java编程中onTouchEvent所能做到的交互)
 4. 使用标准控件无法实现的信息展示方式,或者无法实现的交互方式,需要自制控件的,大部分可以通过xml代码方便地描述实现。
 5. 需要灵活更换皮肤风格,并且界面动画和交互动画甚至部分交互方式都可以通过皮肤更换。无需更改应用程序。

比如一些简单的工具类应用程序、无输入的操作界面希望能更换操作模式(例如来电接听界面)和桌面小工具可以考虑使用,也可以作为一个动画框架实现类似电子贺卡的界面,可支持交互,做为应用程序中的彩蛋(比如短信中的生日彩蛋)


  • MIUI主题使用MORE的模块
百变锁屏 com.android.internal.policy.impl.AwesomeLockScreen
桌面时钟 com.miui.home.launcher.gadget.AwesomeClock
相框小工具 com.miui.home.launcher.gadget.PhotoFrame
闹钟响铃界面 com.android.deskclock.AlarmAlertFullScreen
自由桌面动态小部件 com.miui.home.launcher.gadget.AwesomeGadget
百变壁纸 com.miui.home.launcher.MiWallpaper (基于SurfaceView)
动态图标 与图标相关的包名当其文件夹名
天气小工具 com.miui.home.launcher.gadget.Weather_2x4_custom (v4)

MAML在百变锁屏中的应用

百变锁屏在主题包里的lockscreen/advance目录下,manifest.xml文件是描述脚本

<Lockscreen frameRate="" displayDesktop=""  showSysWallpaper="" screenWidth="720">
   <Image />
   <Image />
   <Unlocker/>
   <Unlocker/>
   <Time/>
   <DateTime/>
    …
   <Text/>
</Lockscreen>
frameRate 指定帧率,如果动画缓慢,可以指定小一点的值,省电。默认为30。
displayDesktop 默认为false,透视到桌面功能,如果没有锁屏壁纸或者锁屏壁纸可以被移开或透明时可以看到桌面launcher或者是锁屏前的应用程序。可以完美实现WP7,Meego,阿里云等的解锁界面。
showSysWallpaper 默认为false,是否在锁屏界面显示桌面壁纸。开启后如果没有指定锁屏壁纸<Wallpaper/>,会将桌面壁纸作为锁屏的壁纸。
screenWidth 设定屏幕宽度标准。如果指定为720,锁屏中所有元素的位置都按720p的布局编写,1080p、480p等分辨率的手机会自动进行缩放。

MAML在百变壁纸及动态图标中的应用

  • 百变壁纸
百变壁纸在主题包的miwallpaper目录下,描述文件也是manifest.xml。根节点表示与百变锁屏基本一致:
<MiWallpaper frameRate="1" width="720" height="1280" screenWidth="720">
     <Var/>
     <Image/>
     <Group/>
      …
</MiWallpaper>
width 表示百变壁纸的宽度。
height 表示高度
wallpaper_offset_pixel_x 偏移的像素数 (0 ~ -1*屏宽)
wallpaper_offset_x 偏移百分比 (0 ~ 1.0)

要实现元素跟随手指滑动的效果,需要借助#wallpaper_offset_pixel_x,#wallpaper_offset_x 二者关系:#wallpaper_offset_x * 屏宽 = -1*#wallpaper_offset_pixel_x

滑动时: 在第一屏,wallpaper_offset_pixel_x = 0, wallpaper_offset_x = 0; 滑到最后一屏,wallpaper_offset_pixel_x = -1*屏宽,wallpaper_offset_x = 1.0

为了适配不同分辨率机型,建议使用#wallpaper_offset_x。建议的设计方式:

  1. 壁纸切成双屏宽 (屏宽指的是screenWidth)
  2. 壁纸定位:x="-#wallpaper_offset_x * 屏宽"
  3. 需要跟随滑动的元素定位:x="-#wallpaper_offset_x * 屏宽 + 相对壁纸的位置"


  • 动态图标

动态图标在主题包icons\fancy_icons\目录下,每个动态图标是一个文件夹,文件夹的名字是对应的app包名。例如日历的动态图标是一个叫"com.android.calendar"的文件夹,里面包含manifest.xml描述文件

<Icon version="1" frameRate="" width="136" height="136" screenWidth="720" useVariableUpdater="" hideApplicationMessage="">
    <Var/>
    <Image/>	   
    <Text/>  
    <DateTime/>
     …  
</Icon>

hideApplicationMessage: 默认false,屏蔽右上角的通知标志。

useVariableUpdater: 指定需要哪些系统变量,目前包括电量和时间,而且可以指定时间的更新周期,如果时间小工具每秒更新一次,需要指定更新周期为1秒,否则默认会每分钟更新一次。如果小工具或动态图标只显示日期,则每天更新一次,可以指定更新周期为1天。尽量选择较长的更新周期,以节约系统资源。

目前支持的tag: Battery, DateTime.Day, DateTime.Hour,DateTime.Minute,DateTime.Second

如果某个小工具不需要电量或时间变量,则需要使用useVariableUpdater="none",指定不做时间变量等更新。

如果某个小工具显示系统电量并且显示根据时间每小时更换背景图片: useVariableUpdater="Battery,DateTime.Hour"

如果需要每秒进行多次刷新,可以直接使用frameRate

机型适配

一份manifest文件,可以同时运行在多个设备,只需要设置好在不同尺寸或密度的设备上使用的资源和缩放比例就可以了。

  • 资源适配:
extraResources="sw1000-den320:den320:1.2,sw1000-den320::1.2,sw1000-den320-large:den320:1.2" 
sw1000-den320:den320:1.2 在屏宽是1000密度是320的机型上,使用的是den320文件夹下的资源,并且放大1.2倍;
sw1000-den320::1.2 在屏宽1000密度320的机型上,使用的是默认的资源,并且放大1.2倍)
extraResources="sw2000-den480:1.8,sw1000-den320-large:1.8" 这两句话中间都是只有一个冒号,意思是sw2000-den480的机型使用的资源就是在sw2000-den480这一同名文件夹下的资源,并且放大1.8倍)
  • 布局适配:
extraScales="sw1000-den320:1.2,sw2000-den480-large:1.8"
适配原理:
  1. 首先把你所有自定义中出现的den和sw做一个列表 (比如这里extraResources="sw1440-den440::0.916, sw720-den320::0.667, sw480-den240::0.444",涉及到的den有440, 320, 240, 再加上一个默认的480; sw有1440, 720, 480, 再加一个默认的1080(与den480对应) )
  2. 然后当你的主题放到一个设备上时,首先拿你的设备密度去上面的den列表中找,找到最贴近的一个,然后如果同样den有多个sw,那么再拿设备的屏宽去这几个sw中找最贴近的一个,这样就找到了最合适的swXXX-denXXX
  3. 最后,资源就取这个sw-den对应的目录下的资源,及缩放比例Sr。代码中数字的缩放比例就使用sw-den对应的Ss.
  4. 最最后,就是缩放比例
如果按密度缩放(即scaleByDensity="true"):   资源真正的缩放比例是Sr * 设备den / 找到的den,代码中的数字缩放比例是Ss * 设备den / 找到的den
如果按屏宽缩放(即scaleByDensity="false"): 资源真正的缩放比例是Sr * 设备sw / 找到的sw ,代码中的数字缩放比例是Ss * 设备sw / 找到的sw

入门教程

这里插一句,看本篇教程之前,最好先看一下入门教程:锁屏入门

壁纸元素

Wallpaper元素引用系统设置的壁纸,除了不能指定图片源外其他和Image元素相同,可以有动画和其他属性控制。如果没有此元素则不显示壁纸。可以有多个。 例子: <Wallpaper/>

变量

变量用Var开头

<Var name="" expression="" type="" const="" threshold="" persist=""/>
代码 解释
name 自定义名称 变量名
expression 表达式 变量对应的表达式或常量 注意:如果定义字符串常量需要多一套单引号: expression="'my string'"
type number/string 定义数值变量或字符串变量 默认:number
const true/false 为true时变量会在初始化后不会重新计算,可以提高效率。如果变量值会在锁屏运行期间改变,const设为false 默认:false
persist true/false 默认false,变量持久化。指定为true后,如果没有重新给定该变量其他的值,那么这个值会一直保存,无论解锁后重新锁定或者重新应用主题都不会还原
threshold 阈值 阈值触发,当变量值的变化超过设定的阈值时,可以触发一些命令。例如:
<Var name="time3" expression="#minute%10" threshold="1">
     <Trigger>
         <Command target="time3_anim.visibility" value="true"/>
         <Command target="time3_anim.animation" value="play"/>
         <Command target="time3_anim_new.visibility" value="false"/>
         <Command target="time_3_anim_new.visiblity" value="true" delay="400"/>
         <Command target="time_3_anim_new.animation" value="play" delay="400"/>
     </Trigger>
</Var>

上述代码表示#time3的值每变化1,就会执行<Trigger/>里面的所有命令

全局变量

解锁

unlocker.move_x 解锁部件在x方向的偏移
unlocker.move_y 解锁部件在y方向的偏移
unlocker.move_dist 解锁部件移动的距离
unlocker.state 解锁部件的状态: 0 normal, 1 pressed, 2 reached

触摸

touch_x 当前触摸点的x坐标
touch_y 当前触摸点的y坐标
touch_begin_x 按下屏幕时的初始x坐标
touch_begin_y 按下屏幕时的初始y坐标
touch_begin_time 按下屏幕时的时间

时间

time 当前时间,long
time_sys 系统时间毫秒数
year 年份
month 月份(取值范围是0~11,0表示一月,1表示二月,以此类推)
date 日期
day_of_week 星期(1表示星期日,2表示星期一,等等)
hour24 24小时制
minute 分钟
second
ampm 上下午 0:am 1:pm
time_format 0:12小时制 1:24小时制

农历

year_lunar 农历年份
year_lunar1864 用来计算天干地支
month_lunar 农历月份 从0开始计
month_lunar_leap 0/1 是否润月
date_lunar 农历日期 从1开始计

battery:

battery_level 当前电量,1~100
battery_state 0正常 1充电 2电量低 3已充满

screen:

screen_width 屏幕宽度
screen_height 屏幕高度
view_width 部件宽度(各插件中才使用,比如时钟)
view_height 部件高度(各插件中才使用,比如时钟)
raw_screen_width 物理宽度(这个表示当前设备的屏幕分辨率,不受根节点screenWidth的影响)
raw_screen_height 物理高度

image:

imageName.actual_w 获取(name="imageName")图片实时宽度
imageName.actual_h 获取(name="imageName")图片实时高度
imageName.actual_x 获取(name="imageName")图片实时位置的x坐标
imageName.actual_y 获取(name="imageName")图片实时位置的y坐标
imageName.bmp_width 图片文件的宽度(不受裁切、缩放的影响,只根据src找到指定的图片,并检测该文件的宽度)
imageName.bmp_height

music:

musicName.music_state 0暂停 1播放(musicName是你为音乐模块定义的名称)

others:

applied_light_wallpaper 仅在桌面时钟生效,0/1,为0时表示桌面壁纸为深色,为1时表示浅色
sms_unread_count 未读短信数
call_missed_count 未接电话数
@next_alarm_time 下一个闹钟时间
frame_rate 当前屏幕帧率
volume_level 现在音量
volume_level_old 调节之前的音量 取值: 1-15 根据二者比较判断是增大还是减小
volume_type
0 通话音量
1 系统音量
2 电话铃声,短信铃声
3 音乐播放器音量
4 闹钟音量
5 通知音量
6 连接蓝牙时的通话音量
7 在某些国家强制的系统音量
8 DTMF音量
9 TTS音量
10 FM音量
注意:一般锁屏下只能调3(音量播放器音量)
volume_type>=0 表示正在调节音量,调节完毕后值为-1 可根据这个显示或隐藏音量显示
ring_mode 0 silent, 1 vibrate, 2 normal
wifi_state 0 disabled, 1 enabled, 2 problem, 3 half(intermediate)
bluetooth_state 0 off, 1 on, 2 half
data_state 0 off, 1 on
usb_mode 0 disconnected, 1 charge only, 2 storage, 3 intermediate

注意:上面五个开关的状态变量必须有相应的按钮才会正常显示

表达式相关

操作符 + - * / %
优先级 4 4 3 3 3
位操作符
与JAVA中的对应
& | ^ ~ {{ }}
优先级 8 9 10 2 5 5
逻辑操作符  ! ==  != ** ||
优先级 2 7 7 11 12
大小比较 } }= { {=
优先级 6 6 6 6


  • 加减乘除取模 : + - * / %
  • (条件表达式)**(条件表达式):与,必须同时满足才为真
  • (条件表达式)||(条件表达式):或,两个条件满足其中一个就为真
  • (条件表达式)==(条件表达式):等于
  • (条件表达式)!=(条件表达式):不等于
  • !(条件表达式):逻辑非,相当于以前的not
例子:!(((#cur_weather_type-1)==4)||((#cur_weather_type-1)==5))

支持函数:sin, cos, tan, asin, acos, atan, sinh, cosh, sqrt, abs, min, max,pow 不解释

len(数字) 给定数字位数 len(1234)=4
digit(数字, 第几位) 取给定数字的第几位 digit(12345, 2) = 4 (注意:原数字位数不能超过10位,下标从右向左,并且从1开始
substr(原字符串,字串开始位置,字串长度) substr('今天真热啊',1,2) = '天真'(注意:字符位置是从左至右,并从0开始
strIsEmpty(字符串变量) 判断字符串变量是否为空 strIsEmpty(@abc)
isnull(数字型变量) 判断变量是否为空 isnull(#abc)
strIndexOf(@string_a,'string_b') 字符string_b第一次出现在字符串@string_a中的位置 如: strIndexOf('string','str')=0
strLastIndexOf(@string_a,'string_b') 字符string_b最后出现在字符串@string_a中的位置 如: str_LastIndexOf('starina','a')=6
strContains(@string_a,'string_b') 字符串@string_a是否包含字符string_b 如: strContains('string','str')=1或(true)
strReplaceAll(@string_a,'string_b','string_c') 用string_c替换@string_a中所有的string_b (strReplaceAll('abc','a','1')='1bc'
preciseeval(@string_a,8) 计算字符串的值,并精确到小数点后8位, 如: preciseeval('1/3',3)=0.333
round() 四舍五入取整
int() 向下取整(32位,有取值范围-2147483648~2147483648,否则会出错,)
rand() 取0到1之间的随机数
int(rand(#time)*a+b) 取到的是b到b+a之间的整数
eq(x, y) x==y ? 1 : 0(X等于Y,结果为1,否则为0)
ne(x, y) x!=y ? 1 : 0 (X不等于Y,结果为1,否则为0)
ge(x, y) x>=y ? 1 : 0
gt(x, y) x>y ? 1 : 0
le(x, y) x<=y ? 1 : 0
lt(x, y) x<y ? 1 : 0
isnull(x) x==null ? 1 : 0 // 是否表达式包含空变量,通常用于判断绑定的变量是否查到数据
not(x) x>0 ? 0 : 1
ifelse(x, y, z) x>0 ? y : z
ifelse(x1, y1, x2, y2, ... , z) (如果X1大于1,则结果为Y1,否则检测X2,如果X2大于1,则为Y2,以此类推,到最后才取Z)if x1>0 return y1; else if x2>0 return y2; ... ; else return z
+ 可以拼接字符串 , 一些函数也可以支持字符串,isnull可以判断字符串变量是否为空,ifelse可以选择字符串,字符串比较函数eqs(@string1, @string2)
formatDate('string',#time_sys) MIUI 7新增日期格式化成字符串的函数
formatFloat('%.3f',#accx) 格式化成小数点后几位,并转换成字符串(%.3f代表四舍五入到小数点后3位)
<Var name="time" expression="formatDate('2个小时以后的时间是:H:mm',#time_sys + 2*60*60*1000)" type="string"/>
<Text x="120" y="405" textExp="@time" size="45" color="#000000"/>

文本

显示指定格式的文字,坐标属性支持变量

color 文字颜色,支持常量:#FFFFFF ;支持字符串变量,如:@abc);支持函数argb(255,255,255,255) (argb(透明度,红,绿,蓝)
size 文字大小
bold 粗体,true表示加粗
format 如果需要在文字中显示变量数字,需要指定格式, 用%d 指定数字位置
paras 如果指定了format, 需要在paras里指定%d对应的变量表达式, 可以有多个变量表达式用"," 隔开
textExp 文字表达式,可以直接调用变量等如想要输出“现在时间是9点”,可以写成textExp="'现在时间是'+#hour12+'点'"
width 文字宽度,当文字超过指定宽度时会被切掉。如果指定了多行显示,则会折行显示。如果指定了文字滚动,则会在指定的位置滚动显示文字
marqueeSpeed 文字滚动速度,配合上面的宽度使用
marqueeGap 滚动间隔。当文字显示完后再次出现的间隔,默认为四个汉字的宽度
rotation 旋转角度,旧的angle也可以使用,不过建议用rotation,因为比较高端大气国际化
multiLine true/false 是否支持多行显示,默认false
spacingMult 行距倍数 默认1
spacingAdd 行距增加量 默认0
shadowDx 水平方向的阴影相对文字的偏移距离
shadowDy 竖直方向的阴影相对文字的偏移距离
shadowRadius 阴影的模糊半径,可以实现模糊的阴影效果
visibility 元素可见性支持表达式<=0 不可见 >0可见
align 坐标点水平对齐方式left, center, right
alignV 坐标点垂直对齐方式top, center, bottom
shadowColor 阴影的颜色,支持透明度
text.text_width 某行文本的宽度,可以用来排版

实例用法:

<Text shadowDx="3" shadowDy="4" shadowRadius="6" shadowColor="#9c000000" .../>

<Text name="tt" .../> //#tt.text_width为此文本的长度

显示下一个闹钟时间
<Text text="@next_alarm_time" .../> 
<Text format="下一个闹钟:[%s] 电池:[%d%%]" paras="@next_alarm_time,#battery_level"/>

textExp属性支持字符串表达式: 
天气信息,如果温度为空显示-- 非空显示数值
<Text visibility="#hasweather" x="(#screen_width/2)+180" y="#screen_height-290" w="360" size="36" color="#ffffff" align="right" textExp="ifelse(isnull(#weather_temperature),'--',#weather_temperature) + '℃'"/>

时间

这里说的时间,其实是由10个数字图片加“:”号图片依时间格式组成的图片组合

<Time x="10" y="10" src="time.png" space="" />
src 表示时间图片的前缀,如下表示使用time_0.png, time_1.png, ... time_9.png, time_dot.png. 坐标属性支持变量表达式
space 表示时间图片的间隙,我们可以使用这个功能来对时间图片进行排版,使用正值时图片间距变大,这时可以将图片切小,节省内存。对于有投影的图片,将space写成负值,可以使投影重叠以节省空间

日期

显示指定格式的日期
formatExp:支持日期表达式格式
format: 支持标准日期格式

<DateTime formatExp=""  value="" size="36" x="100" y="100" color="#ffffff" />
<DateTime x="" y="" color="" size="" format="">
  <PositionAnimation/>
<DateTime>
  • 年:
代码 释义 示例可能出现的结果
A 十二生肖年 鼠、牛、羊
G 公元 公元
Y 汉字年 二〇一五
YY 干支年 甲子
yyyy 数字年 2015
yy 2位数年 15
  • 月:
M 9
MM 月(1-9月加0) 09
MMM 月(汉字)
N 农历月 正,二,三
NN 干支月 乙丑
  • 日:
D 一年中的第几天 189
d 数字日 23
e 农历日 初三
ee 干支日 丙寅
t 节气 冬至
  • 星期:
E 周三
EEEE 星期三
EEEEE
  • 小时:
H 24小时制 0~23
h 12小时制 1~12
k 24小时制 1~24
K 12小时制 1~12
I 时辰地支
II 时辰地支 丁酉
注意:旧时的k是0~23,所以导致许多主题升级到V7时0点显示24点,将k替换为H即可。
  • 分钟:
m 7
mm 07
mmm 007
mmmm 0007
  • 秒钟:
s 7
ss 07
sss 007
ssss 0007
  • 上午下午:
a 上午,下午
aa 上午,下午,傍晚,凌晨,晚上
  • 时区:
Z/ZZ/ZZZ +0800
ZZZZ GMT+08:00
ZZZZZ 08:00
zzzz 中国标准时间
例子:
<DateTime x="20" y="450" color="#000000" size="40" format="GY年N月eH时m分" />
公元二〇一五年八月初十23时42分
<DateTime x="20" y="520" color="#000000" size="40" value="#time_sys+86400000" format="GY年N月eH时m分" />

组(Group)

<Group name="" x="" y="" w="" h=""  clip="true" layered="true" visibility="">
   <PositionAnimation/>
   <SizeAnimation/>
   <Image/>
   <Time/>
   <DateTime/>
   <Text/>
</Group>
Group


组的意思,相当于一个容器,用来包含住其他元素,如图片,时间,控件……等
可理解为一个图层,能便捷地调整内部多个元素的位置和大小,与Image一样添加各种动画
内部元素的坐标是相对坐标(相对于组坐标)
clip

true/false,为true时,会裁剪掉超出w h标注范围的内容,不给予显示
有clip与layered(图片混合中已解释)时,必须添加w h

注意:原先用frameRate来控制Group内元素的刷新,现在已不支持,如需要,请用动态帧率或Layer

常用图形

图片

图片部件用来在锁屏界面上显示一个图片,可以指定各种属性

<Image x="" y="" w="" h="" pivotX="" pivotY="" rotation="" src="" srcid="" alpha="" align="" antiAlias="" visibility=""/>
<Image x="" y="" w="" h="" pivotX="" pivotY="" pivotZ="" rotationX="" rotationY="" rotationZ="" src="" />
x,y 相对于屏幕左上角的坐标
w,h 宽和高
haptic haptic="true"时振动 ,前提是用户没有在系统设置中关闭
pivotX,pivotY 旋转中心
rotation 旋转角度,一周360度
rotationX X轴旋转角度,一周360度
rotationY Y轴旋转角度,一周360度
rotationZ Z轴旋转角度,一周360度
src 图片名称路径
srcid 图片序列后缀数字,一般用变量表示,可以根据变量显示不同的图片,如果src="pic.png"srcid="1"则最后会显示图片"pic_1.png"
alpha 透明度0-255,小于等于0不显示
antiAlias true/false 抗锯齿,如果为true图片在变形旋转时不会有锯齿,但是速度会慢
srcExp 图片源表达式
srcFormat 图片源格式
srcForamtExp 图片源格式表达式
srcParas 图片源参数
blur 高斯模糊,慎用,否则会卡顿,建议用在小尺寸图片
visibility 支持表达式,大于0时则显示
align/alignV 对齐方式,#文本 已经介绍。

例子:

<Image x="0" y="#screen_height-323" src="bottom_bg.png"/>
<Image name="img" ... >
  <PositionAnimation>
    ...
  </PositionAnimation>
</Image>
//srcExp:图片源表达式
<Image x="210" y="-26" align="center" srcExp="'weather/weather_' + #weather_id + @icon_suffix + '.png'"/>

数字图片映射

<ImageNumber number="" src=""/>
string 要显示的文本表达式
src 图片源的文件名,支持数字类型映射加载
  • 数字映射加载:如果src="number.png" 则会使用 number_0.png number_1.png ... 图片文件来绘制数字。即映射"_"字符后的数字字符

文本图片映射

文本图片映射:按照某段文本的字符顺序,不同字符对应不同图片,再把这些图片显示出来(一般用在非系统字体时,代替<Text ……/><DateTime …… />)

<ImageChars x="" y="" src="pic.png" string="like" charNameMap="ll,ii,kkr,ee" />
number 要显示的数字表达式
src 图片源的文件名,支持字符映射加载
space 字符之间的间隔,支持int表达式
charNameMap 映射列表(因为某些字符是非法的文件名,做个映射列表用来兼容,每项用逗号分隔,第一个字符是原始字符,后面字符是要映射的字符串,可以映射到一个或多个字符)
  • 字符映射加载:如果src="a.png" 则会使用 a_0.png a_a.png a_z.png ... 图片文件来对应string值中的各个字符。即映射"_"字符后的字符

图片混合

例子:(说明一下,下面教程为了大家第一次试用这功能不出错,用的全屏,但是大家在以后使用时,混合范围要尽可能小,否则会卡)
<Group w="#screen_width" h="#screen_height" layered="true">
    <Image x="300" y="500" src="test.png"/>
    <Image x="300" y="500" src="mask.png" xfermode="dst_in" />
</Group>
或者
<Group w="#screen_width" h="#screen_height" layered="true">
    <Image x="300" y="500" src="test.png"/>
    <Image x="300" y="500" src="mask.png" xfermodeNum="6" />
</Group>
这俩个例子实现的结果是一样的,都是遮罩效果,即按照mask.png的形状对test.png进行裁剪。
layered 和Group里面的xfermode配合使用,增加该属性旨在让xfermode只作用于Group内部的元素。
xfermode 混合模式,取值有:clear,src, dst, src_over, dst_over, src_in, dst_in, src_out, dst_out, src_atop, dst_atop, xor.
xfermodeNum 混合模式支持变量表达式。表达式的取值对应一种混合模式,src, dst, src_over,...xor依次取值 1,2.3 ...11

注意事项:

  • 在Group使用layered时,请务必指定作用区域w, h,否则无法生效(说明一下,下面教程为了大家第一次试用这功能不出错,用的全屏,但是大家在以后使用时,混合范围要尽可能小,否则会卡)
  • 一个组里可以有>=2张图,最后一个有xfermode的Image会将这个组内前面所有的图片看作一个整体的Image,按照xfermode的取值与之混合

对应关系:
MAML xfermode.png
上图中被混合处理的两张图
src.pngdst.png

笔刷

笔刷绝对是一个很神奇的功能,与混合功能搭配,可以做出刮奖、擦玻璃的效果

笔刷代码:

<Paint x="0" y="0" w="1080" h="1920" color="#ff6600" weight="40" />
weight 笔刷宽度,支持表达式
xfermode 混合模式,参考Image
w h 宽高,定义此笔刷能涂抹的区域


与图片混合功能搭配的示例:

<Wallpaper />                                                    // 调用系统壁纸,在这里是你手指擦过后露出的背景图 
<Group x="0" y="0" w="1080" h="1920" layered="true">             // 图片混合 
       <Image x="0" y="0" src="fg.png"/>    	                  // fg.png是被擦的图片(高斯模糊+水珠雾气效果)
       <Paint weight="50" w="1080" h="1920" xfermode="clear" />  // clear能产生擦除效果
</Group>


Paint.jpg

图片变形【新功能】

示例主题: 主题商店《你绝对没摇过的…》
图片变形的功能估计大家期待很久了!
先看图:
mesharr.jpg

注意了:

  • 控制图片节点用的数组,它的类型必须为 float[],这样
<Var name="mesharr" type="float[]" size="441" />
  • 但是在后面计算中暂时用 number[]
  • 图片变形暂时要自己计算在其它分辨率下的缩放比,示例:
<Var name="img_w" expression="400" const="true" />
<Var name="img_h" expression="400" const="true" />
<Var name="img_m" expression="2" const="true" />
<Var name="img_n" expression="2" const="true" />

<!-- mesharr[]数组中,同时要有X坐标、Y坐标,所以申明范围时是:(#img_m+1)*(#img_n+1)*2=18-->
<Var name="mesharr" type="float[]" size="18" />
<ExternalCommands>
	<Trigger action="init">
		<VariableCommand name="scale_wh" expression="int(#raw_screen_width/1.08)/1000" />      //(你锁屏按1080p写的就用这个计算)
		<!-- 先分网格 -->
		<LoopCommand count="(#img_m+1)*(#img_n+1)" indexName="__a">
			<VariableCommand name="mesharr" type="number[]" index="#__a*2" expression="int(#img_w/#img_m)*(#__a%(#img_m+1))" />
			<VariableCommand name="mesharr" type="number[]" index="#__a*2+1" expression="int(#img_h/#img_n)*int(#__a/(#img_m+1))" />
		</LoopCommand>
		<VariableCommand name="mesh5_x" expression="#mesharr[4*2]" />
		<VariableCommand name="mesh5_y" expression="#mesharr[4*2+1]" />
	</Trigger>
</ExternalCommands>
<Var name="mesharr" type="number[]" index="8" expression="#mesh5_x+int(#touch_x-#touch_begin_x)" />
<Var name="mesharr" type="number[]" index="9" expression="#mesh5_y+int(#touch_y-#touch_begin_y)" />
<Image x="0" y="0" scale="#scale_wh" src="bg3.jpg" mesh="2,2" meshVertsArr="mesharr" />

附件:img007.zip

几何图形

目的:在一些内存要求高的场景下,使用绘制几何图形的方式替代<Image>以减小内存。 1. Rectangle(矩形、圆角矩形)

<Rectangle x="10" y="20" w="100" h="200" cornerRadius="5,10" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" xfermode="" xfermodeNum="">
       <StrokeShaders>
               <LinearGradient x="" y="" x1="" y1="" tile="">
                       <GradientStop color="" position=""/>
                       ......
                       <GradientStop color="" position=""/>
               </LinearGradient>
               <RadialGradient x="" y="" rX="" rY="" tile="">
                       <GradientStop color="" position=""/>
                       ......
                       <GradientStop color="" position=""/>
               </RadialGradient>
               <SweepGradient x="" y="" rotation="" tile="">
                       <GradientStop color="" position=""/>
                       ......
                       <GradientStop color="" position=""/>
               </SweepGradient>
               <BitmapShader src="" tile="," />
       </StrokeShaders>
       <FillShaders>
       </FillShaders>
</Rectangle>
1.1 属性
x, y 左上角点
cornerRadius 倒角半径,格式"x向半径,y向半径"
strokeColor 描边颜色。与Text的color相同,支持多种color指定方式,如无特别说明,以下所有color类的属性都是如此。
fillColor 填充色
weight 描边的线宽
cap 线头形状。butt无线头(默认), round(半圆), squre(方形)
dash 虚线模式。格式“线长,间隔,线长,间隔....”,e.g. dash="1,2,3,4":1像素宽的线段,2像素的间隔,3像素的线段,4像素的间隔,如此重复...
strokeAlign 描边对齐方式,inner内描,center中心描边,outer外描(默认)
xfermode,xfermodeNum 混合模式,与<Image/>相同
1.2 StrokeShaders
描边的着色器:支持线性渐变着色,放射渐变着色(径向渐变),扫描渐变着色(圆周渐变),位图着色。
1.2.1<LinearGradient>线性渐变:
x, y, x1, y1 渐变轴线(x,y) --> (x1,y1)
tile(铺展模式) clamp拉伸(默认),mirror镜像,repeat重复
1.2.2<RadialGradient>放射渐变:
x, y 圆心位置
rX, rY x, y方向的半径
1.2.3<SweepGradient>扫描渐变:
x, y 中心点位置
rotation 旋转角
1.2.4<BitmapShader>位图着色:
src 图片源
tile 格式"x方向模式,y方向模式"
1.2.5 GradientStop渐变点:
在指定的几个渐变点颜色之间做渐变
color 渐变点的颜色
position 渐变点位置,0~1.0的浮点数
1.3 FillShaders
填充的着色器 与StrokeShaders语法完全相同。

2.Ellipse(椭圆)

<Ellipse x="10" y="20" w="100" h="200" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" xfermode="" xfermodeNum="">
       <StrokeShaders>
       </StrokeShaders>
       <FillShaders>
       </FillShaders>
</Ellipse>
x, y 椭圆的中心位置
w, h 椭圆的宽,高

3. Circle(圆)

<Circle x="10" y="20" r="50" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" xfermode="" xfermodeNum="">
       <StrokeShaders>
       </StrokeShaders>
       <FillShaders>
       </FillShaders>
</Circle>
x, y 圆心位置
r 半径

4. Arc(扇形、弧线)

<Arc x="10" y="20" w="100" h="200" startAngle="10" sweep="50" close="true" strokeColor="#ff00ff00" fillColor="#ff000000" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" xfermode="" xfermodeNum="">
       <StrokeShaders>
       </StrokeShaders>
       <FillShaders>
       </FillShaders>
</Arc>
x, y, w, h 定位方式与Ellipse相同
startAngle 起始角
sweep 扫描角(扇形的角度)
close 是否闭合,true闭合是扇形,false不闭合是弧线

5. Line(直线)

<Line x="10" y="20" x1="100" y1="200" strokeColor="#ff00ff00" weight="5" cap="round" dash="1,2,3,4" strokeAlign="center" xfermode="" xfermodeNum="">
       <StrokeShaders>
       </StrokeShaders>
</Line>
x, y, x1, y1 直线起点(x, y),终点(x1, y1)

注意事项:

  • 对齐方式align,alignV:只有Rectangle支持,其他的都不支持,它们的x,y都是指的中心点位置,不需要对齐方式。
  • 填充:线条类的图形忽略fillColor和<FillShaders>;有面积的图形同时支持stroke和fill,分别用于描边和填充。
  • 优先级:当strokeColor和<StrokeShaders>同时存在时,优先使用<StrokeShaders>;fillColor和<FillShaders>亦如此。
  • 描边颜色:如果要出现描边,则strokeColor和<StrokeShaders>必须至少一个存在;都不存在就会没有描边。
  • 渐变定位:<LinearGradient x="" y="">... 中的x,y都是相对它所在的图形元素定位的。

自定义图片

选择任意图片显示,参考http://www.miui.com/thread-2691964-1-1.html

config.xml 新增 ImagePicker 条目,可以让用户选择手机中的某个图片,获取其地址到变量中(注意:图片过大可能不能显示,比如像素过大的照片。)

<Config>
       <Group text="自定义图片" summary="请先将图片裁剪到合适大小和部位以确保显示效果,尺寸过大的照片请先缩小至屏幕大小,否则可能不能正常显示或费内存">
               <ImagePicker text="图片一" summary="选择图片一" id="img1"/>
               <ImagePicker text="图片二" summary="选择图片二" id="img2"/>
       </Group>
</Config>

manifest.xml 中 Image支持Uri类型的src,即config中选择的图片地址,@img1 @img2

<Image  x="0" y="0" src="@img1" srcType="Uri"/>
<Image  x="0" y="500" src="@img2" srcType="Uri"/>

清除图片(将相应变量置空即可):

<VariableCommand name="img1" expression="" type="string" persist="true"/>

示例文件:image_picker.zip

动画

变量动画

示例:

<Var name="num">
 	<VariableAnimation initPause="true" loop="false">
 		<Item value="0" time="0" />
 		<Item value="100" time="300" />
 	</VariableAnimation>
 </Var>
// 按钮触发 
 <Button x="0" y="400" w="1080" h="1520">
 	<Triggers>
 		<Trigger action="up">
 			<Command target="num.animation" value="play(0,300)"/> 
 		</Trigger>
 	</Triggers>
 </Button>
initPause true/false initPause="true"无命令执行这个动画时,它停在初始态(你不理我,我就不动);initPause="false"(你不理我,我也开始播放)
loop true/false 是否循环,默认为true, loop=”false”时,播放一次就停了

灵活运用示例:

<Var name="num">
 	<VariableAnimation initPause="true" loop="false">
 		<Item value="0" time="0" />
 		<Item value="100" time="300" />
		<Triggers>
			<Trigger action="end" condition="#num==100">      //动画播放完时,若#num等于100,则执行名为move的动画
				<AnimationCommand target="move" command="play" />
			</Trigger>
		</Triggers>
 	</VariableAnimation>
 </Var>

元素动画

所有元素都支持动画(为了篇幅,下面仅用Image来举例) 动画分为:图片源,位置,大小,旋转,透明度
每种动画相互独立,各自循环播放,动画由若干关键帧组成,关键帧包括帧属性和时间,除图片源动画外,其它动画会根据当前时间找到相邻的两个关键帧,然后线性插值计算当前的属性。 如果第一帧时间不从0开始,则默认时间为0的第一帧为图片原始属性,时间单位为毫秒。 位置动画中的位置是相对于图片自身的坐标。
源动画

<SourcesAnimation>
	<Source x="" y="" src="pic1.png" time="100"/>
	<Source x="" y="" src="pic2.png" time="200"/>
	<Source x="" y="" src="pic3.png" time="300"/>
</SourcesAnimation>

等比缩放

<Image x="0" y="0" pivotX="50" pivotY="50" src="bg2.png">
	<ScaleAnimation loop="false">
		<Item value="0" time="0" />
		<Item value="1" time="1000" />
	</ScaleAnimation>
</Image>
<Image x="0" y="0" pivotX="50" pivotY="50" src="bg.png">
	//位移
	<PositionAnimation>
		<Item x="10" y="0" time="100" />
		<Item x="10" y="200" time="1000"/>
	</PositionAnimation>
	//平面旋转 
	<RotationAnimation>
		<Item value="0" time="0" />
		<Item value="360" time="2000"/> 
	</RotationAnimation>
	//非等比缩放
	<SizeAnimation>
		<Item w="100" h="100" time="0"/>
		<Item w="200" h="400" time="1000"/>
		<Item w="100" h="100" time="2000"/>
	</SizeAnimation>
	//透明度
	<AlphaAnimation> 
		<Item value="255" time="0"/>
		<Item value="80" time="1000"/>
		<Item value="255" time="2000"/>
	</AlphaAnimation>
</Image>

例子:位置动画表示1秒从屏幕最左端到最右端,停留1秒,透明度动画表示开始透明度为175,在从最左端到最右端过程中透明度不变,到达最右端后0.5秒渐变为不透明,然后0.5秒变为透明消失。 然后循环播放。

<Image x="0" y="#screen_height-177" src="charging_light.png" category="Charging"> 
  <PositionAnimation>
     <Item x="480" y="0" time="1000"/>
     <Item x="480" y="0" time="2000"/>
  </PositionAnimation>
  <AlphaAnimation> 
     <Item value="175" time="0"/>
     <Item value="175" time="1000"/>
     <Item value="255" time="1500"/>
     <Item value="0" time="2000"/>
  </AlphaAnimation>
</Image>

注意:只有图片支持图片源动画
图片源动画稍有不同,没有插值,x, y 可选,表示相对图片的位置, 当前的图片是在列表里的找到第一个大于当前时间的那个点指定的

<SourcesAnimation>
  <Source x="" y="" src="pic1.png" time="100"/>
  <Source x="" y="" src="pic2.png" time="1000"/>
</SourcesAnimation>

动画控制

示例:

<Image name="wao" x="0" y="0" w="1080" h="114" src="dst.png" >
	<PositionAnimation tag="show1" initPause="true" loop="false" >
		<Item y="0" time="0" easeType="ExpoEaseOut"/>
		<Item y="300" time="300"/>			
	</PositionAnimation>
</Image>
<Image name="goal" x="0" y="0" w="1080" h="114" src="dst.png" >
	<PositionAnimation tag="show1" initPause="true" loop="false" >
		<Item y="0" time="0" easeType="ExpoEaseOut"/>
		<Item y="300" time="300"/>			
	</PositionAnimation>
	<PositionAnimation tag="show2" initPause="true" loop="false">
		<Item y="300" time="0" easeType="ExpoEaseOut"/>
		<Item y="600" time="300"/>			
	</PositionAnimation>
</Image>
<Button x="0" y="100" w="1080" h="1820">
	<Triggers>
		<Trigger action="up" >
			<AnimationCommand target="wao" command="play(0,300)" />
			<AnimationCommand target="goal" command="play" tags="show2" />
		</Trigger>
	</Triggers>
</Button>
AnimationCommand 播放动画现在统一用这个标签,原因是旧标签不支持tags,你怀旧的话可以记两个,
target 控制目标名
command play(从头播放)/pause(暂停)/resume(从当前位置继续播放)
tag 用此标签可以实现 一个元素预置多个动画效果,用tags来选择性地播放其中一种
play(startTime,endTime,loop,delay)
    startTime
    endTime
    loop
    delay
4个参数都支持表达式,可只写前面的参数,后面的忽略
动画开始时间
动画结束时间
表示是否循环播放,默认为0,需要写数字0,1 或者数值表达式,如果为0表示只播放一遍就停止
表示是否支持delay,默认0,需要写数字0,1 或者数值表达式

例如:

play(100) 从time 100开始播放到结束,不循环播放
play(100, 500) 从time 100开始播放到500,停止
play(100,500,1) 从time 100开始播放到500,循环播放

缓动函数

<Image name="wao" x="0" y="0" w="1080" h="114" src="dst.png" >
	<PositionAnimation tag="show1" initPause="true" loop="false" >
		<Item y="0" time="0" easeType="ExpoEaseOut"/>
		<Item y="300" time="300"/>			
	</PositionAnimation>
</Image>

缓动动画有两种实现方法:easeType与easeExp

1.用easeType直接选缓动类型,对照表如下
easeType.jpg
不明白?那就点击看这里带动画的(链接页面中缓动名字和MAML里不太一样,注意别混淆了)
SineEaseIn SineEaseOut SineEaseInOut QuadEaseIn QuadEaseOut QuadEaseInOut
CubicEaseIn CubicEaseOut CubicEaseInOut QuartEaseIn QuartEaseOut QuartEaseInOut
QuintEaseIn QuintEaseOut QuintEaseInOut ExpoEaseIn ExpoEaseOut ExpoEaseInOut
CircEaseIn CircEaseOut CircEaseInOut BackEaseIn BackEaseOut BackEaseInOut
ElasticEaseIn ElasticEaseOut ElasticEaseInOut BounceEaseIn BounceEaseOut BounceEaseInOut
  • BackEase可以带1个参数,overshot
  • ElasticEase可以带2个参数,priod和amplitude
  • 参数为常数,比如BackEaseIn(1.5) ElasticEase(2,3)
2.用easeExp来填表达式的方式,引用一个内置变量#__ratio,可以自定义缓动函数
比如 easeType=”QuadEaseIn” 也可以用 easeExp=”#__ratio*#__ratio” 来实现
当有easeExp时,easeType不起作用
属性作用于从该帧到下一帧,最后一帧没用
<Var name="aaaaaa">
 	<VariableAnimation loop="false" initPause="true">
 		<Item value="0" time="0" easeExp="#__ratio*#__ratio"/>
 		<Item value="100" time="2000"/>
 	</VariableAnimation> 
 </Var>

命令汇总

先看示例:

<Button x="" y="" w="" h="">
     <Triggers>
           <Trigger action="up">
		 <Command target="test_a" property="visibility" value="true/false" />
                 <Command target="test_b.visibility" value="true/false" />
		 <Command target="test_c.animation" value="play" />
		 <AnimationCommand target="test_c" command="play" />
                 <VariableCommand name="test1" expression="0" />
                 <SoundCommand sound="famous.wav" volume="0.5" loop="false" keepCur="true" />
                 <ExternCommand command="unlock" />
           </Trigger>
     </Triggers>
</Button>

(注意:上面相同颜色的两行代码,效果是一样的)
上面是一个按钮,现在只针对其中的trigger开讲,想了解按钮的点击《控件的用法详解》

trigger 定义按钮动作引发相关的操作,如控制某元素的属性。(同控件下可以有若干个trigger)

在trigger下能执行多种命令

可见性命令

<Command target="test_a" property="visibility" value="true" />
<Command target="test_a.visibility" value="true" />

上面只不过是两种不同的写法,效果一样

Command 基础命令,可以通过对象名和对象的属性来控制界面里的其他元素。通常控制的是元素的可见性(visibility)和动画播放(animation)。
target 控制目标名
property 属性名,目前支持:visibility
value 属性值,目前针对boolean有: true, false, toggle

动画命令

<Command target="test_c.animation" value="play" />
<AnimationCommand target="test_c" command="play"/>

上面两行是播放动画的两种写法,效果一样!动画的控制之前讲过,点击《动画控制》

声音命令

SoundCommand:声音命令,可以用来播放音频文件

<SoundCommand sound="famous.wav" volume="0.5" loop="false" keepCur="true" />
sound 填声音文件路径名
volume 声音大小,0~1的一个浮点数
loop 是否循环播放,true/false,默认是false.
keepCur 播放此音频时,是否保持当前正在播放的声音,true/false,默认false

注意:声音文件的大小要求不超过500kB,时长不超过10秒(10秒之后的声音播放不出来)。

变量命令

VariableCommand:用来控制变量(Var)的值

<VariableCommand name="w" expression="#screen_width" delay="100" condition="#switch" />
name 变量名
expression 赋值表达式
condition 条件判断,支持表达式。当condition里的条件判断为真时,执行命令;为假时,不执行。
delay 延迟,以毫秒记。读取该命令后延迟一段时间再执行
delayCondition 是延时判断,在delay的时间之后再进行判断

通用命令

1.ExternCommand:用来向外部程序发送命令
<ExternCommand command="命令名" numPara="参数1,数字表达式" strPara="参数2,字符串表达式"/>
<ExternCommand command="unlock" delay="100" />
上面示例就是一种解锁命令,100毫秒后解锁
2.ExternalCommand
与ExternCommand相对,是用来接收外部命令的命令,典型的用法:在锁屏中,通常用来接收开屏/关屏命令,从而执行一些命令;在桌面插件中,用来检测切屏从而执行命令
 resume表示开屏时执行的命令,pause表示关屏时执行的命令
<ExternalCommands>
<Trigger action="resume">
         <Command target="target.animation" value="play"/>
        </Trigger>
   <Trigger action="pause"> 
      <VariableCommand name="pause_time" expression="#time_sys"/>
  </Trigger>
</ExternalCommands>

桌面插件切屏时使用的示例
<ExternalCommands>
    <Trigger action="resume">
       <Command target="__root.animation" value="play"/>
    </Trigger>
    <Trigger action="pause">
        <Command target="__root.animation" value="play"/>
    </Trigger>
</ExternalCommands>

打开程序

<IntentCommand action="android.media.action.STILL_IMAGE_CAMERA" category="android.intent.category.DEFAULT" />
<ExternCommand command="unlock" delay="100" />

要记得加上解锁命令,否则只是在后台打开了,还要解锁才能看到

附5个开关

1.蓝牙(value="on" value="off")
<Command target="Bluetooth" value="toggle"/>
或
<Command target="Bluetooth" value="toggle"/>
2.数据(value="on" value="off")
<Command target="Data" value="toggle"/>
<Command target="Data" value="on"/>
3.铃音/静音/震动
三种状态切换
<Command target="RingMode" value="toggle"/>
下面和上面同样效果
<Command target="RingMode" value="normal,silent,vibrate"/>
仅正常和静音切换
<Command target="RingMode" value="normal,silent"/>
仅正常和震动切换
<Command target="RingMode" value="normal,vibrate"/>
仅切换到静音,用其他button切换到其他状态
<Command target="RingMode" value="silent"/>
4.USB存储 (toggle/on/off)
<Command target="UsbStorage" value="toggle"/> 
<Command target="UsbStorage" value="off"/>
5.Wifi (toggle/on/off)
<Command target="Wifi" value="toggle"/>

控件用法

Trigger在按钮、Unlocker、Slider、命令组调用中的用法基本一样,但各控件有它们各自的特性和各条件下命令的不同用法,这里给大家详细讲下

按钮

按钮元素可以用来接收点击,双击等事件,并可根据trigger的定义来控制界面上其他元素

x, y, w, h 指定坐标、区域大小
haptic haptic="true"时振动 ,前提是用户没有在系统设置中关闭
alignChildren true/false,默认为false,当为true时,内部元素按绝对坐标排布,反之是相对坐标
action down (按下);move(移动); up (抬起);double (双击)
interceptTouch 是否截获以后的触摸事件,避免被其他View捕获,例如在自由桌面widget中可以防止在widget上进行触摸操作时桌面滚动和进入编辑模式
Normal 正常状态,所包含的元素只有在此状态下显示
Pressed 按下状态,所包含的元素只有在此状态下显示

注意:

1.按钮中trigger被triggers包含
2.Button中 Image等各元素的坐标和Button自己的坐标是独立的,都是相对于Button的父元素。
<Button x="" y="" w="" h="" interceptTouch="true">
	<Triggers>
		<Trigger action="down">
			<VariableCommand name="test1" expression="0" />
		</Trigger>
		<Trigger action="up">
			<VariableCommand name="test2" expression="0" />
		</Trigger>
		<Trigger action="double">
			<VariableCommand name="test3" expression="0" />
		</Trigger>
	</Triggers>

	
	<Normal>
		<Image/>
		<Text/>
	</Normal>

	
	<Pressed>
		<Image/>
		<Text/>
	</Pressed>
</Button>

Unlocker,Slider

请先看《百变锁屏入门》文章末尾-解锁 Unlocker
Unlocker与Slider的用法是一样的,都是通过滑动来激活某些操作,只不过Unlocker能直接解锁,用法之前讲过,这是只做些补充,有不懂的地方请点击《百变锁屏入门》文章末尾-解锁 Unlocker

StartPoint 起始点
EndPoint 目标点
haptic haptic="true"时振动 ,前提是用户没有在系统设置中关闭
alignChildren true/false,为true时,内部元素按绝对坐标排布
(旧方案)bounceInitSpeed 回弹动画初始速度(距离单位为像素,时间单位为秒),支持表达式
(旧方案) bounceAcceleration 回弹动画加速度(距离单位为像素,时间单位为秒),支持表达式
(新方案支持缓动函数) easeType=“”或 easeExp=“”为缓动类型;easeTime=“”为缓动时间
alwaysShow true/false" 默认是false,当一个Slider可见时,其他Slider消失。
normalSound(StartPoint下) 指定在normal状态播放的音效
pressedSound(StartPoint下) 指定在press状态播放的音效
reachedSound(EndPoint下) 到达该endpoint后播放的音效

Unlocker与Slider有三种状态,都可以包含任意界面元素,如Image Text等,也可以不指定,相关元素在该状态下才显示

NormalState 正常状态
PressedState 按下状态
ReachedState 激活状态

注意:
看下面示例,起始点(StartPoint)和目标点(EndPoint)它们都可以包含NormalState、PressedState、ReachedState三个状态
但是在起始点(StartPoint)中的按下状态(PressedState)包含的元素会随你手指移动

<Slider name="slider">
	<StartPoint x="0" y="1600" w="1080" h="320" easeType="QuadEaseOut" easeTime="1000">
		<NormalState> 
			<Image x="31" y="#screen_height-117" src="unlock_button.png" /> 
		</NormalState>
		//Trigger Slider的状态切换时,支持Trigger触发
		<PressedState> 
			<Text x="640" y="240" w="640"  color="#FE9D01" size="28" text="小米新国货" />
		</PressedState>

		
		<ReachedState>
			<DateTime/>
			
			<Trigger> 
				<SoundCommand sound="reached.mp3" volume="1"/> 
			</Trigger>
		</ReachedState>
	</StartPoint>

	<EndPoint x="359" y="#screen_height-117" w="90" h="90"> 
		<Path x="0" y="#screen_height-117"> 
			<Position x="31" y="0" /> 
			<Position x="359" y="0" />
		</Path>
		<NormalState> 
		</NormalState>
		<PressedState> 
		</PressedState>
		<ReachedState>
		</ReachedState>
	</EndPoint>
</Slider>

命令组调用

如果某组操作会被很多处调用,可以将这组操作写到一个空的Group中作为一个trigger action,然后可以被多处调用 没什么好解释的,边看示例边实践牢记:

<Group name="triggersContainer">
	<Triggers>
		<Trigger action="fine1">
			<VariableCommand name="month_num" expression="#month_num+1" />
			<VariableCommand name="month_zf" expression="1" />
			<VariableCommand name="day_num" expression="#day_num+#maxday_b" />
		</Trigger>
		<Trigger action="fine2">
			<VariableCommand name="month_num" expression="#month_num+1" />
		</Trigger>
	</Triggers>
</Group>
<Button x="0" y="0" w="500" h="500">
	<Triggers>
		<Trigger action="up">
			
			<MethodCommand target="triggersContainer" method="performAction" paramTypes="String" params="'fine1'"  />
		</Trigger>
	</Triggers>
</Button>

音乐播放器

MusicControl:音乐标签

autoShow true/false,为true时,会根据系统有否播放音乐自动展现和关闭音乐组件;为false时,不会自动展示和关闭
defAlbumCover 指定默认专辑图片,填路径文件名
enableLyric true/false,是否开启歌词支持。因为开启歌词后会有一定性能的损失,所以不需要歌词的时候不要打开
updateProgressInterval 歌词信息的更新间隔。间隔越小,更新越及时,但带来的性能损失也越大,建议在制作卡拉OK歌词的时候,将间隔适当设小,其他时候为了性能考虑,将间隔适当设大
music_album_cover 专辑图
clip true/false,为true时,Group内的元素只显示w、h规定的区域,超出部份切除
name.title 歌曲名
name.artist 歌手名
name.lyric_before 已播放过的歌词
name.lyric_current 正在播放的歌词
name.lyric_after 未播放的歌词
name.lyric_last 上一句歌词
name.lyric_next 下一句歌词
name.lyric_current_line_progress 当前行歌词的行内播放进度,浮点数0~1.0
name.music_duration 歌曲长度,单位ms
name.music_position 歌曲当前播放位置,单位ms

看示例:

<MusicControl name="music" autoShow="true" visibility="false" defAlbumCover="default_bg.png"   enableLyric="true" updateLyricInterval="100">
	<Image x="90" y="90" name="music_album_cover" w="450" h="450" />
	
	<Text x="540" y="200" align="center" textExp="ifelse(isnull(@music.title),'无标题',@music.title)" bold="true" size="48" color="#ffffff" w="800" marqueeSpeed="30" />
	
	<Text x="540" y="290" align="center" textExp="ifelse(isnull(@music.artist),'未知',@music.artist)" size="35" color="#ffffff" alpha="155" w="800" marqueeSpeed="30" />
	
	<Group x="#screen_width/2-640/2" y="1300" w="640" h="240" clip="true">
		<Text x="640/2" y="100" w="640" align="center" multiLine="true" color="#FFFE9D01" size="28" text="@music.lyric_before" alignV="bottom" spacingMult="1.2"/>
		<Text x="640/2" y="100" w="640" align="center" multiLine="true" color="#FF43A102" size="28" text="@music.lyric_current" bold="true" spacingMult="1.2" name="cur_lyric"/>
		<Text x="640/2-#cur_lyric.text_width/2" y="100" w="#cur_lyric.text_width*#music.lyric_current_line_progress" color="#FFFE9D01" size="28" text="@music.lyric_current" bold="true" />
		<Text x="640/2" y="145" w="640" align="center" multiLine="true" color="#FF55A255" size="28" text="@music.lyric_after" spacingMult="1.2"/>
	</Group>
	
	<Group y="1820">
		<Text x="140" y="0" textExp="int(#music.music_position/600000)+int((#music.music_position/60000)%10)+':'+int(#music.music_position/1000%60/10)+int(#music.music_position/1000%60%10)" align="right" size="32" color="#ffffff" />
		<Text x="940" y="0" textExp="int(#music.music_duration/600000)+int((#music.music_duration/60000)%10)+':'+int(#music.music_duration/1000%60/10)+int(#music.music_duration/1000%60%10)" size="32" color="#ffffff" />
		<Image x="170" y="18" src="music/progress_bg.png" />
		<Image x="170" y="18" src="music/progress.9.png" w="int(740*ifelse(not(#music.music_duration),0,#music.music_position/#music.music_duration))" />
	</Group>
	
	<Image x="180" y="1560" src="music/play.png" visibility="not(#music.music_state)" />
	<Image x="180" y="1560" src="music/pause.png" visibility="#music.music_state" />
	<Button name="music_prev"	x="180" y="1560" w="240" h="180" />
	<Button name="music_next"	x="660" y="1560" w="240" h="180" />
	<Button name="music_play"	x="420" y="1560" w="240" h="180" />
	<Button name="music_pause"	x="420" y="1560" w="240" h="180" />
</MusicControl>

数据的获取与传递

WebService

<VariableBinders>
	<WebServiceBinder name="MyWeather" uri="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName" params="theCityName:北京" requestMethod="get" updateInterval="3600" updateIntervalFail="60">
		<Variable name="weather_city"		type="string" xpath="/ArrayOfString/string[2]" 	/>
		<Variable name="weather_temperature"	type="string" xpath="/ArrayOfString/string[6]" 	/>
		<Variable name="weather_summary"	type="string" xpath="/ArrayOfString/string[7]" 	/>
		<Variable name="weather_wind"		type="string" xpath="/ArrayOfString/string[8]" 	/>
		<Variable name="weather_icon1"		type="string" xpath="/ArrayOfString/string[9]" 	/>
		<Variable name="weather_icon2"		type="string" xpath="/ArrayOfString/string[10]" />
		<Variable name="weather_real"		type="string" xpath="/ArrayOfString/string[11]" />
	</WebServiceBinder>
</VariableBinders>

<ExternalCommands>
	<Trigger action="resume">
		<BinderCommand name="MyWeather" command="refresh" />
	</Trigger>
	<Trigger action="pause" />
</ExternalCommands>
VariableBinders 定义各种变量绑定到的源。目前支持ContentProviderBinder、WebServiceBinder、SensorBinder
name 名称,用来在系统中保存查询结果
uri webservice api 地址
params 参数,格式: “参数名1:值1,参数名2:值2,参数名3:值3″
updateInterval 查询间隔,单位:秒
updateIntervalFail 查询失败后的重试时间,单位秒
queryAtStart 是否初始化时自动开始查询一次,否则只有BinderCommand命令对它执行查询
protocol 数据源的类型,json/obj
Variable 定义一个变量
type 参数的类型。string、number
xpath 将返回xml结果中xpath指定的内容绑定到该参数

ContentProvider

content provider 提供了查询应用程序信息的通用接口,定义了新的xml代码来查询content provider,并查询到的信息绑定到变量上,用来显示第三方应用程序的信息,只要第三方应用提供相应的content provider。比如可以显示天气信息,邮件,待办事项,便签,等等

<VariableBinders>
  <ContentProviderBinder name="name1" uri="content://sample/test" c uriFormat="" uriParas="" columns="col1,col2" where="" args="" order="" countName="count_name">
     <Variable name="variable_name1" type="int" column="col1" row="0"/>
     <Variable name="variable_name2" type="string" column="col2" row="0"/>
  </ContentProviderBinder>
  <ContentProviderBinder name="name2" dependency="name1" />
</VariableBinders>
VariableBinders 定义各种变量绑定到的源。目前支持ContentProviderBinder、WebServiceBinder、SensorBinder
ContentProviderBinder 定义一个content provider源和绑定到它上面的变量
uir 指定选用哪个content provider
uriFormat 如果uri需要添加变量,可以用格式化,需要和uriParas一起使用
uriParas 同Text element 的格式
columns 需要查询的列名,用逗号分隔
where 查询条件,同 SQL
args “where” 的参数.
order 排序条件, 同 SQL
countName 将查询结构数量绑定到该变量名
Variable 定一个绑定变量
name 变量名
type content provider中的数据类型: string/double/float/int/long
column 变量绑定到的列的名称.
row 变量绑定到的行数,默认为0.
支持where的格式化 where=”” whereFormat=”” whereParas=””
dependency 支持依赖关系,即某个ContentProviderBinder查询结束后获取的变量作为下一个ContentProviderBinder查询的参数()
<ContentProviderBinder name="name1" />
<ContentProviderBinder name="name2" dependency="name1" />
  • name1查询结束后会触发name2的查询
  • name2的查询可以使用name1的变量
  • 并且如果name1数据发生变化重新查询后,会触发name2的重新查询。

传感器调用

SensorBinder 现在支持重力感应,方向感应,加速度感应,气压感应(海拔高度) 1.重力传感器

<VariableBinders>
	<SensorBinder type="gravity"  rate="2"> 
		<Variable name="" index="0"/> 
		<Variable name="" index="1"/> 
		<Variable name="" index="2" /> 
	</SensorBinder>
</VariableBinders>

在重力感应的数据中:

index=0 x方向的重力加速度
index=1 y方向的重力加速度
index=2 z方向的重力加速度
rate





常量,单位是微秒,有4种特殊值0,1,2,3
默认为3(不写的话)
0表示0微秒
1是20000微秒
2是66667微秒
3是200000微秒

⊙ 注意特别标注颜色的文字

2.方向传感器

<VariableBinders>
	<SensorBinder type="orientation"> 
		<Variable name="" index="0"/> 
		<Variable name="" index="1"/> 
		<Variable name="" index="2" /> 
	</SensorBinder>
</VariableBinders>

在方向感应的数据中:

index=0 方位角,0~359,0=北,90=东,180=南,270=西
index=1 俯仰角,-180~180,z轴转向y轴为正方向
index=2 滚转角,-90~90,x轴转向z轴为正方向

3.加速度传感器:

<VariableBinders>
	<SensorBinder type="accelerometer"> 
		<Variable name="" index="0"/> 
		<Variable name="" index="1"/> 
		<Variable name="" index="2" /> 
	</SensorBinder>
</VariableBinders>

在加速度感应的数据中:

index=0 x方向的加速度
index=1 y方向的加速度
index=2 z方向的加速度

4.线性加速度传感器:

<VariableBinders>
	<SensorBinder type="linear_acceleration"> 
		<Variable name="" index="0"/> 
		<Variable name="" index="1"/> 
		<Variable name="" index="2" /> 
	</SensorBinder>
</VariableBinders>

在线性加速度感应的数据中:

index=0 x方向的线性加速度
index=1 y方向的线性加速度
index=2 z方向的线性加速度

线性加速度是去掉了重力加速度影响的:加速度 = 线性加速度 + 重力加速度

5.气压传感器

<VariableBinders>
	<SensorBinder type="pressure"> 
		<Variable name="" index="0"/> 
	</SensorBinder>
</VariableBinders>
index=0 气压值,单位hPa。海平面的平均气压是1013.25hPa,可以根据气压值估计海拔高度

6.位置传感器

<VariableBinders>
	<LocationBinder type="0" time="30000" distance="10">
		<Variable name="location_str" type="string[]"/>
	</LocationBinder> 
</VariableBinders>
<Text textExp="'定位精度:'+@location_str[0]+' 海拔:'+@location_str[1]+' 方位(°):'+@location_str[2]+' 纬度:'+@location_str[3]+' 经度:'+@location_str[4]+' 实时速度:'+@location_str[5]+' 定位时间(从1970年开始的毫秒值):'+@location_str[6]" size="24" x="50" y="1650" color="#ffffff" />
time 单位毫秒,最小定位间隔时间
distance 单位米,最小定位间隔距离

type:

0 默认,优先使用网络定位,网络定位不可用时使用GPS定位
1 网络定位
2 GPS定位

index 0 - 6 依次为:

0 定位精度
1 海拔
2 方位(单位°)
3 纬度
4 经度
5 实时速度
6 定位时间(从1970年开始的毫秒值)

广播

广播分成两部分:发送与接收
1.发送部分一般放Trigger中

<ExternalCommands>
	<Trigger action="pause">
		<IntentCommand action="initialization" broadcast="true">
			<Extra name="bg_number" type="number" expression="#rand_num" />
		</IntentCommand>
	</Trigger>
</ExternalCommands>

2.接收部分在VariableBinders里

<VariableBinders>
	<BroadcastBinder action="initialization">
		<Variable name="wallpaper_color" type="number" extra="bg_number" />
	</BroadcastBinder>
</VariableBinders>

动态帧率

动态帧率有两种用法:

  1. 普通用法:
    frameRateCharging="30" frameRateBatteryLow="20" frameRateBatteryFull="1"
    除了原来的frameRate属性控制普通状态下的帧率外,还支持分别设置充电、电量低、充满电状态下的帧率,以方便的控制这些状态下的帧率,达到省电的目标。比如普通状态下没有动画,帧率可以设为0,充电状态下有动画,帧率可以设为30,以显示平滑的动画,这样普通状态下就可以非常省电。

  2. 高级用法:
<FramerateController name="framerate_an" loop="false">
	<ControlPoint time="0" frameRate="20"/>
	<ControlPoint time="1000" frameRate="0"/>
	<ControlPoint time="3000"/>
</FramerateController>
为了在xml描述语言中根据时间线指定不同帧率,新增一种element,叫做FramerateController,此元素和其他界面元素一样可以被添加到各个容器中,此元素包含一个帧率控制时间线,指定在一定的时间使用一定的帧率。该容器下的所有元素的动画都会与此元素指定的帧率变化同步

多语言环境适配

百变锁屏和其它MAML模块支持多语言,下面以锁屏为例说下(锁屏的文件都在advance文件夹下,在这我们就其为根目录)
多语言的适配有图片资源的适配和字符串的适配两种:

  • 图片资源的适配
    都放在根目录下,只不过非默认的需新建相应语言的文件夹(如:images_en、images_cn_TW)
默认图片: a.png
英文版: images_en/a.png
台湾繁体 images_zh_TW/a.png

  • 字符串资源的适配
英文: strings/strings.xml
简体: strings/strings_zh_CN.xml
繁体 strings/strings_zh_TW.xml
strings.xml 内容:
<strings>
     <string name="name1" value="Music player"/>
</strings>
strings_zh_CN.xml 内容:
<strings>
     <string name="name1" value="打开音乐播放器"/>
</strings>
strings_zh_TW.xml 内容:
<strings>
      <string name="name1" value="打開音樂播放器"/>
</strings>

manifest.xml 中可直接使用变量 @name1

  • 自定义配置文件的适配(config文件直接加后缀即可)
默认: config.xml
简体: config_zh_CN.xml
繁体 config_zh_TW.xml

锁屏个性化设置

原贴传送阵
主题都可以带一个配置描述xml文件描述可以个性化配置的项目(config.xml 和manifest.xml 放在同一个目录下。)

  • 开关
  • 文字输入
  • 文字选择
  • 数字选择
  • 程序快捷方式。
  • 自定义图片
<Config>											// 根节点
	<Group text="日期时间">									// 设置组  text 设置组名称,显示在设置界面中的文本
		<CheckBox text="" summary="" id="show_date" default="0"/>			// 开关(summary:设置项详细说明;id:设置项对应的变量名称;default:缺省值)
		<StringInput text="日期格式" summary="" id="format" default="M月d日"/>		// 文字输入
		<NumberInput text="文字大小" summary="文字大小" id="size_date" default="28"/>	//数字输入
	</Group>
	<Group text="请选择">
		<StringChoice text="" summary="" customizable="true" id="time_format">		// 文字选择
			<Item value="hh:mm" text="12小时"/>					//文字选择项目 value变量值 text界面显示文字
			<Item value="kk:mm" text="24小时"/>
		</StringChoice>
		<NumberChoice text="" summary="" id="">						//数字选择
			<Item value="0" text="模式1"/>						//数字选择项目 value数值 text界面显示文字
			<Item value="1" text="模式2"/>
		</NumberChoice>
	</Group>
       <Group text="自定义图片" summary="请先将图片裁剪到合适大小和部位以确保显示效果">
               <ImagePicker text="图片一" summary="选择图片一" id="img1"/>
               <ImagePicker text="图片二" summary="选择图片二" id="img2"/>
       </Group>
	<Group text="快捷方式">														
        <AppPicker text="左边快捷方式" id="left_task"/>						
        <AppPicker text="右边快捷方式" id="right_task"/>
	</Group>
</Config>

支持个性化设置的MIUI默认锁屏的附件中的代码MIUI默认锁屏.mtz

清除图片(将相应变量置空即可):

<Image  x="0" y="0" src="@img1" srcType="Uri"/>
<Image  x="0" y="500" src="@img2" srcType="Uri"/>
<Button x="0" y="80" w="100" h="100" visibility="#img1.has_bitmap">
	<Triggers>
		<Trigger action="up">
			<VariableCommand name="img1" expression="" type="string" persist="true" />
		</Trigger>
	</Triggers>
</Button>
<Unlocker name="unlocker" bounceInitSpeed="1000">
       <StartPoint x="0" y="1620" w="1080" h="300" />
       <EndPoint x="-50" y="1320" w="100" h="100">
		<Intent id="left_task" />
		<Path> 
			<Position x="0" y="0" /> 
			<Position x="0" y="-250" />
		</Path>
      </EndPoint>
</Unlocker>

附快速进入锁屏自定义界面的快捷方式

<Button x="#screen_width-200" y="48" w="200" h="200">
	<Triggers>
		<Trigger action="up">
			<IntentCommand action="android.intent.action.MAIN" package="com.android.thememanager" class="miui.maml.MamlConfigSettings">
				<Extra name="maml_code" type="string" expression="'lockstyle'"/>
			</IntentCommand>
			<ExternCommand command="unlock"/>
		</Trigger>
	</Triggers>
</Button>

高级

变量数组

数组,就是相同数据类型的元素按顺序排列的集合,把多个类型相同的变量用一个名字命名,然后用编号区分他们的变量,这个名字称为数组名,编号称为下标。 看下面例子(强烈建议大家复制这段代码到锁屏测试)

<Var name="sss" type="number[]"  index="0" expression="1" size="4" const="true" /> //必须先用size定义数组的范围
<Var name="sss" type="number[]"  index="1" expression="2" const="true" />
<Var name="sss" type="number[]"  index="2" expression="3" const="true" />
<Var name="sss" type="number[]"  index="3" expression="4" const="true" />
<Text x="100" y="400" textExp="#sss[0]+'  '+#sss[1]+'  '+#sss[2]+'  '+#sss[3]" size="30" color="#ffffff" />
name 变量名
type 定义数组类型(数值数组用number[],字符数组用string[])
size 定义变量数组范围(必须先定义数组的范围,后面引用的话不用再次声明
const 变量会在初始化时计算一次(V5),V6V7是两次,以后不会重新计算,可以提高效率。如果变量值会在锁屏运行期间改变,const设为false 默认:false
index 这个可以理解为数组内的编号或是下标

前面示例数组是已经赋值了的,如果不知道它们的值,可以像下面这样表示,先只做一个声明

<Var name="sss" type="number[]" size="4" const="true" />

用按钮给数组赋值的方法

<Button x="0" y="0" w="1080" h="1920" >
	<Triggers>
		<Trigger action="up">
 			<VariableCommand name="sss" type="number[]"  index="0" expression="1" /> //如果在其它地方已经用size定义了该数组的范围,这里就不用重新定义了
 			<VariableCommand name="sss" type="number[]"  index="1" expression="2" />
 			<VariableCommand name="sss" type="number[]"  index="2" expression="3" />
			<VariableCommand name="sss" type="number[]"  index="3" expression="4" />
		</Trigger>
	</Triggers>
</Button>

元素数组

顾名思义,就是界面元素(图形、文字…)以特定规律的形式展现,避免类似代码的重复。
例如:我要把100张80x80的矩形按10行10列(间距为20像素)的形式显示出来,如果是平常,我们至少得写100行代码,但现在用元素数组几行代码就行了。看下面:

<Array x="50" y="500" count="100" indexName="aaa" >
	<Rectangle x="#aaa%10*100" y="int(#aaa/10)*100" w="80" h="80" fillColor="#99ffffff" />  //“%”与“int”的用法请看后面:表达式
	<Text textExp="#aaa" size="36" align="center" x="#aaa%10*100+40" y="int(#aaa/10)*100+20"  color="#000000" />
</Array> 
Array 元素数组的标签(字面意思是排列,没错!就是对各元素进行排列)
indexName 指数名称,可理解为元素数组内,用来给各元素编号的变量名
count 规定这个元素数组内同类型元素的个数,也就是indexName中你自定义的变量名的范围,在Array中的count不支持表达式

循环处理

主要与数组配合使用,可节省大量的代码,并提高效率。
<LoopCommand indexName="" count="" loopCondition="" >

LoopCommand 循环标签
indexName 指数名称,用来标注循环计算次数的变量名(下面实例中,#col等于0时,说明是第一次计算)
count 规定循环次数
begin indexName指定的变量到达某个值时开始计算
end indexName指定的变量到达某个值时终止计算
loopCondition 循环条件,可以用来中断循环

示例:

<Var name="find" type="number[]" size="100" const="true" /> 
<Array x="100" y="300" count="100" indexName="aaa" >
	<Text textExp="#find[#aaa]" size="30" align="center" x="#aaa%10*90" y="int(#aaa/10)*90"  color="#ffffff" />
</Array> 
<Button x="0" y="0" w="1080" h="1920">
	<Triggers>
		<Trigger action="up">
			<LoopCommand count="100" indexName="col">
				<VariableCommand name="find" type="number[]" index="#col" expression="#col"  />
			</LoopCommand>
		</Trigger>
	</Triggers>
</Button>


下面这段与前面中的红色代码等效

<LoopCommand begin="0" end="99" indexName="col">
	<VariableCommand name="find" type="number[]" index="#col" expression="#col"  />
</LoopCommand>

贝塞尔曲线

重点:CanvasDrawer是独立的,必须先计算在其它分辨率下的缩放比,

<?xml version="1.0" encoding="UTF-8"?>
<Lockscreen version="2" frameRate="60" screenWidth="1080">
	<Var name="orix" type="number" expression="400" const="true"/>
	<Var name="oriy" type="number" expression="400" const="true"/>
	<Var name="startx" type="number" expression="0" const="true"/>
	<Var name="starty" type="number" expression="0" const="true"/>
	<Var name="endx" type="number" expression="800" const="true"/>
	<Var name="endy" type="number" expression="0" const="true"/>
	<!-- 先初始化 -->
	<ExternalCommands>
		<Trigger action="init">
			<!-- scale_mum:CanvasDrawer是独立的,先计算在其它分辨率下的缩放比 -->
			<VariableCommand name="scale_mum"  expression="int(#raw_screen_width/1.08)/1000" />
			<MethodCommand targetType="ctor" class="android.graphics.Path" return="path" returnType="object"/>
			<MethodCommand target="path" targetType="var" method="moveTo" paramTypes="float,float" params="#startx,#starty"/>
			<MethodCommand target="path" targetType="var" method="quadTo" paramTypes="float,float,float,float" params="#orix,#oriy,#endx,#endy"/>
			<MethodCommand targetType="ctor" class="android.graphics.Paint" return="paint" returnType="object"/>
			<MethodCommand targetType="var" class="miui.maml.util.ReflectionHelper" method="getEnumConstant" paramTypes="String,String" params="'android.graphics.Paint$Style','STROKE'" return="style" returnType="object"/>
			<MethodCommand target="paint" targetType="var" method="setStyle" paramTypes="android.graphics.Paint$Style" params="'style'"/>
		</Trigger>
	</ExternalCommands>
	<Rectangle x="0" y="0" w="1080" h="1920" fillColor="argb(255,255,255,255)" touchable="true">
		<Triggers>
			<Trigger action="down" >
				<!-- 初始点 -->
				<VariableCommand name="x0" type="number" expression="#touch_x"/>
				<VariableCommand name="y0" type="number" expression="#touch_y"/>
			</Trigger>
			<!-- 滑动过程会刷新数据 -->
			<Trigger action="move" >
				<VariableCommand name="dx" type="number" expression="#touch_x-#x0"/>
				<VariableCommand name="dy" type="number" expression="#touch_y-#y0"/>
				<MethodCommand target="path" targetType="var" method="reset"/>
				<MethodCommand target="path" targetType="var" method="moveTo" paramTypes="float,float" params="#startx,#starty"/>
				<MethodCommand target="path" targetType="var" method="quadTo" paramTypes="float,float,float,float" params="#orix+#dx,#oriy+#dy,#endx,#endy"/>
			</Trigger>
			<Trigger action="up" >
				<VariableCommand name="orix" type="number" expression="#orix+#dx"/>
				<VariableCommand name="oriy" type="number" expression="#oriy+#dy"/>
			</Trigger>
		</Triggers>
	</Rectangle>
	<Group x="140" y="240" scale="#scale_mum">
	<!-- CanvasDrawer;画布哦!对这功能给跪了!大家好好用!谢谢MAML之父 -->
		<CanvasDrawer x="0" y="0">
			<Triggers>
				<Trigger action="draw" >
					<MethodCommand target="paint" targetType="var" method="setStrokeWidth" paramTypes="float" params="8"/>
					<MethodCommand target="paint" targetType="var" method="setColor" paramTypes="int" params="0xff00ff00"/>
					<MethodCommand target="__objCanvas" targetType="var" method="drawPath" paramTypes="android.graphics.Path,android.graphics.Paint" params="'path','paint'"/>

					<MethodCommand target="paint" targetType="var" method="setStrokeWidth" paramTypes="float" params="0"/>
					<MethodCommand target="paint" targetType="var" method="setColor" paramTypes="int" params="0xff000000"/>
					<MethodCommand target="paint" targetType="var" method="setTextSize" paramTypes="float" params="40"/> 
					<MethodCommand target="__objCanvas" targetType="var" method="drawTextOnPath" paramTypes="String,android.graphics.Path,float,float,android.graphics.Paint" params="'Hello World! This is a new awosome feature. 你好,这是很牛逼的新功能!','path',0,-25,'paint'"/>
				</Trigger>
			</Triggers>
		</CanvasDrawer>
	</Group>
</Lockscreen>

分享

在百变壁纸为时钟插件添加大背景

放时钟插件内

<ExternalCommands>
	<Trigger action="resume">
		<!-- 每次用不同的值发送一个广播(用于百变壁纸收到广播后触发计算) -->
		<IntentCommand action="initialization" broadcast="true">
			<Extra name="bg_number" type="number" expression="#time%1000000" />
		</IntentCommand>
	</Trigger>
</ExternalCommands>

放百变壁纸内

<VariableBinders>
	<!-- 收到时钟插件发的广播后开始计算有时钟时wallpaper_offset_x的值,并用ui_num记录 -->
	<BroadcastBinder action="initialization">
		<Variable name="bg_num" type="number" extra="bg_number" />
		<Trigger condition="#bg_num">
			<VariableCommand name="ui_num" expression="int(#wallpaper_offset_x*10000000)" />
		</Trigger>
	</BroadcastBinder>
</VariableBinders>
<!-- 背景 -->
<Rectangle x="0" y="0" w="1080" h="2160" fillColor="#fffcfed8"   >
<!-- wallpaper_offset_step:wallpaper_offset_x等于ui_num时,透明度为255;单屏偏移量,wallpaper_offset_x当前值与ui_num的差值的绝对值等于wallpaper_offset_step时,透明度为0 -->
<Rectangle x="0" y="0" w="1080" h="1080" fillColor="#fffcfed8" alpha="255-int(255*abs(int(#wallpaper_offset_x*10000000)-#ui_num)/10000000/#wallpaper_offset_step)"  >
	<FillShaders>
		<LinearGradient x="0" y="0" x1="0" y1="1080" tile="clamp">
			<GradientStop color="#ff46e1c6" position="0"/>
			<GradientStop color="#fffcfed8" position="1"/>
		</LinearGradient>
	</FillShaders>
</Rectangle>

天气查询

<VariableBinders>
	<!-- MIUI天气代码 -->
	<ContentProviderBinder name="weather" uri="content://weather/actualWeatherData/1"
columns="city_id,city_name,temperature,temperature_range,weather_type,wind,humidity,publish_time,weathernamesfrom,weathernamesto,tmplows,tmphighs,description" 
countName="hasweather" dependency="selected_city">
		<Variable name="city_id" type="string" column="city_id"/>
		<Variable name="weather_location" type="string" column="city_name" />
		<Variable name="w_tem" type="int" column="temperature" />
		<Variable name="w_id" type="int" column="weather_type" />
		<Variable name="w_wind" type="string" column="wind" />
		<Variable name="w_tpr" type="string" column="temperature_range" />
		<Variable name="w_humidity" type="string" column="humidity" />
		<Variable name="w_publish_time" type="long" column="publish_time" />
		<Variable name="tmp_low0" type="int" column="tmplows" row="0" />
		<Variable name="tmp_high0" type="int" column="tmphighs" row="0" />
		<Variable name="w_dp0" type="string" column="description" row="0" />
		<Variable name="wf1" type="string" column="weathernamesfrom" row="1" />
		<Variable name="wt1" type="string" column="weathernamesto" row="1" />
		<Variable name="tmp_low1" type="int" column="tmplows" row="1" />
		<Variable name="tmp_high1" type="int" column="tmphighs" row="1" />
	</ContentProviderBinder>
	<ContentProviderBinder name="weather_aqi" dependency="weather" uriFormat="content://weatherinfo/aqi/%s" uriParas="@city_id" columns="aqi,pm25,pm10,so2,no2,pub_time,src,spot" countName="hasweatherinfo">
		<Variable name="aqi" type="int" column="aqi" />
		<Variable name="pm25" type="int" column="pm25" />
		<Variable name="pm10" type="int" column="pm10" />
		<Variable name="so2" type="int" column="so2" />
		<Variable name="no2" type="int" column="no2" />
	</ContentProviderBinder>
</VariableBinders>
<Var name="air_quality" expression="ifelse(ge(#aqi,0)*lt(#aqi,50),'优',ge(#aqi,50)*lt(#aqi,100),'良',ge(#aqi,100)*lt(#aqi,150),'轻度污染',ge(#aqi,150)*lt(#aqi,200),'中度污染',ge(#aqi,200)*lt(#aqi,300),'重度污染',ge(#aqi,300)*le(#aqi,500),'严重污染','获取信息异常')" type="string"/>
<Text   x="100" y="300"  size="24" color="#FFFFFF" textExp="@city_id+' '+@weather_location+' '+#pm25+' '+@air_quality’’" />

通知

<VariableBinders>
	<ContentProviderBinder name="data" uri="content://keyguard.notification/notifications" columns="icon,title,content,time,info,subtext,key" countName="hasnotifications">
		<List name="notification_list" />
	</ContentProviderBinder>
</VariableBinders>
<List name="notification_list" x="0" y="0" w="858" maxHeight="370" data="icon:bitmap,title:string,content:string,time:string,info:string,subtext:string,key:int" visibility="#hasnotifications">
	<Item x="0" y="0" w="858" h="185">
		<Button x="0" y="0" w="858" h="185" >
			<Normal>
				<Image x="0" y="0" src="notice_bg.png" />
			</Normal>
			<Pressed>
				<Image x="0" y="0" src="notice_bg.png" alpha="120" />
			</Pressed>
			<Image x="50" y="21" w="114" h="114" name="notification_icon" />
			<Text x="190" y="42" size="32" color="#ee000000" bold="true" w="500" h="36" marqueeSpeed="30" name="notification_title" />
			<Text x="190" y="80" size="28" color="#b4000000" w="500" h="42" marqueeSpeed="30" name="notification_content" />
			<Text x="800" y="64" size="28" align="right" color="#b4000000"  name="notification_time" />
			<Triggers>
				<Trigger action="up">
					<IntentCommand action="com.miui.app.ExtraStatusBarManager.action_remove_keyguard_notification" broadcast="true">
						<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_key" type="int" expression="#notification_list.key" />
						<Extra name="com.miui.app.ExtraStatusBarManager.extra_notification_click" type="int" expression="(#touch_x-#touch_begin_x) } -100" />
					</IntentCommand>
				</Trigger>
			</Triggers>
		</Button>
	</Item>
	<AttrDataBinders>
		<AttrDataBinder target="notification_icon" attr="bitmap" data="icon" />
		<AttrDataBinder target="notification_title" attr="text" data="title" />
		<AttrDataBinder target="notification_content" attr="text" data="content" />
		<AttrDataBinder target="notification_time" attr="text" data="time" />
	</AttrDataBinders>
</List>

便签

<VariableBinders>
	<ContentProviderBinder uri="content://notes/note" columns="modified_date,snippet" order="modified_date DESC" countName="note_count">
		<Variable name="note_snippet1" type="string" column="snippet" row="0" />
		<Variable name="note_snippet2" type="string" column="snippet" row="1" />
	</ContentProviderBinder>
</VariableBinders>

常用应用Intent

便签 action="android.intent.action.MAIN" package="com.miui.notes" class="com.miui.notes.ui.NotesListActivity"
新浪微博 action="android.intent.action.MAIN" package="com.sina.weibo" class="com.sina.weibo.SplashActivity"
用户反馈 action="android.intent.action.MAIN" package="com.miui.bugreport" class="com.miui.bugreport.ui.TypeSelectionActivity"
米聊2014 action="android.intent.action.MAIN" package="com.xiaomi.channel" class="com.xiaomi.channel.ui.ChannelLauncherActivity"
手电筒 action="android.intent.action.MAIN" package="com.android.systemui" class="com.android.systemui.TorchActivity"
文件管理 action="android.intent.action.MAIN" package="com.android.fileexplorer" class="com.android.fileexplorer.FileExplorerTabActivity"
图库 action="android.intent.action.MAIN" package="com.miui.gallery" class="com.miui.gallery.app.Gallery"
相册 action="android.intent.action.MAIN" package="com.miui.gallery" class="com.miui.gallery.activity.HomePageActivity" (V8)
系统更新 action="android.intent.action.MAIN" package="com.android.updater" class="com.android.updater.MainActivity"
浏览器 action="android.intent.action.MAIN" package="com.android.browser" class="com.android.browser.BrowserActivity"
计算器 action="android.intent.action.MAIN" package="com.miui.calculator" class="com.miui.calculator.cal.CalculatorActivity"
短信 action="android.intent.action.MAIN" package="com.android.mms" class="com.android.mms.ui.MmsTabActivity"
联系人 action="android.intent.action.MAIN" package="com.android.contacts" class="com.android.contacts.activities.PeopleActivity"
拨号 action="android.intent.action.VIEW" type="vnd.android.cursor.dir/calls"
action="android.intent.action.MAIN" package="com.android.contacts" class="com.android.contacts.activities.TwelveKeyDialer"
指南针 action="android.intent.action.MAIN" package="com.miui.compass" class="com.miui.compass.CompassActivity"
米币中心 action="android.intent.action.MAIN" package="com.xiaomi.payment" class="com.xiaomi.payment.MiliCenterEntryActivity"
百度地图 action="android.intent.action.MAIN" package="com.baidu.BaiduMap" class="com.baidu.baidumaps.WelcomeScreen"
相机 action="android.intent.action.MAIN" package="com.android.camera" class="com.android.camera.Camera"
视频 action="android.intent.action.MAIN" package="com.miui.video" class="com.miui.video.HomeActivity"
主题风格 action="android.intent.action.MAIN" package="com.android.thememanager" class="com.android.thememanager.ThemeResourceTabActivity"
密码保护 action="android.intent.action.MAIN" package="com.android.settings" class="com.android.settings.MiuiPasswordGuardActivity"
游戏中心 action="android.intent.action.MAIN" package="com.xiaomi.gamecenter" class="com.xiaomi.gamecenter.ui.MainTabActivity"
设置 action="android.intent.action.MAIN" package="com.android.settings" class="com.android.settings.MiuiSettings"
日历 action="android.intent.action.MAIN" package="com.android.calendar" class="com.android.calendar.AllInOneActivity"
应用商店 action="android.intent.action.MAIN" package="com.xiaomi.market" class="com.xiaomi.market.ui.MarketTabActivity"
音乐 action="android.intent.action.MAIN" package="com.miui.player" class="com.miui.player.ui.MusicBrowserActivity"
天气 action="android.intent.action.MAIN" package="com.miui.weather2" class="com.miui.weather2.ActivityWeatherMain"
action="android.intent.action.MAIN" package="com.miui.weather2" class="com.miui.weather2.ActivityWeatherMain"
语音助手 action="android.intent.action.MAIN" package="com.miui.voiceassist" class="com.miui.voiceassist.CTAAlertActivity"
时钟 action="android.intent.action.MAIN" package="com.android.deskclock" class="com.android.deskclock.DeskClockTabActivity"
安全中心 action="android.intent.action.MAIN" package="com.android.settings" class="com.miui.securitycenter.Main"
电子邮件 action="android.intent.action.MAIN" package="com.android.email" class="com.android.email.activity.Welcome"
备份 action="android.intent.action.MAIN" package="com.miui.backup" class="com.miui.backup.BackupActivity"
收音机 action="android.intent.action.MAIN" package="com.miui.fmradio" class="com.miui.fmradio.FmRadioActivity"
收音机 action="android.intent.action.MAIN" package="com.android.soundrecorder" class="com.android.soundrecorder.SoundRecorder"
下载管理 action="android.intent.action.MAIN" package="com.android.providers.downloads.ui" class="com.android.providers.downloads.ui.DownloadList"
qq action="android.intent.action.MAIN" package="com.tencent.mobileqq" class="com.tencent.mobileqq.activity.SplashActivity"
qq空间 action="android.intent.action.MAIN" package="com.qzone" class="com.tencent.sc.activity.SplashActivity"
微信 action="android.intent.action.MAIN" package="com.tencent.mm" class="com.tencent.mm.ui.LauncherUI"
UC action="android.intent.action.MAIN" package="com.UCMobile" class="com.UCMobile.main.UCMobile"
小米商城 action="android.intent.action.MAIN" package="com.xiaomi.shop" class="com.xiaomi.shop.activity.MainTabActivity"
小米生活 action="android.intent.action.MAIN" package="com.xiaomi.o2o" class="com.xiaomi.o2o.activity.O2OTabActivity"
支付宝 action="android.intent.action.MAIN" package="com.eg.android.AlipayGphone" class="com.eg.android.AlipayGphone.AlipayLogin"
美图秀秀 action="android.intent.action.MAIN" package="com.mt.mtxx.mtxx" class="com.mt.mtxx.mtxx.TopViewActivity"
头条 action="android.intent.action.MAIN" package="com.ss.android.article.news" class="com.ss.android.article.news.activity.SplashActivity"
淘宝 action="android.intent.action.MAIN" package="com.taobao.taobao" class="com.taobao.tao.welcome.Welcome"

自定义系统密码

请点下面传送阵去看大神原贴
自定义系统密码锁屏

三屏切换

 <!-- 三屏划动切换控制动画 -->
 <Var name="move" expression="max(min(int((#touch_x-#touch_begin_x)*#move_ok+#move_go),1080),-1080)" />
 <Var name="move_go">
 	<VariableAnimation initPause="true" loop="false">
 		<Item value="#move1" time="0" easeType="QuartEaseOut" />
 		<Item value="#move2" time="200" />
 	</VariableAnimation>
 </Var>
 <!-- 此按钮用来判断是向左还是向右滑动 -->
 <Button x="0" y="120" w="1080" h="1800">
 	<Triggers>
 		<Trigger action="down">
 			<VariableCommand name="move_ok" expression="1" />
 		</Trigger>
 		<Trigger action="up,cancel">
 			<VariableCommand name="zf" expression="(gt(#touch_x,#touch_begin_x)*2-1)*gt(abs(#touch_x-#touch_begin_x),200)" />
 			<VariableCommand name="zf_end" expression="not(lt(#move,-1080)*eq(#zf,-1))*not(gt(#move,1080)*eq(#zf,1))*#zf" />
 			<VariableCommand name="move_ok" expression="0" />
 			<VariableCommand name="move1" expression="#move2+#touch_x-#touch_begin_x" />
 			<VariableCommand name="move2" expression="max(min(#move2+#zf_end*1080,1080),-1080)" />
 			<Command target="move_go.animation" value="play" />
 		</Trigger>
 	</Triggers>
 </Button>
 <Group x="#move">
 	<!-- 第1屏-->
 	<Group x="-1080" visibility="gt(#move,0)">
 		<Rectangle x="0" y="0" w="1080" h="1920" fillColor="#a6937c" />
 	</Group>
 	<!-- 第2屏-->
 	<Group x="0" visibility="gt(#move,-1080)*lt(#move,1080)">
 		<Rectangle x="0" y="0" w="1080" h="1920" fillColor="#7e6b5a" />
 	</Group>
 	<!-- 第3屏-->
 	<Group x="1080" visibility="lt(#move,0)">
 		<Rectangle x="0" y="0" w="1080" h="1920" fillColor="#59493f" />
 	</Group>
 </Group>

日历模块

<!-- layer对内部刷新不高的元素有着非常大的优化作用,但有一点要注意:它会自动移至当前界面图层的最上方 -->
<Layer x="200" y="300" width="560" height="560" frameRate="0" >
	<!-- 每月的第一天在日历表第一排的位置 -->
	<Var name="dw0_1" expression="(7-(#date-#day_of_week)%7)%7+1" const="true" />
	<!-- 闰年么? -->
	<Var name="leap_year" expression="eq((#year%4),0)*ne((#year%100),0)+eq((#year%400),0)" const="true" />
	<!-- 这个月有多少天 -->
	<Var name="max_date" expression="28+ne(#leap_year+ne(#month+1,2),0)+ne((#month+1),2)+ne(#month,1)*ne(#month,3)*ne(#month,5)*ne(#month,8)*ne(#month,10)" const="true" />
	<Array name="fde" count="42" indexName="__i" >
		<Text textExp="substr('日一二三四五六',#__i,1)" size="36" x="20+#__i%7*80" y="0"  color="#ffffff"  visibility="lt(#__i,7)" />
		<Rectangle x="#__i%7*80" y="int(#__i/7)*80+70" w="79" h="79" fillColor="argb(55,255,255,255)" visibility="int(#__i-#dw0_1+2)!=#date" />
		<!-- 当天背景 -->
		<Rectangle x="#__i%7*80" y="int(#__i/7)*80+70" w="79" h="79" fillColor="argb(255,50,177,105)" visibility="int(#__i-#dw0_1+2)==#date" />
		<DateTime format="d" value="#time_sys+(#__i+2-#date-#dw0_1)*86400000" size="36" x="40+#__i%7*80" y="int(#__i/7)*80+70" align="center" color="#ffffff" alpha="int(120+135*eq(#__i-#dw0_1+2,#date))"  visibility="int(#__i-#dw0_1+2)*le(int(#__i-#dw0_1+2),#max_date)" />        
		<!-- 农历 (注意:其中的formatDate只有V7才支持)-->
		<DateTime formatExp="ifelse(eqs(formatDate('e',#time_sys+(#__i+2-#date-#dw0_1)*86400000),'初一'),'N月e','e')"  value="#time_sys+(#__i+2-#date-#dw0_1)*86400000" size="18" x="40+#__i%7*80" y="50+int(#__i/7)*80+70" align="center" color="#ffffff" alpha="int(120+135*eq(#__i-#dw0_1+2,#date))" visibility="int(#__i-#dw0_1+2)*le(int(#__i-#dw0_1+2),#max_date)"  />
	</Array>
</Layer>

倒计时

倒计时的计算逻辑其实很简单,看下面之前, 先搞清楚两点 日历的规律(四年一闰、百年不闰、四百年又闰 闰年时2月份多一天) 下面的倒计时以0000年为参照年份,计算方法是:目标年份至0000年过了多少天,现在的时间过了多少天,求两值的差值

<?xml version="1.0" encoding="UTF-8"?>
<Lockscreen frameRate="30" screenWidth="1080" version="1">
	<ExternalCommands>
		<Trigger action="resume">
			<VariableCommand name="djr1" expression="ifelse(isnull(@djr1),'距离高考',@djr1)" type="string" persist="true" />
			<VariableCommand name="lya" expression="eq((#year%4),0)*ne((#year%100),0)+eq((#year%400),0)" />
			<VariableCommand name="da" expression="(ge(#month,1)*31+(28+#lya)*ge(#month,2)+ge(#month,3)*31+ge(#month,4)*30+ge(#month,5)*31+ge(#month,6)*30+ge(#month,7)*31+ge(#month,8)*31+ge(#month,9)*30+ge(#month,10)*31+ge(#month,11)*30+ge(#month,12)*31)+(365*#year+int(#year/4)+int(#year/400)-int(#year/100))+#date-1" />
			<VariableCommand name="fa" expression="#hour24*60+#minute" />
			<!-- 目标日期(2015/6/7 8:30)与计算 -->
			<VariableCommand name="y1" expression="ifelse(isnull(#y1),2016,#y1)" persist="true" />
			<VariableCommand name="m1" expression="ifelse(isnull(#m1),7,#m1)" persist="true" />
			<VariableCommand name="d1" expression="ifelse(isnull(#d1),8,#d1)" persist="true" />
			<VariableCommand name="h1" expression="ifelse(isnull(#h1),8,#h1)" persist="true" />
			<VariableCommand name="f1" expression="ifelse(isnull(#f1),30,#f1)" persist="true" />
			<VariableCommand name="lyz1" expression="eq((#y1%4),0)*ne((#y1%100),0)+eq((#y1%400),0)" />
			<VariableCommand name="dz1" expression="(gt(#m1,1)*31+(28+#lyz1)*gt(#m1,2)+gt(#m1,3)*31+gt(#m1,4)*30+gt(#m1,5)*31+gt(#m1,6)*30+gt(#m1,7)*31+gt(#m1,8)*31+gt(#m1,9)*30+gt(#m1,10)*31+gt(#m1,11)*30+gt(#m1,12)*31)+(365*#y1+int(#y1/4)+int(#y1/400)-int(#y1/100))+#d1-1" />
			<VariableCommand name="fz1" expression="#h1*60+#f1" />
		</Trigger>
	</ExternalCommands>

	<Var name="vvv1" expression="ge(#dz1*1440+#fz1,#da*1440+#fa)" />
	<Var name="date1" expression="ifelse(#vvv1,int(abs(#dz1-#da)-gt(#fa,#fz1)),int(abs(#dz1-#da)-gt(#fz1,#fa)))" />
	<Var name="hour1" expression="ifelse(#vvv1,int((gt(#fa,#fz1)*1440+#fz1-#fa)/60),int((gt(#fz1,#fa)*1440+#fa-#fz1)/60))" />
	<Var name="minute1" expression="ifelse(#vvv1,int((gt(#fa,#fz1)*1440+#fz1-#fa)%60),int((gt(#fz1,#fa)*1440+#fa-#fz1)%60))" />

	<!-- 
lya                今年是否闰年
da                计算现在至0000年有多少天
dz1                计算目标日期至0000年有多少天
fa                今天过了多少分
vvv1        判断目标日期在现在之前还是之后
-->
	<Wallpaper x="0" y="0"  />
	<Text x="220-100*#to" y="200" color="#ffffff" size="24" textExp="@djr1+ifelse(#to,'('+#y1+'/'+#m1+'/'+#d1+'/'+#h1+':'+#f1+')','')+ifelse(#vvv1,'还有','已经')+#date1+'天'+#hour1+'时'+#minute1+'分'" alpha="180" />
</Lockscreen>

天干地支

<Var name="tgdz" expression="substr('庚辛壬癸甲乙丙丁戊己',#year_lunar%10,1)+substr('申酉戌亥子丑寅卯辰巳午未',#year_lunar%12,1)+substr('鼠牛虎兔龙蛇马羊猴鸡狗猪',(#year+8)%12,1)+'年  '" type="string" const="true" />

摇一摇调出NFC界面

把下面代码放VariableBinders中

<SensorBinder type="linear_acceleration" rate="2">
	<Variable name="va_x" index="0" />
	<Variable name="va_y" index="1" />
	<Variable name="va_z" index="2" />
	<Trigger condition="abs(#va_x*10)}120">
		<!-- 线性加速度数据校正(取最大值),解决不同硬件灵敏度不同造成的问题 -->
		<VariableCommand name="va_num" expression="abs(#va_x*10)"  persist="true" condition="(abs(#va_x*10))}#va_num" />
		<!-- 如果va_x实时值大于va_num*0.8,则打开NFC界面 -->
		<IntentCommand action="com.miui.intent.action.DOUBLE_CLICK" package="com.miui.tsmclient" condition="abs(#va_x*10)}(0.8*#va_num)">
			<Extra name="event_source" type="string" expression="'key_volume_down'"/>
		</IntentCommand>
	</Trigger>
</SensorBinder>

调取手环数据

放VariableBinders中

<!-- 小米手环 -->
<ContentProviderBinder name="Miband" uriExp="'content://com.xiaomi.hm.health.ext/summary/step?from_date='+#year+'-'+ifelse(ge(#month,9),int(#month+1),'0'+int(#month+1))+'-'+ifelse(ge(#date,10),#date,'0'+#date)" columns="step,cal" countName="getColumnIndex">
	<!-- 步数 -->
	<Variable name="Miband_step" type="int" column="step"/>
	<!-- 卡路里 -->
	<Variable name="Miband_cal" type="int" column="cal"/>
</ContentProviderBinder>

MIX系列锁屏适配

MIX为高屏占比的概念机,与其它1080P的手机自然有区别

  • MIX锁屏分辨率为:  宽:1080 高:2040
  • MIX2锁屏分辨率为:  宽:1080 高:2160
  • 其它1080P手机分辨率:宽:1080 高:1920

解决方案:

  • 全局变量中有一个screen_height,表示在你当前代码设定分辨率下相对应的屏幕高度
  • 平常的背景我们就用1080*2160的尺寸
  • 屏幕底的按钮坐标,以屏幕高度(screen_height)为参照,通俗地讲就是从底部上去多少像素

瞅代码:

<Group y="#screen_height-130" >
	<Image x="968" y="0" src="camera.png" />
	<Button  x="921" y="0" w="159" h="260/>
	……
</Group>

用MAML制作自己的APP

先说明一下,这个与锁屏无关,不过对不会安卓开发的同学来说,这功能来的太实用、太贴心了,简直就是神器!做一个自己的APP,是一件多么6666的事!比如按自己想法制作的计算器、课程表、甚至小游戏…… 方法:

  • 先安装框架主程序,下载解压安装到手机
  • 编写你的程序(除了开头结尾用“Device”,其它与锁屏、插件完全一样) 下载示例
  • 把刚才你制作好的文件打包成ZIP包,放手机根目录下
  • 打开主程序,按“音量+”选择刚才放在根目录下的zip包,就能看到你制作的界面了(下次启动会自动使用上次选择的包)
back 按键响应 添加root属性 interceptBack​=true
通用命令添加"back"
<ExternalCommand command="back"  >
<ExternCommand command="back"  >

性能优化

优化中需要注意的几点

一个好的锁屏,不仅需要很好的视觉效果,还需要流畅的体验(用户手机的性能、玩机水平有高低,我们制作主题时,要考虑下如何用户最大化)

  • 测试时尽量不要用顶配机,frameRate写60,实测效果:普通手机不要低于30帧/s,好一点的手机必需高于40帧/s(这里指的是能够达到的最高帧率,并非你在开头限定的帧率或是你在代码中写的动态帧率)
  • 降低图片文件大小,减少缓存时读取的时间,节省运行内存:存储图片必需导出为WEB格式,或者后期压缩;能用JPG绝不用PNG,webp也是一个很好的选择!
  • 尽量用合适尺寸的图片,能用小尺寸绝不用大尺寸,有效减少计算量!
  • 代码逻辑上不要有冲突,必须精减
  • 充分利用visibility控制各模块可见性,用<Group visibility="#num"></Group>通过变量#num是否大于0只显示当前需要显示的部分

Layer优化利器详解

请点下面传送阵去看大神原贴
MIUI百变锁屏脚本语言MAML的高级优化: Layer

MAML编辑器

MAML_Editor内测版(MIGO_V_19.3.11)