效果预览
目前项目中用到了一个倒计时控件, 觉的还不错. 所以分享出来. 有需要的同学可以直接拿去用. 废话不多说, 先看看效果:
分析需求
实现一个自定义控件, 先分析控件的初始状态和构成.
- 初始状态: 分析这个控件发现这个控件是一个整体 不是分散 控制的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地址: 如果对你有帮助 欢迎star ~~