偶然有天发现朋友圈有人晒出苹果手机的一个就寝功能,里面一个显示睡眠时间的控件,觉得这个控件非常好看,而且正好看了一系列的自定义控件文章,就模仿这个试一试。

1 先看效果图:完整源码在这里
仿ios 就寝控件

2.具体实现:
(1)初始化一些画笔
从图中可以分析出,我们需要如下画笔:画外面黑色大圆,画时钟数字,画中间文字,画时钟刻度等等,也就是需要什么画笔new出来就好。

 /**
     * 初始化画笔:
     */
    private void init() {

        blackPaint = new Paint();
        blackPaint.setColor(Color.parseColor("#171717"));
        blackPaint.setStyle(Paint.Style.STROKE);//填充
        blackPaint.setStrokeWidth(60);
        blackPaint.setAntiAlias(true);

        bigPaint = new Paint();
        bigPaint.setColor(Color.BLACK);
        bigPaint.setStyle(Paint.Style.STROKE);
        bigPaint.setStrokeWidth(1);
        bigPaint.setAntiAlias(true);


        yellowPaint = new Paint();
        yellowPaint.setStyle(Paint.Style.STROKE);//填充
        yellowPaint.setStrokeWidth(60);
        yellowPaint.setAntiAlias(true);

        //头部画笔
        headTailPaint = new Paint();
        headTailPaint.setStyle(Paint.Style.STROKE);//填充
        headTailPaint.setColor(Color.parseColor("#ff9801"));
        headTailPaint.setStrokeWidth(58);
        headTailPaint.setAntiAlias(true);

        //尾部画笔
        headTailPaint1 = new Paint();
        headTailPaint1.setStyle(Paint.Style.STROKE);//填充
        headTailPaint1.setColor(Color.parseColor("#ffcb05"));
        headTailPaint1.setStrokeWidth(58);
        headTailPaint1.setAntiAlias(true);

        smallPaint = new Paint();
        smallPaint.setColor(Color.BLACK);
        smallPaint.setStyle(Paint.Style.STROKE);
        smallPaint.setStrokeWidth(52);
        smallPaint.setAntiAlias(true);

        linePaint1 = new Paint();
        linePaint1.setColor(Color.WHITE);
        linePaint1.setStyle(Paint.Style.FILL);
        linePaint1.setStrokeWidth(5);
        linePaint1.setAntiAlias(true);

        linePaint2 = new Paint();
        linePaint2.setColor(Color.GRAY);
        linePaint2.setStyle(Paint.Style.FILL);
        linePaint2.setStrokeWidth(2);
        linePaint2.setAntiAlias(true);

        //画数字:
        clockPaint = new Paint();
        clockPaint.setColor(Color.parseColor("#8F8F91"));
        clockPaint.setTextSize(28);
        clockPaint.setTextAlign(Paint.Align.CENTER);
        clockPaint.setAntiAlias(true);

        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(36);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setAntiAlias(true);
    }

(2)有了画笔,我们就开始画想要的形状了:
可以把这个控件理解为一个进度控件,就好画多了,所以我这个控件在外部调用需要传递一个progrgess进度

 public void setProgress(int progress) {
        this.progress = progress;
        invalidate();
    }
 //画中间的文字:将进度转化为文字:2*progress min
        int number = 2*progress;
        String text = "";
        int hour = 0;
        int min = 0;
        if(number>60){
            hour = number/60;
            min = number%60;
            text= hour+ "小时"+min+"分钟";
        }else{
            text =  number+"分钟";
        }
	 canvas.drawText(text, mWidth / 2, mHeight / 2 + 20, textPaint);

画好文字之后,将画布平移到中心点,为什么这样做呢?因为Android坐标系是从左上角开始的,个人觉得这样某些点的坐标不好计算。而平移之后,就和我们数学当中的坐标系保持一致了,这个大家非常熟悉,计算起来就得心应手了。
仿ios 就寝控件
平移坐标到中心的代码:

		 //坐标转移到中心:
        canvas.translate(mWidth / 2, mHeight / 2);

坐标转移到中心,画下面的就非常简单了:

  		//画外面的大圆:
        canvas.drawCircle(0, 0, raduis, blackPaint);

画圆上的数字:我这个写得比较复杂一点,是由于实际过程中,有些数字会画得有点偏,没有对准,然后就针对一些具体的数字做了微调,里面看似复杂的(raduis - 80) * Math.cos(Math.toRadians(90))这个其实就是圆弧上坐标的计算:
仿ios 就寝控件

 //画文字:不要旋转
        for (int i = 0; i < 12; i++) {
            //先从12位置开始算起:
            if (i == 0) {
                canvas.drawText("12", (float) ((raduis - 80) * Math.cos(Math.toRadians(90))), (float) (-(raduis - 80) * Math.sin(Math.toRadians(90))), clockPaint);
            } else if (i <= 3) {
                if(i==3){
                    canvas.drawText(i + "", (float) ((raduis - 80) * Math.cos(Math.toRadians(90-i * 30))+10), (float) (-(raduis - 80) * Math.sin(Math.toRadians(90-i * 30))+10), clockPaint);
                }else{
                    canvas.drawText(i + "", (float) ((raduis - 80) * Math.cos(Math.toRadians(90-i * 30))), (float) (-(raduis - 80) * Math.sin(Math.toRadians(90-i * 30))), clockPaint);
                }

            } else if (i <= 6) {
                int newIndex = i-3;
                canvas.drawText(i + "", (float) ((raduis - 80) * Math.cos(Math.toRadians(newIndex * 30))), (float) (((raduis - 80) * Math.sin(Math.toRadians(newIndex * 30))+20)), clockPaint);
            } else if (i <= 9) {
                int newIndex1 = i-6;
                if(i==9){
                    canvas.drawText(i + "", (float) (-(raduis - 80) * Math.cos(Math.toRadians(90 - newIndex1 * 30))-10), (float) ((raduis - 80) * Math.sin(Math.toRadians(90 - newIndex1 * 30))+10), clockPaint);
                }else{
                    canvas.drawText(i + "", (float) (-(raduis - 80) * Math.cos(Math.toRadians(90 - newIndex1 * 30))), (float) ((raduis - 80) * Math.sin(Math.toRadians(90 - newIndex1 * 30))+20), clockPaint);
                }
            } else if (i <= 11) {
                int newIndex2 = i-9;
                canvas.drawText(i + "", (float) (-(raduis - 80) * Math.cos(Math.toRadians(newIndex2 * 30))), (float) (-(raduis - 80) * Math.sin(Math.toRadians(newIndex2* 30))), clockPaint);
            }
        }

之后是画刻度,这比较简单,每画完一根就旋转下角度就好

		 //画刻度:
        for (int i = 0; i < 48; i++) {
            if (i % 4 == 0) {
                canvas.drawLine(0, raduis - 20 - 30, 0, raduis - 5 - 30, linePaint1);
            } else {
                canvas.drawLine(0, raduis - 20 - 30, 0, raduis - 5 - 30, linePaint2);
            }
            canvas.rotate(7.5f);
        }

画总计睡眠时间走过的圆弧:

 mRectF = new RectF(-mWidth / 2 + 40, -mWidth / 2 + 40, mWidth / 2 - 40, mWidth / 2 - 40);
        //设置渐变色
        LinearGradient linearGradient = new LinearGradient((float) (raduis * Math.cos(Math.toRadians(progress / 2))), (float) (-raduis * Math.sin(Math.toRadians(progress / 2))), (float) (raduis * Math.cos(Math.toRadians(progress / 2))), (float) (raduis * Math.sin(Math.toRadians(progress / 2))), Color.parseColor("#ff9801"), Color.parseColor("#ffcb05"), Shader.TileMode.CLAMP);
        yellowPaint.setShader(linearGradient);
        canvas.drawArc(mRectF, -progress / 2, progress, false, yellowPaint);

首尾处也有2个半圆,这个是为了让进度条头尾处的弧度好看不生硬。

 //画首尾2端:头部:尾部,角度转化为弧度
        canvas.drawCircle((float) (raduis * Math.cos(Math.toRadians(progress / 2))), (float) (-raduis * Math.sin(Math.toRadians(progress / 2))), 1, headTailPaint);
        canvas.drawCircle((float) (raduis * Math.cos(Math.toRadians(progress / 2))), (float) (raduis * Math.sin(Math.toRadians(progress / 2))), 1, headTailPaint1);

最后就是首尾处各有一个黑色小圆:

 //画中间套住的两个圆
        canvas.drawCircle((float) (raduis * Math.cos(Math.toRadians(progress / 2 - 0.3))), (float) (-raduis * Math.sin(Math.toRadians(progress / 2 - 0.3))), 1, smallPaint);
        canvas.drawCircle((float) (raduis * Math.cos(Math.toRadians(progress / 2 - 0.3))), (float) (raduis * Math.sin(Math.toRadians(progress / 2 - 0.3))), 1, smallPaint);

以上画完之后,这个控件自定义就基本完成,但是我们要给它加点动画效果,定义一个方法,不断改变progress的值即可

public void startAnimation(){
        mObjectAnimator = ObjectAnimator.ofInt(this, "progress", 0, progress);
        mObjectAnimator.setDuration(4000);
        mObjectAnimator.start();
    }

一个完整的仿ios就寝控件就做好了。

外界调用方法如下:

  1. xml布局中引入此控件
  2. 设置progress
  3. 调用startAnimation

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-12-05
  • 2021-10-08
  • 2021-10-22
  • 2022-02-25
  • 2022-12-23
  • 2021-10-21
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-02
  • 2021-10-24
  • 2021-11-09
  • 2021-07-03
相关资源
相似解决方案