欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter CustomPaint自定義繪畫示例詳解

 更新時間:2022年11月09日 14:41:04   作者:風(fēng)雨_83  
這篇文章主要為大家介紹了Flutter CustomPaint自定義繪畫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

CustomPaint是Flutter中用于自由繪制的一個widget,它與android原生的繪制規(guī)則基本一致,以當(dāng)前Canves(畫布)的左上角為原點進(jìn)行繪制。在有些場景中,我們會需要繪制一些高度定制化的組件,比如 UI 設(shè)計師給我們出了個難題 —— 弄一個奇形怪狀的邊框。這個時候我們就不能直接使用 Flutter 自帶的那些組件了,而是需要手動繪制組件,那就會需要用到 CuntomPaint 組件。CustomPaint 組件和前端的 Canvas差不多,允許我們在一個畫布上繪制各種元素,包括點、線、矩形、圓弧、文字、圖片等等。

CustomPaint 介紹

CustomPaint是一個 Widget,其中有三個重要的參數(shù):

CustomPaint(
  child: childWidget(),
  foregroundPainter: foregroundPainter(),
  painter: backgroundPainter(),
)

childCustomPaint的子組件;

painterforegroundPainter:都是 CustomPainter 類,用于定義 canvas 繪制的內(nèi)容。區(qū)別在于,首先是執(zhí)行 painter 的繪制指令。然后是在背景上渲染 child 子組件。最后,foregroundPainter 的內(nèi)容會繪制在 child 上一層。

案例展示:

import 'package:demo202112/utils/common_appbar.dart';
import "package:flutter/material.dart";
class MyPaint extends StatelessWidget {
  const MyPaint({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: getAppBar('CustomPaint'),
      body: CustomPaint(
        painter: MyPainer(),
        child: Container(height: 80,width: 80,child: Text('child測試'),color: Colors.red,),
        foregroundPainter: MyForeGroundPainer(),
      ),
    );
  }
}
class MyPainer extends CustomPainter{
  late Paint _paint;
  @override
  void paint(Canvas canvas, Size size) {
    _paint = Paint();
    _paint.color = Colors.blue;
    canvas.drawCircle(Offset(100, 100), 100, _paint);
    canvas.drawLine(Offset(300, 300), Offset(400, 400), _paint);
    // TODO: implement paint
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}
class MyForeGroundPainer extends CustomPainter{
  late Paint _paint;
  @override
  void paint(Canvas canvas, Size size) {
    _paint = Paint();
    _paint.color = Colors.green;
    canvas.drawCircle(Offset(100, 100), 70, _paint);
    // canvas.drawLine(Offset(300, 300), Offset(400, 400), _paint);
    // TODO: implement paint
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}

運行效果:

child: 紅色區(qū)域,傳入一個子widget,這個widget圖層會在painter在上,在foregroundPainter之下。

painter:藍(lán)色區(qū)域。

foregroundPainter:綠色區(qū)域,它與painter都是CustomPainter類型的。通過名字大概也就知道了,它會在painter的上層,也就是說在同樣的位置去繪制,foregroundPainter 會覆蓋painter。

CustomPainter提供了一個paint繪圖方法供我們繪制圖形,該方法攜帶canvassize兩個參數(shù),其中 canvas 是畫布,size 是畫布大小。canvas 提供了很多繪制圖形的方法,比如繪制路徑、矩形、圓形和線條等等。

//畫圓
drawCircle(Offset c, double radius, Paint paint) → void
//畫圖片
drawImage(Image image, Offset p, Paint paint) → void
//畫九宮圖
drawImageNine(Image image, Rect center, Rect dst, Paint paint) → void
//畫線
drawLine(Offset p1, Offset p2, Paint paint) → void
//畫橢圓
drawOval(Rect rect, Paint paint) → void
//畫文字
drawParagraph(Paragraph paragraph, Offset offset) → void
//畫Rect區(qū)域
drawRect(Rect rect, Paint paint) → void
//畫陰影
drawShadow(Path path, Color color, double elevation, bool transparentOccluder) → void

繪制點

class MyPoints extends CustomPainter{
  Paint _paint = Paint()
  ..color = Colors.red
  ..strokeWidth = 15;
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    var points =[
      Offset(0, 0),
      Offset(size.width/2, size.height/2),
      Offset(size.width, size.height),
    ];
    canvas.drawPoints(PointMode.points, points, _paint);
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}

運行效果:

PointMode3種模式

  • points:點
  • lines:將2個點繪制為線段,如果點的個數(shù)為奇數(shù),最后一個點將會被忽略
  • polygon:將整個點繪制為一條線

繪制線 和路徑

class MyGraph extends CustomPainter{
  final Paint _paint = Paint()
    ..color = Colors.red
    ..strokeWidth = 15;
  final Paint _paintPath = Paint()
    ..color = Colors.blue
    ..strokeWidth = 5
  ..style = PaintingStyle.fill;
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    //繪制線
    canvas.drawLine(Offset(0, 30),Offset(size.width-30, size.height), _paint);
    //繪制路徑
    var _path = Path()
    ..moveTo(0, 0)
    ..lineTo(size.width, 0)
    ..lineTo(size.width, size.height)
    ..close();
    canvas.drawPath(_path, _paintPath);
    //這里注意Paint.style,還可以設(shè)置為PaintingStyle.fill,
    //繪制圓形
    canvas.drawCircle(Offset(size.width/2+50, size.height/2+50), 20, _paint);
    //繪制橢圓
    canvas.drawOval(Rect.fromLTRB(0, 0, size.width, size.height/2), _paint);
    //繪制弧
    canvas.drawArc(Rect.fromLTRB(0, 0, size.width, size.height), 0, pi/2, true, _paint);
    //繪制圓角矩形
    canvas.drawRRect(RRect.fromLTRBR(0, 0, size.width, size.height, Radius.circular(10)), _paint);
  }
  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    throw UnimplementedError();
  }
}

運行效果:

繪制五子棋

首先繪制背景,淡黃色,再繪制棋盤網(wǎng)格線,隨后繪制黑白子,具體代碼:

class CustomPaintRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: CustomPaint(
        size: Size(300, 300), //指定畫布大小
        painter: MyPainter(),
      ),
    );
  }
}
class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double eWidth = size.width / 15;
    double eHeight = size.height / 15;
    //畫棋盤背景
    var paint = Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill //填充
      ..color = Color(0x77cdb175); //背景為紙黃色
    canvas.drawRect(Offset.zero & size, paint);
    //畫棋盤網(wǎng)格
    paint
      ..style = PaintingStyle.stroke //線
      ..color = Colors.black87
      ..strokeWidth = 1.0;
    for (int i = 0; i <= 15; ++i) {
      double dy = eHeight * i;
      canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paint);
    }
    for (int i = 0; i <= 15; ++i) {
      double dx = eWidth * i;
      canvas.drawLine(Offset(dx, 0), Offset(dx, size.height), paint);
    }
    //畫一個黑子
    paint
      ..style = PaintingStyle.fill
      ..color = Colors.black;
    canvas.drawCircle(
      Offset(size.width / 2 - eWidth / 2, size.height / 2 - eHeight / 2),
      min(eWidth / 2, eHeight / 2) - 2,
      paint,
    );
    //畫一個白子
    paint.color = Colors.white;
    canvas.drawCircle(
      Offset(size.width / 2 + eWidth / 2, size.height / 2 - eHeight / 2),
      min(eWidth / 2, eHeight / 2) - 2,
      paint,
    );
  }
  //在實際場景中正確利用此回調(diào)可以避免重繪開銷,本示例我們簡單的返回true
  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

運行效果:

繪制是比較昂貴的操作,所以我們在實現(xiàn)自繪控件時應(yīng)該考慮到性能開銷,下面是兩條關(guān)于性能優(yōu)化的建議:

  • 盡可能的利用好shouldRepaint返回值;在UI樹重新build時,控件在繪制前都會先調(diào)用該方法以確定是否有必要重繪;假如我們繪制的UI不依賴外部狀態(tài),那么就應(yīng)該始終返回false,因為外部狀態(tài)改變導(dǎo)致重新build時不會影響我們的UI外觀;如果繪制依賴外部狀態(tài),那么我們就應(yīng)該在shouldRepaint中判斷依賴的狀態(tài)是否改變,如果已改變則應(yīng)返回true來重繪,反之則應(yīng)返回false不需要重繪。
  • 繪制盡可能多的分層;在上面五子棋的示例中,我們將棋盤和棋子的繪制放在了一起,這樣會有一個問題:由于棋盤始終是不變的,用戶每次落子時變的只是棋子,但是如果按照上面的代碼來實現(xiàn),每次繪制棋子時都要重新繪制一次棋盤,這是沒必要的。優(yōu)化的方法就是將棋盤單獨抽為一個Widget,并設(shè)置其shouldRepaint回調(diào)值為false,然后將棋盤Widget作為背景。然后將棋子的繪制放到另一個Widget中,這樣落子時只需要繪制棋子。

總結(jié)

CustomPaint class提供了讓用戶自定義widget的能力,它暴露了一個canvas,可以通過這個canvas來繪制widget,CustomPaint會先調(diào)用painter繪制背景,然后再繪制child,最后調(diào)用foregroundPainter來繪制前景。

canvas--畫布,真正的繪制是由canvas跟paint來完成的,畫布提供了各種繪制的接口來繪制圖形,除此以外畫布還提供了平移、縮放、旋轉(zhuǎn)等矩陣變換接口,畫布都有固定大小跟形狀,還可以使用畫布提供的裁剪接口來裁剪畫布的大小形狀等等

Paint---筆畫,是用來設(shè)置在畫布上面繪制圖形時的一些筆畫屬性,如:顏色、線寬、繪制模式、抗鋸齒等等.

自繪控件非常強大,理論上可以實現(xiàn)任何2D圖像外觀,想更深入的了解,可以找到其對應(yīng)的RenderObject對象,如Text Widget最終會通過RenderParagraph對象來通過Canvas實現(xiàn)文本繪制邏輯。了解了更底層的繪制邏輯,才能更好的在實際項目中靈活應(yīng)用。

以上就是Flutter CustomPaint自定義繪畫示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter CustomPaint 繪畫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • iOS搭建簡易購物車頁面

    iOS搭建簡易購物車頁面

    這篇文章主要為大家詳細(xì)介紹了iOS搭建簡易購物車頁面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • iOS如何獲取當(dāng)前View所在控制器的方法

    iOS如何獲取當(dāng)前View所在控制器的方法

    在開發(fā)iOS的時候經(jīng)常需要獲取當(dāng)前View所在的控制器,下面小編給大家分享個方法,文章給出了示例代碼,對大家的學(xué)習(xí)和理解很有幫助,下面來一起看看吧。
    2016-09-09
  • iOS通過http post上傳圖片

    iOS通過http post上傳圖片

    這篇文章主要介紹了iOS通過http post上傳圖片的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • 檢測iOS設(shè)備是否越獄的方法

    檢測iOS設(shè)備是否越獄的方法

    這篇文章主要介紹了檢測iOS設(shè)備是否越獄的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下
    2015-10-10
  • iOS NSTimer循環(huán)引用的辦法

    iOS NSTimer循環(huán)引用的辦法

    這篇文章主要介紹了iOS NSTimer循環(huán)引用的辦法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • iOS監(jiān)聽手機(jī)鎖屏狀態(tài)

    iOS監(jiān)聽手機(jī)鎖屏狀態(tài)

    iPhone的鎖屏監(jiān)測分為兩種方式監(jiān)聽,本文給大家介紹的非常詳細(xì),具體內(nèi)容詳情大家通過本文詳細(xì)了解下吧
    2017-05-05
  • iOS 如何高效的使用多線程

    iOS 如何高效的使用多線程

    這篇文章主要介紹了iOS 如何高效使用的多線程,幫助大家提高ios 開發(fā)的效率,感興趣的朋友可以了解下
    2020-09-09
  • iOS開發(fā)狀態(tài)欄及設(shè)置功能全面詳解

    iOS開發(fā)狀態(tài)欄及設(shè)置功能全面詳解

    這篇文章主要為大家介紹了iOS開發(fā)狀態(tài)欄及設(shè)置功能全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • iOS中多線程的經(jīng)典崩潰總結(jié)大全

    iOS中多線程的經(jīng)典崩潰總結(jié)大全

    這篇文章主要給大家介紹了關(guān)于iOS中多線程的一些經(jīng)典崩潰的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • iOS推送增加右側(cè)顯示圖Service Extension

    iOS推送增加右側(cè)顯示圖Service Extension

    這篇文章主要為大家介紹了iOS推送增加右側(cè)顯示圖Service Extension,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評論