在空闲冲浪的时候,无意间看到了这张设计图,眼睛一亮,感觉这个设计和创意十分酷,打算着手实现一下。对于设计图的作者没找到,如果有人晓得的话,请告知我,我会增加设计援用的,欢送来我的Github

设计图如下:

整体效果图如下:

源码Github: https://github.com/DingMouRen...

剖析设计,咱们分两局部来实现:1.表盘局部  2.下部的开关局部。以下为了省略的逻辑代码,须要残缺的代码的请移步Github

1.表盘局部

1.1 察看表盘的款式,把须要做的工作划分
  • 突变的背景,用作秒针
  • 绘制红色的小圆点
  • 绘制带暗影的黄色大圆点,用作时针
  • 绘制工夫文字
  • 开启定时器,让时钟动起来

表盘局部很多都是须要咱们本人绘制的,这里咱们能够应用CustomPaint来实现所有的绘制。CustomPaint提供了自定义widget的能力,它会裸露一个canvas,能够通过这个canvas来绘制widget,有没有很相熟,跟原生android的canvas是不是很类似。

咱们这里将表盘作为背景来绘制,也就是painter属性,自定义CustomPainter,重写paint(Canvas canvas,Size size)shouldRepaint(covariant CustomPainter oldDelegate)函数,来实现咱们所有的绘制操作。好的,让咱们欢快的开始吧o( ̄▽ ̄)ブ。

1.2 突变的背景,用作秒针

从图中咱们能够晓得,渐变色是扫描突变,并布满屏幕,同时地位在整个屏幕的上半局部。首先创立扫描突变对象,这个扫描突变能够创立出一个着色器,而后咱们将这个着色器附着在一个画笔上,通过画布去绘制。留神画布绘制时canvas.save()canvas.restore()在对应机会的调用。要让秒针动起来,须要开启一个Timer定时工作,每一秒刷新一下视图。

var circle = Rect.fromCircle(center: Offset(0, 0), radius: _screenHeight);    //扫面突变    var sweepGradient = SweepGradient(      colors: [        _startColor,        _endColor      ],    );    //画笔对象   Paint  _paintGradient = Paint()      ..isAntiAlias = true      ..shader = sweepGradient.createShader(circle)      ..style = PaintingStyle.fill;    //获取以后的工夫    DateTime dateTime = DateTime.now();    var hour = dateTime.hour;    var minute = dateTime.minute;    var second = dateTime.second;    //画布位移    canvas.translate(_screenWidth / 2, _screenHeight / 100 * 35);    //绘制突变背景    canvas.save();    //每秒旋转对应角度,模仿秒针挪动    canvas.rotate(_getRotate(second));    canvas.drawCircle(Offset(0, 0), _screenHeight, _paintGradient);    canvas.restore();

1.3 绘制红色的小圆点

这里须要绘制24个红色小圆点,平均分360度,通过画布的旋转来绘制不同角度的红色小圆点。

for (double i = 0; i < _numPoint; i++) {      canvas.save();      //      double deg = 360 / _numPoint * i;      canvas.rotate(deg / 180 * pi);        _paintDial.color = Colors.white;       //绘制红色小圆点       canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);       canvas.restore();      ......      canvas.restore();    }

1.4 绘制带暗影的黄色大圆点,用作时针

黄色的大圆点作为时针来解决,因为时针和画布的角度不是吻合的,须要换算,另外咱们为大圆点增加暗影。

for (double i = 0; i < _numPoint; i++) {      canvas.save();      double deg = 360 / _numPoint * i;      canvas.rotate(deg / 180 * pi);      _paintDial.color = Colors.white;      canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);      //isShowBigCircle(hour, i)是判断以后圆点是不是以后的时针地位      if (isShowBigCircle(hour, i)) {        //绘制暗影        Path path = Path()          ..addArc(Rect.fromCircle(center: Offset(_radius, 0), radius: 8), 0,              pi * 2);        canvas.drawShadow(path, Colors.yellow, 4, true);        //绘制小时的圆点        _paintDial.color = Colors.yellow;        canvas.drawCircle(Offset(_radius, 0), 8, _paintDial);      } else {        _paintDial.color = Colors.white;        canvas.drawCircle(Offset(_radius, 0), 3, _paintDial);      }      canvas.restore();      ......    }
1.5 绘制工夫文字

画布绘制文字调用canvas.drawParagraph(Paragraph Offset)函数,Paragraph 对象通过ParagraphBuilder来创立,字体款式能够通过ParagraphBuilder来设置

//设置文字款式    _timeParagraphBuilder = ParagraphBuilder(ParagraphStyle(        textAlign: TextAlign.center,        fontSize: 70,        maxLines: 1,        fontWeight: FontWeight.bold));   //获取以后的工夫   DateTime dateTime = DateTime.now();    var hour = dateTime.hour;    var minute = dateTime.minute;    var second = dateTime.second;      //绘制文字      canvas.save();      _timeParagraphBuilder.addText(_getTimeStr(hour, minute));      Paragraph paragraph = _timeParagraphBuilder.build();      paragraph.layout(ParagraphConstraints(width: 230));      canvas.drawParagraph(paragraph, Offset(-115,-42));      canvas.restore();

2. 开关局部

整体布局剖析,开关局部的UI绝对于整个屏幕来讲位于底部,应用 Stack 和 Align就能够实现这种布局款式。

  //整体布局  @override  Widget build(BuildContext context) {    return Scaffold(        body: Stack(      children: [        CustomPaint(painter: DialPlate(context,Color.fromARGB(255, 70, 0, 144),Color.fromARGB(255, 121, 83, 254))),        _getAlarms(),      ],    ));  }//下部视图  _getAlarms() {    return Align(      alignment: Alignment.bottomLeft,      child: Container(        margin: EdgeInsets.only(left: 16, right: 16),        height: 200,        width: double.infinity,        child: Column(          children: [            _getRow1(),            _getRow2(),            _getRow3(),          ],        ),      ),    );  }//_getRow1()、_getRow2()、_getRow3()相似 _getRow1() {    return Container(      alignment: Alignment.centerLeft,      width: double.infinity,      height: 50,      child: Row(        children: [          Text(            '06:45',            style: TextStyle(                fontSize: 25,                color: _firstSwitch == true ? _colorOn : _colorOff),          ),          Padding(            padding: EdgeInsets.only(left: 18),            child: Text(              'Wake up',              style: TextStyle(                  fontSize: 18,                  color: _firstSwitch == true ? _colorOn : _colorOff),            ),          ),          Expanded(child: SizedBox()),          Container(            width: 90,            height: 10,            child: Switch(                value: _firstSwitch,                onChanged: (onChanged) {                  setState(() {_firstSwitch = onChanged;});                },                activeColor: _switchActiveColor,                activeTrackColor: Colors.black.withAlpha(100),                inactiveThumbColor: _switchInActiveColor,                inactiveTrackColor: Colors.black.withAlpha(20),            ),          )        ],      ),    );  }

原文链接:https://juejin.cn/post/695909...

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享Android干货,交换Android技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!