为了账号安全,请及时绑定邮箱和手机立即绑定

Swift的一次函数式之旅

标签:
Android

ui.png 在闲暇冲浪的时候,无意间看到了这张设计图,眼睛一亮,感觉这个设计和创意非常酷,打算着手实现一下。

分析设计,我们分两部分来实现: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();
复制代码

渐变的背景.gif

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();
    }
复制代码

绘制小圆点.png

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),
            ),

          )
        ],
      ),
    );
  }
复制代码

整体效果图如下:

效果图.gif

作者:码农钉某人
链接:https://juejin.cn/post/6959093549304381454
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消