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" />

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值