主要分为View动画和属性动画两种
View动画又可分为两种:补间动画和帧动画
补间动画是对View进行一系列图像操作(如缩放,平移,旋转,改变透明度)而形成的的动画
a.补间动画支持四种类型:平移(Translate)、旋转(Rotate)、缩放(Scale)、不透明度
b. 只是显示的位置变动,View的实际位置未改变,表现为View移动到其他地方,点击事件仍在原处才能响应。(View的布局位置未变)(即只改变onDraw)
c. 组合使用步骤较复杂。
d. View Animation 也是指此动画。
1,设置方法一:在Java代码中设置
public class SecondActivity extends Activity {
private Context context;
private MyButton btnTween;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
context=SecondActivity.this;
btnTween= (MyButton) findViewById(R.id.btn_tween);
// 步骤1:创建平移动画的对象:平移动画对应的Animation子类为TranslateAnimation
// 参数分别是:
// 1. fromXDelta :视图在水平方向x 移动的起始值
// 2. toXDelta :视图在水平方向x 移动的结束值
// 3. fromYDelta :视图在竖直方向y 移动的起始值
// 4. toYDelta :视图在竖直方向y 移动的结束值
Animation translateAnimation = new TranslateAnimation(0,500,0,500);
// 固定属性的设置都是在其属性前加“set”,如setDuration()
translateAnimation.setDuration(5000);
// 步骤3:播放动画
btnTween.startAnimation(translateAnimation);
btnTween.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, btnTween.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
}
从上面的演示也可以看出补间动画执行中,焦点还停留在原地,不随动画移动而改变。
2,设置方法二:在XML中设置
在 res/anim
的文件夹里创建动画效果.xml
文件
然后再在Activity中加载xml动画文件
// 步骤1:创建 需要设置动画的 视图View
Animation translateAnimation1 = AnimationUtils.loadAnimation(this, R.anim.translate);
// 步骤2:创建 动画对象 并传入设置的动画效果xml文件
btnTween.startAnimation(translateAnimation1);
补间动画就介绍到这里,如果大家想了解更多使用方法,可以查看以下链接:
点击打开链接
在绘制的过程中,尝试获取动画在当前时刻的变换,然后应用到view的绘制中。
下面我们开始分析Animation的源码(基于Android 6.0)
/**
* Start the specified animation now.
*
* @param animation the animation to start now
*/
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
从以上代码可以看出,View先设置完Animation,然后调用invalidate进行重绘
再看下setAnimation(animation)方法
public void setAnimation(Animation animation) {
mCurrentAnimation = animation;
if (animation != null) {
// If the screen is off assume the animation start time is now instead of
// the next frame we draw. Keeping the START_ON_FIRST_FRAME start time
// would cause the animation to start when the screen turns back on
if (mAttachInfo != null && mAttachInfo.mDisplayState == Display.STATE_OFF
&& animation.getStartTime() == Animation.START_ON_FIRST_FRAME) {
animation.setStartTime(AnimationUtils.currentAnimationTimeMillis());
}
animation.reset();
}
}
/**
* When this animation should start. When the start time is set to
* {@link #START_ON_FIRST_FRAME}, the animation will start the first time
* {@link #getTransformation(long, Transformation)} is invoked. The time passed
* to this method should be obtained by calling
* {@link AnimationUtils#currentAnimationTimeMillis()} instead of
* {@link System#currentTimeMillis()}.
*
* @param startTimeMillis the start time in milliseconds
*/
public void setStartTime(long startTimeMillis) {
mStartTime = startTimeMillis;
mStarted = mEnded = false;
mCycleFlip = false;
mRepeated = 0;
mMore = true;
}
从Animation的setStartTime()的注释可以看出,该方法将调用getTransformation(long currentTime, Transformation outTransformation)
而继续往下查看代码可以发现在Animation在getTransformation中调用了以下代码:
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
mInterpolator是动画插值器,该插值器影响动画的速度
下面以TranslateAnimation和AccelerateInterpolator来说明是如何实现的
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float dx = mFromXDelta;
float dy = mFromYDelta;
if (mFromXDelta != mToXDelta) {
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
}
if (mFromYDelta != mToYDelta) {
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
}
t.getMatrix().setTranslate(dx, dy);//改变了Transformation的矩阵偏移
}
从以上代码可得出动画的变化是在applyTransformation,随着传进来的interpolatedTime,而对matrix进行相应的坐标转换,而实现动画效果的。
若有读者对于matrix不熟悉的,可以看下以下文章:
点击打开链接
到这里,View动画变换原理已经分析完了。通过实现Animation的applyTransformation方法,可以实现我们的自定义View动画。
接下来看看View是怎么绘制动画的:
在View的draw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中,可以找到以下方法:
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......省略
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); //返回值为true,表示动画还在运行
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
}
......省略
可以看到draw方法调用了a.getTransformation方法,使得相应的transformation矩阵发生了变换
接下来,还是在上述的draw(Canvas canvas, ViewGroup parent, long drawingTime) 方法中
......//省略
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1) {
alpha *= transformAlpha;
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
}
......//省略
如果支持RenderNode绘制(即硬件加速),则直接将生成的DisplayList交给GPU处理,显示到屏幕上。否则,则将执行以下流程
Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU
有关硬件加速的文章,读者可以看下美团点评开发团队以下的文章:
点击打开链接
至此,补间动画的工作原理就分析到这里结束了。
定义:
帧动画是按照固定的顺序播放一组图像而形成的动画
优缺点:
OOM
,因为会使用大量 & 尺寸较大的图片资源建议避免使用尺寸较大的图片
使用:
1,设置方法一:在Java代码中设置
animationDrawable = new AnimationDrawable();
// 为AnimationDrawable添加动画帧
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img00), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img01), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img02), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img03), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img04), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img05), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img06), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img07), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img08), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img09), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img10), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img11), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img12), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img13), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img14), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img15), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img16), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img17), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img18), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img19), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img20), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img21), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img22), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img23), 50);
animationDrawable.addFrame(
getResources().getDrawable(R.drawable.img24), 50);
// 设置为循环播放
animationDrawable.setOneShot(false);
imageView.setBackground(animationDrawable);
if (animationDrawable != null && !animationDrawable.isRunning()) {
//启动动画
animationDrawable.start();
}
2,设置方法二:在XML中设置
在 res/anim
的文件夹里创建动画效果.xml
文件
然后再在Activity中加载xml动画文件
// 通过逐帧动画的资源文件获得AnimationDrawable示例
animationDrawable = (AnimationDrawable) getResources().getDrawable(
R.drawable.frame_anim);
imageView.setBackground(animationDrawable);
//控制动画的启动
animationDrawable.start();
//停止动画
animationDrawable.stop();
下面来张帧动画的效果图:
定义:
在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果
《Android开发艺术与探索》
https://blog.csdn.net/xiaohao0724/article/details/54582965
https://www.cnblogs.com/vete-l/p/7063285.html
https://zhuanlan.zhihu.com/p/25477828