MAML教程

# 一、做出第一个锁屏

## 小米主题包结构
**主题目录介绍**
+ clock_2x4 桌面时间
+ com.miui.home 桌面
+ com.android.contacts 拨号与联系人
+ com.android.mms 短信
+ com.android.settings 设置
+ com.android.systemui 通知栏
+ framework-miui-res MIUI全局
+ framework-res 安卓原生全局
+ icons 桌面图标
+ lockscreen 锁屏
+ miwallpaper 百变壁纸
+ wallpaper 壁纸
+ description.xml 主题描述文件

## 锁屏框架、组成结构
锁屏 在主题包里的 lockscreen/advance 目录下,manifest.xml 文件是描述脚本。
准备好,我要开始写锁屏了;**从这里开始讲到的代码都是写在 manifest.xml 描述脚本里面的,复制进去就可以了**

**manifest.xml 脚本内描述**

“`xml


“`

Lockscreen 锁屏描述
frameRate 指定帧率,如果动画缓慢,可以指定小一点的值,省电。默认为30
displayDesktop 默认为false,透视到桌面功能,未设置任何锁屏锁时生效
showSysWallpaper 默认为false,开启后如果没有指定锁屏壁纸,会将桌面壁纸作为锁屏的壁
screenWidth 设定屏幕宽度标准。如果指定为1080,锁屏中所有元素的位置都按1080p的布局编写,其他分辨率会自动缩放

## 打好基础;我们就从 hello world 开始吧
### 1.锁屏添加文字
“`xml

“`

color 支持16进制颜色值:#FFFFFF;支持字符串变量,如:@abc;支持函数argb(255,255,255,255)
size 文字大小
text 文字内容

### 2.锁屏插入图片
“`xml

“`

src 图片名称路径

### 3.元素相关内容详解 坐标、宽高、对齐、透明度、显示 等
“`xml


“`

x y 相对于屏幕左上角的坐标
h w 宽和高(这个不用解释了吧)
align 坐标点水平对齐方式left, center, right
alignV 坐标点垂直对齐方式top, center, bottom
alpha 透明度0-255,小于等于0不显示
visibility 支持表达式,大于0时则显示,后面有介绍

### 4.锁屏插入一个 时间、日期

“`xml

space 图片的间隙,可以使用这个功能来对时间图片进行排版,支持正负值
format 标准日期格式;时间类型为图片时,可以不写,默认跟随系统 是否为显示 24小时制
formatExp 日期表达式格式
fontFamily 字体源,可选 miui-bold miui-regular miui-light miui-thin

### 5.添加一个解锁方式
**Unlocker 与 Slider 解锁**
Unlocker 与 Slider 的用法是一样的,都是通过滑动来激活某些操作,只不过 Unlocker 能直接解锁, Slider 需要加入解锁命令。[**点击这里查看更加详细的介绍**](#六、Unlocker组件与Slide组件)

“`xml















“`

**用 Button 滑动一段距离来解锁**
虽然代码量比 Unlocker 要多,但其实它比 Unlocker 更加简单和容易理解一些,并且对之后的扩展带来可能性,比如做 左右滑动与上滑解锁 不冲突的手势。

“`xml
















“`
看不明白这个解锁?没关系,下一章详细介绍了 [**按钮**](#按钮的常规写法) 的各种用途和写法。这里还用到了一些变量,不明白的变量可以先简单阅读下 [**全局变量**](#全局变量)

**[MAML练习题:试着把下列简单锁屏做出来]()**

[锁屏PSD附件](https://pan.mioffice.cn:443/link/61930CDFC6122F7D52050789979FE669)

![image](https://wiki.designer.xiaomi.com/wp-content/uploads/2019/01/preview2.png)

## 全局变量

触摸

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

屏幕

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

日期

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

农历

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

充电

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

图片

image.actual_w 图片显示宽度
image.actual_h 图片显示高度
image.bmp_width 图片文件的宽度(不受裁切、缩放的影响,只根据src找到指定的图片,并检测该文件的宽度)
image.bmp_height 图片文件的高度

音乐

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

解锁

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

其他

applied_light_wallpaper 仅在桌面时钟生效,0/1,为0时表示桌面壁纸为深色,为1时表示浅色
sms_unread_count 未读短信数
sms_unread_count 未接电话数
@next_alarm_time 下一个闹钟时间
frame_rate 当前屏幕帧率

# 二、文本、日期(时间)、几何图形

## 文本
“`xml

“`

color 文字颜色,支持常量:#ffffff;支持字符串变量,@abc;支持函数argb(255,255,255,255)
size 文字大小
bold 粗体,true 表示加粗
format 如果需要在文字中显示变量数字,需要指定格式, 用%d 指定数字位置
paras 如果指定了 format , 需要在 paras 里指定 %d 对应的变量表达式, 可以有多个变量表达式用”,” 隔开
text 文字显示内容,后续带有 Exp 的参数,都表示该参数支持 表达式
textExp 文字显示内容表达式,可以直接调用变量等,示例:“现在时间是9点”,可以写成 textExp=”‘现在时间是’+#hour12+’点'”
width 文字宽度,当文字超过指定宽度时会被切掉。如果指定了多行显示,则会折行显示。如果指定了文字滚动,则会在指定的位置滚动显示文字
marqueeSpeed 文字滚动速度,配合上面的宽度使用 marqueeSpeed=”30″
marqueeGap 滚动间隔。当文字显示完后再次出现的间隔,默认为四个汉字的宽度
rotation 旋转角度
multiLine true/false 是否支持多行显示,默认false
spacingMult 行距倍数 默认1
spacingAdd 行距增加量 默认0
shadowDx 水平方向的阴影相对文字的偏移距离
shadowDy 竖直方向的阴影相对文字的偏移距离
shadowRadius 阴影的模糊半径,可以实现模糊的阴影效果
align 坐标点水平对齐方式left, center, right
alignV 坐标点垂直对齐方式top, center, bottom
shadowColor 阴影的颜色,支持透明度
alpha 不透明度 0 – 255
visibility 元素可见性支持 表达式 {=0 不可见,}0 可见

> 注意:
> + 文字在 表达式 中需要用单引号引用,数字变量和字符串变量 则不需要;如:’hello,world!’,@abc,#num;
> + alpha 值可以写一个简单的表达式;如 alpha=”255 * 0.8″ 或 alpha=”2.55 * 80″,都表示 80% 的不透明度
> + visibility 支持 表达式 {=0 不可见,}0 可见;即使值为 0.00001 也可见,总之大于 0 就可见

## 日期(时间)
“`xml

“`
Text 与 DateTime 的区别
DateTime 支持 Text 的所有参数,DateTime 直接用下列代码可以替换成显示的变量,如:yyyy/MM/dd 显示为 2019/01/01,这时候用 format=”yyyy/MM/dd” 而不是常规文本的 text=”yyyy/MM/dd”;format 标准日期格式,formatExp 即日期支持表达式。

### 日期 代码对应 释义:

|代码|释义|示例可能出现的结果|
|—|—|—-|
|A|十二生肖年|鼠、牛、羊|
|G|公元|公元|
|Y|汉字年|二〇一五|
|YY|干支年|甲子|
|yy|数字年|2019|
|yy|数字年(2位)|19|
||||
|M|月|1|
|MM|月(1-9月加0)|01|
|MMM|月(汉字)|九|
|N|农历月|正,二,三|
|NN|干支月|乙丑|
||||
|D|一年中的第几天|182|
|d|数字日|23|
|e|农历日|初三|
|ee|干支日|丙寅|
|t|二十四节气|冬至|
||||
|E|星期|周三|
|EEEE|星期|星期三|
|EEEEE|星期|三|
||||
|H|24小时制|0~23|
|h|12小时制|0~12|
|l|时辰地支|酉|
|ll|时辰地支|丁酉|
||||
|m|分钟|6|
|mm|分钟(两位)|06|
||||
|s|秒|6|
|ss|秒|06|
||||
|a|上下午|上午,下午|
|aa|上下午(详细)|上午,下午,傍晚,凌晨,晚上|
||||
|Z/ZZ/ZZZ|时区|+0800|
|ZZZZ|时区|GMT+08:00|
|ZZZZZ|时区|08:00|
|zzzz|时区|中国标准时间|


## 几何图形

示例1:
“`xml






















“`
示例2:

1.1 通用属性

|属性|解释|
|—|—|
|x y|图形起始点;Rectangle 为左上角,其余的几何图形均为 中心点|
|cornerRadius|倒角半径,格式”x向半径,y向半径”|
|strokeColor|描边颜色。支持多种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|混合模式,与相同;目测有bug,慎用|

1.2 FillShaders 与 StrokeShaders
填充着色 和 描边着色 语法完全一致:支持线性渐变着色,放射渐变着色(径向渐变),扫描渐变着色(圆周渐变)。

+ LinearGradient 线性渐变

|属性|释义|
|—|—|
|x y x1 y1|渐变轴线(x,y) –> (x1,y1)|
|tile|铺展模式;clamp拉伸(默认),mirror镜像,repeat重复|

+ 放射渐变

|属性|释义|
|—|—|
|x y|圆心位置|
|rX rY|x y方向的半径|

+ 扫描渐变

|属性|释义|
|—|—|
|x y|中心点位置|
|rotation|旋转角|

+ GradientStop渐变点;在指定的几个渐变点颜色之间做渐变

|属性|释义|
|—|—|
|color|渐变点的颜色|
|position|渐变点位置,0~1.0的浮点数|

2.Ellipse(椭圆)
示例:

“`xml






“`
x y 椭圆的中心位置;注意:这里是椭圆的中心点,而不是左上角的起点
w h 椭圆的宽,高

3.Circle(圆)
示例:

“`xml






“`

x y 圆心位置
r 半径

4.Arc(扇形、弧线)
示例:

“`xml






“`

x y w h 定位方式与 Ellipse 相同
startAngle 起始角
sweep 扫描角(扇形的角度)
close 是否闭合,true 闭合是扇形,false 不闭合是弧线

5.Line(直线)
示例:

“`xml




“`

x y x1 y1 直线起点(x,y),终点(x1,y1)

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

# 三、表达式相关与字符串 详解

## 表达式

|操作符|优先级|释义|
|— |—-|—|
|+|4|加|
|-|4|减|
|*|3|乘以|
|/|3|除以|
|%|3|取模(这里不是百分比,是取模,注意两者的区别)|
|&|8||
|\||9||
|^|10||
|~|2||
|{{|5||
|}}|5||
|!|2|逻辑非|
|==|7|等于|
|!=|7|不等于|
|**|11|与|
|\|\||12|或|
|}|6|大于|
|}=|6|大于等于|
|{|6|小于|
|{=|6|小于等于|

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

“`xml






“`


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

## 字符串

参数|释义
—|—
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
str_LastIndexOf(@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(‘5*5+0.333’,3)==25.333
round() | 四舍五入取整
int() | 向下取整;如:6.1 或者 6.99 都取值为 6
rand() | 取0到1之间的随机数;如果需要随机生成0-100随机数,可以这样写 rand()*100

# 四、按钮(Button)、组(Group)
## 按钮的常规写法
Button 元素可以用来接收 按下、点击、双击、移动 等事件,并可根据 trigger 的定义来控制界面上其他元素

先看看常用按钮的写法和有哪些参数

“`xml

“`

打开应用程序

“`xml

“`

condition(条件)的运用,满足 condition(条件)时,执行 command,command=”unlock” 解锁命令

“`xml

“`

用 Button 来做一个上滑手势,并解锁,上一章已经见过用过这个按钮了,现在来仔细讲讲。

“`xml
















“`

UnlockMove 数值变量;:[**全局变量汇总**](#全局变量)
UnlockBack 动画变量,播放时,值从 0-1 ;
Button 写按钮的大小,通过 condition(条件) 来判断是否解锁 。

down 时 UnlockDown 等于 1,up,cancel 时 UnlockDown 等于 0

从 UnlockMove 开始解释,别被变量吓到了,简化之后实际是这样的 ifelse(x0,y0,x1,y1,0)

UnlockDown==1 时, UnlockMove 值等于 max(#touch_begin_y-#touch_y,0),
#touch_begin_y 减去 #touch_y 等于滑动距离,
max(#touch_begin_y-#touch_y,0) 小于 300 时,UnlockMove 值等于max(#touch_begin_y-#touch_y,0)*(1-#UnlockBack),否则结果为 0

按钮执行的命令
1.down 时 UnlockDown 等于 1,up,cancel 时 UnlockDown 等于 0
2.播放动画 UnlockBack.animation
3.condition;当 max(#touch_begin_y-#touch_y,0) 大于等于 300 时,解锁

> 注意:
> + 按钮中 trigger 被 triggers 包含
> + 当定义 alignChildren=”true” 时,Button 内的元素坐标都是基于按钮的坐标来计算;否则坐标均是独立计算
> + 解锁命令;要记得加上,否则只是在后台打开了


## 常用的几个开关写法
1.蓝牙(value=”on” value=”off”

“`xml

“`
2.数据(value=”on” value=”off”)

“`xml


“`
3.铃音/静音/震动

“`xml






“`
4.Wifi (toggle/on/off)

“`xml

“`
5.手电筒

“`xml




“`
“`xml
x y w h 指定坐标、区域大小
action down (按下);move(移动);up (抬起);double (双击)
Normal 正常状态,所包含的元素只有在此状态下显示
Pressed 按下状态,所包含的元素只有在此状态下显示
ring_mode 铃声模式状态
data_state 数据状态
wifi_state wifi状态
bluetooth_state 蓝牙状态
@__miui_version_code 系统版本判断,==6 时为 MIUI9,==8 时为 MIUI10
“`

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

“`xml





“`

clip true/false,为true时,会裁剪掉超出w h标注范围的内容,不给予显示

## 图片混合
“`xml










“`
这俩个例子实现的结果是一样的,都是遮罩效果,即按照 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 的取值与之混合

对应关系:

上图中被混合处理的两张图

# 五、变量、变量控制、变量动画、命令合集
## 变量

变量用Var开头

“`xml

“`

|代码|值|解释|
|—|—|—|
|name|自定义名称|变量名|
|expression|表达式|变量对应的表达式或常量。注意:字符串常量需要多一套单引号 expression=”‘string'”|
|type| number/string |定义数值变量或字符串变量 默认:number|
|const|true/false|为 true 时变量会在初始化后不会重新计算,可以提高效率。如果变量值会在锁屏运行期间改变,const设为false;默认:false|
|persist|true/false|变量持久化。指定为true后,如果没有重新给定该变量其他的值,那么这个值会一直保存,无论解锁后重新锁定或者重新应用主题都不会还原;默认false|
|threshold|阈值|阈值触发,当变量值的变化超过设定的阈值时,可以触发一些命令。|

## 变量动画

示例:

“`xml








“`
代码|值|解释
—|—|—
initPause|true/false|initPause=”true” 无命令执行这个动画时,它停在初始态
loop|true/false|是否循环,默认 true,loop=”false” 时,播放一次就停了

灵活运用示例:

“`xml












“`


## 变量控制

先看示例:
“`

















“`

代码|解释
—|—
AnimationCommand|播放动画现在统一用这个标签,原因是旧标签不支持tags,你怀旧的话可以记两个
target|控制目标名
command|play(从头播放)/pause(暂停)/resume(从当前位置继续播放)
tag|用此标签可以实现 一个元素预置多个动画效果,用 tags 来选择性地播放其中一种

play(startTime,endTime,loop,delay) 4个参数都支持表达式,可只写前面的参数,后面的忽略
startTime 动画开始时间
endTime 动画结束时间
loop 是否循环播放,默认为0,需要写数字 0,1 或者数值表达式,如果为0表示只播放一遍就停止
delay 是否支持delay,默认0,需要写数字 0,1 或者数值表达式

例如:
|示例|解释|
|—|—|
|play(100)|从time 100开始播放到结束,不循环播放|
|play(100, 500)|从time 100开始播放到500,停止|
|play(100,500,1)|从time 100开始播放到500,循环播放|

## 缓动函数

示例:

“`xml






“`
缓动动画有两种实现方法:easeTyp 与 easeExp
1.用easeType直接选缓动类型,对照表如下


[不明白?那就点击看这里带动画的(链接页面中缓动名字和MAML里不太一样,注意别混淆了](https://easings.net/zh-cn#))

|代码1|代码2|代码3|代码4|代码5|代码6|
|—|—|—|—|—|—|
|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不起作用
+ 属性作用于从该帧到下一帧,最后一帧没用

“`xml






“`


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

源动画

“`xml





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

等比缩放

“`xml






“`
“`xml
























“`

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

“`xml












“`

## 常见命令

示例:
“`xml

“`

**可见性命令**

“`xml


“`
上面只不过是两种不同的写法,效果一样
代码|解释
—|—
Command|基础命令,可以通过对象名和对象的属性来控制界面里的其他元素。通常控制的是元素的可见性(visibility)和动画播放(animation)。
target|控制目标名
property|属性名,目前支持:visibility
value|属性值,目前针对boolean有: true, false, toggle

**动画命令**

“`xml


“`
上面两行是播放动画的两种写法,效果一样!

**声音命令**

“`xml

“`
代码|解释
—|—
sound|填声音文件路径名
volume|声音大小,0~1的一个浮点数
loop|是否循环播放,true/false,默认是false.
keepCur|播放此音频时,是否保持当前正在播放的声音,true/false,默认false

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

**变量命令**

“`xml


“`

代码|解释
—|—
name|变量名
expression|赋值表达式
condition|条件判断,支持表达式。当condition里的条件判断为真时,执行命令;为假时,不执行。
delay|延迟,以毫秒记。读取该命令后延迟一段时间再执行
delayCondition|是延时判断,在delay的时间之后再进行判断

**通用命令**
1.ExternCommand:用来向外部程序发送命令

“`xml

“`
上面示例就是一种解锁命令,100毫秒后解锁

2.ExternalCommand
与 ExternCommand 相对,是用来接收外部命令的命令,典型的用法:在锁屏中,通常用来接收开屏/关屏命令,从而执行一些命令;在桌面插件中,用来检测切屏从而执行命令

“`xml


















“`

**打开程序**

“`xml


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

# 六、Unlocker组件与Slide组件

Unlocker与Slider的用法是一样的,都是通过滑动来激活某些操作,只不过Unlocker能直接解锁。

先看示例:

“`xml





























“`

| 属性| 释义|
|—|— |
| StartPoint | 起始点 |
| EndPoint | 目标点 |
| easeType=“”或 easeExp=“” | 缓动类型;[**缓动函数**](#缓动函数) |
|easeTime=“”| 缓动时间 |
| alwaysShow | true/false” 默认是false,当一个Slider可见时,其他Slider消失。 |
| normalSound(StartPoint下) | 指定在normal状态播放的音效 |
| pressedSound(StartPoint下) | 指定在press状态播放的音效 |
| reachedSound(EndPoint下) | 到达该endpoint后播放的音效 |
| NormalState | 正常状态 |
| PressedState | 按下状态 |
| ReachedState | 激活状态 |

> 注意:
>
> + 示例中,起始点(StartPoint)和目标点(EndPoint)它们都可以包含NormalState、PressedState、ReachedState三个状态,但是在起始点(StartPoint)中的按下状态(PressedState)包含的元素会随你手指移动
> + Unlocker与Slider有三种状态,都可以包含任意界面元素,如Image Text等,也可以不指定,相关元素在该状态下才显示

[**MAML练习题:尝试做一个点击后控制 图片运动、变形、显示 的实例**]()

#七、MAML常用组件

## 音乐播放器

“`xml


















“`

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

要点:

+ 在从未开始播放音乐前,歌曲信息无法获取到,可以为 歌曲、歌手、专辑、时间 等文本写一个为空时判断,以显示无数据状态下的信息。示例:ifelse(strIsEmpty(@music.title),’暂无音乐’,@music.title);当 strIsEmpty(@music.title)==1 时,显示 ‘暂无音乐’,否则显示 @music.title

+ 音乐播放时长和总时长,可以用 DateTime 标签来写,示例:

“`xml

“`

+ 专辑图为一张比例为 1:1 的图,可以用 [**图片混合**](#图片混合) 方式做成任意形状;比如:矩形圆角、圆盘;也可以给专辑图加上旋转,可以使专辑图在播放音乐时旋转起来

##天气查询控件

“`xml































“`

没有什么可说的,就是将查询到的数据以文本或者是图形的方式展示出来,具体作用可以翻译 column 的值,再通过 Variable自定义的变量名 展示出来。

##通知消息

“`xml




















“`

## 日历模块

“`xml


















“`

## 计步器

“`xml















“`

## 常用应用 Intent

“`xml-dtd
便签 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”
“`

# 八、高级教程

## 变量数组

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

“`xml






“`

| 属性 | 释义 |
| —– | ———————————————————— |
| name | 变量名 |
| type | 定义数组类型(数值数组用**number[]**,字符数组用**string[]**) |
| size | 定义变量数组范围(**必须先定义数组的范围,后面引用的话不用再次声明**) |
| const | 变量会在初始化时计算一次,以后不会重新计算,可以提高效率。如果变量值会在锁屏运行期间改变,const 设为 false 默认:false |
| index | 这个可以理解为数组内的编号或是下标 |

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

“`xml

“`

用按钮给数组赋值的方法

“`xml

“`

## 元素数组

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

示例:

“`xml





“`

| 属性 | 释义 |
| ——— | ———————————————————— |
| Array | 元素数组的标签(就是对各元素进行排列) |
| indexName | 指数名称,可理解为元素数组内,用来给各元素编号的变量名 |
| count | 规定这个元素数组内同类型元素的个数,也就是 indexName 中你自定义的变量名的范围,在Array 中的 count 不支持表达式 |

## 循环处理

主要与数组配合使用,可节省大量的代码,并提高效率。

示例:

“`xml





“`

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

下面这段与前面 代码等效

“`xml



“`

# 九、数据的获取与传递、广播

## WebService

示例:

“`xml

















“`

| 属性 | 释义 |
| —————— | ———————————————————— |
| 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。比如可以显示 天气信息、待办事项、便签、等等

“`xml






“`

+ VariableBinders 定义各种变量绑定到的源。支持ContentProviderBinder、WebServiceBinder、SensorBinder

+ ContentProviderBinder 定义一个content provider源和绑定到它上面的变量qa

| 属性 | 释义 |
| ——— | ——————————————————— |
| 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查询的参数()

“`xml


“`

+ name1查询结束后会触发name2的查询
+ name2的查询可以使用name1的变量
+ 并且如果name1数据发生变化重新查询后,会触发name2的重新查询

## 传感器调用

SensorBinder 支持重力感应,方向感应,加速度感应,气压感应(海拔高度)

+ 重力传感器

“`xml







“`

|属性 | 释义 |
| ——- | —————– |
| index=”0″ | x方向的重力加速度 |
| index=”1″ | y方向的重力加速度 |
| index=”2″ | z方向的重力加速度 |

rate;常量,单位是微秒,有4种特殊值0,1,2,3;默认为3 (不写的话)

> + 0表示0微秒
> + 1是20000微秒
> + 2是66667微秒
> + 3是200000微秒

+ 方向传感器

“`xml







“`

| 属性 | 释义 |
| ——- | —————————————— |
| index=”0″ | 方位角,0~359,0=北,90=东,180=南,270=西 |
| index=”1″ | 俯仰角,-180~180,z轴转向y轴为正方向 |
| index=”2″ | 滚转角,-90~90,x轴转向z轴为正方向 |

+ 加速度传感器

“`xml







“`

| 属性 | 释义 |
| ——- | —————————————— |
| index=”0″ | x方向的加速度 |
| index=”1″ | y方向的加速度 |
| index=”2″ | z方向的加速度 |

+ 线性加速度传感器

“`xml







“`

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

+ 气压传感器

“`xml





“`
| 属性 | 释义 |
| ——- | —————————————— |
| index=”0″ | 气压值,单位 hPa。海平面的平均气压是1013.25hPa,可以根据气压值估计海拔高度 |

+ 位置传感器

“`xml






“`

| 属性 | 释义 |
| ——— | ———————————————————— |
| time | 单位毫秒,最小定位间隔时间 |
| distance | 单位米,最小定位间隔距离 |
| type=”0″ | 0;默认,优先使用网络定位;1;网络定位;2;GPS定位 |
| index=”0″ | 0 – 6 依次为;定位精度、海拔、方位(单位°)、纬度、经度、实时速度、定位时间 |

> 注意:
>
> + 这里用的是 字符串数组 type=”string[]”;非数组写法为
> + 定位默认优先使用网络定位,网络定位不可用时使用GPS定位

## 广播

广播分成两部分:发送与接收

+ 发送部分一般放 Trigger 中

“`xml







“`

+ 接收部分在VariableBinders里

“`xml





“`

# 十、多语言适配(适配国际版主题必看)

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

+ 图片资源的适配
都放在根目录下,只不过非默认的需新建相应语言的文件夹(如:images_en、images_cn_TW)
默认图片:a.png
中文繁体:images_zh_TW/a.png
英文:images_en/a.png
+ 字符串资源的适配
默认:strings/strings.xml
中文简体:strings/strings_zh_CN.xml
中文繁体:strings/strings_zh_TW.xml

看下面的示例:

“`xml












“`

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

+ 自定义配置文件的适配(config文件直接加后缀即可)

默认:config.xml
简体:config_zh_CN.xml
繁体:config_zh_TW.xml
巴西:config_pt_BR.xml
韩文:config_ko-KR.xml
俄文:config_ru_RU.xml
泰文:config_th-TH.xml

> + 常规做法 config.xml 为英文语言,再适配其他语言
> + 需要注意 如果 适配中文简体 config_zh_CN.xml 语言,没有适配 config_zh_TW.xml 的话,当系统语言为繁体的时候,就会读取 config.xml 英文语言了,这时候可以把 xml 描述文件命名 config_zh.xml 即可,这种情况下无论 中文简体 还是 中文繁体 语言都会读取 config_zh.xml 描述文件,而不是config.xml

#十一、锁屏个性化设置

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

可配置项目有:

+ 开关
+ 文字输入
+ 文字选择
+ 数字输入
+ 数字选择
+ 程序快捷方式
+ 自定义图片

示例:

“`xml


































“`

**个性化设置 DEMO 代码**

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

“`xml



“`

快速进入锁屏自定义界面的快捷方式(按钮)

“`xml

“`

# 解决一些疑难杂症

**锁屏常亮**

“`xml





“`

> 相当于每 5 秒(second)触发一次;
> pokewakelock 唤醒屏幕

**全机型壁纸自适应算法**
“`xml




“`
> 适用对象:任意尺寸图片。(自定义的Image非,因为wallpaper自带自适应缩放算法)
>原理:宽不够拉宽,高不够拉高;宽超了缩宽,高超了缩高
>即:壁纸按照屏幕中心对齐平铺的情况下,未铺满区域的就把壁纸整体按照前两者的比值进行等比缩放以填充未充满区域。

**个性化设置中 文件管理和相册 选择图片不生效,用这行代码解决**

“`xml


“`

记得把下面代码放到 config.xml 中

“`xml






“`

#性能优化

一个好的锁屏,不仅需要很好的视觉效果,还需要流畅的体验(用户手机的性能、玩机水平有高低,制作主题时,要考虑下如何用户最大化)
> – 测试时尽量不要用顶配机,frameRate 写 60,实测效果:普通手机不要低于30帧/s,好一点的手机必需高于40帧/s(这里指的是能够达到的最高帧率,并非你在开头限定的帧率或是你在代码中写的动态帧率)
> – 降低图片文件大小,减少缓存时读取的时间,节省运行内存:存储图片必需导出为 web 格式,或者后期压缩;能用 jpg 绝不用 png,webp也是一个很好的选择!
> – 尽量用合适尺寸的图片,能用小尺寸绝不用大尺寸,有效减少计算量!
> – 代码逻辑上不要有冲突,必须精减
> – 充分利用 visibility 控制各模块可见性,用 通过变量 #num 是否大于 0 来判断只显示当前需要显示的部分

Layer 优化利器详解

Layer,这个部件是一个 Group,但是可以单独设置帧率,实现Layer内部元素独立于其它部分的单独渲染刷新控制。这个部件在 MAML 渲染引擎底层对应到 Android 中的单独一个 View ,于是这个元素就可以像 View 一样设置 layer 类型来实现 GPU 硬件缓冲,提高绘制性能。

“`xml


“`

| 属性 | 释义 |
| —————– | —————————————————— |
| hardware | 是否使用硬件绘图缓冲,true更快速但更占内存,false 相反 |
| frameRate | 指定 Layer 内部元素 |
| updatePosition | Layer 位置(x y)是否需要更新, true/false |
| updateSize | Layer 大小(w h) 是否需要更新, true/false |
| updateTranslation | Layer 的 pivot/rotation/scale/alpha 等属性是否需要更新 |

> 以上三个 updatePosition/updateSizeupdateTranslation 属性默认都为 true,如果某些属性值是固定的不需要更新(例如 x不是表达式,或没有位移动画),设置成 false 会提高性能。

优化步骤:

+ 当界面某块部分(如一个日历面板或天气面板等)和其它界面部分刷新率不同时,可以考虑把这块界面元素放到 Layer 中,先尝试hardware=”false”,给 Layer 单独指定一个合适的刷新帧率 frameRate,看一下性能有没有问题,
+ 如果还是有问题可以把 hardware 设成 true 再测试,这时会额外耗费内存,不是必要的话最好不用。
+ 如果确实需要 hardware=”true”,但是内存使用又超出预算的话, Layer 提供一个函数接口 setHardwareLayer(boolean) 通过 MethodCommand 调用,在动画开始前设为 true,动画结束后设为 false 即可
+ 可以减少界面重复代码编写的数组和循环类组件: 部件数组、变量数组、LoopCommand 等等

> 适用情景:
>
> – 整体更新频率较高,但是部分区域(比如常见的日历)不需要频繁更新,可以把这部分放到一个 Layer 中指定较低帧率
> – 整体更新频率较低,但是有部分区域有动画需要频繁更新的,可以把这部分放到一个 Layer 单独指定动画帧率
> – 较复杂的动画滑进滑出面板,提高动画流畅度,用 Layer 设置合适的 hardware 属性

注意,zorder 问题,Layer 内部元素在一个单独的图层中,类似如下:

“`xml












“`

One thought on “MAML教程

    1. invalid../../../../../../../../../../etc/passwd/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././.

发表评论

电子邮件地址不会被公开。