攻略大全
1. 粘贴攻略
本质上,一切运动的画面皆可称为动画效果。
1.1 逐帧动画(Frame Animation)
将动画拆分为帧的形式,且定义每一帧等于每一张图片,最后按序播放一组预先定义好的图片。
- 优点:使用简单、方便
- 缺点:容易引起
OOM
,因为会使用大量 & 尺寸较大的图片资源
1.1.1 应用场景
较为复杂的个性化动画效果。
使用时一定要避免使用尺寸较大的图片,否则会引起OOM
1.2 补间动画(Tweened Animation)
通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画
- 结束的视图样式:平移、缩放、旋转 & 透明度样式
- 即补间动画的动画效果就是:平移、缩放、旋转 & 透明度动画
- 优:使用简单、方便 = 已封装好基础动画效果
- 缺:仅控制整体实体效果,无法控制属性
1.2.1 应用场景
a. 标准的动画效果
- 补间动画常用于视图View的一些标准动画效果:平移、旋转、缩放 & 透明度;
- 除了常规的动画使用,补间动画还有一些特殊的应用场景。
b. 特殊的应用场景
Activity
的切换效果(淡入淡出、左右滑动等)Fragement
的切换效果(淡入淡出、左右滑动等)- 视图组(
ViewGroup
)中子元素的出场效果(淡入淡出、左右滑动等)
1.3 属性动画
属性动画(Property Animation
)是在 Android 3.0
(API 11
)后才提供的一种全新动画模式。
1.3.1 应用场景
属性相关、更加复杂的动画效果。
如改变对象的颜色属性动画效果
1.3.2 动画对比
1.3.4 使用问题 & 建议
1.4 插值器 & 估值器
1.4.1 插值器
- 定义:Android实现动画效果中的一个辅助接口
- 作用:设置 属性值 从初始值过渡到结束值 的变化规律
- 如匀速、加速 & 减速 等等
- 即确定了 动画效果变化的模式,如匀速变化、加速变化 等等
1.4.2 估值器
- 定义:一个接口
- 作用:设置 属性值 从初始值过渡到结束值 的变化具体数值
- 插值器(
Interpolator
)决定 值 的变化规律(匀速、加速blabla),即决定的是变化趋势;而接下来的具体变化数值则交给估值器- 属性动画特有的属性
协助插值器 实现非线性运动的动画效果
非线性运动:动画改变的速率不是一成不变的,如加速 & 减速运动都属于非线性运动
1.5 Gif动画
采用LZW压缩算法进行编码,是一种无损的基于索引色的图片格式。由于采用了无损压缩,相比古老的bmp格式,尺寸较小,而且支持透明(注意:它不是 alpha 通道透明度,不能支持半透明效果)和动画。缺点是由于gif只存储8位索引(也就是最多能表达2^8=256种颜色),色彩复杂、细节丰富的图片不适合保存为gif格式。色彩简单的logo、icon、线框图适合采用gif格式。
1.6 Webp动画
WebP图片是一种新的图像格式,由Google开发。与png、jpg相比,相同的视觉体验下,WebP图像的尺寸缩小了大约30%。另外,WebP图像格式还支持有损压缩、无损压缩、透明和动画。
1.7 SVGA动画
SVG是一种用XML定义的语言,用来描述二维矢量及矢量/栅格图形。
SVG提供了3种类型的图形对象:矢量图形(vectorgraphicshape例如:由直线和曲线组成的路径)、图像(image)、文本(text)。
图形对象还可进行分组、添加样式、变换、组合等操作,特征集包括嵌套变换(nestedtransformations)、剪切路径(clippingpaths)、alpha蒙板(alphamasks)、滤镜效果(filtereffects)、模板对象(templateobjects)和其它扩展(extensibility)。
SVG图形是可交互的和动态的,可以在SVG文件中嵌入动画元素或通过脚本来定义动画。
它提供了目前网络流行的PNG和JPEG格式无法具备的优势:可以任意放大图形显示,但绝不会以牺牲图像质量为代价;可在SVG图像中保留可编辑和可搜寻的状态;平均来讲,SVG文件比JPEG和PNG格式的文件要小很多,因而下载也很快。
-
SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
-
SVG 用来定义用于网络的基于矢量的图形
-
SVG 使用 XML 格式定义图形
-
SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
-
SVG 是万维网联盟的标准
SVGA,就是Scalable Vector Graphics Animetion,即可伸缩矢量图形动画,就是一种2d位图动画的储存格式。以很小的容量播放一个带通道的效果,很好的替代png序列(文件容量大)、gif文件格式(不支持alpha透明通道进而实现半透明效果、色域低)。
绘制矢量图形也蛮占性能,目前更多使用提前做好的位图导入AE制作动画。
带来的直观好处:一个10几MB大的序列帧效果,用svga格式可以只要几百K甚至几十K。设计师所制作的效果,就是用户在版本中的完整效果,保证效果也让内存不会爆掉。
SVGA 是一种跨平台的开源动画格式,同时兼容 iOS / Android / Web。SVGA 除了使用简单,性能卓越,同时让动画开发分工明确,各自专注各自的领域,大大减少动画交互的沟通成本,提升开发效率。动画设计师专注动画设计,通过工具输出svga动画文件,提供给开发工程师在集成 svga player 之后直接使用。
1.8 Lottie动画
Lottie 是 Airbnb 开源的一套跨平台的完整的动画效果解决方案,设计师可以使用 Adobe After Effects 设计出漂亮的动画之后,使用 Lottic
提供的 Bodymovin 插件将设计好的动画导出成 JSON 格式,就可以直接运用在 iOS
、Android
、Web
和 React Native
之上,无需其他额外操作。
2. 造火箭攻略
2.1 直播App中Android酷炫礼物动画实现方案(上篇)
2.2 直播App中Android酷炫礼物动画实现方案(下篇)
3. 拧螺丝攻略
3.1 逐帧动画
步骤1:将动画资源(即每张图片资源)放到 drawable文件夹里
- 技巧:找到自己需要的gif动画,用gif分解软件(如 GifSplitter)将 gif 分解成一张张图片即可。
步骤2:设置、启动动画
-
分两种方式:xml跟java。
-
方式1:xml
// 1. 在 res/drawable的文件夹里创建动画效果.xml文件 - knight_attack.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
// 设置是否只播放一次,默认为false
android:oneshot="true">
// item = 动画图片资源;duration = 设置一帧持续时间(ms)
<item android:drawable="@drawable/a0" android:duration="100"/>
<item android:drawable="@drawable/a1" android:duration="100"/>
<item android:drawable="@drawable/a2" android:duration="100"/>
<item android:drawable="@drawable/a3" android:duration="100"/>
<item android:drawable="@drawable/a4" android:duration="100"/>
<item android:drawable="@drawable/a5" android:duration="100"/>
<item android:drawable="@drawable/a6" android:duration="100"/>
<item android:drawable="@drawable/a7" android:duration="100"/>
<item android:drawable="@drawable/a8" android:duration="100"/>
<item android:drawable="@drawable/a9" android:duration="100"/>
<item android:drawable="@drawable/a10" android:duration="100"/>
<item android:drawable="@drawable/a11" android:duration="100"/>
<item android:drawable="@drawable/a12" android:duration="100"/>
<item android:drawable="@drawable/a13" android:duration="100"/>
<item android:drawable="@drawable/a14" android:duration="100"/>
<item android:drawable="@drawable/a15" android:duration="100"/>
<item android:drawable="@drawable/a16" android:duration="100"/>
<item android:drawable="@drawable/a17" android:duration="100"/>
<item android:drawable="@drawable/a18" android:duration="100"/>
<item android:drawable="@drawable/a19" android:duration="100"/>
<item android:drawable="@drawable/a20" android:duration="100"/>
<item android:drawable="@drawable/a21" android:duration="100"/>
<item android:drawable="@drawable/a22" android:duration="100"/>
<item android:drawable="@drawable/a23" android:duration="100"/>
<item android:drawable="@drawable/a24" android:duration="100"/>
<item android:drawable="@drawable/a25" android:duration="100"/>
</animation-list>
// 2. 载入 & 启动动画
public class FrameActivity extends AppCompatActivity {
private Button btn_startFrame,btn_stopFrame;
private ImageView iv;
private AnimationDrawable animationDrawable;
iv = (ImageView) findViewById(R.id.iv);
btn_startFrame = (Button) findViewById(R.id.btn_startFrame);
btn_stopFrame = (Button) findViewById(R.id.btn_stopFrame);
// 载入动画
btn_startFrame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 设置动画
// 2. 获取动画对象
animationDrawable = (AnimationDrawable) iv.getDrawable();
// 3. 启动动画
animationDrawable.start();
}
});
// 停止动画
btn_stopFrame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 设置动画
// 2. 获取动画对象
animationDrawable = (AnimationDrawable) iv.getDrawable();
// 3. 暂停动画
animationDrawable.stop();
}
});
}
}
- 方式2:java
// 直接从drawable文件夹获取动画资源(图片)
animationDrawable = new AnimationDrawable();
for (int i = 0; i <= 25; i++) {
int id = getResources().getIdentifier("a" + i, "drawable", getPackageName());
Drawable drawable = getResources().getDrawable(id);
animationDrawable.addFrame(drawable, 100);
}
// 载入动画
btn_startFrame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 获取资源对象
iv.setImageDrawable(animationDrawable);
// 2. 停止动画
// 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
animationDrawable.stop();
// 3. 启动动画
animationDrawable.start();
}
});
// 停止动画
btn_stopFrame.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 获取资源对象
iv.setImageDrawable(animationDrawable);
// 2. 停止动画
animationDrawable.stop();
}
});
}
3.2 补间动画
- 补间动画的使用方式分为两种:在
XML
代码 /Java
代码里设置
- 前者优点:动画描述的可读性更好
- 后者优点:动画效果可动态创建
3.2.1 平移动画(Translate)
/*
* 设置方式1:xml
*/
// 步骤1:在 res/anim的文件夹里创建动画效果.xml文件
// 此处路径为res/anim/view_animation.xml
// 步骤2:根据不同动画效果的语法设置不同动画参数-view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
// 采用<translate /> 标签表示平移动画
// 以下参数是4种动画效果的公共属性,即都有的属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 以下参数是平移动画特有的属性
android:fromXDelta="0" // 视图在水平方向x 移动的起始值
android:toXDelta="500" // 视图在水平方向x 移动的结束值
android:fromYDelta="0" // 视图在竖直方向y 移动的起始值
android:toYDelta="500" // 视图在竖直方向y 移动的结束值
/>
// 步骤3:在Java代码中创建Animation对象并播放动画
// 1. 创建需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 2. 创建动画对象并传入设置的动画效果xml文件
Animation translateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
// 3. 播放动画
mButton.startAnimation(translateAnimation);
/*
* 设置方式2:Java
*/
// 步骤1:创建需要设置动画的视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建平移动画的对象
// 平移动画对应的Animation子类为TranslateAnimation
Animation translateAnimation = new TranslateAnimation(0,500,0,500);
// 参数说明
// fromXDelta :视图在水平方向x 移动的起始值
// toXDelta :视图在水平方向x 移动的结束值
// fromYDelta :视图在竖直方向y 移动的起始值
// toYDelta:视图在竖直方向y 移动的结束值
// 步骤3:属性设置:方法名是在其属性前加“set”,如设置时长setDuration()
translateAnimation.setDuration(3000);
// 步骤4:播放动画
mButton.startAnimation(translateAnimation);
3.2.2 缩放动画(Scale)
缩放动画有一个“缩放中心”的概念,说明如下:
/*
* 设置方式1:xml
*/
// 步骤1:在 res/anim的文件夹里创建动画效果.xml文件
// 此处路径为res/anim/view_animation.xml
// 步骤2:根据不同动画效果的语法设置不同动画参数-view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
// 采用<scale/> 标签表示是缩放动画
<scale xmlns:android="http://schemas.android.com/apk/res/android"
// 以下参数是4种动画效果的公共属性,即都有的属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 以下参数是缩放动画特有的属性
android:fromXScale="0.0"
// 动画在水平方向X的起始缩放倍数
// 0.0表示收缩到没有;1.0表示正常无伸缩
// 值小于1.0表示收缩;值大于1.0表示放大
android:toXScale="2" //动画在水平方向X的结束缩放倍数
android:fromYScale="0.0" //动画开始前在竖直方向Y的起始缩放倍数
android:toYScale="2" //动画在竖直方向Y的结束缩放倍数
android:pivotX="50%" // 缩放轴点的x坐标
android:pivotY="50%" // 缩放轴点的y坐标
// 轴点 = 视图缩放的中心点
// pivotX pivotY,可取值为数字,百分比,或者百分比p
// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT
// 两个50%表示动画从自身中间开始,具体如下图
/>
// 步骤3:在Java代码中创建Animation对象并播放动画
// 1. 创建需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 2. 创建动画对象并传入设置的动画效果xml文件
Animation scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
// 3. 播放动画
mButton.startAnimation(scaleAnimation);
/*
* 设置方式2:Java
*/
// 步骤1:创建 需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建缩放动画的对象 & 设置动画效果
// 缩放动画对应的Animation子类为RotateAnimation
Animation scaleAnimation = new ScaleAnimation(0,2,0,2,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// 参数说明:
// 1. fromX :动画在水平方向X的结束缩放倍数
// 2. toX :动画在水平方向X的结束缩放倍数
// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
// 4. toY:动画在竖直方向Y的结束缩放倍数
// 5. pivotXType:缩放轴点的x坐标的模式
// 6. pivotXValue:缩放轴点x坐标的相对值
// 7. pivotYType:缩放轴点的y坐标的模式
// 8. pivotYValue:缩放轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
// 步骤3:属性设置:方法名是在其属性前加“set”,如设置时长setDuration()
scaleAnimation.setDuration(3000);
// 步骤4:播放动画
mButton.startAnimation(scaleAnimation);
3.2.3 旋转动画(Rotate)
类似于缩放动画的“缩放中心”,旋转动画也有一个“旋转轴点”的概念:
/*
* 设置方式1:xml
*/
// 步骤1:在 res/anim的文件夹里创建动画效果.xml文件
// 此处路径为res/anim/view_animation.xml
// 步骤2:根据不同动画效果的语法设置不同动画参数-view_animation.xml
// 采用<rotate/> 标签表示是旋转动画
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
// 以下参数是4种动画效果的公共属性,即都有的属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 以下参数是旋转动画特有的属性
android:duration="1000"
android:fromDegrees="0" // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
android:toDegrees="270" // 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
android:pivotX="50%" // 旋转轴点的x坐标
android:pivotY="0" // 旋转轴点的y坐标
// 轴点 = 视图缩放的中心点
// pivotX pivotY,可取值为数字,百分比,或者百分比p
// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT
// 两个50%表示动画从自身中间开始,具体如下图
/>
// 步骤3:在Java代码中创建Animation对象并播放动画
// 1. 创建需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 2. 创建动画对象并传入设置的动画效果xml文件
Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
// 3. 播放动画
mButton.startAnimation(rotateAnimation);
/*
* 设置方式2:Java
*/
// 步骤1:创建需要设置动画的视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建旋转动画的对象 & 设置动画效果
// 旋转动画对应的Animation子类为RotateAnimation
Animation rotateAnimation = new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
// 参数说明:
// 1. fromX :动画在水平方向X的结束缩放倍数
// 2. toX :动画在水平方向X的结束缩放倍数
// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
// 4. toY:动画在竖直方向Y的结束缩放倍数
// 5. pivotXType:缩放轴点的x坐标的模式
// 6. pivotXValue:缩放轴点x坐标的相对值
// 7. pivotYType:缩放轴点的y坐标的模式
// 8. pivotYValue:缩放轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
// 步骤3:属性设置:方法名是在其属性前加“set”,如设置时长setDuration()
rotateAnimation.setDuration(3000);
// 步骤4:播放动画
mButton.startAnimation(rotateAnimation);
3.2.4 透明动画(Alpha)
/*
* 设置方式1:xml
*/
// 步骤1:在 res/anim的文件夹里创建动画效果.xml文件
// 此处路径为res/anim/view_animation.xml
// 步骤2:根据不同动画效果的语法设置不同动画参数-view_animation.xml
// 采用<alpha/> 标签表示是透明度动画
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
// 以下参数是4种动画效果的公共属性,即都有的属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 以下参数是透明度动画特有的属性
android:fromAlpha="1.0" // 动画开始时视图的透明度(取值范围: -1 ~ 1)
android:toAlpha="0.0"// 动画结束时视图的透明度(取值范围: -1 ~ 1)
/>
// 步骤3:在Java代码中创建Animation对象并播放动画
// 1. 创建需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 2. 创建动画对象并传入设置的动画效果xml文件
Animation alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
// 3. 播放动画
mButton.startAnimation(alphaAnimation);
/*
* 设置方式2:Java
*/
// 步骤1:创建需要设置动画的视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建透明度动画的对象 & 设置动画效果
// 透明度动画对应的Animation子类为AlphaAnimation
Animation alphaAnimation = new AlphaAnimation(1,0);
// 参数说明:
// 1. fromX :动画在水平方向X的结束缩放倍数
// 2. toX :动画在水平方向X的结束缩放倍数
// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
// 4. toY:动画在竖直方向Y的结束缩放倍数
// 5. pivotXType:缩放轴点的x坐标的模式
// 6. pivotXValue:缩放轴点x坐标的相对值
// 7. pivotYType:缩放轴点的y坐标的模式
// 8. pivotYValue:缩放轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
// 步骤3:属性设置:方法名是在其属性前加“set”,如设置时长setDuration()
AlphaAnimation.setDuration(3000);
// 步骤4:播放动画
mButton.startAnimation(AlphaAnimation);
3.2.5 Activity 的切换效果
即 Activity
启动 / 退出时的动画效果,主要包括淡入淡出、左滑右滑等。
3.2.5.1 系统预设
本身系统已经封装好了淡入淡出、左滑右滑的效果。
// 系统已经封装好的动画效果
// 淡入淡出:android.R.anim.fade_in、android.R.anim.fade_out
// 由左向右滑入:android.R.anim.slide_in_left、android.R.anim.slide_out_right
// 核心方法:overridePendingTransition(int enterAnim, int exitAnim)
// 调用时机:Activity的onCreate() 或 finish()
// 参数说明
// 对于在onCreate()设置:
// enterAnim:进入该Activity时的动画效果资源ID
// exitAnim:进入该Activity时上一个Activity离开时的动画效果资源ID
// 对于在finish()设置:
// enterAnim:进入其他Activity时 进入Activity的动画效果资源ID
// exitAnim:进入其他Activity时 该Activity离开时的动画效果资源ID
// 具体使用
// 方式1:在onCreate()设置
@Override
public void onCreate(Bundle savedInstanceState) {
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
super.onCreate(savedInstanceState);
}
// 方式2:在finish()设置
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
这里需要特别注意的是:如果进入退出页面:一个需要动画、另外一个不需要动画,但也必须设置时间相同的、没有任何变化的动画,否则会出现黑屏。
3.2.5.2 自定义切换效果
3.2.6 Fragment动画切换效果
类似于Activity,Fragment的动画切换效果同样存在两种方式:使用系统预设和自定义切换动画效果。
// 方式1:系统预设
// 通过setTransition(int transit)进行设置
// transit参数说明
// 1. FragmentTransaction.TRANSIT_NONE:无动画
// 2. FragmentTransaction.TRANSIT_FRAGMENT_OPEN:标准的打开动画效果
// 3. FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:标准的关闭动画效果
// 标准动画设置好后,在Fragment添加和移除的时候都会有。
// 具体使用
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// 方式2:自定义动画效果
// 通过FragmentTransavtion.setCustomAnimations()设置
// 此处的自定义动画效果同Activity,此处不再过多描述
// 具体使用
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.in_from_right,R.anim.out_to_left);
3.2.7 视图组(ViewGroup)中子元素的出场效果
视图组(ViewGroup
)中子元素可以具备出场时的补间动画效果。常用需求场景:为ListView
的 item
设置出场动画。
// 步骤1:设置子元素的出场动画
// res/anim/view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
// 此处采用了组合动画
<set xmlns:android="http://schemas.android.com/apk/res/android" >
android:duration="3000"
<alpha
android:duration="1500"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
<translate
android:fromXDelta="500"
android:toXDelta="0"
/>
</set>
// 步骤2:设置 视图组(ViewGroup)的动画文件
// res/anim/anim_layout.xml
<?xml version="1.0" encoding="utf-8"?>
// 采用LayoutAnimation标签
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
// 子元素开始动画的时间延迟
// 如子元素入场动画的时间总长设置为300ms
// 那么 delay = "0.5" 表示每个子元素都会延迟150ms才会播放动画效果
// 第一个子元素延迟150ms播放入场效果;第二个延迟300ms,以此类推
android:animationOrder="normal"
// 表示子元素动画的顺序
// 可设置属性为:
// 1. normal :顺序显示,即排在前面的子元素先播放入场动画
// 2. reverse:倒序显示,即排在后面的子元素先播放入场动画
// 3. random:随机播放入场动画
android:animation="@anim/view_animation"
// 设置入场的具体动画效果
// 将步骤1的子元素出场动画设置到这里
/>
// 步骤3:为视图组(ViewGroup)指定andorid:layoutAnimation属性
// 指定的方式有两种: XML / Java代码
// 方式1:XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >
<ListView
android:id="@+id/listView1"
android:layoutAnimation="@anim/anim_layout"
// 指定layoutAnimation属性用以指定子元素的入场动画
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
// 方式2:Java
// 注:不需额外设置res/ anim /anim_layout.xml该xml文件了
ListView lv = (ListView) findViewById(R.id.listView1);
// 加载子元素的出场动画
Animation animation = AnimationUtils.loadAnimation(this,R.anim.anim_item);
// 设置LayoutAnimation的属性
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 为ListView设置LayoutAnimation的属性
lv.setLayoutAnimation(controller);
3.2.8 组合动画
// 方式:xml跟java代码设置
// 方式1:xml
// 步骤1:在路径 res/anim 的文件夹里创建动画效果 .xml文件 - view_animation.xml
// 步骤2:设置组合动画(同单个动画设置)
<?xml version="1.0" encoding="utf-8"?>
// 采用< Set/>标签
<set xmlns:android="http://schemas.android.com/apk/res/android">
// 组合动画同样具备公共属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 组合动画独特的属性
android:shareinterpolator = “true”
// 表示组合动画中的动画是否和集合共享同一个差值器
// 如果集合不指定插值器,那么子动画需要单独设置
// 组合动画播放时是全部动画同时开始
// 如果想不同动画不同时间开始就要使用android:startOffset属性来延迟单个动画播放时间
// 设置旋转动画,语法同单个动画
<rotate
android:duration="1000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatMode="restart"
android:repeatCount="infinite"
/>
// 设置平移动画,语法同单个动画
<translate
android:duration="10000"
android:startOffset = “1000”// 延迟该动画播放时间
android:fromXDelta="-50%p"
android:fromYDelta="0"
android:toXDelta="50%p"
android:toYDelta="0" />
// 设置透明度动画,语法同单个动画
<alpha
android:startOffset="7000"
android:duration="3000"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
// 设置缩放动画,语法同单个动画
<scale
android:startOffset="4000"
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.5"
android:toYScale="0.5" />
// 特别注意:
// 1. 在组合动画里scale缩放动画设置的repeatCount(重复播放)和fillBefore(播放完后,视图是否会停留在动画开始的状态)是无效的。
// 2. 所以如果需要重复播放或者回到原位的话需要在set标签里设置
// 3. 但是由于此处rotate旋转动画里已设置repeatCount为infinite,所以动画不会结束,也就看不到重播和回复原位
</set>
// 步骤3:播放动画
// 创建 需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 创建 动画对象 并传入设置的动画效果xml文件
Animation setAnimation = AnimationUtils.loadAnimation(this, R.anim.view_animation);
// 播放动画
mButton.startAnimation(setAnimation);
// 方式2:java代码设置
// 创建 需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 组合动画设置
AnimationSet setAnimation = new AnimationSet(true);
// 步骤1:创建组合动画对象(设置为true)
// 步骤2:设置组合动画的属性
// 特别说明以下情况
// 因为在下面的旋转动画设置了无限循环(RepeatCount = INFINITE)
// 所以动画不会结束,而是无限循环
// 所以组合动画的下面两行设置是无效的
setAnimation.setRepeatMode(Animation.RESTART);
setAnimation.setRepeatCount(1);// 设置了循环一次,但无效
// 步骤3:逐个创建子动画(方式同单个动画创建方式,此处不作过多描述)
// 子动画1:旋转动画
Animation rotate = new RotateAnimation(0,360,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
rotate.setDuration(1000);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
// 子动画2:平移动画
Animation translate = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_PARENT,-0.5f,
TranslateAnimation.RELATIVE_TO_PARENT,0.5f,
TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,0);
translate.setDuration(10000);
// 子动画3:透明度动画
Animation alpha = new AlphaAnimation(1,0);
alpha.setDuration(3000);
alpha.setStartOffset(7000);
// 子动画4:缩放动画
Animation scale1 = new ScaleAnimation(1,0.5f,1,0.5f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scale1.setDuration(1000);
scale1.setStartOffset(4000);
// 步骤4:将创建的子动画添加到组合动画里
setAnimation.addAnimation(alpha);
setAnimation.addAnimation(rotate);
setAnimation.addAnimation(translate);
setAnimation.addAnimation(scale1);
// 步骤5:播放动画
mButton.startAnimation(setAnimation);
3.2.9 监听动画
// 主要通过setAnimationListener()设置
Animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 动画开始时回调
}
@Override
public void onAnimationEnd(Animation animation) {
// 动画结束时回调
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复执行的时候回调
}
});
3.3 属性动画
对于属性动画的使用,主要是:
- 两个使用方法类:
ValueAnimator
类 &ObjectAnimator
类 - 两个辅助使用类:插值器 & 估值器
3.3.1 ValueAnimator类
- 定义:属性动画机制中 最核心的一个类
- 实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。如图下:
从上面原理可以看出:ValueAnimator
类中有3个重要方法:
ValueAnimator.ofInt(int values)
ValueAnimator.ofFloat(float values)
ValueAnimator.ofObject(int values)
操作值的方式 分为 XML
设置 / Java
代码设置,具体如下:
/*
* 设置方式1:xml
*/
// 步骤1:在路径 res/animator的文件夹里创建相应的动画 .xml文件 - set_animation.xml
// 步骤2:设置动画参数
// ValueAnimator采用<animator> 标签
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0" // 初始值
android:valueTo="3" // 结束值
android:valueType="intType" // 变化值类型 :floatType & intType
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度
/>
// 步骤3:启动动画
// 载入XML动画
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.set_animation);
// 设置动画对象
animator.setTarget(view);
// 启动动画
animator.start();
/*
* 设置方式2:Java
*/
// 步骤1:设置动画属性的初始值 & 结束值
ValueAnimator anim = ValueAnimator.ofInt(0, 3);
// ofInt()作用:
// 1. 创建动画实例
// 2. 将传入的多个Int参数进行平滑过渡:此处传入0和3,表示将值从0平滑过渡到3
// 如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推
// ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值 过渡到 结束值
// 关注1:ofInt()源码分析
public static ValueAnimator ofInt(int... values) {
// 允许传入一个或多个Int参数
// 1. 输入一个的情况(如a):从0过渡到a;
// 2. 输入多个的情况(如a,b,c):先从a平滑过渡到b,再从b平滑过渡到C
ValueAnimator anim = new ValueAnimator();
// 创建动画对象
anim.setIntValues(values);
// 将传入的值赋值给动画对象
return anim;
}
// 步骤2:设置动画的播放各种属性
// 设置动画运行的时长
anim.setDuration(500);
// 设置动画延迟播放时间
anim.setStartDelay(500);
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
anim.setRepeatCount(0);
// 设置重复播放动画模式
anim.setRepeatMode(ValueAnimator.RESTART);
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
// 步骤3:将改变的值手动赋值给对象的属性值:通过动画的更新监听器
// 设置值的更新监听器,即:值每次改变、变化一次,该方法就会被调用一次
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 获得改变后的值
int currentValue = (Integer) animation.getAnimatedValue();
// 将改变后的值赋给对象的属性值
View.setproperty(currentValue);
// 刷新视图,即重新绘制,从而实现动画效果
View.requestLayout();
}
});
// 步骤4:启动动画
anim.start();
3.3.2 ObjectAnimator类
直接对对象的属性值进行改变操作,从而实现动画效果
- 如直接改变
View
的alpha
属性 从而实现透明度的动画效果- 继承自
ValueAnimator
类,即底层的动画实现机制是基于ValueAnimator
类
- 本质原理: 通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。如下图:
从上面的工作原理可以看出:ObjectAnimator
与 ValueAnimator
类的区别:
ValueAnimator
类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;ObjectAnimator
类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作
3.4 插值器
3.4.1 使用插值器
插值器在动画的使用有两种方式:在XML / Java代码中设置:
/*
* 使用方式1:xml
* 主要是设置插值器属性 android:interpolator
*/
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
// 通过资源ID设置插值器
android:interpolator="@android:anim/overshoot_interpolator"
android:duration="3000"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="2"
android:toYScale="2" />
>
/*
* 使用方式2:java
*/
// 步骤1:创建 需要设置动画的 视图View
Button mButton = (Button) findViewById(R.id.Button);
// 步骤2:创建透明度动画的对象 & 设置动画效果
Animation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(3000);
// 步骤3:创建对应的插值器类对象
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步骤4:给动画设置插值器
alphaAnimation.setInterpolator(overshootInterpolator);
// 步骤5:播放动画
mButton.startAnimation(alphaAnimation);
3.4.2 系统内置插值器
Android
内置了 9 种内置的插值器实现:
系统默认的插值器是
AccelerateDecelerateInterpolator
,即先加速后减速
3.4.3 自定义插值器
自定义插值器需要实现Interpolator或TimeInterpolator接口,并复写getInterpolation()方法
- 补间动画 实现
Interpolator
接口;属性动画实现TimeInterpolator
接口TimeInterpolator
接口是属性动画中新增的,用于兼容Interpolator
接口,这使得所有过去的Interpolator
实现类都可以直接在属性动画使用
Interpolator接口和TimeInterpolator接口说明如下:
// Interpolator接口
public interface Interpolator {
// 内部只有一个方法:getInterpolation()
float getInterpolation(float input) {
// 参数说明
// input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化
// 即动画开始时,input值 = 0;动画结束时input = 1
// 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加
...// 插值器的计算逻辑
return xxx;
// 返回的值就是用于估值器继续计算的fraction值
}
// TimeInterpolator接口
// 同上
public interface TimeInterpolator {
float getInterpolation(float input){
...
};
}
从上面可以看出,自定义插值器的关键在于:对input值根据动画的进度(0%-100%)通过逻辑计算从而计算出当前属性值改变的百分比。先来看两个已经实现好的系统内置差值器:
- 匀速插值器:
LinearInterpolator
- 先加速再减速 插值器:
AccelerateDecelerateInterpolator
/*
* 匀速差值器:LinearInterpolator
*/
@HasNativeInterpolator
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
... // 仅贴出关键代码
public float getInterpolation(float input) {
return input;
// 没有对input值进行任何逻辑处理,直接返回
// 即input值 = fraction值
// 因为input值是匀速增加的,因此fraction值也是匀速增加的,
// 所以动画的运动情况也是匀速的,所以是匀速插值器
}
/*
* 先加速再减速 差值器:AccelerateDecelerateInterpolator
*/
@HasNativeInterpolator
public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
... // 仅贴出关键代码
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
// input的运算逻辑如下:
// 使用了余弦函数,因input的取值范围是0到1,那么cos函数中的取值范围就是π到2π。
// 而cos(π)的结果是-1,cos(2π)的结果是1
// 所以该值除以2加上0.5后,getInterpolation()方法最终返回的结果值还是在0到1之间。
// 只不过经过了余弦运算之后,最终的结果不再是匀速增加的了,而是经历了一个先加速后减速的过程
// 所以最终,fraction值 = 运算后的值 = 先加速后减速
// 所以该差值器是先加速再减速的
}
}
3.5 估值器
3.5.1 使用估值器
ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);
// 系统内置的估值器有3个:
// IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
// FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
// ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡
3.5.2 自定义估值器
根据 插值器计算出当前属性值改变的百分比 & 初始值 & 结束值 来计算 当前属性具体的数值
如:动画进行了50%(初始值=100,结束值=200 ),那么匀速插值器计算出了当前属性值改变的百分比是50%,那么估值器则负责计算当前属性值 = 100 + (200-100)x50% = 150.
自定义估值器需要实现 TypeEvaluator接口 & 复写evaluate()
// 步骤1:实现TypeEvaluator接口
public class ObjectEvaluator implements TypeEvaluator{
// 步骤2:复写evaluate()
// 作用:估值器的计算逻辑,即写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值),也是插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值
// 估值器的计算逻辑
...
// 返回对象动画过渡逻辑计算后的值
// 即赋给动画属性的具体数值
return value;
}
// 特别注意
// 那么插值器的input值 和 估值器fraction有什么关系呢?
// 答:input的值决定了fraction的值:input值经过计算后传入到插值器的getInterpolation(),
// 然后通过实现getInterpolation()中的逻辑算法,
// 根据input值来计算出一个返回值,而这个返回值就是fraction了
属性动画中的ValueAnimator.ofInt()
& ValueAnimator.ofFloat()
都具备系统内置的估值器,即FloatEvaluator
& IntEvaluator
,即系统已经默认实现了 如何从初始值 过渡到 结束值 的逻辑
但对于ValueAnimator.ofObject()
,从上面的工作原理可以看出并没有系统默认实现,因为对对象的动画操作复杂 & 多样,系统无法知道如何从初始对象过度到结束对象
因此,对于ValueAnimator.ofObject()
,我们需自定义估值器(TypeEvaluator
)来告知系统如何进行从 初始对象 过渡到 结束对象的逻辑。
3.6 SVGA动画
3.6.1 svg
可将svg图片导入studio中,经过IDE的转化后即可使用。
3.6.1.1 svg标签
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300" height="300" viewBox="0, 0, 100, 200" xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="49" stroke="black"
stroke-width="2" fill="red" />
</svg>
3.6.1.2 path标签
在 SVG 里,你可以把 path 看成是最基本的绘制元素,正因为它是最基本的,万变不离其宗,他能演化出各种复杂的绘制效果。所以 path 是最基本也是最复杂的绘制元素。
我们知道一个 path 标签,最重要的属性是 d 属性,它是一组指令和参数的集合。在 d 属性的值里,我们能看到一堆非常复杂的指令字符串。
<path d="
M73.8616812,68.8664775
L74.5015359,74.5939423
L68.1746283,71.7969507
C66.2299599,72.4159872 64.1377269,72.7711218 61.9444643,72.7711218
C51.9719158,72.7711218 43.8883163,65.7823167 43.8883163,57.1611168
C43.8883163,48.5399169 51.9719158,41.5511118 61.9444643,41.5511118
C71.9164005,41.5511118 80,48.5399169 80,57.1611168
C80,61.8286883 77.6181486,66.006419 73.8616812,68.8664775" id="Fill-1" fill="#FFFFFF"></path>
指令 | 参数 | 含义 |
---|---|---|
M | x y | 将画笔移动到点(x,y) |
L | x y | 画笔从当前的点绘制线段到点(x,y) |
H | x | 画笔从当前的点绘制水平线段到点(x,y0),y0 表示绘制前画笔所在 y 轴坐标,也就是 y 轴不变 |
V | y | 画笔从当前的点绘制竖直线段到点(x0,y),x0 表示绘制前画笔所在 x 轴坐标,也就是 x 轴不变 |
A | rx ry x-axis-rotation large-arc-flag sweep-flag x y | 画笔从当前的点绘制一段圆弧到点(x,y) |
C | x1 y1, x2 y2, x y | 画笔从当前的点绘制一段三次贝塞尔曲线到点(x,y) |
S | x2 y2, x y | 特殊版本的三次贝塞尔曲线(省略第一个控制点) |
Q | x1 y1, x y | 绘制二次贝塞尔曲线到点(x,y) |
T | x y | 特殊版本的二次贝塞尔曲线(省略控制点) |
Z | 无参数 | 绘制闭合图形,如果 d 属性不指定Z命令,则绘制线段,而不是封闭图形 |
以上是 path 路径中的全部指令,相对来说比较好理解。每个指令都有对应的小写指令。例如M 10,10 有对应的 m 10,10 。大写代表绝对位置,所谓绝对位置即对 SVG 画布左上角原点的绝对。小写代表相对位置,所谓相对位置是以当前画笔所在位置进行定位。
3.6.1.3 基本图形
图形 | 标签 | 模板 | 含义 | |
---|---|---|---|---|
矩形 | < rect > | < rect x="60" y="10" rx="10" ry="10" width="30" height="30"/> | x:起点横坐标,y:起点纵坐标,rx:倒角x轴方向半径,ry:倒角x轴方向半径,width:宽度,height:高度 | |
圆形 | < circle > | < circle cx="100" cy="100" r="50" fill="#fff"> | cx:圆心横坐标,cy:圆心纵坐标,r:半径 | |
椭圆 | < ellipse > | < ellipse cx="75" cy="75" rx="20" ry="5"/> | cx:椭圆心横坐标,cy:椭圆心纵坐标,rx:椭圆x轴方向半径,ry:椭圆y轴方向半径 | |
直线 | < line > | < line x1="10" x2="50" y1="110" y2="150"/> | x:起点横坐标,y:起点纵坐标,rx:倒角x轴方向半径,ry:倒角x轴方向半径,width:宽度,height:高度 | x1,y1:起点,x2,y2:终点 |
折线 | < polyline > | < polyline points="60 110, 65 120, 70 115, 75 130, 80 125, 85 140, 90 135, 95 150, 100 145"/> | 每两个点以空格配对为一个坐标点,逗号隔开形成坐标集合。连成折线。 | |
多边形 | < polygon > | < polygon points="50 160, 55 180, 70 180, 60 190, 65 205, 50 195, 35 205, 40 190, 30 180, 45 180"/> | 类似折线,不同的是,最后一个点会自动闭合第一个点,形成闭环。 |
3.6.1.4 symbol标签
它的作用说白话点就是相当于是一个元件,放在我们的工具箱里,就像下面这样:
<svg class="svg-sprite">[工具箱]
<symbol id="icon-wave_add" viewBox="0 0 76 76"><path d="M38 0a4 4 0 014 4v30h30a4 4 0 110 8H41.999L42 72a4 4 0 11-8 0l-.001-30H4a4 4 0 110-8h30V4a4 4 0 014-4z" fill="currentColor" fill-rule="evenodd" opacity="none"></path></symbol>
<symbol id="icon-time" viewBox="0 0 10 10"><path d="M5 0a5 5 0 110 10A5 5 0 015 0zm0 1.5a.5.5 0 00-.5.5v3.02l.008.088a.5.5 0 00.238.343L7.02 6.794l.082.039a.5.5 0 00.603-.215l.039-.082a.5.5 0 00-.216-.603L5.5 4.735V2l-.008-.09A.5.5 0 005 1.5z" fill="rgba(153,153,153,1)" fill-rule="evenodd" class=" "></path></symbol>
<symbol id="icon-wave_delete" viewBox="0 0 40 40"><g fill="none" fill-rule="evenodd"><circle fill="#000" opacity="0.2" cx="20" cy="20" r="20"></circle><path stroke="#FFF" stroke-width="4" stroke-linecap="round" d="M13 13l14 14M27 13L13 27"></path></g></symbol>
</svg>
3.7 Lottie动画
3.7.1 Lottie依赖
Gradle
是唯一支持的构建配置,所以只需要在项目的 build.gradle
文件中添加依赖即可:
dependencies {
implementation "com.airbnb.android:lottie:$lottieVersion"
}
3.7.2 Lottie 核心类
-
LottieAnimationView:继承自
AppCompatImageView
,是加载Lottie
动画的默认和最简单的方式。 -
LottieDrawable:具有大多数与
LottieAnimationView
相同的API
,因此可以在任何视图上使用它。
3.7.3 加载动画
Lottie 支持 Jellybean (API 16)
及以上版本。Lottie 动画支持从以下位置加载动画:
src/main/res/raw
中的 json 动画。src/main/assets
中的 json 文件。src/main/assets
中的 zip 文件。- json 或 zip 文件的
Url
。 json 字符串
。源可以来自任何东西,包括自己的网络堆栈。- json 文件或 zip 文件的
InputStream
。
3.7.3.1 在 XML 中使用
最简单的使用方法是使用 LottieAnimationView
。Lottie 支持加载来自 res/raw
或 assets/
的动画资源。建议使用 res/raw
,因为可以对动画通过 R 文件
使用静态引用,而不只是使用字符串名称。这也可以帮助构建静态分析,因为它可以跟踪动画的使用。
LottieAnimationView
的常用属性及其功能如下:
属性 | 功能 |
---|---|
lottie_fileName | 设置播放动画的 json 文件名称 |
lottie_rawRes | 设置播放动画的 json 文件资源 |
lottie_autoPlay | 设置动画是否自动播放(默认为false) |
lottie_loop | 设置动画是否循环(默认为false) |
lottie_repeatMode | 设置动画的重复模式(默认为restart) |
lottie_repeatCount | 设置动画的重复次数(默认为-1) |
lottie_cacheStrategy | 设置动画的缓存策略(默认为weak) |
lottie_colorFilter | 设置动画的着色颜色(优先级最低) |
lottie_scale | 设置动画的比例(默认为1f) |
lottie_progress | 设置动画的播放进度 |
lottie_imageAssetsFolder | 设置动画依赖的图片资源文件地址 |
在 res/raw (lottie_rawRes)
或 assets/ (lottie_fileName)
中存放动画的 JSON 文件,然后就可以在 xml 中直接使用,如下:
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:lottie_rawRes="@raw/hello_world"
// or
app:lottie_fileName="hello_world.json"
app:lottie_loop="true"
app:lottie_autoPlay="true" />
3.7.3.2 在代码中使用
LottieAnimationView
的常用方法及其功能如下:
方法 | 功能 |
---|---|
setAnimation(String) | 设置播放动画的 json 文件名称 |
setAnimation(String, CacheStrategy) | 设置播放动画的 json 文件资源和缓存策略 |
setAnimation(int) | 设置播放动画的 json 文件名称 |
setAnimation(int, CacheStrategy) | 设置播放动画的 json 文件资源和缓存策略 |
loop(boolean) | 设置动画是否循环(默认为false) |
setRepeatMode(int) | 设置动画的重复模式(默认为restart) |
setRepeatCount(int) | 设置动画的重复次数(默认为-1) |
lottie_cacheStrategy | 设置动画的缓存策略(默认为weak) |
lottie_colorFilter | 设置动画的着色颜色(优先级最低) |
setScale(float) | 设置动画的比例(默认为1f) |
setProgress(float) | 设置动画的播放进度 |
setImageAssetsFolder(String) | 设置动画依赖的图片资源文件地址 |
playAnimation() | 从头开始播放动画 |
pauseAnimation() | 暂停播放动画 |
resumeAnimation() | 继续从当前位置播放动画 |
cancelAnimation() | 取消播放动画 |
如果不想用 xml 实现,可以通过代码来实现,可以直接加载本地动画资源,也可以从网络请求加载动画。
- 从
res/raw
或assets/
加载动画资源:
LottieAnimationView animationView = ...
animationView.setAnimation(R.raw.hello_world);
// or
animationView.setAnimation(R.raw.hello_world.json);
animationView.playAnimation();
该方法在后台加载文件并解析动画,并在完成后异步开始渲染。
- 从网络请求加载动画: Lottie 的一个优点是可以从网络请求加载动画。所以,应该将网络请求的响应内容转换为字符串格式。Lottie 使用一个流化的 json 反序列化器来提高性能和内存使用率,所以不要将它转换成您自己的 JSONObject,这只会损害性能。
LottieAnimationView animationView = ...
// This allows lottie to use the streaming deserializer mentioned above.
JsonReader jsonReader = new JsonReader(new StringReader(json.toString()));
animationView.setAnimation(jsonReader);
animationView.playAnimation();
3.7.4 Lottie的缓存策略
你的应用程序中可能会有一些经常使用的动画,比如加载动画等等。为了避免每次加载文件和发序列化的开销,你可以在你的动画上设置一个缓存策略。上面所有的 setAnimation
APIs都可以采用可选的第二个参数 CacheStrategy
。在默认情况下,Lottie 将保存对动画的弱引用,这对于大多数情况来说应该足够了。但是,如果确定某个动画肯定会经常使用,那么请将其缓存策略更改为 CacheStrategy.Strong
;或者如果确定某个动画很大而且不会经常使用,把缓存策略改成 CacheStrategy.None
。
CacheStrategy
可以是None
、Weak
和 Strong
三种形式来让 LottieAnimationView
对加载和解析动画的使用强或弱引用的方式。弱或强表示缓存中组合的 GC 引用强度。
3.7.5 直接使用LottieDrawable
LottieAnimationView 是基于 LottieDrawable
的一个包装好的 ImageView
。LottieAnimationView 上的所有 API 都在 LottieDrawable 上进行镜像,因此可以创建自己的实例并在任何可以使用drawable的地方使用它,例如自定义 View 或菜单。
3.7.6 使用 LottieComposition
去预加载动画
动画的支持模型是 LottieComposition
。在大多数情况下,在 LottieAnimationView
或 LottieDrawable
上调用 setAnimation(…)
便足够了。但是,如果要预加载动画以使其立即可用,则可以使用 LottieComposition.Factory
API返回可以直接在 LottieAnimationView
或 LottieDrawable
设置的 LottieComposition
对象。同样,通常不需要自己添加管理 compositions 的开销。LottieAnimationView
中的默认缓存足以满足大多数用例的需要。
LottieAnimationView animationView = ...;
...
Cancellable compositionLoader = LottieComposition.Factory.fromJsonString(getResources(), jsonString, (composition) -> {
animationView.setComposition(composition);
animationView.playAnimation();
});
// Cancel to stop asynchronous loading of composition
// compositionCancellable.cancel();
3.7.7 动画监听
animationView.addAnimatorUpdateListener((animation) -> {
// do something.
});
animationView.playAnimation();
...
if (animationView.isAnimating()) {
// do something.
}
...
animationView.setProgress(0.5f);
...
在更新侦听器回调中:
animation.getAnimatedValue()
将返回动画的播放进度,而不考虑当前设置的最小/最大帧[0,1]。animation.getAnimatedFraction()
将返回动画的播放进度,同时考虑设置的最小/最大帧[minFrame,maxFrame]。
3.7.8 控制 Lottie 动画执行的速度和时长
尽管 playAnimation()
对于绝大多数用例来说已足够,但可以在更新回调中调用 setProgress(...)
方法为自己的动画设置进度。
// Custom animation speed or duration.
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.addUpdateListener(animation -> {
animationView.setProgress(animation.getAnimatedValue());
});
animator.start();
...
3.7.9 循环播放
可以像使用 ValueAnimator
一样通过 setRepeatMode(...)
或 setRepeatCount(...)
方法控制动画的循环播放,或者你直接在 xml
中使用 lottie_loop="true"
开启循环播放。
3.7.10 动画尺寸(px vs dp)
Lottie
将 After Effects
中的所有 px
值转换为设备上的 dps
,以便在设备上以相同的大小呈现所有内容。这意味着它不是在 After Effects 中制作1920x1080的动画,而是在After Effects中更像411x731px,它大致对应于当今大多数手机的dp屏幕尺寸。
但是,如果您的动画不是完美尺寸,则有两种选择:
3.7.10.1 ImageView scaleType
LottieAnimationView
是一个包装好的 ImageView
,它支持 centerCrop
和 centerInside
,所以可以像使用其他 image 一样使用这两个工具方法。
3.7.10.2 Lottie setScale(...)
LottieAnimationView
和 LottieDrawable
都有 setScale(float)
API,可以使用它来手动放大或缩小动画。这很少有用,但可以在某些情况下使用。
如果动画执行缓慢,请务必查看有关性能的文档。但是,可以尝试使用 scaleType
缩放动画,这将减少 Lottie 每帧渲染的数量。如果你有大的 mask 和 mattes,这将特别有用。
3.7.11 播放动画片段
播放/循环动画的单个部分通常很方便。例如,动画的前半部分可能处于“开启”状态,下半部分处于“关闭”状态。LottieAnimationView 和 LottieDrawable 有用于控制当前段的 API:
setMinFrame(...)
setMaxFrame(...)
setMinProgress(...)
setMaxProgress(...)
setMinAndMaxFrame(...)
setMinAndMaxProgress(...)
循环API将遵循此处设置的最小/最大帧。
3.8 硬件加速
所谓硬件加速,指的是把某些计算工作交给专门的硬件来做,而不是和普通的计算工作一样交给 CPU 来处理。这样不仅减轻了 CPU 的压力,而且由于有了「专人」的处理,这份计算工作的速度也被加快了。这就是「硬件加速」。
而对于 Android 来说,硬件加速有它专属的意思:在 Android 里,硬件加速专指把 View 中绘制的计算工作交给 GPU 来处理。进一步地再明确一下,这个「绘制的计算工作」指的就是把绘制方法中的那些 Canvas.drawXXX() 变成实际的像素这件事。
3.8.1 原理
在硬件加速关闭的时候,Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕。这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程(例如由一句 Canvas.drawCircle() 来获得一个具体的圆的像素信息),这个过程的计算是由 CPU 来完成的。大致就像这样:
而在硬件加速开启时,Canvas 的工作方式改变了:它只是把绘制的内容转换为 GPU 的操作保存了下来,然后就把它交给 GPU,最终由 GPU 来完成实际的显示工作。大致是这样:
如图,在硬件加速开启时,CPU 做的事只是把绘制工作转换成 GPU 的操作,这个工作量相对来说是非常小的。
3.8.2 怎么就「加速」了?
从上面的图中可以看出,硬件加速开启后,绘制的计算工作由 CPU 转交给了 GPU。不过这怎么就能起到「加速」作用,让绘制变快了呢?
硬件加速能够让绘制变快,主要有三个原因:
- 本来由 CPU 自己来做的事,分摊给了 GPU 一部分,自然可以提高效率;
- 相对于 CPU 来说,GPU 自身的设计本来就对于很多常见类型内容的计算(例如简单的圆形、简单的方形)具有优势;
- 由于绘制流程的不同,硬件加速在界面内容发生重绘的时候绘制流程可以得到优化,避免了一些重复操作,从而大幅提升绘制效率。
其中前两点可以总结为一句:用了 GPU,绘制就是快。原因很直观,不再多说。
关于第三点,它的原理我大致说一下:
前面说到,在硬件加速关闭时,绘制内容会被 CPU 转换成实际的像素,然后直接渲染到屏幕。具体来说,这个「实际的像素」,它是由 Bitmap 来承载的。在界面中的某个 View 由于内容发生改变而调用 invalidate() 方法时,如果没有开启硬件加速,那么为了正确计算 Bitmap 的像素,这个 View 的父 View、父 View 的父 View 乃至一直向上直到最顶级 View,以及所有和它相交的兄弟 View,都需要被调用 invalidate()来重绘。一个 View 的改变使得大半个界面甚至整个界面都重绘一遍,这个工作量是非常大的。
而在硬件加速开启时,前面说过,绘制的内容会被转换成 GPU 的操作保存下来(承载的形式称为 display list,对应的类也叫做 DisplayList),再转交给 GPU。由于所有的绘制内容都没有变成最终的像素,所以它们之间是相互独立的,那么在界面内容发生改变的时候,只要把发生了改变的 View 调用 invalidate() 方法以更新它所对应的 GPU 操作就好,至于它的父 View 和兄弟 View,只需要保持原样。那么这个工作量就很小了。
正是由于上面的原因,硬件加速不仅是由于 GPU 的引入而提高了绘制效率,还由于绘制机制的改变,而极大地提高了界面内容改变时的刷新效率。
所以把上面的三条压缩总结一下,硬件加速更快的原因有两条:
- 用了 GPU,绘制变快了;
- 绘制机制的改变,导致界面内容改变时的刷新效率极大提高。
3.8.3 限制
如果仅仅是这样,硬件加速只有好处没有缺陷,那大家都不必关心硬件加速了,这篇文章也不会出现:既然是好东西就用呗,关心那么多原理干吗?
可事实就是,硬件加速不只是好处,也有它的限制:受到 GPU 绘制方式的限制,Canvas 的有些方法在硬件加速开启时会失效或无法正常工作。比如,在硬件加速开启时, clipPath() 在 API 18 及以上的系统中才有效。具体的 API 限制和 API 版本的关系如下图:
所以,如果你的自定义控件中有自定义绘制的内容,最好参照一下这份表格,确保你的绘制操作可以正确地在所有用户的手机里能够正常显示,而不是只在你的运行了最新版本 Android 系统的 Nexus 或 Pixel 里测试一遍没问题就发布了。小心被祭天。
不过有一点可以放心的是,所有的原生自带控件,都没有用到 API 版本不兼容的绘制操作,可以放心使用。所以你只要检查你写的自定义绘制就好。
3.8.4 View Layer
在之前几期的内容里我提到过几次,如果你的绘制操作不支持硬件加速,你需要手动关闭硬件加速来绘制界面,关闭的方式是通过这行代码:
view.setLayerType(LAYER_TYPE_SOFTWARE, null);
有不少人都有过疑问:什么是 layer type?如果这个方法是硬件加速的开关,那么它的参数为什么不是一个 LAYER_TYPE_SOFTWARE 来关闭硬件加速以及一个 LAYER_TYPE_HARDWARE 来打开硬件加速这么两个参数,而是三个参数,在 SOFTWARE 和 HARDWARE 之外还有一个 LAYER_TYPE_NONE?难道还能既不用软件绘制,也不用硬件绘制吗?
事实上,这个方法的本来作用并不是用来开关硬件加速的,只是当它的参数为 LAYER_TYPE_SOFTWARE 的时候,可以「顺便」把硬件加速关掉而已;并且除了这个方法之外,Android 并没有提供专门的 View 级别的硬件加速开关,所以它就「顺便」成了一个开关硬件加速的方法。
setLayerType() 这个方法,它的作用其实就是名字里的意思:设置 View Layer 的类型。所谓 View Layer,又称为离屏缓冲(Off-screen Buffer),它的作用是单独启用一块地方来绘制这个 View ,而不是使用软件绘制的 Bitmap 或者通过硬件加速的 GPU。这块「地方」可能是一块单独的 Bitmap,也可能是一块 OpenGL 的纹理(texture,OpenGL 的纹理可以简单理解为图像的意思),具体取决于硬件加速是否开启。采用什么来绘制 View 不是关键,关键在于当设置了 View Layer 的时候,它的绘制会被缓存下来,而且缓存的是最终的绘制结果,而不是像硬件加速那样只是把 GPU 的操作保存下来再交给 GPU 去计算。通过这样更进一步的缓存方式,View 的重绘效率进一步提高了:只要绘制的内容没有变,那么不论是 CPU 绘制还是 GPU 绘制,它们都不用重新计算,而只要只用之前缓存的绘制结果就可以了。
多说一句,其实这个离屏缓冲(Off-screen Buffer),更准确的说应该叫做离屏缓存(Off-screen Cache)会更合适一点。原因在上面这一段里已经说过了,因为它其实是缓存而不是缓冲。(这段话仅代表个人意见)
基于这样的原理,在进行移动、旋转等(无需调用 invalidate())的属性动画的时候开启 Hardware Layer 将会极大地提升动画的效率,因为在动画过程中 View 本身并没有发生改变,只是它的位置或角度改变了,而这种改变是可以由 GPU 通过简单计算就完成的,并不需要重绘整个 View。所以在这种动画的过程中开启 Hardware Layer,可以让本来就依靠硬件加速而变流畅了的动画变得更加流畅。实现方式大概是这样:
view.setLayerType(LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setLayerType(LAYER_TYPE_NONE, null);
}
});
animator.start();
或者如果是使用 ViewPropertyAnimator,那么更简单:
view.animate()
.rotationY(90)
.withLayer(); // withLayer() 可以自动完成上面这段代码的复杂操作
不过一定要注意,只有你在对 translationX translationY rotation alpha 等无需调用 invalidate() 的属性做动画的时候,这种方法才适用,因为这种方法本身利用的就是当界面不发生时,缓存未更新所带来的时间的节省。所以简单地说——
这种方式不适用于基于自定义属性绘制的动画。一定记得这句话。
另外,除了用于关闭硬件加速和辅助属性动画这两项功能外,Layer 还可以用于给 View 增加一些绘制效果,例如设置一个 ColorMatrixColorFilter 来让 View 变成黑白的:
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
view.setLayerType(LAYER_TYPE_HARDWARE, paint);
另外,由于设置了 View Layer 后,View 在初次绘制时以及每次 invalidate() 后重绘时,需要进行两次的绘制工作(一次绘制到 Layer,一次从 Layer 绘制到显示屏),所以其实它的每次绘制的效率是被降低了的。所以一定要慎重使用 View Layer,在需要用到它的时候再去使用。
3.8.5 总结
硬件加速指的是使用 GPU 来完成绘制的计算工作,代替 CPU。它从工作分摊和绘制机制优化这两个角度提升了绘制的速度。
硬件加速可以使用 setLayerType() 来关闭硬件加速,但这个方法其实是用来设置 View Layer 的:
- 参数为 LAYER_TYPE_SOFTWARE 时,使用软件来绘制 View Layer,绘制到一个 Bitmap,并顺便关闭硬件加速;
- 参数为 LAYER_TYPE_HARDWARE 时,使用 GPU 来绘制 View Layer,绘制到一个 OpenGL texture(如果硬件加速关闭,那么行为和 VIEW_TYPE_SOFTWARE 一致);
- 参数为 LAYER_TYPE_NONE 时,关闭 View Layer。
View Layer 可以加速无 invalidate() 时的刷新效率,但对于需要调用 invalidate() 的刷新无法加速。
View Layer 绘制所消耗的实际时间是比不使用 View Layer 时要高的,所以要慎重使用。