Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android动画学习笔记(三)—基础动画小结

$
0
0

前言:又有一段时间没写博客了,本来打算一个星期搞定Android动画学习的,但是计划跟不上变化哈,最近在赶项目,不多说了,继续我的Android动画学习,加油!骚年~~~~

前面介绍了基础动画的一些知识,感兴趣的童鞋可以去看看我前面两篇博客,文采有点不好,凑合看吧,O(∩_∩)O哈哈~

今天主要是对前面知识的一个我个人的总结,说不定你以前也跟我犯过同样的错误哦!!大牛勿喷哈(^__^)

package com.cisetech.animationdemo.demo.practise;

/**
 * author:yinqingy
 * date:2016-11-23 14:53
 * blog:http://blog.csdn.net/vv_bug
 * desc:基础动画练习
 */

public class BasicPractiseActivity1 extends ListActivity {
    private static final String TITLE = "TITLE";
    private static final String RESID = "RESID";
    private List<Map<String,Object>> datas=new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().getDecorView().setBackgroundColor(Color.WHITE);
        getListView().setBackgroundColor(Color.DKGRAY);
        FrameLayout.LayoutParams lp= (FrameLayout.LayoutParams) getListView().getLayoutParams();
        lp.topMargin=10;
        lp.bottomMargin=10;
        lp.leftMargin=10;
        lp.rightMargin=10;
        initDatas();
        setListAdapter(new SimpleAdapter(this,datas,android.R.layout.simple_list_item_1,
                new String[]{TITLE},new int[]{android.R.id.text1}));
    }
    private void initDatas() {
        addData("alpha(从0.1到1透明度增加)", R.anim.pra_anim_alpha1);
        addData("alpha+rotate", R.anim.pra_anim_alpha_rotate);
        addData("alpha+scale", R.anim.pra_anim_alpha_scale);
        addData("alpha+scale 2", R.anim.pra_anim_alpha_scale2);
        addData("自定义动画1", R.anim.pra_anim_own_design);
        addData("slide left in", R.anim.pra_anim_slide_left);
        addData("slide right in", R.anim.pra_anim_slide_right);
        addData("slide right up", R.anim.pra_anim_slide_up);
        addData("slide right bottom", R.anim.pra_anim_slide_bottom);
        addData("Zoom enter", R.anim.pra_zoom_enter);
        addData("Zoom exit", R.anim.pra_zoom_exit);
        addData("shake test", R.anim.pra_anim_shake);
    }
    private void addData(String title,int resId){
        Map<String,Object>demo=new TreeMap<>();
        demo.put(TITLE,title);
        demo.put(RESID,resId);
        datas.add(demo);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        ListView listView=getListView();
        Animation a= AnimationUtils.loadAnimation(this, (Integer) datas.get(position).get(RESID));
        if((Integer) datas.get(position).get(RESID)==R.anim.pra_anim_shake){
            Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
            vibrator.cancel();
            long [] pattern = {100,200};   // 停止 开启
            vibrator.vibrate(pattern,-1);
        }
        listView.startAnimation(a);
    }
}

第一个要实现的效果(alpha(从0.1到1透明度增加):
这里写图片描述

显示效果为变化先快后慢的效果,我们加了一个减速插值器。

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.1"
    android:toAlpha="1"
    android:duration="3000"
  android:interpolator="@android:anim/decelerate_interpolator"
    />

代码很简单,旋转、缩放、平移的我就不演示了,小伙伴一定要多练哦,孰能生巧,上一节说了插值器的用法还有解析,那么如果我们自己要定义一个插值器该怎么做能?

我们创建一个文件叫MyInterpolator.java 实现Interpolator接口,然后重写getInterpolation方法:

package com.cisetech.animationdemo;

import android.view.animation.Interpolator;

/**
 * author:yinqingy
 * date:2016-11-28 21:35
 * blog:http://blog.csdn.net/vv_bug
 * desc:
 */

public class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        return input;
    }
}

运行代码:
这里写图片描述

可看到,跟我们前面运行时的效果是一样的,因为我们的插值器没有对动画的input做任何处理,直接返回了,所以可看出是匀速运动的。

我们改改代码,对input对一下处理:

public class MyInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        return 1-input;
    }
}

我们仅仅是返回了1-input,然后我们再次运行代码:

我就不上图了,运行效果是反的,也就是透明度从1-0.1转变了。
其它复杂的插值器我就不演示了哈,因为我也写不出来,O(∩_∩)O哈哈~!数学物理好的自己好好研究研究哈。

下面重点说一下AnimationSet(联合动画)

AnimationSet就像名字描述的那样,动画的一个集合,就是用一个集合把动画一个一个的装起来,然后同时播放,我们撸一撸它的源码:

首先把动画一个一个装起来:

 public void addAnimation(Animation a) {
        mAnimations.add(a);
        ......
    }

可以看到,第一行代码就是 mAnimations.add(a);我们看看mAnimations是个什么东西?

 private ArrayList<Animation> mAnimations = new ArrayList<Animation>();

看到了没?是不是就是一个集合呢!那么它又是怎么同时播放动画的呢?

首先AnimationSet本身就是一个动画,当我们执行view.startAnimation(set)的时候,在我们的第一节中我有带着一起走了一遍动画的代码,我们可以知道,当开始动画的时候,会调用view的invalidate()方法—->view的onDraw方法会执行—->判断view是否有动画—->动画的getTransformation方法—->对view的canvas的矩阵matrix进行改变(透明度、平移、旋转、缩放)。

我们看看AnimationSet的getTransformation方法:

  @Override
    public boolean getTransformation(long currentTime, Transformation t) {
        final int count = mAnimations.size();
        final ArrayList<Animation> animations = mAnimations;
        final Transformation temp = mTempTransformation;

        boolean more = false;
        boolean started = false;
        boolean ended = true;

        t.clear();

        for (int i = count - 1; i >= 0; --i) {
            final Animation a = animations.get(i);

            temp.clear();
            more = a.getTransformation(currentTime, temp, getScaleFactor()) || more;
            t.compose(temp);

            started = started || a.hasStarted();
            ended = a.hasEnded() && ended;
        }

        if (started && !mStarted) {
            if (mListener != null) {
                mListener.onAnimationStart(this);
            }
            mStarted = true;
        }

        if (ended != mEnded) {
            if (mListener != null) {
                mListener.onAnimationEnd(this);
            }
            mEnded = ended;
        }

        return more;
    }

我们看到,有一个for循环,然后同时播放集合中的所有动画。
既然里面放的是集合,那么有一些方法一定是作用于所有的Animation的:
我们看到了一个执行动画前初始化动画的方法:

   /**
     * @see android.view.animation.Animation#initialize(int, int, int, int)
     */
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        }

那么initialize又是谁去初始化的呢? 想必小伙伴猜都猜到了,肯定是view调用的,于是我们到view中去搜索一下,

在view中我们找到了这么一段代码:

 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();
        }

看到了这,小伙伴是不是有点明白了!!

那么我们的AnimationSet初始化的时候都干了什么呢?

   boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
        boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK;
        boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK;
        boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK;
        boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK)
                == PROPERTY_SHARE_INTERPOLATOR_MASK;
        boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
                == PROPERTY_START_OFFSET_MASK;

首先判断我们的AnimationSet中有没有设置这些属性,如:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:startOffset="3000"
    >

我们在set标签中给了两个属性android:duration跟android:startOffset,然后AnimationSet拿到这两个属性后,就会走initialize方法,然后就会把属性值赋值给所有的Animation:

 for (int i = 0; i < count; i++) {
            Animation a = children.get(i);
            if (durationSet) {
                a.setDuration(duration);
            }
            if (fillAfterSet) {
                a.setFillAfter(fillAfter);
            }
            if (fillBeforeSet) {
                a.setFillBefore(fillBefore);
            }
            if (repeatModeSet) {
                a.setRepeatMode(repeatMode);
            }
            if (shareInterpolator) {
                a.setInterpolator(interpolator);
            }
            if (startOffsetSet) {
                long offset = a.getStartOffset();
                a.setStartOffset(offset + startOffset);
                storedOffsets[i] = offset;
            }
            a.initialize(width, height, parentWidth, parentHeight);
        }

这里写图片描述

效果很简单,我直接上代码了:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    >
    <alpha
        android:fromAlpha="0.5"
        android:toAlpha="1"
        />
    <rotate
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

然后我们想让alpha执行完毕后再去执行rotate,我们改改代码:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    >
    <alpha
        android:fromAlpha="0.5"
        android:toAlpha="1"
        />
    <rotate
        android:startOffset="3000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

让rotate在3s后再运行:
这里写图片描述

效果是达到了,然后我们又想让我们的透明度从0-1然后再执行1-0,然后再执行rotate,我们改改代码:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    >
    <alpha
        android:fromAlpha="0.5"
        android:toAlpha="1"
        android:repeatCount="1"
        android:repeatMode="reverse"
        />
    <rotate
        android:startOffset="3000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

我们再次运行代码:
这里写图片描述

我们发现,透明度从0-1是对的,然后我们的透明度1-0是跟rotate动画一起执行的。

我们发现问题?repeatCount设置成了几的话此动画就会连续播放几次,也就是说我们的alpha执行完0.5-1的时候已经花了3s了,然后当再执行1-0.5的时候跟rotate一起执行了,所以按照我们现在的说法的话,我们要达到我们一开始说的那种效果的话(执行完0-1然后执行完1-0 最后执行rotate动画)我们需要把rotate的延时改为6s:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    >
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:repeatCount="1"
        android:repeatMode="reverse"
        />
    <rotate
        android:startOffset="6000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

我们再次运行代码:
这里写图片描述

我们可以看到,执行了0-1然后1-0,但是rotate不执行了。。。 尼玛!!!这是什么情况? 因为当我们执行rotate的时候,此时我们view的canvas的状态为透明度0,也就是说隐藏了,,,所以此时执行rotate的话,默认是看不到的!!!

为了验证我们的推测,我们改改最后执行的透明度值,把0-1 1-0改为0.2-1 1-0.2

再次执行代码:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    >
    <alpha
        android:fromAlpha="0.2"
        android:toAlpha="1"
        android:repeatCount="1"
        android:repeatMode="reverse"
        />
    <rotate
        android:startOffset="6000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        />
</set>

这里写图片描述

怎么样?是不是跟我们预期效果是一样的呢??

最后调查一下:有朋友把repeatCount=1看做成“一来一回”然后repeatCount=2看做成“一来一回,一来一回,往返两次”的请举手!!!O(∩_∩)O哈哈~ 我最初就是这么以为的,直到写博客前还这么认为,所以有些东西还是得多敲多练哈,不能只凭字面上掌握了就说自己掌握了额

demo还有一些我就不演示了,我已经提交到github了,包括前面两篇文章的demo

https://github.com/913453448/AnDemo

作者:vv_bug 发表于2016/11/28 22:53:27 原文链接
阅读:18 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>