自定義控件實踐-倒計時控件

效果預(yù)覽

目前項目中用到了一個倒計時控件, 覺的還不錯. 所以分享出來. 有需要的同學可以直接拿去用. 廢話不多說, 先看看效果:

jdfw.gif

分析需求

實現(xiàn)一個自定義控件, 先分析控件的初始狀態(tài)和構(gòu)成.

  • 初始狀態(tài):


    image.png

    分析這個控件發(fā)現(xiàn)這個控件是一個整體 不是分散 控制的view... (⊙o⊙)…(什么是分散控制的view.常見的比如說滑動開關(guān)之類的) 既然如此 可知這個view應(yīng)該是繼承View的而不是ViewGroup.

  • 控制狀態(tài)
    這個自定義view需要根據(jù)時間倒計時. 外層有個圈圈顯示進度 內(nèi)層有文本數(shù)字顯示倒計時.倒計時就需要計時器 我用的是CountDownTimer來實現(xiàn)倒計時功能的.

代碼實現(xiàn)

  • 第一步: 繼承View,重寫構(gòu)造方法. 初始化一些參數(shù).如下:
  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);
        //環(huán)的畫筆
        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);
    } 
  • 第二步: 構(gòu)造方法走完后就走 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方法中就要繪制了. 控件在倒計時的時候外層有變化. 里面也有變化. 這些變化都應(yīng)該是更具剩余時間百分比來的. 而背景 剩余時間 這些元素沒有發(fā)生變化


    @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);
        //外環(huán) 背景綠色
        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);

//        動態(tài)改變的部分
        //內(nèi)環(huán) 背景灰色
        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);
    }
  • 第三步: 控件的倒計時運行. 使用計時器 然后一秒鐘回調(diào)一次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();
    }

  • 第四步: 實現(xiàn)了界面. 還需要讓控件可以被外部控制. 一個倒計時控件 需要被設(shè)置總時間 和剩余時間.
 /**
     * 設(shè)置總時間時間
     *
     * @param second 秒
     */
    public void setTotalTime(int second) {
        mTotalTime = second * 1000;
        changeForTimeText(second);
    }

    /**
     * 設(shè)置倒計時時間
     *
     * @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();
    }

總結(jié)

實現(xiàn)這個自定義控件主要分析其中的邏輯. 找到總時間爸吮、剩余時間、控件外圈進度條和倒計時時間文本之間的關(guān)系 然后一步步寫幼东。 應(yīng)該還是比較簡單的~~
Demo地址: https://github.com/zmin666/LoopView
如果對你有幫助 歡迎star ~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末业扒,一起剝皮案震驚了整個濱河市坏为,隨后出現(xiàn)的幾起案子缤剧,更是在濱河造成了極大的恐慌客叉,老刑警劉巖乃戈,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屡立,死亡現(xiàn)場離奇詭異直晨,居然都是意外死亡,警方通過查閱死者的電腦和手機膨俐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門勇皇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人焚刺,你說我怎么就攤上這事敛摘。” “怎么了乳愉?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵兄淫,是天一觀的道長。 經(jīng)常有香客問我蔓姚,道長捕虽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任赂乐,我火速辦了婚禮薯鳍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挨措。我一直安慰自己挖滤,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布浅役。 她就那樣靜靜地躺著斩松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪觉既。 梳的紋絲不亂的頭發(fā)上惧盹,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音瞪讼,去河邊找鬼钧椰。 笑死,一個胖子當著我的面吹牛符欠,可吹牛的內(nèi)容都是我干的嫡霞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼希柿,長吁一口氣:“原來是場噩夢啊……” “哼诊沪!你這毒婦竟也來了养筒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤端姚,失蹤者是張志新(化名)和其女友劉穎晕粪,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渐裸,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡巫湘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了橄仆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剩膘。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盆顾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情畏梆,我是刑警寧澤您宪,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站奠涌,受9級特大地震影響宪巨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溜畅,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一捏卓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慈格,春花似錦怠晴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至选泻,卻和暖如春冲粤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背页眯。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工梯捕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窝撵。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓傀顾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親忿族。 傳聞我的和親對象是個殘疾皇子锣笨,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容