前言:又有一段时间没写博客了,本来打算一个星期搞定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