Android自定义view之(CSDN应用splash界面的倒计时View)

最近有点闲,准确来说是很闲,每天早上要在公车上度过一段时间,一般我就会打开csdn应用看看别人的博客,哎哟~! 看到CSDN欢迎页的倒计时view还不错哦,正好最近在学自定义View,所以就捣腾了一下,以前感觉遥不可及的东西真正做起来也不难哈,菜鸟一个,大牛勿喷^~^!!
先看一下效果图:
Android自定义view之(CSDN应用splash界面的倒计时View)_第1张图片
思路:
1、肯定是自定义个View了,偷懒下一个,为了不自己定义progress就直接继承ProgressBar,通过getProgress和getMax获取当前进度跟最大值。
2、重写onMeasure,测量View,自定义View的老套路了,测量的规则大概是这样的,如果明确了宽高就用定义的宽高,如果没有也就是wrap_content类型的时候,拿到文本也就是这里的“跳过”字的长度+一个自定义的offset偏移量的东西。
3、在ondraw里面不断的绘制,通过改变弧的startAngle绘制。
4、当达到最大值的时候开始回调(我这里为了测试通过监听动画结束来实现回调了)

atrr文件:

 <!-- countdown view-->
    <attr name="text" format="string"></attr>
    <attr name="progress_color" format="color"></attr>
    <attr name="center_bg" format="color"></attr>
    <attr name="text_color" format="color"></attr>
    <attr name="progress_width" format="dimension"></attr>
    <declare-styleable name="count_down" >
        <attr name="text" />
        <attr name="progress_color" />
        <attr name="center_bg" />
        <attr name="text_color" />
        <attr name="text_size"/>
        <attr name="progress_width" />
    </declare-styleable>

第一步:自定义一个CountDownView继承ProgressBar,照样是覆盖三个构造方法。

public class CountDownView extends ProgressBar {
/** * 结束的位置 */
    private int endAngle=270;
    private Paint mTextPaint;
    private Paint mBgPaint;
    /** * 绘画的半径 */
    private int mRadius=dp2px(30);
    /** * 进度条color默认红色 */
    private int mProgressColor = Color.RED;
    /** * 进度条宽度,默认2dp */
    private int mProgressWidth = dp2px(2);
    /** * 文本 */
    private String mText="跳过";
    /** * 文本颜色 */
    private int mTextColor=Color.WHITE;
    /** * 文本大小,默认14.5sp */
    private int mTextSize=sp2px(14.5f);
    /** * 默认中心背景色 */
    private int centerBg=Color.GRAY;
    /** * 当wrapcontent的时候 * 控件的宽高为文字的宽度加上offset */
    private float offset=15;
 public CountDownView(Context context) {
        this(context, null);
    }

    public CountDownView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /** 获取参数和初始化Paint */
        obtainStyleAttr(context, attrs, defStyleAttr);
        ////////////////测试
        setMax(100);
        startAnim();
        ///////////////测试
    }
     /** * 获取参数 * @param context * @param attrs * @param defStyleAttr */
    private void obtainStyleAttr(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.count_down, defStyleAttr, 0);
        int count=a.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr=a.getIndex(i);
            switch (attr){
                case R.styleable.count_down_center_bg:
                    centerBg=a.getColor(attr,centerBg);
                    break;
                case R.styleable.count_down_text:
                    mText=a.getString(attr);
                    break;
                case R.styleable.count_down_progress_color:
                    mProgressColor=a.getColor(attr,mProgressColor);
                    break;
                case R.styleable.count_down_text_color:
                    mTextColor=a.getColor(attr,mTextColor);
                    break;
                case R.styleable.count_down_text_size:
                    mTextSize=a.getDimensionPixelSize(attr,mTextSize);
                    break;
                case R.styleable.count_down_progress_width:
                    mProgressWidth=a.getDimensionPixelSize(attr,mProgressWidth);
                    break;
            }
        }
        a.recycle();
        mTextPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setDither(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setStyle(Paint.Style.FILL);


        mBgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mBgPaint.setDither(true);
        mBgPaint.setStyle(Paint.Style.STROKE);
        mBgPaint.setStrokeCap(Paint.Cap.ROUND);
        mBgPaint.setStrokeJoin(Paint.Join.ROUND);
        mBgPaint.setStrokeWidth(mProgressWidth);
        mBgPaint.setColor(mProgressColor);
    }
}

第二步:重写onMeasure方法告诉父控件大小

 @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /** * 当为wrap_content的时候,返回的大小为 (文本的长度+红色进度条的宽度*2+一个自定义的offset) */
        int result= (int) (getPaddingLeft()+getPaddingRight()+mTextPaint.measureText(mText)+mProgressWidth*2)+dp2px(offset);
        /** * 传入一个result,当widthMode为MeasureSpec.UNSPECIFIED的时候返回result, * 否则为设定的值 */
        MeasureSpec.UNSPECIFIED
        int width=resolveSize(result, widthMeasureSpec);
        int height=resolveSize(result,heightMeasureSpec);
        result=Math.min(width,height);
        setMeasuredDimension(result, result);
        mRadius=getMeasuredWidth()/2;

    }

点进resolveSize方法的代码你就懂了,

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case MeasureSpec.AT_MOST:
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }

是不是很熟悉。嘻嘻!~ 没事多ctrl+左键一下就可以了~

第三步:来到了最关键的代码了

 @Override
    protected synchronized void onDraw(Canvas canvas) {
        //画中间的圈
        mTextPaint.setColor(centerBg);
        canvas.drawCircle(mRadius, mRadius, mRadius-mProgressWidth/2, mTextPaint);
        //绘制文本
        mTextPaint.setColor(mTextColor);
        int baseX= (int) (mRadius-mTextPaint.measureText(mText)/2);
        int baseY= (int) (mRadius+(mTextPaint.getFontMetrics().bottom-mTextPaint.getFontMetrics().top)/2
                        -mTextPaint.getFontMetrics().bottom);
        canvas.drawText(mText, baseX, baseY, mTextPaint);
        /** * 绘制进度条, * 比如从startAngle从-90度的位置开始,endAngle为270位置(y的负轴位置) * 需要绘制的角度为 * 270--90(开始)=360; * 270-0(开始)=270 * 270-90(开始)=180 * 所以sweepAngle=startAngle-270(endAngle) */
        int startAngle= (int) ((getProgress()*1.0f/getMax())*360)-90;
        canvas.drawArc(new RectF(0+mProgressWidth/2, 0+mProgressWidth/2,mRadius*2-mProgressWidth/2, mRadius*2-mProgressWidth/2), startAngle, endAngle-startAngle, false, mBgPaint);
    }

附带sp2px,dp2px的方法

 /** * dp2px * -90 360 0 270 90 180 * @param value * @return px */
    private int dp2px(float value) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
    }

    /** * sp2px * * @param value * @return px */
    private int sp2px(float value) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, getResources().getDisplayMetrics());
    }

第四步:打工告成了,测试一下(我们这就偷偷懒,用属性动画测试了,不过开发中可以换子线程+Handler或者别的方式实现,因为属性动画不兼容低版本)

 public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainStyleAttr(context, attrs, defStyleAttr);
        ////////////////测试
        setMax(100);
        startAnim();
    }
    private void startAnim() {
        ValueAnimator a=ValueAnimator.ofInt(0, getMax() + 1);
        a.setDuration(5000);
        a.setInterpolator(new LinearInterpolator());
        a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                setProgress(value);
                postInvalidate();
            }
        });
        a.start();
        a.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Toast.makeText(getContext(), "计时完成,进入主页", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

其实很简单吧,加起来才几十行代码,以前总感觉自定义view离我是那么的远,只要动手敲敲,摸索着前进,一定会有结果的~!!!
最后附上CountDownView代码:

package com.cisetech.customer.customer.Animation;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.animation.LinearInterpolator;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.cisetech.customer.customer.R;

/** * Author:Yqy * Date:2016-08-08 * Desc:倒计时View * Company:cisetech */
public class CountDownView extends ProgressBar {
    /** * 结束的位置 */
    private int endAngle=270;
    private Paint mTextPaint;
    private Paint mBgPaint;
    /** * 绘画的半径 */
    private int mRadius=dp2px(30);
    /** * 进度条color默认红色 */
    private int mProgressColor = Color.RED;
    /** * 进度条宽度,默认2dp */
    private int mProgressWidth = dp2px(2);
    /** * 文本 */
    private String mText="跳过";
    /** * 文本颜色 */
    private int mTextColor=Color.WHITE;
    /** * 文本大小,默认14.5sp */
    private int mTextSize=sp2px(14.5f);
    /** * 默认中心背景色 */
    private int centerBg=Color.GRAY;
    /** * 当wrapcontent的时候 * 控件的宽高为文字的宽度加上offset */
    private float offset=15;

    public CountDownView(Context context) {
        this(context, null);
    }

    public CountDownView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        obtainStyleAttr(context, attrs, defStyleAttr);
        ////////////////测试
        setMax(100);
        startAnim();
    }

    private void startAnim() {
        ValueAnimator a=ValueAnimator.ofInt(0, getMax() + 1);
        a.setDuration(5000);
        a.setInterpolator(new LinearInterpolator());
        a.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                setProgress(value);
                postInvalidate();
            }
        });
        a.start();
        a.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Toast.makeText(getContext(), "完成", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

    /** * 获取参数 * @param context * @param attrs * @param defStyleAttr */
    private void obtainStyleAttr(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.count_down, defStyleAttr, 0);
        int count=a.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr=a.getIndex(i);
            switch (attr){
                case R.styleable.count_down_center_bg:
                    centerBg=a.getColor(attr,centerBg);
                    break;
                case R.styleable.count_down_text:
                    mText=a.getString(attr);
                    break;
                case R.styleable.count_down_progress_color:
                    mProgressColor=a.getColor(attr,mProgressColor);
                    break;
                case R.styleable.count_down_text_color:
                    mTextColor=a.getColor(attr,mTextColor);
                    break;
                case R.styleable.count_down_text_size:
                    mTextSize=a.getDimensionPixelSize(attr,mTextSize);
                    break;
                case R.styleable.count_down_progress_width:
                    mProgressWidth=a.getDimensionPixelSize(attr,mProgressWidth);
                    break;
            }
        }
        a.recycle();
        mTextPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setDither(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setStyle(Paint.Style.FILL);


        mBgPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
        mBgPaint.setDither(true);
        mBgPaint.setStyle(Paint.Style.STROKE);
        mBgPaint.setStrokeCap(Paint.Cap.ROUND);
        mBgPaint.setStrokeJoin(Paint.Join.ROUND);
        mBgPaint.setStrokeWidth(mProgressWidth);
        mBgPaint.setColor(mProgressColor);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /** * 当为wrap_content的时候,返回的大小为 (文本的长度+红色进度条的宽度*2+一个自定义的offset) */
        int result= (int) (getPaddingLeft()+getPaddingRight()+mTextPaint.measureText(mText)+mProgressWidth*2)+dp2px(offset);
        /** * 传入一个result,当widthMode为MeasureSpec.UNSPECIFIED的时候返回result, * 否则为设定的值 */
        int width=resolveSize(result, widthMeasureSpec);
        int height=resolveSize(result,heightMeasureSpec);
        result=Math.min(width,height);
        setMeasuredDimension(result, result);
        mRadius=getMeasuredWidth()/2;

    }
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        //画中间的圈
        mTextPaint.setColor(centerBg);
        canvas.drawCircle(mRadius, mRadius, mRadius-mProgressWidth/2, mTextPaint);
        //绘制文本
        mTextPaint.setColor(mTextColor);
        int baseX= (int) (mRadius-mTextPaint.measureText(mText)/2);
        int baseY= (int) (mRadius+(mTextPaint.getFontMetrics().bottom-mTextPaint.getFontMetrics().top)/2
                        -mTextPaint.getFontMetrics().bottom);
        canvas.drawText(mText, baseX, baseY, mTextPaint);
        /** * 绘制进度条, * 比如从startAngle从-90度的位置开始,endAngle为270位置(y的负轴位置) * 需要绘制的角度为 * 270--90(开始)=360; * 270-0(开始)=270 * 270-90(开始)=180 * 所以sweepAngle=startAngle-270(endAngle) */
        int startAngle= (int) ((getProgress()*1.0f/getMax())*360)-90;
        canvas.drawArc(new RectF(0+mProgressWidth/2, 0+mProgressWidth/2,mRadius*2-mProgressWidth/2, mRadius*2-mProgressWidth/2), startAngle, endAngle-startAngle, false, mBgPaint);
    }
    /** * dp2px * -90 360 0 270 90 180 * @param value * @return px */
    private int dp2px(float value) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
    }

    /** * sp2px * * @param value * @return px */
    private int sp2px(float value) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, value, getResources().getDisplayMetrics());
    }
}

你可能感兴趣的:(android)