Android View动画

本文是《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();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值