iOS開發(fā)Fultter探索-自定義的涂鴉畫板(12)

qnz4d-vd1dz.gif

前言

今天看下Flutter中關于繪圖相關的部件,這里主要用到的有:
CustomPaint()

  CustomPaint({
    this.painter,  
    this.foregroundPainter,
    this.size = Size.zero,
    this.isComplex = false,
    this.willChange = false,
    Widget child,
  }) 
}

內部需要接受一個painter,是CustomPainter類型,這里我們需要繼承并創(chuàng)建一個CustomPainter子類,并傳給painter,這里我創(chuàng)建了_BackDrawPainter子類,并重寫了void paint(Canvas canvas, Size size)方式,這個方式是繪制的核心;當需要繪制的時候會觸發(fā)paint()方法;shouldRepaint () 會在繪制前觸發(fā),這里可以通過一些邏輯判斷來控制是否需要執(zhí)行一次繪制;

class _BackDrawPainter extends CustomPainter {
  List<Path> linePaths;
  int drawType;
  _BackDrawPainter(this.linePaths, {this.drawType});
  int linesCount;
  @override
  void paint(Canvas canvas, Size size) {
//    print('_BackDrawPainter draw......');
    var custompPaint = Paint()
      ..style = PaintingStyle.stroke
      ..color = Colors.black
      ..isAntiAlias = true
      ..strokeJoin = StrokeJoin.round
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 2.0;
    for (int i = 0; i < linePaths.length; i++) {
      Path path = linePaths[i];
      canvas.drawPath(path, custompPaint);
    }
  }

  @override
  bool shouldRepaint(_BackDrawPainter oldDelegate) {
    return oldDelegate.linePaths.length != this.linePaths.length;
  }
}

同時我也給foregroundPainter,配置了CustomPainter,這里主要維護實時繪制時的處理:

class _RealDrawPainter extends CustomPainter {

  Path realPath;
  int realDrawType;

  _RealDrawPainter(this.realPath, {this.realDrawType});

  @override
  void paint(Canvas canvas, Size size) {
//    print('_RealDrawPainter draw...... $realPath');
    if (realPath != null) {
//      print('內部=>_RealDrawPainter draw...... $realPath');
      var paint = Paint()
        ..style = PaintingStyle.stroke //填充
        ..color = Colors.black
        ..strokeJoin = StrokeJoin.round
        ..strokeCap = StrokeCap.round
//        ..blendMode = BlendMode.colorDodge
        ..isAntiAlias = true
        ..strokeWidth = 2.0;
      canvas.drawPath(realPath, paint);
    }
  }
  @override
  bool shouldRepaint(_RealDrawPainter oldDelegate) {
    return realPath != null;
  }
}

其它的就是繪制部分了,通過GestureDetector()來接收并處理事件:

@override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        GestureDetector(
          onPanDown: (DragDownDetails details) {
            _currentPath = Path();
            if(_drawType == 0){//一般曲線
              _currentPath = Path.from(_currentPath)
                ..moveTo(details.localPosition.dx, details.localPosition.dy);
            }else if(_drawType == 1){//矩形
              _currentPath = Path.from(_currentPath)..addRect(Rect.fromPoints(details.localPosition, details.localPosition));
            }else if(_drawType == 2){//內切圓
              _currentPath = Path.from(_currentPath)..addOval(Rect.fromPoints(details.localPosition, details.localPosition));
            }else if(_drawType == 3){//正圓
              _currentPath = Path.from(_currentPath)..addArc(Rect.fromCircle(center: details.localPosition,radius: 0.0), 0, 2.0 * pi);
            }
            _startPoint = details.localPosition;
            setState(() {});
          },
          onPanUpdate: (DragUpdateDetails details) {
            if(_drawType == 0){//一般曲線
              _currentPath = Path.from(_currentPath)
                ..lineTo(details.localPosition.dx, details.localPosition.dy);
            }else if(_drawType == 1){//矩形
              _currentPath.reset();
              _currentPath = Path.from(_currentPath)..addRect(Rect.fromPoints(_startPoint, details.localPosition));
            }else if(_drawType == 2){//內切圓
              _currentPath.reset();
              _currentPath = Path.from(_currentPath)..addOval(Rect.fromPoints(_startPoint, details.localPosition));
            }else if(_drawType == 3){//正圓
              _currentPath.reset();
              Rect frame =  Rect.fromPoints(_startPoint, details.localPosition);
              double radius = sqrt(pow(frame.width,2) * pow(frame.height,2));
              print('radius:$radius');
              _currentPath = Path.from(_currentPath)..addArc(Rect.fromCircle(center: _startPoint,radius: radius*0.5), 0, 2.0 * pi);
            }
            setState(() {});
          },
          onPanEnd: (DragEndDetails details) {
              print('onPanEnd');
              linePathArray = List.from(linePathArray)..add(_currentPath);
              setState(() {});
              _currentPath = null;
          },
          child: ClipRect(
            child: Container(
              child: CustomPaint(
                painter: _BackDrawPainter(linePathArray,drawType: _drawType),
                foregroundPainter: _RealDrawPainter(_currentPath,realDrawType: _drawType),
                isComplex: true,
                willChange: true,
                child: Container(),
              ),
            ),
          )
        ),
        Positioned(
          width: 700,
          height: 40,
          bottom: 0,
          child: Container(
//            color: Colors.red,
            child: Row(
              children: _getButtons(),
            ),
          )
        )
      ],
    );
  }

問題

  • shouldRepaint()并并不是我理解的方式來控制繪制的,有待研究
    我想要的效果是當我在foregroundPainter實時繪制時,painter不要觸發(fā)繪制,當foregroundPainter結束后執(zhí)行一次painter繪制,但繪制時需要setState(),這樣兩個painter都會執(zhí)行,而且在paintershouldRepaint ()中無論返回trueorfalse都會發(fā)生繪制,問題?
  • 性能有待考量

總結

參考文獻:https://book.flutterchina.club/chapter10/custom_paint.html

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容