自定义环形倒计时控件

自定义一个倒计时进度提示控件,适用于欢迎页面或广告页面倒计时退出等,逻辑很简单

以下内容仅是个人开发经验,欢迎指正~

话不多说,先上图

shortCut.jpg

实现步骤

  • 第一步:自定义属性
<declare-styleable name="MyTextView" >
    <attr name="progressBgColor" format="reference|color"/>
    <attr name="progressColor" format="reference|color"/>
    <attr name="progressLineWidth" format="dimension"/>
    <attr name="circleBgColr" format="reference|color"/> 
   <attr name="circleEndColor" format="reference|color"/>
    <attr name="totalTime" format="integer"/>
    <attr name="delayTime" format="integer"/>
</declare-styleable>
  • 第二步:代码实现
package cn.smartarvin.countdownview.widget;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
 
import java.util.Timer;
import java.util.TimerTask;
 
import cn.smartarvin.countdownview.R;
 
/**
 * Todo:倒计时控件
 * Author:Arvin
 * Date:2016/11/19 17:16
 * Email:smartarvin199111@gmail.com
 */
 
public class CountDownButton extends Button implements View.OnClickListener{
 
    private Context mContext ;
    private onCountClickListener mCountClickListener ;
 
    private static final int DEFAULT_PROGRESS_BG_COLOR = Color.LTGRAY;
    private static final int DEFAULT_PROGRESS_COLOR = Color.BLUE ;
    private static final float DEFAULT_PROGRESS_LINE_WIDTH = 5 ;
    private static final int DEFAULT_CIRCLE_BG_COLOR = Color.DKGRAY ;
    private static final int DEFAULT_TOTAL_TIME = 5000 ;
    //定义刷新间隔时长ms
    private static final int DELAY = 500 ;
 
    private int mProgressBgColor = DEFAULT_PROGRESS_BG_COLOR ;
    private int mProgressColor = DEFAULT_PROGRESS_COLOR ;
    private float mProgressLineWidth = DEFAULT_PROGRESS_LINE_WIDTH ;
    private int mCircleBgColor = DEFAULT_CIRCLE_BG_COLOR ;
    private int mTotalTime = DEFAULT_TOTAL_TIME ;
    private int mDelayTime = DELAY ;
 
    private int mProgress ;
    private int mCountingVal ;
    private int mCountPer ;
    private int mCircleRadius ;
 
    //定义中心坐标
    private int mCenterX;
    private int mCenterY;
 
    //定义画笔、计时器等
    private Paint mPaint ;
    private Timer mTimer ;
    private Rect mRect ;
    private RectF mRectF ;
 
    //定义正在进行计时标志
    private boolean isCounting = false ;
 
    public CountDownButton(Context context) {
        this(context , null , 0);
    }
 
    public CountDownButton(Context context, AttributeSet attrs) {
        this(context, attrs , 0);
    }
 
    public CountDownButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context ;
        initView(attrs);
    }
 
    private void initView(AttributeSet attrs) {
        TypedArray mTypeArry = getContext().obtainStyledAttributes(attrs, R.styleable.MyTextView);
        mProgressBgColor = mTypeArry.getColor(R.styleable.MyTextView_progressBgColor , DEFAULT_PROGRESS_BG_COLOR) ;
        mProgressColor = mTypeArry.getColor(R.styleable.MyTextView_progressColor , DEFAULT_PROGRESS_COLOR) ;
        mProgressLineWidth = mTypeArry.getDimension(R.styleable.MyTextView_progressLineWidth , DEFAULT_PROGRESS_LINE_WIDTH);
        mCircleBgColor = mTypeArry.getColor(R.styleable.MyTextView_circleBgColr , DEFAULT_CIRCLE_BG_COLOR)  ;
        mTotalTime = mTypeArry.getInt(R.styleable.MyTextView_totalTime , DEFAULT_TOTAL_TIME) ;
        mDelayTime = mTypeArry.getInt(R.styleable.MyTextView_delayTime , DELAY) ;
        mTypeArry.recycle();
 
        mTimer = new Timer();
        mRect = new Rect();
        mRectF = new RectF();
        mPaint = new Paint();
 
        setOnClickListener(this);
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取控件的宽高
        int width = getMeasuredWidth() ;
        int height = getMeasuredHeight();
 
        //确定为正方形布局
        if(width > height){
            height = width ;
        }else if(width < height){
            width = height;
        }
 
        //确定圆半径为控件宽/高一半
        mCircleRadius = width/2 ;
 
        //设置画布大小
        setMeasuredDimension(width,height);
 
    }
 
 
 
    @Override
    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);//不要继承父类的方法,避免部分属性设置不成功
        //获取正在绘制的控件边界
        getDrawingRect(mRect);
 
        //确定控件的中心坐标
        mCenterX = mRect.centerX();
        mCenterY = mRect.centerY();
 
        //开始绘制实心圆
        mPaint.setColor(mCircleBgColor);
        mPaint.setAntiAlias(true);//去锯齿
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(mCenterX , mCenterY ,mCircleRadius ,mPaint);
 
        //开始绘制进度条圆环
        mPaint.setColor(mProgressBgColor);
        mPaint.setStyle(Paint.Style.STROKE);//设置空心效果
        mPaint.setStrokeWidth(mProgressLineWidth);
        canvas.drawCircle(mCenterX , mCenterY ,mCircleRadius-mProgressLineWidth/2 ,mPaint);
 
        //绘制文字(必须使用getPaint())
        getPaint().setColor(getCurrentTextColor());
        getPaint().setTextAlign(Paint.Align.CENTER);
        getPaint().setAntiAlias(true);  //防锯齿
        float textY = mCenterY-(getPaint().descent()+getPaint().ascent())/2 ;
        canvas.drawText(getText().toString(), mCenterX , textY , getPaint());
 
        //开始绘制进度条
        mPaint.setColor(mProgressColor);
        mPaint.setStrokeWidth(mProgressLineWidth);
        mPaint.setStrokeCap(Paint.Cap.ROUND);//设置笔刷类型
        mRectF.set(mRect.left + mProgressLineWidth/2, mRect.top + mProgressLineWidth/2,
                mRect.right - mProgressLineWidth/2, mRect.bottom - mProgressLineWidth/2);
        canvas.drawArc(mRectF, -90, (float)mProgress* (360/(float)mCountPer), false, mPaint);
 
    }
 
 
    public int getProgressBgColor() {
        return mProgressBgColor;
    }
 
    public void setProgressBgColor(int mProgressBgColor) {
        this.mProgressBgColor = mProgressBgColor;
    }
 
    public int getProgressColor() {
        return mProgressColor;
    }
 
    public void setProgressColor(int mProgressColor) {
        this.mProgressColor = mProgressColor;
    }
 
    public float getmProgressWidth() {
        return mProgressLineWidth;
    }
 
    public void setProgressWidth(float mProgressWidth) {
        this.mProgressLineWidth = mProgressWidth;
    }
 
    public int getCircleBgColor() {
        return mCircleBgColor;
    }
 
    public void setCircleBgColor(int mCircleBgColor) {
        this.mCircleBgColor = mCircleBgColor;
    }
 
    public int getTotalTime() {
        return mTotalTime;
    }
 
    public void setTotalTime(int mTotalTime) {
        this.mTotalTime = mTotalTime;
    }
 
    public int getDelayTime() {
        return mDelayTime;
    }
 
    public void setDelayTime(int mDelayTime) {
        this.mDelayTime = mDelayTime;
    }
 
    public void cancle(){
        if(mTimer != null){
            mTimer.cancel();
        }
    }
 
    public void startTimer(){
        mCountPer = mTotalTime/mDelayTime;
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                mProgress ++;
                mCountingVal += mDelayTime;
                isCounting = true ;
                postInvalidate();
                if (mCountingVal >= mTotalTime) {
                    post(new Runnable() {
                        @Override
                        public void run() {
                            isCounting = false ;
                            cancle();
                            if(mCountClickListener != null){
                                mCountClickListener.onCountOverClick();
                            }
                        }
                    });
                }
            }
        }, mDelayTime, mDelayTime);
    }
 
    public void setOnCountClickListener(onCountClickListener mCountClickListener){
        this.mCountClickListener = mCountClickListener;
    }
 
    @Override
    public void onClick(View view) {
        if(mCountClickListener != null){
            if(isCounting){
                cancle();
                mCountClickListener.onCountingClick();
            } else{
                mCountClickListener.onCountOverClick();
            }
        }
    }
 
    public interface onCountClickListener{
        void onCountingClick();
        void onCountOverClick();
    };
}
  • 第三步:XML引用
    根据需要配置各属性值
<cn.smartarvin.countdownview.widget.CountDownButton
    android:id="@+id/countdown2" 
    android:layout_width="200dp" 
    android:layout_height="200dp" 
    android:textColor="@android:color/white" 
    android:textSize="20sp" 
    android:text="跳过"   
    app:totalTime="5000"  
    app:progressLineWidth="10dp" 
   />
  • 第四步:代码中引用
    根据需要在代码中配置各属性值
mTextView = (CountDownButton) findViewById(R.id.countdown2);
// mTextView.setTotalTime(5000);
// mTextView.setText("计时"); 
mTextView.startTimer();
mTextView.setOnCountClickListener(new
CountDownButton.onCountClickListener() {
@Override            
public void onCountingClick() { 
               Toast.makeText(MainActivity.this,"跳转",Toast.LENGTH_SHORT).show();  
}   
@Override 
public void onCountOverClick() { 
       Toast.makeText(MainActivity.this,"结束了",Toast.LENGTH_SHORT).show(); 
  }  
});

到此已完成倒计时控件的自定义以及引用过程,原理和逻辑很简单,自定义性灵活

谨以作为开发记录,如果有帮到您,记得点赞哦

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,056评论 25 707
  • 在购物平台上,经常可以看到购物车这样的自增自减的控件。 假如我们的项目中也需要这样的控件,并且在多个地方需要调用,...
    洪生鹏阅读 2,067评论 15 61
  • 本人初学Android,最近做了一个实现安卓简单音乐播放功能的播放器,收获不少,于是便记录下来自己的思路与知识总结...
    落日柳风阅读 19,126评论 2 41
  • 例如“活到老学到老”一类的话谁都听过,这些都是前人实践总结而流传下来的,对于我们的指导作用毋庸置疑。 人作为社交动...
    昵称难注册阅读 1,018评论 0 0
  • 我以为只要保持微笑就是开心,我以为只要遗忘就会解脱,这只不过是苍白无力的信仰,终将抵不过现实的各种压力。得到一个之...
    白旸阅读 227评论 0 1