动画实践(1)-View动画
转载请注意:http://blog.csdn.net/wjzj000/article/details/53524646
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
第一次尝试使用Markdown编辑模式,感觉还是蛮不错的。
- View动画 :View动画,仅仅是视觉上的动画,直击View的位置并未发生变化,也就是监听事件的触发仍在原位置;
- 属性动画 :弥补View动画上View的实际位置不变的问题,但不支持3.0以下的设备(现在3.0以下应该没了吧);
接下来进入View动画的普通应用
View动画可以在视觉上实现平移,缩放,旋转,透明度的效果。 接来下进入简单的Demo实例。
xml声明
<?xml version="1.0" encoding="utf-8"?>
<!-- 持续时间1.5秒,动画结束后停留在原地 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1500"
android:fillAfter="true">
<!-- 透明度,从100%到50% -->
<alpha
android:fromAlpha="1"
android:toAlpha="0.5"/>
<!-- 移动,X轴从100像素到300像素 -->
<translate
android:fromXDelta="100"
android:toXDelta="300"/>
<!-- 缩放,X轴大小从1到1.5倍,此处要注意:XY的缩放都得有 -->
<scale
android:fromXScale="1"
android:pivotY="0"
android:pivotX="0"
android:fromYScale="1"
android:toYScale="1.5"
android:toXScale="1.5"/>
<!-- 旋转,从0度到180度,旋转轴心x,y的坐标 -->
<rotate
android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="200"
android:pivotY="200"/>
</set>
如果使用就比较简单了:
test= (TextView) findViewById(R.id.test);
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation testAnim= AnimationUtils.loadAnimation(AnimActivity.this,
R.anim.view_anim);
test.startAnimation(testAnim);
}
});
效果如下:
使用纯Java的实现方式,效果同上:
//true,创建动画集。多个动画同时执行。
set=new AnimationSet(true);
AlphaAnimation alphaAnim=new AlphaAnimation(1,0.5f);
//TranslateAnimation的构造方法里必须要传递x和y的起始以及到达值
TranslateAnimation translateAnim=new TranslateAnimation(0,300,0,0);
RotateAnimation rotateAnim=new RotateAnimation(0,180);
ScaleAnimation scaleAnim=new ScaleAnimation(1,1.5f,1,1.5f);
set.addAnimation(alphaAnim);
set.addAnimation(translateAnim);
set.addAnimation(rotateAnim);
set.addAnimation(scaleAnim);
set.setDuration(1500);
set.setFillAfter(true);
test= (TextView) findViewById(R.id.test);
test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**
* 注意!
* 此处如果使用setAnimation(set),那么并不会有效果。
* 如果移至监听事件外使用,那么将会在开始时触发动画效果。
* 所以在监听中应该使用startAnimation(set)
*/
test.startAnimation(set);
}
});
View动画监听:
AlphaAnimation alphaAnim=new AlphaAnimation(1,0.5f);
alphaAnim.setDuration(1500);
//设置重复效果,从头重新播放。REVERSE:从后往前播放,逆顺序。
alphaAnim.setRepeatMode(Animation.REVERSE);
//设置重复次数3
alphaAnim.setRepeatCount(3);
alphaAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Toast.makeText(AnimActivity.this,"透明度动画开始了!",
Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationEnd(Animation animation) {
Toast.makeText(AnimActivity.this,"透明度动画结束了!",
Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationRepeat(Animation animation) {
/**
* 此方法触发的条件:
* 设置了重复效果。
*/
Toast.makeText(AnimActivity.this,"onAnimationRepeat()我执行了!",
Toast.LENGTH_SHORT).show();
}
});
test.startAnimation(alphaAnim);
效果如下:
View动画的另一种用法
View动画除了可以控制某个控件的各种视觉效果以外,它还可以控制一些包含类的控件的动画效果。比如:ViewGroup中的子控件的动画。
xml:
<!-- animationOrder="normal"正常顺讯播放动画 -->
<!-- delay="0.5"递推是延时,效果为子view动画持续时间乘0.5 -->
<!-- animation="@anim/view_anim"设置使用子控件使用的动画文件,就是上文中的xml -->
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animationOrder="normal"
android:delay="0.5"
android:animation="@anim/view_anim"/>
<!-- 直接通过layoutAnimation="@anim/layout_anim"设置动画 -->
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layoutAnimation="@anim/layout_anim"
android:layout_height="match_parent"/>
通过java代码使用:
Animation animation=AnimationUtils.loadAnimation(this,R.anim.view_anim);
LayoutAnimationController controller=new
LayoutAnimationController(animation);
listView.setLayoutAnimation(controller);
此处加载的是view_anim,也就是说直接使用view的xml动画。
效果如下:
自定义View动画
此案例来自于《Android开发艺术探索》,通过这个案例,总结一下Matrix类。
public class Rotate3DAnimation extends Animation {
private final float fromDegrees;
private final float toDefrees;
private final float centerX;
private final float centerY;
private final float depthZ;
private final boolean reverse=false;
private Camera camera;
public Rotate3DAnimation(float fromDegrees,float toDefrees,float centerX,
float centerY,float depthZ,boolean reverse){
this.fromDegrees=fromDegrees;
this.toDefrees=toDefrees;
this.centerX=centerX;
this.centerY=centerY;
this.depthZ=depthZ;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
camera=new Camera();
}
//此方法会在整个动画过程中不断调用,其中interpolatedTime是从0-1递增的值
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float _fromDegress=fromDegrees;
float degress=_fromDegress+((toDefrees-_fromDegress))*interpolatedTime;
final float _centerX=centerX;
final float _centerY=centerY;
final Camera _camera=camera;
final Matrix matrix=t.getMatrix();
camera.save();
if (reverse){
_camera.translate(0,0,depthZ*interpolatedTime);
}else{
camera.translate(0,0,depthZ*(1-interpolatedTime));
}
_camera.rotateY(degress);
_camera.getMatrix(matrix);
_camera.restore();
matrix.preTranslate(-_centerX,-_centerY);
matrix.postTranslate(_centerX,centerY);
}
}
rotate3DAnimation=new Rotate3DAnimation(0,360,0,0,-300,true);
rotate3DAnimation.setDuration(1500);
test.startAnimation(rotate3DAnimation);
动画效果如下:
让我们简单的分析一下这几个动画出现的过程。首先我们得明确一个问题,动画出现就是View的不断的重新绘制,出现的视觉效果。让我们先从View的startAnimation方法开始入手:
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}
既然是设置动画,那么setAnimation(animation)必然是将动画对象设置进来。而invalidate(true)则通知View自身重绘,所以View的draw()必然是关键。让我们进去一探究竟!
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
//省略一部分...
//此处获取Animation对象,并且通过调用applyLegacyAnimation进行传参
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
}
//省略余下部分
}
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
Animation a, boolean scalingRequired) {
Transformation invalidationTransform;
final int flags = parent.mGroupFlags;
final boolean initialized = a.isInitialized();
if (!initialized) {
a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());
a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);
if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
onAnimationStart();
}
//省略一部分
}
到此Animation中的方法便得到了调用,也就是我们自定义View动画的initialize()初始化方法和applyTransformation()都直接或间接的在此处被调用。
OK,到此View动画出现的过程就简单的梳理了一遍!
Matrix:
这个类出现在哪?其实离我们很近,我们只需要进到View动画的源码里看一下就会发现!
@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);
}
Android中的Matrix是一个3 x 3的矩阵,一张经典的表示图:
这个矩阵其实比较好理解,前提是要仔细的看一下图中矩阵所代表的含义:
比如第一行:比例_X,错切_X(错切…先自行百度,稍后展开),移动_X。
Matrix的对图像的处理可分为四类基本变换:其中前三种是我们动画中最常用的。
- setTranslate 平移变换
- setRotate 旋转变换
- setScale 缩放变换
- setSkew 错切变换
是不是有点感觉了?这里提到的四个方法,其实就是对应上边Animation源码里的getMatrix().setXXXXX…那么如何和Matrix所代表的的3x3的矩阵想对应呢?我觉得也这里也没有必要展开了。因为上图的矩阵中位置对应的英文很明显,翻译过来就一目了然。就是通过特定的set方法,改变矩阵中对应的值。
不知道大家有没有注意到,上文那个自定义View动画里操作Matrix时用的并不是setXXX方法,而是proXXX以及postXXX。那么接下来就是这三个方法的记录总结:
- set: 用于直接设置Matrix中矩阵的值。
- pro: 计算方法就是,矩阵运算中的右乘;原矩阵*想要变换的矩阵
- post:计算方法就是,矩阵运算中的左乘;想要变换的矩阵*原矩阵
如果没记错,矩阵的计算是大学里线代的东西…忘得差不多了,其实压根也就没听过..
- C属于B右乘A,也是A左乘B。结果就是C的每行是A的每行乘B的每列。
- 注意D,左乘和右乘答案并不一样!
接下来我们进入一段
简单的应用
直接在Activity中new相关的类,然后进行操作。
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
Bitmap bitmap2 = Bitmap.createBitmap(bitmap1.getWidth() * 2,
bitmap1.getHeight() * 2, bitmap1.getConfig());
Canvas canvas = new Canvas(bitmap2);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
Matrix matrix=new Matrix();
//此行的意思:X轴缩放至原来的2倍,Y至原来的-2。-2?什么鬼!其实缩放-2的效果就是反向拉伸,但是注意!由于是反向所以Bitmap拉伸后我们是看不见的所以,有了下边的postTanslate....
matrix.setScale(2, -2);
//上文说到post相当于这个矩阵左乘远矩阵,经过计算其实我们可以得到最终的矩阵,其实就是在缩放的基础之上进行了移动。(下图附带计算答案)
matrix.postTranslate(0, bitmap1.getHeight()*2);
canvas.drawBitmap(bitmap1, matrix, paint);
iv_test.setImageBitmap(bitmap2);
###效果如下:
###矩阵答案:
PS:相关源码基本都存放于我的这个开源项目之中:
https://github.com/zhiaixinyang/PersonalCollect
尾声
那么关于其他的方法的计算,我们就可以举一反三了。
OK,到此关于View动画就正式结束,下一章将记录总结属性动画。
先记录到此,以后遇到实用的就继续加上。
最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp