Android 自定义控件,模仿小米秒表样式的时钟,完整代码注解

一个模仿小米手机秒表样式的安卓小应用程序,利用自定义控件实现。

超详细的代码注释

开发工具为:Android Studio

看图

Android 自定义控件,模仿小米秒表样式的时钟,完整代码注解_第1张图片

代码:

大表盘自定义控件:【xiaomiClock 】

public class xiaomiClock extends View {
    private Paint textPaint,paint;
    private Path mTriangle;
    private Timer mTimer;
    private float agree = 1;
    private Shader mshader;
    private PathEffect mEffect;

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

    public xiaomiClock(Context context, AttributeSet attrs) {
        super(context, attrs);
        //mEffect = new DashPathEffect(new float[]{1,2,5,10,50,20}, 0);    // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
        mEffect = new DashPathEffect(new float[]{5,0}, 0);    // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
        mshader = new SweepGradient(500, 500, Color.parseColor("#3b3b3b"), Color.parseColor("#ffffff"));    //渐变遮罩样式


        textPaint = new Paint();
        textPaint.setStrokeWidth(16);
        textPaint.setColor(Color.WHITE);
        textPaint.setStrokeCap(Paint.Cap.ROUND);
        textPaint.setAntiAlias(true);

        paint = new Paint();
        paint.setAntiAlias(true);

        mTriangle = new Path();
        mTriangle.moveTo(960, 500);// 此点为多边形的顶点【三角形(指针)】
        //下面两个x 相等,表示底边的位置
        mTriangle.lineTo(1000, 525);  // y:底边宽的其中一个顶点
        mTriangle.lineTo(1000, 475);  //y:底边宽的其中一个顶点
        mTriangle.close();
        setmTimer();
        System.out.println("度数" + ((float) 6.0 / 10));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //绘画手表盘
        drawbeauty(canvas);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //设置宽高
        setMeasuredDimension(1000,1000);  //画布大小
    }

    public void drawbeauty(Canvas canvas) {
        int canvasWidth = canvas.getWidth();
        int canvasHeight = canvas.getHeight();
        //设置缓存层,因为下面要实用xfermode,使用xfemode必须使用缓存层,否则会出现黑色背景
        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);

        //初始化画笔,因为上下两层做画需要的画笔属性不一样,所以只能每次重新设置一次
        paint.setStyle(Paint.Style.STROKE);     //设置画笔为不填充模式
        paint.setPathEffect(mEffect);           //设置笔画样式,这里设置的是虚线样式
        paint.setStrokeWidth(50);               //设置笔画宽度
        canvas.drawCircle(500, 500, 420, paint);    //画一个纯色表盘,虚线,空心圆形 【表盘位置和大小】

        //设置画笔属性,SRC_IN属性,让第二个图案只能花在第一个图案上面,也就是只能画在上面所说那个纯色表盘里面
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //把画笔虚线属性去掉,因为我要的是一个实心圆形,然后让这个实心但是颜色不一样圆形画在上面所说表盘上面,因为设置了xfermode,所以显示的一样会是虚线圆形表盘,但是颜色会变成你现在的颜色
        paint.setPathEffect(null);

        //设置画笔shader属性,这里设置的是SweepGradient模式,可以让颜色过渡泾渭分明,以圆形为中心开始变化
        paint.setShader(mshader);
        paint.setStyle(Paint.Style.FILL);

        canvas.save();      //保存画布

        //旋转画布,然后你就会发现时钟表盘开始动了
        canvas.rotate(agree, 500, 500);    //画布旋转的中心点
        canvas.drawRect(10, 10, 1000, 1100, paint);   //渐变矩形绘制
        canvas.drawPath(mTriangle, textPaint);    //绘制小三角形
        canvas.restore();

        //最后将画笔去除Xfermode
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    private void setmTimer() {
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                agree = agree + 0.022f;//数值越大,旋转速度越快
                if (agree > 360)
                    agree = 1;
                postInvalidate();
            }
        }, 1000, 3);  //延时/周期
    }
}

小码表表盘自定义控件:【xiaomiClock 02】

public class xiaomiClock02 extends View {
    private Paint textPaint,paint;
    private Path mTriangle;
    private Timer mTimer;
    private float agree = 1;
    private Shader  mshader;
    private PathEffect mEffect;

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

    public xiaomiClock02(Context context, AttributeSet attrs) {
        super(context, attrs);
        //mEffect = new DashPathEffect(new float[]{1,2,5,10,50,20}, 0);    // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
        mEffect = new DashPathEffect(new float[]{5,0}, 0);    // float[]{ 虚线的厚度, 虚线的间距,虚线的厚度, 虚线的间距 ......}
        mshader = new SweepGradient(110, 110, Color.parseColor("#3b3b3b"), Color.parseColor("#ffffff"));    //渐变遮罩样式【渐变色的渐变中心点以及颜色】

        textPaint = new Paint();
        textPaint.setStrokeWidth(16);
        textPaint.setColor(Color.WHITE);
        textPaint.setStrokeCap(Paint.Cap.ROUND);
        textPaint.setAntiAlias(true);

        paint = new Paint();
        paint.setAntiAlias(true);

        mTriangle = new Path();
        mTriangle.moveTo(190, 110);// 此点为多边形的顶点【三角形(指针)】
        //下面两个x 相等,表示底边的位置
        mTriangle.lineTo(110, 112);  // y:底边宽的其中一个顶点
        mTriangle.lineTo(110, 108);  //y:底边宽的其中一个顶点
        mTriangle.close();
        setmTimer();
        System.out.println("度数" + ((float) 6.0 / 10));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //绘画手表盘
        drawbeauty(canvas);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //设置宽高
        setMeasuredDimension(220,220);  //画布大小
    }

    public void drawbeauty(Canvas canvas) {
        int canvasWidth = canvas.getWidth();
        int canvasHeight = canvas.getHeight();
        //设置缓存层,因为下面要实用xfermode,使用xfemode必须使用缓存层,否则会出现黑色背景
        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);

        //初始化画笔,因为上下两层做画需要的画笔属性不一样,所以只能每次重新设置一次
        paint.setStyle(Paint.Style.STROKE);     //设置画笔为不填充模式
        paint.setPathEffect(mEffect);           //设置笔画样式,这里设置的是虚线样式
        paint.setStrokeWidth(2);               //设置笔画宽度
        canvas.drawCircle(110, 110, 100, paint);    //画一个纯色表盘,虚线,空心圆形 【表盘位置和大小】

        //设置画笔属性,SRC_IN属性,让第二个图案只能花在第一个图案上面,也就是只能画在上面所说那个纯色表盘里面
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //把画笔虚线属性去掉,因为我要的是一个实心圆形,然后让这个实心但是颜色不一样圆形画在上面所说表盘上面,因为设置了xfermode,所以显示的一样会是虚线圆形表盘,但是颜色会变成你现在的颜色
        paint.setPathEffect(null);

        //设置画笔shader属性,这里设置的是SweepGradient模式,可以让颜色过渡泾渭分明,以圆形为中心开始变化
        paint.setShader(mshader);
        paint.setStyle(Paint.Style.FILL);

        canvas.save();      //保存画布

        //旋转画布,然后你就会发现时钟表盘开始动了
        canvas.rotate(agree, 110, 110);    //画布旋转的中心点
        canvas.drawRect(1, 1, 220, 220, paint);   //渐变矩形绘制
        canvas.drawPath(mTriangle, textPaint);    //绘制小三角形
        canvas.restore();

        //最后将画笔去除Xfermode
        paint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    private void setmTimer() {
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                agree = agree + 0.2f + 1f;//数值越大,旋转速度越快
                if (agree > 360)
                    agree = 1;
                postInvalidate();
            }
        }, 1000, 3);  //延时/周期
    }
}

BaseActivity设置主题为黑色

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //主题设置
        setTheme(R.style.AppTheme01);
    }
}

主题Styles样式

    

MainActivity 设置时间代码

public class MainActivity extends BaseActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new DataThread().start();  //时间控制线程

    }

    //设定显示系统时间
    private String getData() {
        long sysTime = System.currentTimeMillis();
        CharSequence sysTimeStr = DateFormat.format("" + "yyyy/MM/dd", sysTime);
        return (String) sysTimeStr;
    }
    @SuppressLint("HandlerLeak")
    private Handler mHandlerData = new Handler() {
        public void handleMessage(android.os.Message msg) {
            TextView textView = findViewById(R.id.Time_TextView01);  //确定系统时间变化的控件
            textView.setText((String) msg.obj);
        }
    };


    //设定显示系统时间
    private String getTime() {
        long sysTime = System.currentTimeMillis();
        CharSequence sysTimeStr = DateFormat.format("" + "HH:mm:ss", sysTime);
        return (String) sysTimeStr;
    }
    @SuppressLint("HandlerLeak")
    private Handler mHandlerTime = new Handler() {
        public void handleMessage(android.os.Message msg) {
            TextView textView = findViewById(R.id.Time_TextView02);  //确定系统时间变化的控件
            textView.setText((String) msg.obj);
        }
    };

    private class DataThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 999999999; i++) {    //一共变化多少次次
                try {
                    Thread.sleep(10);   //每一毫秒变化一次
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                final String sysTime = getTime();
                final String sysData = getData();
                // 只能在主线程中修改ui控件
                mHandlerData.sendMessage(mHandlerData.obtainMessage(0, sysData));  //系统日期
                mHandlerTime.sendMessage(mHandlerTime.obtainMessage(0, sysTime));  //系统时间
            }
        }
    }
}

.
App下载:https://aifabu.com/iUfi

源码下载:https://download.csdn.net/download/erp_lxkun_jak/11240191
【注意:使用开发工具是 Android Studio 哦!】
.
.
感谢你的查阅,希望可以帮到你,祝你学习愉快!

我是和你一起学习的 易君

你可能感兴趣的:(Android)