android动画效果

http://blog.csdn.net/L_wwbs/article/details/53408830

本篇通过自定义View模拟一个物理现象——竖直平面内小球在最低点以一定初速度在重力作用下绕圆环做变速圆周运动的效果(从最低点减速到0时上升到最高点再加速到初始速度时回到最低点)。可以用于加载等待等场景,下面按照自定义View的步骤具体说明一下。

1、定义属性

为了能在布局文件中使用我们的自定义控件,定制其属性,我们需要自定义一些控件的属性,以供更灵活的使用此控件。

 

[java] view plain copy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <attr name="ringColor" format="color"></attr>  
  4.     <attr name="ringWidth" format="dimension"></attr>  
  5.     <attr name="globuleColor" format="color"></attr>  
  6.     <attr name="globuleRadius" format="dimension"></attr>  
  7.     <attr name="cycleTime" format="float"></attr>  
  8.     <declare-styleable name="AccelerateCircularView">  
  9.         <attr name="ringColor" />  
  10.         <attr name="ringWidth" />  
  11.         <attr name="globuleColor" />  
  12.         <attr name="globuleRadius" />  
  13.         <attr name="cycleTime" />  
  14.     </declare-styleable>  
  15. </resources>  
  16. 2、获取属性

    自定义属性完成后,我们需要在自定义View的构造方法中逐一获取这些属性。
    [java] view plain copy
     
    1. public AccelerateCircularView(Context context) {  
    2.        this(context, null);  
    3.    }  
    4.   
    5.    public AccelerateCircularView(Context context, AttributeSet attrs) {  
    6.        this(context, attrs, 0);  
    7.    }  
    8.   
    9.    public AccelerateCircularView(Context context, AttributeSet attrs,  
    10.                                  int defStyle) {  
    11.        super(context, attrs, defStyle);  
    12.        TypedArray attrsArray = context.getTheme().obtainStyledAttributes(  
    13.                attrs, R.styleable.AccelerateCircularView, defStyle, 0);  
    14.        mRingColor = attrsArray.getColor(  
    15.                R.styleable.AccelerateCircularView_ringColor, Color.GRAY);  
    16.        mGlobuleColor = attrsArray.getColor(  
    17.                R.styleable.AccelerateCircularView_globuleColor, Color.BLUE);  
    18.        mRingWidth = attrsArray.getDimension(  
    19.                R.styleable.AccelerateCircularView_ringWidth, TypedValue  
    20.                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,  
    21.                                getResources().getDisplayMetrics()));  
    22.        mGlobuleRadius = attrsArray.getDimension(  
    23.                R.styleable.AccelerateCircularView_globuleRadius, TypedValue  
    24.                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6,  
    25.                                getResources().getDisplayMetrics()));  
    26.        mCycleTime = attrsArray.getFloat(  
    27.                R.styleable.AccelerateCircularView_cycleTime, 3000);  
    28.        attrsArray.recycle();  
    29.        mPaint = new Paint();  
    30.   
    31.    }  
    这里要注意通过attrsArray.recycle()及时回收TypedArray ,避免浪费内存资源 。

    2、重写onMeasure

    [java] view plain copy
     
    1. @Override  
    2.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    3.         int mWidth , mHeight ;  
    4.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
    5.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
    6.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
    7.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
    8.         if (widthMode == MeasureSpec.EXACTLY) {  
    9.             mWidth = widthSize;  
    10.         } else {  
    11.             mWidth = 169;  
    12.             if (widthMode == MeasureSpec.AT_MOST) {  
    13.                 mWidth = Math.min(mWidth, widthSize);  
    14.             }  
    15.   
    16.         }  
    17.         if (heightMode == MeasureSpec.EXACTLY) {  
    18.             mHeight = heightSize;  
    19.         } else {  
    20.             mHeight = 169;  
    21.             if (heightMode == MeasureSpec.AT_MOST) {  
    22.                 mHeight = Math.min(mWidth, heightSize);  
    23.             }  
    24.   
    25.         }  
    26.   
    27.         setMeasuredDimension(mWidth, mHeight);  
    28.   
    29.     }  

    这里主要是处理当VIew设置为“wrap_content”时需要自己给出测量结果,否则系统默认给我们测量的结果将是"match_parent"的大小。

    3、重写onDraw

    [java] view plain copy
     
    1. @Override  
    2.    protected void onDraw(Canvas canvas) {  
    3.        super.onDraw(canvas);  
    4.   
    5.        int central = Math.min(getWidth(), getHeight()) / 2;  
    6.   
    7.        mRingRadius = central - mGlobuleRadius;  
    8.   
    9.        if (mGlobuleRadius < mRingWidth / 2) {// 小球嵌在环里  
    10.            mRingRadius = central - mRingWidth / 2;  
    11.        }  
    12.        mPaint.setStrokeWidth(mRingWidth);  
    13.        mPaint.setStyle(Style.STROKE);  
    14.        mPaint.setAntiAlias(true);  
    15.        mPaint.setColor(mRingColor);  
    16.        canvas.drawCircle(central, central, mRingRadius, mPaint);// 绘制圆环  
    17.        mPaint.setStyle(Style.FILL);  
    18.        mPaint.setAntiAlias(true);  
    19.        mPaint.setColor(mGlobuleColor);  
    20.   
    21.        if (currentAngle == -1) {  
    22.            startCirMotion();  
    23.        }  
    24.        drawGlobule(canvas, central);// 绘制小球  
    25.    }  
    [java] view plain copy
     
    1. /** 
    2.    * 绘制小球,起始位置为圆环最低点 
    3.    * 
    4.    * @param canvas 
    5.    * @param central 
    6.    */  
    7.   private void drawGlobule(Canvas canvas, float central) {  
    8.   
    9.       float cx = central + (float) (mRingRadius * Math.cos(currentAngle));  
    10.       float cy = (float) (central + mRingRadius * Math.sin(currentAngle));  
    11.       canvas.drawCircle(cx, cy, mGlobuleRadius, mPaint);  
    12.   
    13.   }  
    这里完成对控件的绘制,这里根据圆环半径、当前旋转的角度结合三角函数关系来定位小球的当前坐标。其中对小球速度的控制是通过属性动画获取当前旋转角度实现。

    4、定义动画

    [java] view plain copy
     
    1. /** 
    2.     * 旋转小球 
    3.     */  
    4.    private void startCirMotion() {  
    5.        ValueAnimator animator = ValueAnimator.ofFloat(90f, 450f);//起始位置在最低点  
    6.        animator.setDuration((long) mCycleTime).setRepeatCount(  
    7.                ValueAnimator.INFINITE);  
    8.        animator.addUpdateListener(new AnimatorUpdateListener() {  
    9.            @Override  
    10.            public void onAnimationUpdate(ValueAnimator animation) {  
    11.                Float angle = (Float) animation.getAnimatedValue();  
    12.                currentAngle = angle * Math.PI / 180;  
    13.                invalidate();  
    14.            }  
    15.        });  
    16.        // animator.setInterpolator(new LinearInterpolator());// 匀速旋转  
    17.        // 自定义开始减速到0后加速到初始值的Interpolator  
    18.        animator.setInterpolator(new TimeInterpolator() {  
    19.   
    20.            @Override  
    21.            public float getInterpolation(float input) {  
    22.                float output;  
    23.                if (input < 0.5) {  
    24.                    output = (float) Math.sin(input * Math.PI) / 2;// 先加速  
    25.                } else {  
    26.                    output = 1 - (float) Math.sin(input * Math.PI) / 2;// 后减速,最高点(中间)速度为0  
    27.                }  
    28.                return output;  
    29.            }  
    30.        });  
    31.        animator.start();  
    32.    }  

    这里通过自定义Interpolator来实现对动画进度变化快慢的控制,动画设置的值为小球的当前角度。初值90°保证小球从最低点开始运动。
    http://blog.csdn.net/chjr1000/article/details/41823505
    http://blog.csdn.net/lmj623565791/article/details/45460089
posted @ 2017-11-25 02:48  小毛驴  阅读(504)  评论(0编辑  收藏  举报