Android自定义圆形按钮点击进度动画

hloong 于 2015-04-18 发布

拿来就可用的项目下载地址:https://github.com/hloong/CricleBarDemo

一个月前我去面试的时候被人问到一个类似的问题,当时没答上来,一回家找了下资料:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0325/1603.html, 根据上面链接,我自己改了下半小时后实现了比较粗糙的效果,如下图,截图不太好见谅哈~ 这里写图片描述

具体分解成4个部分: 底部一个灰色的空心描边圈,中间一个文字,2边各一个半圆来做向上的动画;

具体代码:

public class CircleBarTwoSider extends View {
    private RectF mColorWheelRectangle = new RectF();//圆圈的矩形范围
    private Paint mDefaultWheelPaint;  //绘制底部灰色圆圈的画笔
    private Paint mColorWheelPaintLeft; //绘制蓝色扇形的画笔 左边的
    private Paint mColorWheelPaintRight; //绘制蓝色扇形的画笔 右边的
    
    private Paint textPaint; //中间文字的画笔
    private float mColorWheelRadius; //圆圈普通状态下的半径
    private float circleStrokeWidth; //圆圈的线条粗细 

    private float pressExtraStrokeWidth;//按下状态下增加的圆圈线条增加的粗细   

    private String mText;//中间文字内容
    private int mCount; //为了达到数字增加效果而添加的变量,他和mText其实代表一个意思
    private float mSweepAnglePer;  //为了达到蓝色扇形增加效果而添加的变量,他和mSweepAngle其实代表一个意思   

    private float mSweepAngle; //扇形弧度
    private int mTextSize;//文字颜色
    BarAnimation anim;//动画类
    private int TIME = 300;
    
    public CircleBarTwoSider(Context context) {
        super(context);
        init(null, 0);
    }
    public CircleBarTwoSider(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }
    public CircleBarTwoSider(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    
    private void init(AttributeSet attrs, int defStyle) {
        circleStrokeWidth = MyUtils.dip2px(getContext(), 10);
        pressExtraStrokeWidth = MyUtils.dip2px(getContext(), 2);
        mTextSize = MyUtils.dip2px(getContext(), 20);
                                                                          
        mColorWheelPaintRight = new Paint(Paint.ANTI_ALIAS_FLAG);
        mColorWheelPaintRight.setColor(0xFF29a6f6);
        mColorWheelPaintRight.setStyle(Paint.Style.STROKE);
        mColorWheelPaintRight.setStrokeMiter(0);
//        mColorWheelPaintRight.setStrokeCap(Paint.Cap.ROUND);//开启显示边缘为圆形
        mColorWheelPaintRight.setStrokeWidth(circleStrokeWidth);
        
        mColorWheelPaintLeft = new Paint(Paint.ANTI_ALIAS_FLAG);
        mColorWheelPaintLeft.setColor(0xFF29a6f6);
        mColorWheelPaintLeft.setStyle(Paint.Style.STROKE);
        mColorWheelPaintLeft.setStrokeMiter(0);
//        mColorWheelPaintLeft.setStrokeCap(Paint.Cap.ROUND);//开启显示边缘为圆形
        mColorWheelPaintLeft.setStrokeWidth(circleStrokeWidth);
                                                                          
        mDefaultWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDefaultWheelPaint.setColor(0xFFeeefef);
        mDefaultWheelPaint.setStyle(Paint.Style.STROKE);
        mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);
        textPaint.setColor(0xFF333333);
        textPaint.setStyle(Style.FILL_AND_STROKE);
        textPaint.setTextAlign(Align.LEFT);
        textPaint.setTextSize(mTextSize);
                                                                          
        mText = "0";
        mSweepAngle = 1;
                                                                          
        anim = new BarAnimation();
        anim.setDuration(TIME);//设置动画时常
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        canvas.drawArc(mColorWheelRectangle, -90, 360, false, mDefaultWheelPaint);
        canvas.drawArc(mColorWheelRectangle, 90, mSweepAnglePer, false, mColorWheelPaintRight);
        canvas.drawArc(mColorWheelRectangle, 90, -mSweepAnglePer, false, mColorWheelPaintLeft);
        Rect bounds = new Rect();
        String textstr="可以买:"+mCount+"";
        textPaint.getTextBounds(textstr, 0, textstr.length(), bounds);
        canvas.drawText(textstr+"", (mColorWheelRectangle.centerX()) - (textPaint.measureText(textstr) / 2),
                mColorWheelRectangle.centerY() + bounds.height() / 2,textPaint);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // TODO Auto-generated method stub
        int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        int min = Math.min(width, height);
        setMeasuredDimension(min, min);
        mColorWheelRadius = min - circleStrokeWidth -pressExtraStrokeWidth ;
        mColorWheelRectangle.set(circleStrokeWidth+pressExtraStrokeWidth, circleStrokeWidth+pressExtraStrokeWidth,
                mColorWheelRadius, mColorWheelRadius);
    }
    
    @Override
    public void setPressed(boolean pressed) {
        // TODO Auto-generated method stub
        Log.i("hloong","call setPressed ");
        if (pressed) {
            mColorWheelPaintLeft.setColor(0xFF165da6);
            mColorWheelPaintRight.setColor(0xFF165da6);
            textPaint.setColor(0xFF070707);
            mColorWheelPaintLeft.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
            mColorWheelPaintRight.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
            mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);
            textPaint.setTextSize(mTextSize-pressExtraStrokeWidth);
        } else {
            mColorWheelPaintLeft.setColor(0xFF29a6f6);
            mColorWheelPaintRight.setColor(0xFF29a6f6);
            textPaint.setColor(0xFF333333);
            mColorWheelPaintLeft.setStrokeWidth(circleStrokeWidth);
            mColorWheelPaintRight.setStrokeWidth(circleStrokeWidth);
            mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);
            textPaint.setTextSize(mTextSize);
        }
        super.setPressed(pressed);
        this.invalidate();
    }
    
    public void startCustomAnimation(){
        this.startAnimation(anim);
        
    }
                                                                      
    public void setText(String text){
        mText = text;
        this.startAnimation(anim);
    }
                                                                      
    public void setSweepAngle(float sweepAngle){
        mSweepAngle = sweepAngle;
    }
               
    
    /**
     * 动画
     * @author long 
     * 2015-3-20下午2:11:22
     */
    public class BarAnimation extends Animation {
        /**
         * Initializes expand collapse animation, has two types, collapse (1) and expand (0).
         * @param view The view to animate
         * @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml.
         * 1 will collapse view and set to gone
         * 动画类利用了applyTransformation参数中的interpolatedTime参数(从0到1)的变化特点,
         * 实现了该View的某个属性随时间改变而改变。原理是在每次系统调用animation的applyTransformation()方法时,
         * 改变mSweepAnglePer,mCount的值,
         * 然后调用postInvalidate()不停的绘制view。
         */
        public BarAnimation() {
        }
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            //mSweepAnglePer,mCount这两个属性只是动画过程中要用到的临时属性,
            //mText和mSweepAngle才是动画结束之后表示扇形弧度和中间数值的真实值。
            if (interpolatedTime < 1.0f) {
                mSweepAnglePer =  interpolatedTime * mSweepAngle;
                mCount = (int)(interpolatedTime * Float.parseFloat(mText));
            } else {
                mSweepAnglePer = mSweepAngle;
                mCount = Integer.parseInt(mText);
            }
            postInvalidate();
        }
    }
}

Activity的代码:

public class MainActivity extends Activity {
    private CircleBarTwoSider circleBar;
    private CircleBar circleBar2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        circleBar = (CircleBarTwoSider) findViewById(R.id.circle);
        circleBar.setSweepAngle(120);//设置角度
        circleBar.setText("27000");
        circleBar.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View view){
                circleBar.startCustomAnimation();
            }
        });
        
        circleBar2 = (CircleBar) findViewById(R.id.circle2);
        circleBar2.setSweepAngle(120);
        circleBar2.setText("500");
        circleBar2.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View view){
                circleBar2.startCustomAnimation();
            }
        });
    }
}