Android从零开搞系列:动画系列(1)View动画

动画实践(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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值