自定义控件实践-倒计时控件

效果预览

目前项目中用到了一个倒计时控件, 觉的还不错. 所以分享出来. 有需要的同学可以直接拿去用. 废话不多说, 先看看效果:

自定义控件实践-倒计时控件_第1张图片
jdfw.gif

分析需求

实现一个自定义控件, 先分析控件的初始状态和构成.

  • 初始状态:


    自定义控件实践-倒计时控件_第2张图片
    image.png

    分析这个控件发现这个控件是一个整体 不是分散 控制的view... (⊙o⊙)…(什么是分散控制的view.常见的比如说滑动开关之类的) 既然如此 可知这个view应该是继承View的而不是ViewGroup.

  • 控制状态
    这个自定义view需要根据时间倒计时. 外层有个圈圈显示进度 内层有文本数字显示倒计时.倒计时就需要计时器 我用的是CountDownTimer来实现倒计时功能的.

代码实现

  • 第一步: 继承View,重写构造方法. 初始化一些参数.如下:
  public LoopView(Context context) {
        this(context, null);
    }

    public LoopView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoopView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        mDisplayMetrics = getContext().getResources().getDisplayMetrics();
        t = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, t, mDisplayMetrics);
        //环的画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        float strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, mDisplayMetrics);
        mPaint.setStrokeWidth(strokeWidth);//圆圈的线条粗细
        //文本画笔
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(Color.WHITE);

        //默认总时间
        mM = String.valueOf(mTotalTime / 1000 / 60);
        mS = "00";

        setClickable(true);
    } 
  • 第二步: 构造方法走完后就走 onMeasure onDraw方法了 这时候我们就要重写这些方法了.
    1.onMeasure 方法中要拿到控件的宽高信息 方便后面绘制的时候用到. 控件的范围采用矩形来约束
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();

        int mHigh = mMWidth;
        //范围
        mRectF = new RectF(t, t, mMWidth - t, mHigh - t);
    }

2.onDraw方法中就要绘制了. 控件在倒计时的时候外层有变化. 里面也有变化. 这些变化都应该是更具剩余时间百分比来的. 而背景 剩余时间 这些元素没有发生变化


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //整体背景
        mPaint.setStyle(Paint.Style.FILL); //绘制图形的描边
        mPaint.setColor(getResources().getColor(R.color.black_27));
        canvas.drawArc(mRectF, -90, 360, false, mPaint);
        //外环 背景绿色
        mPaint.setStyle(Paint.Style.STROKE); //绘制图形的描边
        mPaint.setColor(getResources().getColor(R.color.blue_86));
        canvas.drawArc(mRectF, -90, 360, false, mPaint);
        //剩余时间 文字
        String s = "剩余时间";
        float ts = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, mDisplayMetrics);
        mTextPaint.setTextSize(ts);
        mTextPaint.setColor(getResources().getColor(R.color.text_color_99));
        float x = mTextPaint.measureText(s);
        canvas.drawText(s, mMWidth / 2 - x / 2, (float) mHeight * 0.4f, mTextPaint);

//        动态改变的部分
        //内环 背景灰色
        mPaint.setColor(getResources().getColor(R.color.gray_c1));
        mPaint.setStrokeCap(Paint.Cap.SQUARE);
        float swap = mPersent * 360;
        canvas.drawArc(mRectF, -90, swap, false, mPaint);

        // 绘制进度文案显示
        String text = mM + ":" + mS;
        float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 21, mDisplayMetrics);
        float textWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7, mDisplayMetrics);
        mTextPaint.setStrokeWidth(textWidth);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setColor(getResources().getColor(R.color.text_color_ff));
        mTextPaint.setStyle(Paint.Style.FILL);
        float width = mTextPaint.measureText(text);
        canvas.drawText(text, mMWidth / 2 - width / 2, (float) mHeight * 0.65f, mTextPaint);
    }
  • 第三步: 控件的倒计时运行. 使用计时器 然后一秒钟回调一次onDraw方法 就能达到效果
  /**
     * 开始倒计时
     */
    public void starTimecount() {
        if (mCountDownTimer != null) {
            mCountDownTimer.cancel();
        }

        mRemineTime = mRemineTime == -1 ? mTotalTime : mRemineTime;

        mCountDownTimer = new CountDownTimer(mRemineTime, 1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                mPersent = (mTotalTime - millisUntilFinished) / (float)mTotalTime;
                Log.i("zmin.............", ".persent..." + mPersent);
                changeForTimeText((int) millisUntilFinished / 1000);
                invalidate();
            }

            @Override
            public void onFinish() {
                mPersent = 1;
                changeForTimeText(0);
                invalidate();
                if (mOnTimeCountListener != null) {
                    mOnTimeCountListener.finish();
                }
            }
        };
        mCountDownTimer.start();
    }

  • 第四步: 实现了界面. 还需要让控件可以被外部控制. 一个倒计时控件 需要被设置总时间 和剩余时间.
 /**
     * 设置总时间时间
     *
     * @param second 秒
     */
    public void setTotalTime(int second) {
        mTotalTime = second * 1000;
        changeForTimeText(second);
    }

    /**
     * 设置倒计时时间
     *
     * @param second 剩余时间单位 秒
     */
    public void setRemineTime(int second) {
        if (mCountDownTimer != null) {
            mCountDownTimer.cancel();
            mCountDownTimer = null;
        }
        if (second > mTotalTime / 1000) {
            try {
                throw new Exception("the time your set is less than total time, please reset it ");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }
        mRemineTime = second * 1000;
        changeForTimeText(second);
        starTimecount();
    }

总结

实现这个自定义控件主要分析其中的逻辑. 找到总时间、剩余时间、控件外圈进度条和倒计时时间文本之间的关系 然后一步步写。 应该还是比较简单的~~
Demo地址: https://github.com/zmin666/LoopView
如果对你有帮助 欢迎star ~~

你可能感兴趣的:(自定义控件实践-倒计时控件)