Android仿小米時鐘

最近在學自定義View,看到小米時鐘這個效果,很想去了解一下是界面是如何繪製以及秒針,分針,時針是如何轉動的,還有那個弧形顏色漸變效果。這是我參照Github上的效果弄的,Github地址:高仿小米時鐘

首先看一下效果圖,

 直接看代碼,說明註釋已添加。

package com.example.threeversionasproject.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.example.threeversionasproject.DensityUtils;
import com.example.threeversionasproject.R;

import java.util.Calendar;

/**
 * Created by Ocean on 2019/3/15.
 */

public class MiClockView extends View {

    /* 時鐘半徑,不包括padding值 */
    private float mRadius;
    
    /*當前畫布*/
    private Canvas mCanvas;
    
    
    /*刻度線畫筆*/
    private Paint linePaint;
    /* 刻度線長度 */
    private float mLineLength;
    /* 刻度線顏色 */
    private int mLineColor;
    
    /* 外層圓弧畫筆 */
    private Paint mOutCirclePaint;
    /* 外層圓弧畫筆 顏色 */
    private int mOutArcColor;
    /* 外層圓弧的外接矩形 */
    private RectF mOutRect = new RectF();
    
    /* 外環字體畫筆 */
    private TextPaint mTextPaint;
    /* 外環字體顏色 */
    private int mTextColor;
    /*外環字體尺寸*/
    private float mTextSize;
    /* 字體外接矩形 */
    private Rect mTextRect = new Rect();
   

    /* 梯度掃描漸變 */
    private SweepGradient mSweepGradient;
    /* 漸變矩陣,作用在SweepGradient */
    private Matrix mGradientMatrix;
    
    /* 刻度圓弧畫筆 */
    private Paint mLineArcPaint;
    /* 刻度圓弧的外接矩形 */
    private RectF mLineArcRectF = new RectF();
    

    /*用於分針、秒針、時針色 */
    private int mHourNeedleColor;
    private int mMinuteNeedleColor;
    private int mSecondNeedleColor;
    
    
    /* 加一個默認的padding值,爲了防止用camera旋轉時鐘時造成四周超出view大小 */
    private float mDefaultPadding;
    private float mPaddingLeft;
    private float mPaddingTop;
    private float mPaddingRight;
    private float mPaddingBottom;

    /* 時針畫筆 */
    private Paint mHourHandPaint;
    /* 分針畫筆 */
    private Paint mMinuteHandPaint;
    /* 秒針畫筆 */
    private Paint mSecondHandPaint;
    
    /* 時針角度 */
    private float mHourDegree;
    /* 分針角度 */
    private float mMinuteDegree;
    /* 秒針角度 */
    private float mSecondDegree;

    /* 時針路徑 */
    private Path mHourHandPath = new Path();
    /* 分針路徑 */
    private Path mMinuteHandPath = new Path();
    /* 秒針路徑 */
    private Path mSecondHandPath = new Path();
    

    public MiClockView(Context context) {
        super(context);
    }

    public MiClockView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ClockView, 0, 0);
        mOutArcColor = ta.getColor(R.styleable.ClockView_clock_outArcColor, Color.parseColor("#237EAD"));
        mHourNeedleColor = ta.getColor(R.styleable.ClockView_clock_hourNeedletColor, Color.parseColor("#ffffff"));
        mMinuteNeedleColor = ta.getColor(R.styleable.ClockView_clock_minuteNeedletColor, Color.parseColor("#ffffff"));
        mSecondNeedleColor = ta.getColor(R.styleable.ClockView_clock_secondNeedletColor, Color.parseColor("#ffffff"));
        mLineColor = ta.getColor(R.styleable.ClockView_clock_lineColor, Color.parseColor("#ffffff"));
        mTextColor = ta.getColor(R.styleable.ClockView_clock_textColor, Color.parseColor("#000000"));
        mTextSize = ta.getDimension(R.styleable.ClockView_clock_textSize, DensityUtils.sp2px(context, 14));
        ta.recycle();

       
        linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        linePaint.setColor(mLineColor);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(3);

        mOutCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mOutCirclePaint.setStyle(Paint.Style.STROKE);
        mOutCirclePaint.setStrokeWidth(1);
        mOutCirclePaint.setColor(mOutArcColor);

        mLineArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLineArcPaint.setStyle(Paint.Style.STROKE);

        mGradientMatrix = new Matrix();

        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setStyle(Paint.Style.STROKE);
        mTextPaint.setColor(mTextColor);
        //居中繪製文字,不然外環的字體會往右偏
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(mTextSize);


        mSecondHandPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSecondHandPaint.setStyle(Paint.Style.FILL);
        mSecondHandPaint.setColor(mSecondNeedleColor);

        mHourHandPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mHourHandPaint.setStyle(Paint.Style.FILL);
        mHourHandPaint.setColor(mHourNeedleColor);

        mMinuteHandPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mMinuteHandPaint.setStyle(Paint.Style.FILL);
        mMinuteHandPaint.setColor(mMinuteNeedleColor);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec));
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRadius = Math.min(w - getPaddingLeft() - getPaddingRight(),
                h - getPaddingTop() - getPaddingBottom()) / 2;
        mDefaultPadding = 0.12f * mRadius;
        //這裏已有默認的padding值,所以在佈局中可以不設置padding的話,取得就是mDefaultPadding
        mPaddingLeft = mDefaultPadding + getPaddingLeft();
        mPaddingRight = mDefaultPadding + getPaddingRight();
        mPaddingTop = mDefaultPadding + getPaddingTop();
        mPaddingBottom = mDefaultPadding + getPaddingBottom();


        mLineLength = 0.12f * mRadius;//根據比例確定刻度線長度

        mLineArcPaint.setStrokeWidth(mLineLength);

        //梯度掃描漸變,以(w/2,h/2)爲中心點,兩種起止顏色梯度漸變
        //float數組表示,[0,0.75)爲起始顏色所佔比例,[0.75,1}爲起止顏色漸變所佔比例
        mSweepGradient = new SweepGradient(w / 2, h / 2,
                new int[]{mOutArcColor, mHourNeedleColor}, new float[]{0.75f, 1});
    }

    public int measureSize(int measureSpec) {
        int defaultSize = 800;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);

        switch (mode) {
            case MeasureSpec.AT_MOST:
                Log.i("", "----Width------AT_MOST:");
                return Math.min(size, defaultSize);
            case MeasureSpec.EXACTLY:
                Log.i("", "----Width------EXACTLY:");
                return size;
            case MeasureSpec.UNSPECIFIED:
                Log.i("", "----Width------UNSPECIFIED:");
                return defaultSize;
            default:
                return defaultSize;
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mCanvas = canvas;
        getCurrentTime();
        drawOutSide();
        drawLine(canvas);
        drawSecondNeedle();
        drawMinuteNeedle();
        drawHourHand();
        invalidate();
    }

    /*獲取系統當前時間,並將其轉換成對應的角度*/
    private void getCurrentTime() {
        Calendar calendar = Calendar.getInstance();
        float milliSecord = calendar.get(Calendar.MILLISECOND);
        float second = calendar.get(Calendar.SECOND) + milliSecord / 1000;
        float minute = calendar.get(Calendar.MINUTE) + second / 60;
        float hour = calendar.get(Calendar.HOUR) + minute / 60;
        Log.i("", "----second:" + second + "---minute" + minute + "----hour:" + hour);
        mSecondDegree = second / 60 * 360;
        mMinuteDegree = minute / 60 * 360;
        mHourDegree = hour / 12 * 360;

    }


    /*繪製外環的字體和4段弧線*/
    public void drawOutSide() {       
        String strList[] = new String[]{"12", "3", "6", "9",};
        mTextPaint.getTextBounds(strList[1], 0, strList[1].length(), mTextRect);
        mOutRect.set(mPaddingLeft + mTextRect.width() / 2, mPaddingTop + mTextRect.height() / 2, getWidth() - mPaddingRight - mTextRect.width() / 2, getHeight() - mPaddingBottom - mTextRect.height() / 2);

        mCanvas.drawText(strList[0], getWidth() / 2, mOutRect.top + mTextRect.height() / 2, mTextPaint);
        mCanvas.drawText(strList[1], mOutRect.right, getHeight() / 2 + mTextRect.height() / 2, mTextPaint);
        mCanvas.drawText(strList[2], getWidth() / 2, mOutRect.bottom + mTextRect.height() / 2, mTextPaint);
        mCanvas.drawText(strList[3], mOutRect.left, getHeight() / 2 + mTextRect.height() / 2, mTextPaint);


        for (int i = 0; i < strList.length; i++) {
            mCanvas.drawArc(mOutRect, 90 * i + 5, 80, false, mOutCirclePaint);
        }
    }


    /*繪製刻度線,以及漸變效果*/
    public void drawLine(Canvas canvas) {
        canvas.save();
        canvas.translate(0, 0);
        mLineArcRectF.set(mPaddingLeft + 1f * mLineLength + mTextRect.height(),
                mPaddingTop + mTextRect.height() + mLineLength * 1f,
                getWidth() - mPaddingRight - mTextRect.height() - 1f * mLineLength,
                getHeight() - mPaddingBottom - mTextRect.height() - 1f * mLineLength);


        //matrix默認會在三點鐘方向開始顏色的漸變,爲了吻合鐘錶十二點鐘順時針旋轉的方向,把秒針旋轉的角度減去90度
        mGradientMatrix.setRotate(mSecondDegree - 90, getWidth() / 2, getHeight() / 2);
        mSweepGradient.setLocalMatrix(mGradientMatrix);
        mLineArcPaint.setShader(mSweepGradient);
        //繪製矩形內接弧形時弧形的寬度剛好位於矩形邊長中心線的兩邊
        mCanvas.drawArc(mLineArcRectF, 0, 360, false, mLineArcPaint);

        for (int i = 0; i < 200; i++) {
            canvas.drawLine(getWidth() / 2, mPaddingTop + mTextRect.height() + mLineLength * 0.5f,
                    getWidth() / 2, mPaddingTop + mTextRect.height() + mLineLength * 1.5f, linePaint);
            canvas.rotate(1.8f, getWidth() / 2, getHeight() / 2);
        }       
        mCanvas.restore();
    }


    /*繪製秒針*/
    private void drawSecondNeedle() {
        mCanvas.save();
        mCanvas.rotate(mSecondDegree, getWidth() / 2, getHeight() / 2);
        mSecondHandPath.reset();
        float offset = mPaddingTop + mTextRect.height() / 2;

        mSecondHandPath.moveTo(getWidth() / 2, offset + mRadius * 0.27f);
        mSecondHandPath.lineTo(getWidth() / 2 - 0.05f * mRadius, offset + mRadius * 0.33f);
        mSecondHandPath.lineTo(getWidth() / 2 + 0.05f * mRadius, offset + mRadius * 0.33f);
        mSecondHandPath.close();
        mCanvas.drawPath(mSecondHandPath, mSecondHandPaint);
        mCanvas.restore();
    }


    /**
     * 繪製分針
     */
    private void drawMinuteNeedle() {
        mCanvas.save();
        mCanvas.rotate(mMinuteDegree, getWidth() / 2, getHeight() / 2);
        mMinuteHandPath.reset();

        float offset = mPaddingTop + mTextRect.height() / 2;
        mMinuteHandPath.moveTo(getWidth() / 2 - mRadius * 0.01f, getHeight() / 2 - mRadius * 0.03f);
        mMinuteHandPath.lineTo(getWidth() / 2 - mRadius * 0.008f, offset + mRadius * 0.435f);
        //這裏是繪製二階貝塞爾曲線,將分針的頭部繪製圓弧
        mMinuteHandPath.quadTo(getWidth() / 2, offset + mRadius * 0.415f, getWidth() / 2 + mRadius * 0.008f, offset + mRadius * 0.435f);
        mMinuteHandPath.lineTo(getWidth() / 2 + mRadius * 0.01f, getHeight() / 2 - mRadius * 0.03f);
        mMinuteHandPath.close();

        mMinuteHandPaint.setStyle(Paint.Style.FILL);
        mMinuteHandPaint.setColor(mMinuteNeedleColor);
        mCanvas.drawPath(mMinuteHandPath, mMinuteHandPaint);

        mOutRect.set(getWidth() / 2 - 0.03f * mRadius, getHeight() / 2 - 0.03f * mRadius,
                getWidth() / 2 + 0.03f * mRadius, getHeight() / 2 + 0.03f * mRadius);
        mMinuteHandPaint.setStyle(Paint.Style.STROKE);
        mMinuteHandPaint.setStrokeWidth(0.02f * mRadius);
        mMinuteHandPaint.setColor(Color.WHITE);
        mCanvas.drawArc(mOutRect, 0, 360, false, mMinuteHandPaint);
        mCanvas.restore();

    }

    /**
     * 繪製時針
     */
    private void drawHourHand() {
        mCanvas.save();
        mCanvas.rotate(mHourDegree, getWidth() / 2, getHeight() / 2);
        mHourHandPath.reset();
        float offset = mPaddingTop + mTextRect.height() / 2;
        mHourHandPath.moveTo(getWidth() / 2 - 0.018f * mRadius, getHeight() / 2 - 0.03f * mRadius);
        mHourHandPath.lineTo(getWidth() / 2 - 0.009f * mRadius, offset + 0.58f * mRadius);
        mHourHandPath.quadTo(getWidth() / 2, offset + 0.56f * mRadius,
                getWidth() / 2 + 0.009f * mRadius, offset + 0.58f * mRadius);
        mHourHandPath.lineTo(getWidth() / 2 + 0.018f * mRadius, getHeight() / 2 - 0.03f * mRadius);
        mHourHandPath.close();
        mHourHandPaint.setStyle(Paint.Style.FILL);
        mHourHandPaint.setColor(mHourNeedleColor);
        mCanvas.drawPath(mHourHandPath, mHourHandPaint);

        mOutRect.set(getWidth() / 2 - 0.03f * mRadius, getHeight() / 2 - 0.03f * mRadius,
                getWidth() / 2 + 0.03f * mRadius, getHeight() / 2 + 0.03f * mRadius);
        mHourHandPaint.setStyle(Paint.Style.STROKE);
        mHourHandPaint.setStrokeWidth(0.02f * mRadius);
        mHourHandPaint.setColor(Color.WHITE);
        mCanvas.drawArc(mOutRect, 0, 360, false, mHourHandPaint);
        mCanvas.restore();
    }
    
}

attr自定義屬性

<declare-styleable name="ClockView">
        <attr name="clock_outArcColor" format="color" /><!--外接弧顏色-->
        <attr name="clock_hourNeedletColor" format="color" /><!--時針顏色-->
        <attr name="clock_minuteNeedletColor" format="color" /><!--分針顏色-->
        <attr name="clock_secondNeedletColor" format="color" /><!--秒針顏色-->
        <attr name="clock_textColor" format="color" /><!--字體顏色-->
        <attr name="clock_lineColor" format="color" /><!--刻度線顏色-->
        <attr name="clock_textSize" format="dimension" /><!--字體大小-->
</declare-styleable>

佈局代碼

<com.example.threeversionasproject.widget.MiClockView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/colorPrimary"
        app:clock_hourNeedletColor="@color/colorWhite"
        app:clock_minuteNeedletColor="@color/colorWhite"
        app:clock_secondNeedletColor="@color/colorWhite"
        app:clock_lineColor="@color/colorPrimary"
        app:clock_outArcColor="@color/colorLightWhite"
        app:clock_textColor="@color/colorLightWhite"
        app:clock_textSize="14sp" />

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章