Android动画(一)

(《群英传》)笔记:

1、视图动画
动画实现原理:每次绘制视图时,View所在的ViewGroup中drawChild函数获取该View的Animation的Transformation值,调用canvans.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧。如果动画没有完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。

特点:不能实现交互,元素发生视图动画后,其响应的事件依然在动画前的地方。但效率比较高且使用方便。

四种动画方式:
(1) 透明度动画(Alpha):

AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
view.startAnimation(aa);

(2)旋转动画(Rotate):

// 起始角度、旋转中心坐标
        RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);
        ra.setDuration(1000);
    view.startAnimation(ra);

位移动画(TranslateAnimation):

// (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
        TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
        ta.setDuration(1000);
    view .startAnimation(ta);

缩放动画(Scale):

        // (float fromX, float toX, float fromY, float toY)
        ScaleAnimation sa = new ScaleAnimation(0, 2, 0, 2);
        sa.setDuration(1000);
    view.startAnimation(sa);

动画集合:
通过AnimationSet,可以将动画以组合形式展现出来:

AnimationSet as = new AnimationSet(true);
        as.setDuration(1000);

        AlphaAnimation aa = new AlphaAnimation(0, 1);
        aa.setDuration(1000);
        as.addAnimation(aa);

        TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
        ta.setDuration(1000);
    as.addAnimation(ta);

对应的动画事件监听回调:

    // 可以获取到动画的开始、结束合重复事件,并针对相应的事件作出不同的处理。
        animation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }
        });

2、 属性动画:
原理:属性动画调用属性的get、set方法来真实地控制了一个View的属性值。通过对AnimatorSet和ObjectAnimator进行精细化控制的配合,只控制一个对象的一个属性值,而使用多个ObjectAnimator组合到AnimatorSet形成一个动画。ObjectAnimator调用setFrameDelay(longframeDelay)设置动画帧之间的间隙时间,调整帧率,减少动画过程中频繁绘制界面,在不影响动画效果的前提下减少CPU的资源消耗。

(1)ObjectAnimator
基本用法:

    // 操纵的View;     操纵的属性;  可变数组参数,该属性变化的一个取值过程。
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 300);

可以直接使用属性动画的属性值:
translationX和translationY:控制View对象从他的布局容器左上角坐标偏移的位置;
rotation、rotationX和rotationY:控制View对象围绕支点进行2D和3D旋转;
scaleX和scaleY:控制View对象围绕它的支点进行2D缩放;
x和y:描述了View对象在它的容器中的最终位置,它是最初的左上角坐标和translationX和translationY值得累计和;
alpha:它表示View对象的alpha透明度。默认值是1(不透明),0代表完全透明(不可见)。

如果一个属性没有get、set方法。可以通过ValueAnimator来实现。
(2)ValueAnimator
ObjectAnimator是继承自ValueAnimator的。ValueAnimator本身不提供任何动画效果,用来产生具有一定规律的数字,从而让调用者来控制动画的实现效果。

// ValueAnimator的一般使用方法
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setTarget(view);
        animator.setDuration(1000).start();
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float value = (Float) animation.getAnimatedValue();
                // TODO use the value
            }
        });

(3)PropertyValuesHolder:
组合动画。比如一个动画在平移过程中,同时改变X、Y轴的缩放。

    PropertyValuesHolder pHolder1 = PropertyValuesHolder.ofFloat("translationX", 300f);
    PropertyValuesHolder pHolder2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
    PropertyValuesHolder pHolder3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
    ObjectAnimator.ofPropertyValuesHolder(view, pHolder1, pHolder2, pHolder3).setDuration(1000);

动画事件的监听

ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
        anim.addListener(new AnimatorListener() {

            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }

            // 大部分时候只关心onAnimationEnd动画
            @Override
            public void onAnimationEnd(Animator animation) {

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }
        });
anima.start();      

Android提供了AnimatorListenerAdapter来让我们选择必要的事件进行监听。
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
            }
        });

(5)AnimatorSet
不仅能实现PropertyValuesHolder的效果,也能实现更为精确的顺序控制。

    ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
    ObjectAnimator anim2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
    ObjectAnimator anim3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
    AnimatorSet set = new AnimatorSet();
    set.setDuration(1000);
    // 通过playTogether()、playSequentially()、animSet.play().with()、before()、after()
    // 这些方法来控制多个动画的协同工作方式。
    set.playTogether(anim1, anim2, anim3);
    set.start();

(6)在XML中使用动画:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:propertyName="scaleX"
    android:valueFrom="1.0"
    android:valueTo="2.0"
    android:valueType="floatType" >

</objectAnimator>

在程序中使用XML定义的属性动画:

    Animator animator = AnimatorInflater.loadAnimator(this, R.animator.property);
    animator.setTarget(view);
    animator.start();

(7)View的animate方法:
直接驱动属性动画。是属性动画的一种简写方式。

view.animate().alpha(0).y(300).setDuration(1000)
        .withStartAction(new Runnable() {

            @Override
            public void run() {

            }
        }).withEndAction(new Runnable() {

            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                    }
                });
            }
        });

3、布局动画
作用在ViewGroup上,给ViewGroup增加View时添加一个动画过渡效果。
(1)最简单的布局动画:

Android:animateLayoutChanges="true"

(2)使用LayoutAnimationController 类来自定义一个子View的过渡效果。
示例:子View出现的时候,有一个缩放的动画效果。

 LinearLayout ll = findViewById(R.id.ll);
        //设置过渡动画
        ScaleAnimation sa = new ScaleAnimation(0, 1 ,0, 1);
        sa.setDuration(1000);
        // 设置布局动画的显示属性
        LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
        lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
        // 为ViewGroup设置布局动画
        ll.setLayoutAnimation(lac);

ORDER_NORMAL:顺序
ORDER_RANDOM:随机
ORDER_REVERSE:反序

4、 Interpolators(插值器)
可以定义动画变换速率,类似加速度,作用是控制目标变量的变化值进行对应的变化。

5、自定义动画
只需要实现它的applyTransformation的逻辑就可以了。

public class CustomAnim extends Animation {

    private int mCenterWidth;
private int mCenterHeight;
// Camera 封装了openGL的3D动画
    private Camera mCamera = new Camera();
    private float mRotateY = 0.0f;

    @Override
    public void initialize(int width,
                           int height,
                           int parentWidth,
                           int parentHeight) {

        super.initialize(width, height, parentWidth, parentHeight);
        // 设置默认时长
        setDuration(2000);
        // 动画结束后保留状态
        setFillAfter(false);
        // 设置默认插值器
        setInterpolator(new BounceInterpolator());
        mCenterWidth = width / 2;
        mCenterHeight = height / 2;
    }

    // 暴露接口-设置旋转角度
    public void setRotateY(float rotateY) {
        mRotateY = rotateY;
    }

    // 自定义动画的核心-----如何定义动画的进行过程
    //第一个参数:插值器的时间因子0~1.0, 第二个参数:矩阵的封装类。
    @Override
    protected void applyTransformation(
            float interpolatedTime,
            Transformation t) {
        final Matrix matrix = t.getMatrix();
        mCamera.save();
        // 使用Camera设置旋转的角度
        mCamera.rotateY(mRotateY * interpolatedTime);
        // 将旋转变换作用到matrix上
        mCamera.getMatrix(matrix);
        mCamera.restore();
        // 通过matrix的各种操作来实现动画
        // 通过pre方法设置矩阵作用前的偏移量来改变旋转中心
        matrix.preTranslate(mCenterWidth, mCenterHeight);
        matrix.postTranslate(-mCenterWidth, -mCenterHeight);
    }
}

模拟电视机关闭的效果:让一个图片纵向比例不断缩小。

Final Matrix matrix = t.getMartrix();
matrix.preScale(1,
1 - interpolatedTime,
// 缩放的中心点
mCenterWidth,
mCenterHeight);

7、Android动画特效
示例1:灵动菜单
思路:使用属性动画,每个不同的按钮设置不同的动画,并设置相应的插值器实现展开、合拢效果。

/**
 * 点击主菜单向四周展出子菜单的动画
 * @author Administrator
 *
 */
public class PropertyTest extends Activity implements View.OnClickListener {

    private int[] mRes = {R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,
            R.id.imageView_d, R.id.imageView_e};
    private List<ImageView> mImageViews = new ArrayList<ImageView>();
    private boolean mFlag = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.property);
        for (int i = 0; i < mRes.length; i++) {
            ImageView imageView = (ImageView) findViewById(mRes[i]);
            imageView.setOnClickListener(this);
            mImageViews.add(imageView);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.imageView_a:
                if (mFlag) {
                    startAnim();
                } else {
                    closeAnim();
                }
                break;
            default:
                Toast.makeText(PropertyTest.this, "哈哈" + v.getId(),
                        Toast.LENGTH_SHORT).show();
                break;
        }
    }

    private void closeAnim() {
        ObjectAnimator animator0 = ObjectAnimator.ofFloat(mImageViews.get(0),
                "alpha", 0.5F, 1F);
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageViews.get(1),
                "translationY", 200F, 0);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageViews.get(2),
                "translationX", 200F, 0);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageViews.get(3),
                "translationY", -200F, 0);
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageViews.get(4),
                "translationX", -200F, 0);
        AnimatorSet set = new AnimatorSet();
        set.setDuration(500);
        set.setInterpolator(new BounceInterpolator());
        set.playTogether(animator0, animator1, animator2, animator3, animator4);
        set.start();
        mFlag = true;

    }

    private void startAnim() {
        ObjectAnimator animator0 = ObjectAnimator.ofFloat(
                mImageViews.get(0),
                "alpha",
                1F,
                0.5F);
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(
                mImageViews.get(1),
                "translationY",
                200F);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(
                mImageViews.get(2),
                "translationX",
                200F);
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(
                mImageViews.get(3),
                "translationY",
                -200F);
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(
                mImageViews.get(4),
                "translationX",
                -200F);
        AnimatorSet set = new AnimatorSet();
        set.setDuration(500);
        set.setInterpolator(new BounceInterpolator());
        set.playTogether(
                animator0,
                animator1,
                animator2,
                animator3,
                animator4);
        set.start();
        mFlag = false;
    }
}

示例2:计时器(使用ValueAnimator)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:onClick="onClick"
        android:text="点我!!"
        android:textSize="60sp" />


</RelativeLayout>
/**
 * 计时器
 * @author Administrator
 *
 */
public class TimerTest extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.timer);
    }

    public void onClick(final View view) {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
        valueAnimator.addUpdateListener(
                new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 借助ValueAnimator来实现数字的不断增加,并将值设置给TextView
                ((TextView) view).setText("$ " +
                        (Integer) animation.getAnimatedValue());
            }
        });
        valueAnimator.setDuration(3000);
        valueAnimator.start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值