自定義View( 啟動(dòng)頁倒計(jì)時(shí))

項(xiàng)目需要在啟動(dòng)頁加上倒計(jì)時(shí)的功能,所以自定義了一個(gè)倒計(jì)時(shí)的View,下面的是具體的分析

1、自定義View的基礎(chǔ)

一般情況下,自定義View可以有三種方式,
第一種:就是繼承View或者ViewGroup來自己從頭開始實(shí)現(xiàn),
第二種:就是繼承系統(tǒng)已經(jīng)實(shí)現(xiàn)了特定功能的View或者ViewGroup,例如TextView,ImageView,LinearLayout等等,這樣做的原因和好處就是,可以繼承部分功能,在此基礎(chǔ)上再進(jìn)行自己需要的擴(kuò)展
第三種:就是利用布局將一些View進(jìn)行特定的組合來組成一個(gè)復(fù)合的組件

2、倒計(jì)時(shí)View的具體實(shí)現(xiàn)

因?yàn)楸疚牡慕M件是使用第一種方式進(jìn)行實(shí)現(xiàn)的,所以下面就是結(jié)合代碼對(duì)第一種的實(shí)現(xiàn)方式進(jìn)行分析,代碼如下

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CountDownView">
        <attr name="arc_circle_color" format="color"/>
        <attr name="in_circle_color" format="color"/>
        <attr name="txt_time_color" format="color"/>
    </declare-styleable>
</resources>

上面的代碼是自定義的屬性,它放在values/attr.xml文件中,當(dāng)然這個(gè)attr.xml文件需要我們自己創(chuàng)建,它的主要目的就是使我們可以在xml文件中進(jìn)行屬性的設(shè)置,如果自己實(shí)現(xiàn)的自定義View中沒有自定義的屬性,則這個(gè)可以忽略

public class CountDownView extends View {
     //繪制內(nèi)圓的畫筆對(duì)象
    private Paint mInCirclePaint = new Paint();
    //繪制文字的畫筆對(duì)象
    private Paint mTxtPaint = new Paint();
    //繪制圓弧的畫筆對(duì)象
    private Paint mArcPaint = new Paint();

    //計(jì)時(shí)類
    private Timer mTimer = null;
    //外部圓當(dāng)前繪制的弧度
    private int currentAngle = 360;
    //外部圓最終繪制的弧度
    private int progress = 0;
    //當(dāng)前的描述,這里默認(rèn)為4秒,不可修改
    private int currentMillon = 4;

    //外部圓的背景顏色
    private int arcCircleColor;
    //內(nèi)部圓的背景顏色
    private int inCircleColor;
    //文字的顏色
    private int txtTimeColor;
    
    ..............
}

以上是需要用到的屬性,代碼中都有詳細(xì)的注釋了

public class CountDownView extends View {

    .......

    public CountDownView(Context context) {
        this(context, null);
    }


    public CountDownView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }


    public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CountDownView,
                defStyleAttr, 0);
        arcCircleColor = a.getColor(R.styleable.CountDownView_arc_circle_color, Color.RED);
        inCircleColor = a.getColor(R.styleable.CountDownView_in_circle_color, Color.parseColor
                ("#FFB7B6B6"));
        txtTimeColor = a.getColor(R.styleable.CountDownView_txt_time_color, Color.WHITE);
        a.recycle();
        init();
    }

    private void init() {
        mTimer = new Timer();
    }
    ......
}

接下來是三個(gè)構(gòu)造函數(shù),這里是最先被初始化的地方,所以在這里我們要把在attr.xml文件中的自定義屬性取出來,并且設(shè)置默認(rèn)值

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        int height = 0;

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = (int) ViewUtils.dp2px(50);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            if (getLayoutParams().height == WindowManager.LayoutParams.WRAP_CONTENT) {
                height = (int) ViewUtils.dp2px(50);
            } else {
                height = heightSize;
            }
        }

        if (width <= height) {
            setMeasuredDimension(width, width);
        } else {
            setMeasuredDimension(height, height);
        }
    }

接下來就是自定義View中比較重要的一個(gè)方法,這個(gè)方法的主要作用就是測(cè)量,它的兩個(gè)傳入的參數(shù)分別是由父布局測(cè)量后傳遞下來的測(cè)量寬度和測(cè)量高度,這個(gè)測(cè)量寬度包括了寬度的大小和測(cè)量模式,而測(cè)量高度也是一樣。這個(gè)方法的主要作用就是測(cè)量View的大小,如果說自定義View是畫畫的話,那么這個(gè)方法就是先測(cè)量出一塊畫布的大小,以便我們后續(xù)在這個(gè)畫布上進(jìn)行作畫

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //獲取屏幕的寬度
        int width = getMeasuredWidth();
        //獲取屏幕的高度
        int height = getMeasuredHeight();
        //繪制內(nèi)部的圓
        mInCirclePaint.setStyle(Paint.Style.FILL);
        mInCirclePaint.setAntiAlias(true);
        mInCirclePaint.setColor(inCircleColor);
        canvas.drawCircle(width / 2, height / 2, width / 2 - 10, mInCirclePaint);

        //繪制文字
        int mTxtSize = (int) ViewUtils.dp2px(14);
        mTxtPaint.setTextSize(mTxtSize);
        Rect mBound = new Rect();
        mTxtPaint.getTextBounds(String.valueOf(currentMillon), 0, String.valueOf(currentMillon)
                .length(), mBound);
        mTxtPaint.setColor(txtTimeColor);
        canvas.drawText(String.valueOf(currentMillon), width / 2 - mBound.width() / 2, height / 2
                + mBound.height() / 2, mTxtPaint);

        // 繪制圓弧
        mArcPaint.setStrokeWidth(10);
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setAntiAlias(true);
        mArcPaint.setColor(arcCircleColor);
        RectF rect = new RectF(10, 10, width - 10, height - 10);
        canvas.drawArc(rect, -90, currentAngle, false, mArcPaint);

    }

這個(gè)方法是自定義View中的另一個(gè)比較重要的方法,它的主要作用就是繪制具體的內(nèi)容,在這個(gè)方法里面,我們會(huì)持有畫布的對(duì)象,因此我們可以利用系統(tǒng)提供的api來繪制出你想要的圖形,上面的代碼就繪制了一個(gè)圓,文字,圓弧。那這一步就相當(dāng)于作畫中的在畫布上畫畫了。

3、最后一步

經(jīng)過前面的代碼,我們可以繪制出倒計(jì)時(shí)View的大致的樣子,但是怎樣實(shí)現(xiàn)倒計(jì)時(shí)呢,代碼如下:

 /**
     * 動(dòng)畫開始
     */
    public void start() {
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                postInvalidate();
                if (currentAngle <= progress) {
                    //到這里,自動(dòng)執(zhí)行的任務(wù)已經(jīng)結(jié)束,在這里我們可以定義回調(diào)接口來進(jìn)行特定的處理
                    mTimer.cancel();
                } else {
                    currentAngle -= 5;
                }
                if (currentAngle % 90 == 0 && currentMillon > 0) {
                    currentMillon--;
                }
            }
        }, 50, 50);
    }

以上的代碼就是倒計(jì)時(shí)之行的關(guān)鍵,它主要是這樣的,在onDraw方法中先在外部繪制出一個(gè)圓滿的圓形,但是繪制的弧度(currentAngle)是一個(gè)變量,我們?cè)诙〞r(shí)任務(wù)中不斷將這個(gè)變量進(jìn)行自減,并且進(jìn)行View的重繪(通過postInvalidate()方法),這樣就可以實(shí)現(xiàn)圓形圖案縮減的動(dòng)畫了。另外,上面代碼中的秒數(shù)的計(jì)算只是我粗略的計(jì)算,并不算準(zhǔn)確,有興趣可以實(shí)現(xiàn)更佳精確的計(jì)算方法

以上就是該View的全部實(shí)現(xiàn)過程,這是一個(gè)簡單的自定義View,并沒有涉及到很多其它復(fù)雜的操作,但是大致的流程都是這個(gè)樣子的,只是可能在實(shí)現(xiàn)過程中會(huì)需要結(jié)合其他知識(shí)來進(jìn)行更復(fù)雜的View的定制,最后,自定義View,講究一步一步實(shí)現(xiàn),我們并不能一步到位地實(shí)現(xiàn)一個(gè)很復(fù)雜的View,因此,我建議可以先實(shí)現(xiàn)你要定制的View的大致輪廓,然后再根據(jù)具體的需求對(duì)這個(gè)輪廓進(jìn)行更加細(xì)節(jié)的實(shí)現(xiàn)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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