本文是《Android开发艺术探索》第7章 和hencoder 6、7 学习学习笔记
属性动画部分代码代码来自:
https://github.com/hencoder/PracticeDraw6
https://github.com/hencoder/PracticeDraw7
具体可访问:http://hencoder.com/ui-1-6/
http://hencoder.com/ui-1-7/
一 、View 动画基础概念
使用view动画创建xml文件,路径:res/anim/filename.xml
1.alpha
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- <alpha> 标签标识透明度动画,对应AlphAnimation 它可以改变view的透明度
fromAlpha 表示透明度的起始值
toAlpha 表示透明度的结束值
duration 动画的持续时间
-->
<alpha
android:duration="1000"
android:fromAlpha="0.1"
android:toAlpha="1.0" >
</alpha>
</set>
2.rotate
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!--
插值器,插值器影响动画的速度 。
默认为android:anim/accelerate_decelerate_interpolator
轴点 :默认情况下 轴点为view 的中心,这个时候缩放会导致view
向左右两个方向进行缩放,但是如果把轴点设为view的右边界,那么view将会向左边进行缩放
fromDegrees 旋转开始的角度
toDegrees 旋转结束的角度
pivotX 旋转的轴点的x坐标
pivotY 旋转的轴点的y坐标
-->
<rotate
android:duration="1000"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="+360" />
</set>
3.scale
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!--
fromXScale 水平缩放的起始值
toXScale 水平缩放的结束值
fromYScale 垂直缩放的起始值
toYScale 垂直缩放的结束值
fillAfter 动画结束后view是否停留在结束位置,true表示停留在结束为止。
-->
<scale
android:duration="2000"
android:fillAfter="false"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="0%"
android:pivotY="0%"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
4.translate 平移动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- set 表示动画集合 对应AniimatioSet类
interpolator
shapInterpolator:表示集合中是否共享插值器,如果集合不指定插值器,
那么子动画就需要单独指定插值器或使用默认的插值器。
-->
<translate
android:duration="1000"
android:fromXDelta="10"
android:fromYDelta="10"
android:toXDelta="100"
android:toYDelta="100" />
</set>
5.帧动画
帧动画是顺序播放一组预先定义好的图片,类似于放电影。
系统 提供一个AnimationDrawable来使用帧动画。
备注:使用帧动画容易产生oom,因此应避免使用过多尺寸过大的图片。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!--
帧动画是顺序播放一组预先定义好的图片,类似于放电影。
系统 提供一个AnimationDrawable来使用帧动画。
备注:使用帧动画容易产生oom,因此应避免使用过多尺寸过大的图片
-->
<item
android:drawable="@drawable/one"
android:duration="500"/>
<item
android:drawable="@drawable/two"
android:duration="500"/>
<item
android:drawable="@drawable/three"
android:duration="500"/>
<item
android:drawable="@drawable/four"
android:duration="500"/>
<item
android:drawable="@drawable/five"
android:duration="500"/>
<item
android:drawable="@drawable/six"
android:duration="500"/>
</animation-list>
然后将上述的Drawble作为view的背景并通过Drawble来播放动画。
// ImageView
image.setImageResource(R.drawable.anim_list);
// Button
frame.setBackgroundResource(R.drawable.anim_list);
AnimationDrawable drawable = (AnimationDrawable) frame
.getBackground();
drawable.start();
二、View动画的特殊使用场景
1.LayoutAnimation
layoutAnimation 作用于viewGroup,为viewGroup指定一个动画,这样的它的子元素出场时就会出现这种动画效果。这种效果常用在listview上,可以使每个item以一定的形式出现。
使用步骤:
(1)定义LayoutAnimation
<!--
layoutAnimation 作用于viewGroup,为viewGroup指定一个动画,这样的它的子元素出场时就会出现这种动画效果。这种效果常用在listview
上,可以使每个item以一定的形式出现。
delay 表示子元素开始动画的时间延迟,比如元素入场动画的时间周期为300ms,那么0.5表示每个子元素需要延长150ms才能播放如入场动画。
总体来说第一个元素延迟150ms开始播放入场动画,第二个元素延迟300ms开始边哦放入场动画,依次类推。
animationOrder 表示元素动画的顺序,有3种选项。normal(顺序显示,排在前边的元素先显示)、
reverse(逆向显示)、random(随机显示);
animation 指定入场动画。
-->
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/continue_anim"
android:animationOrder="reverse"
android:delay="0.5" />
(2)指定入场动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<alpha
android:duration="3000"
android:fromAlpha="0.2"
android:toAlpha="1.0" />
<alpha
android:duration="3000"
android:fromAlpha="1.0"
android:startOffset="3000"
android:toAlpha="0.2" />
</set>
(3)为viewGroup指定 android:layoutAnimation属性:android:layoutAnimation=”@anim/continue_anim”
对于listview来说,这样Listview的item就具有出场动画了。这个方式适合所有的viewGroup。
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="@anim/continue_anim"
android:background="#fff4f7f9"
android:cacheColorHint="#00000000"
android:divider="#dddbdb"
android:dividerHeight="1.0px"
android:listSelector="@android:color/transparent" />
除了在xml中指定android:layoutAnimation外,还可以通过LayoutAnimationController来实现。
LayoutAnimationController lac=new LayoutAnimationController(AnimationUtils.loadAnimation(this, R.anim.zoom_in));
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(lac);
listView.startLayoutAnimation();
}
2.Activity 的切换效果
Activity 有默认的切换效果,但是这个切换效果是可以自定义的。主要用到 overridePendingTransition(int enterAnim, int exitAnim) enterAnim Activity被打开时,所需的动画id,exitAnim Activity Activity被暂停时,所需的动画id.
启动一个Activity。
备注:overridePendingTransition必须位于startActivity或者放finish()后面,否则动画将不起作用。
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
startActivity(intent);
overridePendingTransition(R.anim.zoom_in, R.anim.zoom_out);
```
当Activity退出时,也可以为其指定切换效果
```
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.zoom_in, R.anim.zoom_out);
}
属性动画
属性动画可以对任意属性进行动画而不仅仅是View,动画默认的时间间隔是300ms,默认帧率是10ms/帧。其可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值得改变。但是属性动画API11才有,如果要兼容可以使用nineoldandroids http://nineoldandroids.com/ nineoldandroids 对属性动画做了兼容,在API11前的版本其内部是通过代理View动画来实现的。
translationX、translationY、translationZ
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
switch (translationState) {
case 0:
imageView.animate().translationX(Utils.dpToPixel(100));
break;
case 1:
imageView.animate().translationX(0);
break;
case 2:
imageView.animate().translationY(Utils.dpToPixel(50));
break;
case 3:
imageView.animate().translationY(0);
break;
case 4:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
imageView.animate().translationZ(Utils.dpToPixel(15));
}
break;
case 5:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
imageView.animate().translationZ(0);
}
break;
}
translationState++;
if (translationState == translationStateCount) {
translationState = 0;
}
}
});
rotation
旋转
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
switch (state) {
case 0:
imageView.animate().rotation(180);
break;
case 1:
imageView.animate().rotation(0);
break;
case 2:
imageView.animate().rotationX(180);
break;
case 3:
imageView.animate().rotationX(0);
break;
case 4:
imageView.animate().rotationY(180);
break;
case 5:
imageView.animate().rotationY(0);
break;
}
state++;
if (state == 6) {
state = 0;
}
}
});
scale缩放
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
switch (state) {
case 0:
imageView.animate().scaleX(1.5f);
break;
case 1:
imageView.animate().scaleX(1);
break;
case 2:
imageView.animate().scaleY(.5f);
break;
case 3:
imageView.animate().scaleY(1);
break;
}
state++;
if (state == 4) {
state = 0;
}
}
});
Alpha
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
switch (state) {
case 0:
imageView.animate().alpha(0);
break;
case 1:
imageView.animate().alpha(1);
break;
}
state++;
if (state == 2) {
state = 0;
}
}
MultiProperties
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt = (Button) findViewById(R.id.animateBt);
imageView = (ImageView) findViewById(R.id.imageView);
imageView.setScaleX(0);
imageView.setScaleY(0);
imageView.setAlpha(0f);
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!animated) {
imageView.animate()
.translationX(Utils.dpToPixel(200))
.rotation(360)
.scaleX(1)
.scaleY(1)
.alpha(1);
} else {
imageView.animate()
.translationX(0)
.rotation(0)
.scaleX(0)
.scaleY(0)
.alpha(0);
}
animated = !animated;
}
});
}
Duration
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switch (translationState) {
case 0:
imageView.animate().translationX(Utils.dpToPixel(100)).setDuration(duration);
break;
case 1:
imageView.animate().translationX(0).setDuration(duration);
break;
}
if (translationState < 1) {
translationState++;
} else {
translationState = 0;
}
}
});
Interpolator
差值器:作用是根据当前属性改变的百分比来计算改变后的属性值。
代码来自:https://github.com/hencoder/PracticeDraw6
具体可访问:http://hencoder.com/ui-1-6/
public class Sample07Interpolator extends LinearLayout {
Spinner spinner;
Button animateBt;
ImageView imageView;
Interpolator[] interpolators = new Interpolator[13];
Path interpolatorPath;
public Sample07Interpolator(Context context) {
super(context);
}
public Sample07Interpolator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public Sample07Interpolator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
interpolatorPath = new Path();
interpolatorPath.lineTo(0.25f, 0.25f);
interpolatorPath.moveTo(0.25f, 1.5f);
interpolatorPath.lineTo(1, 1);
interpolators[0] = new AccelerateDecelerateInterpolator();
interpolators[1] = new LinearInterpolator();
interpolators[2] = new AccelerateInterpolator();
interpolators[3] = new DecelerateInterpolator();
interpolators[4] = new AnticipateInterpolator();
interpolators[5] = new OvershootInterpolator();
interpolators[6] = new AnticipateOvershootInterpolator();
interpolators[7] = new BounceInterpolator();
interpolators[8] = new CycleInterpolator(0.5f);
interpolators[9] = PathInterpolatorCompat.create(interpolatorPath);
interpolators[10] = new FastOutLinearInInterpolator();
interpolators[11] = new FastOutSlowInInterpolator();
interpolators[12] = new LinearOutSlowInInterpolator();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
spinner = (Spinner) findViewById(R.id.interpolatorSpinner);
animateBt = (Button) findViewById(R.id.animateBt);
imageView = (ImageView) findViewById(R.id.imageView);
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
imageView.animate()
.translationX(Utils.dpToPixel(150))
.setDuration(600)
.setInterpolator(interpolators[spinner.getSelectedItemPosition()])
.withEndAction(new Runnable() {
@Override
public void run() {
imageView.postDelayed(new Runnable() {
@Override
public void run() {
imageView.setTranslationX(0);
}
}, 500);
}
});
}
});
}
}
ObjectAnimator
属性动画要求动画的作用对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始化值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切的说是随着时间的推移,所传递的值越来越接近最终值
动画对应的属性实现get 和set方法
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
view = (Sample08ObjectAnimatorView) findViewById(R.id.objectAnimatorView);
animateBt = (Button) findViewById(R.id.animateBt);
animateBt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
animator.setDuration(1000);
animator.setInterpolator(new FastOutSlowInInterpolator());
animator.start();
}
});
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
public class Sample08ObjectAnimatorView extends View {
final float radius = dpToPixel(80);
float progress = 0;
public float getProgress() {
return progress;
}
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
或者用一个类来包装原始对象,间接为其提供get和set方法
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
private void performAnimate() {
ViewWrapper viewWrapper = new ViewWrapper(btnTanslate);
ObjectAnimator.ofInt(viewWrapper, "width", 500).setDuration(1000).start();
}
private static class ViewWrapper {
private View mTarget;
public ViewWrapper(View target){
this.mTarget = target;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
ArgbEvaluator
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(2000);
animator.start();
自定义 Evaluator
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
private class HsvEvaluator implements TypeEvaluator<Integer> {
float[] startHsv = new float[3];
float[] endHsv = new float[3];
float[] outHsv = new float[3];
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
// 把 ARGB 转换成 HSV
Color.colorToHSV(startValue, startHsv);
Color.colorToHSV(endValue, endHsv);
// 计算当前动画完成度(fraction)所对应的颜色值
if (endHsv[0] - startHsv[0] > 180) {
endHsv[0] -= 360;
} else if (endHsv[0] - startHsv[0] < -180) {
endHsv[0] += 360;
}
outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
if (outHsv[0] > 360) {
outHsv[0] -= 360;
} else if (outHsv[0] < 0) {
outHsv[0] += 360;
}
outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;
// 计算当前动画完成度(fraction)所对应的透明度
int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);
// 把 HSV 转换回 ARGB 返回
return Color.HSVToColor(alpha, outHsv);
}
}
ofObject()
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(1000);
animator.start();
PropertyValuesHolder 同一个动画中改变多个属性
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 0, 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 0, 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 0, 1);
ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3).start();
AnimatorSet 多个动画配合执行
代码来自http://hencoder.com/ui-1-7/
具体可访问:https://github.com/hencoder/PracticeDraw7
view.setTranslationX(-200f);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "alpha", 0, 1);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "translationX", -200, 200);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "rotation", 0, 1080);
animator3.setDuration(1000);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(animator1).before(animator2); // 先执行 1 再执行 2
animatorSet.playTogether(animator2, animator3); // 2 和 3 同时开始
animatorSet.start();