自定義控件實(shí)踐-倒計(jì)時(shí)控件

效果預(yù)覽

目前項(xiàng)目中用到了一個(gè)倒計(jì)時(shí)控件, 覺(jué)的還不錯(cuò). 所以分享出來(lái). 有需要的同學(xué)可以直接拿去用. 廢話不多說(shuō), 先看看效果:

jdfw.gif

分析需求

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

  • 初始狀態(tài):


    image.png

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

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

代碼實(shí)現(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);//圓圈的線條粗細(xì)
        //文本畫筆
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(Color.WHITE);

        //默認(rèn)總時(shí)間
        mM = String.valueOf(mTotalTime / 1000 / 60);
        mS = "00";

        setClickable(true);
    } 
  • 第二步: 構(gòu)造方法走完后就走 onMeasure onDraw方法了 這時(shí)候我們就要重寫這些方法了.
    1.onMeasure 方法中要拿到控件的寬高信息 方便后面繪制的時(shí)候用到. 控件的范圍采用矩形來(lái)約束
 @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方法中就要繪制了. 控件在倒計(jì)時(shí)的時(shí)候外層有變化. 里面也有變化. 這些變化都應(yīng)該是更具剩余時(shí)間百分比來(lái)的. 而背景 剩余時(shí)間 這些元素沒(méi)有發(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);
        //剩余時(shí)間 文字
        String s = "剩余時(shí)間";
        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);

//        動(dòng)態(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);

        // 繪制進(jìn)度文案顯示
        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);
    }
  • 第三步: 控件的倒計(jì)時(shí)運(yùn)行. 使用計(jì)時(shí)器 然后一秒鐘回調(diào)一次onDraw方法 就能達(dá)到效果
  /**
     * 開(kāi)始倒計(jì)時(shí)
     */
    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();
    }

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

    /**
     * 設(shè)置倒計(jì)時(shí)時(shí)間
     *
     * @param second 剩余時(shí)間單位 秒
     */
    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é)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 6、View的繪制 (1)當(dāng)測(cè)量好一個(gè)View之后,我們就可以簡(jiǎn)單的重寫 onDraw()方法,并在 Canvas...
    b5e7a6386c84閱讀 1,970評(píng)論 0 3
  • 2017.8.5第5天 星期六 陰間晴 又到周末了,時(shí)間過(guò)得真快。這個(gè)暑假也開(kāi)始倒計(jì)時(shí)了,遺憾的是我們沒(méi)...
    鑫隆媽媽閱讀 330評(píng)論 0 0
  • [日就月將]2017-4-16學(xué)習(xí)力踐行D32天。早晨自己抓面條吃,對(duì)著自拍,會(huì)吃的比較好。貝瓦兒歌不錯(cuò),可以跟大...
    wxgcgb閱讀 164評(píng)論 0 0
  • Mac OS X EI下載和編譯運(yùn)行SeetaFaceEngine學(xué)習(xí)筆記及Error處理 環(huán)境: Mac OS ...
    seutwom_J閱讀 1,271評(píng)論 3 0

友情鏈接更多精彩內(nèi)容