在Flutter开发中,我们常用的Text、Image、Container等组件,本质上都是Flutter底层通过Canvas绘制而成的。而当原生组件无法满足我们的个性化需求——比如绘制自定义图表、复杂形状、动态效果、自定义控件时,CustomPaintCanvas 就是最核心的解决方案。

很多开发者对CustomPaint望而却步,觉得它“复杂、难上手”,其实核心逻辑很简单:CustomPaint是一个“画布容器”,而Canvas是真正的“画笔”,我们只需要在Canvas上调用各种绘制方法,就能实现任意想要的图形和效果。

本文将以「实战为核心」,从基础到进阶,搭配6个可直接复制运行的示例,覆盖80%的实际开发场景,帮你快速掌握CustomPaint与Canvas的使用技巧,看完就能上手写自定义绘制。

前置说明:本文所有示例基于Flutter 3.10+,无需额外引入依赖,代码可直接复制到项目中运行,每个示例都附带详细注释,新手也能轻松看懂。

一、核心概念:CustomPaint与Canvas是什么?

在开始实战前,先搞懂3个核心类的关系,避免 confusion:

  • CustomPaint:Flutter提供的“画布组件”,本质是一个StatefulWidget,它的作用是提供一个Canvas,并控制画布的大小、背景等基础属性,我们需要通过它的 painter 属性传入自定义的绘制逻辑。

  • CustomPainter:自定义绘制的“逻辑载体”,我们必须继承这个类,并重写 paint(Canvas canvas, Size size) 方法——这是绘制的核心方法,所有图形都在这个方法里通过Canvas绘制。同时需要重写 shouldRepaint 方法,控制是否重绘(优化性能)。

  • Canvas:真正的“画笔”,提供了各种绘制方法(绘制点、线、矩形、圆形、路径等),我们通过调用它的方法,就能在画布上“画画”。

核心流程:CustomPaint → 传入CustomPainter → 在CustomPainter的paint方法中,通过Canvas绘制图形 → 渲染到屏幕。

小技巧:Canvas的绘制是“叠加式”的——后绘制的图形会覆盖先绘制的图形,类似PS的图层叠加,这个特性在绘制复杂图形时非常重要。

二、基础实战:5个入门示例(覆盖核心绘制方法)

入门示例以“简单、实用”为原则,覆盖Canvas最常用的绘制方法,每个示例都可独立运行,帮你快速熟悉API。

示例1:绘制基础图形(点、线、矩形、圆形)

这是最基础的示例,覆盖Canvas的4个核心基础绘制方法,掌握它,就掌握了CustomPaint的入门用法。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Canvas基础绘制',
      home: Scaffold(
        appBar: AppBar(title: const Text('基础图形绘制')),
        body: Center(
          // 1. 自定义画布:设置宽高、背景
          child: CustomPaint(
            size: const Size(300, 300), // 画布大小
            painter: BasicShapePainter(), // 传入自定义绘制逻辑
            foregroundPainter: null, // 前景绘制(覆盖在背景之上)
            backgroundImage: null, // 画布背景图
          ),
        ),
      ),
    );
  }
}

// 2. 自定义绘制逻辑:继承CustomPainter
class BasicShapePainter extends CustomPainter {
  // 画笔:控制线条颜色、宽度、样式等
  final Paint _paint = Paint()
    ..color = Colors.blue // 颜色
    ..strokeWidth = 3 // 线条宽度
    ..style = PaintingStyle.stroke; // 绘制样式:stroke(描边)、fill(填充)

  @override
  void paint(Canvas canvas, Size size) {
    // 画布中心坐标(方便后续绘制居中图形)
    final center = Offset(size.width / 2, size.height / 2);

    // 1. 绘制点(参数:偏移量、画笔)
    canvas.drawPoint(center, _paint..color = Colors.red, Offset.zero);

    // 2. 绘制线(参数:起点、终点、画笔)
    canvas.drawLine(
      Offset(50, 50), // 起点
      Offset(250, 50), // 终点
      _paint..color = Colors.green,
    );

    // 3. 绘制矩形(参数:矩形区域、画笔)
    // Rect.fromLTWH:left、top、width、height
    canvas.drawRect(
      Rect.fromLTWH(50, 100, 200, 80),
      _paint..color = Colors.orange,
    );

    // 4. 绘制圆形(参数:中心点、半径、画笔)
    canvas.drawCircle(
      center,
      60, // 半径
      _paint..color = Colors.purple,
    );
  }

  // 重写:控制是否重绘(优化性能)
  // 返回true:每次刷新都重绘;返回false:只有当Painter实例改变时才重绘
  @override
  bool shouldRepaint(covariant BasicShapePainter oldDelegate) {
    return false;
  }
}

关键说明:

  • Paint是画笔,可控制颜色、线条宽度、绘制样式(描边/填充)、线条圆角、阴影等,后续所有示例都会用到。

  • Offset 是坐标类,代表画布上的一个点(x轴、y轴),画布的左上角是原点 (0,0),向右是x轴正方向,向下是y轴正方向。

  • Rect 是矩形类,常用 fromLTWH(左、上、宽、高)和 fromCenter(中心点、宽、高)两种构造方法。

示例2:绘制路径(Path)—— 自定义不规则图形

当基础图形满足不了需求(比如绘制三角形、多边形、自定义曲线)时,就需要用 Path(路径)。Path可以拼接多个点,形成任意不规则图形,是自定义绘制的核心。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Canvas Path绘制',
      home: Scaffold(
        appBar: AppBar(title: const Text('自定义路径绘制')),
        body: Center(
          child: CustomPaint(
            size: const Size(300, 300),
            painter: PathPainter(),
          ),
        ),
      ),
    );
  }
}

class PathPainter extends CustomPainter {
  final Paint _paint = Paint()
    ..color = Colors.pink
    ..strokeWidth = 2
    ..style = PaintingStyle.fill; // 填充模式,绘制闭合图形

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);

    // 1. 创建路径
    final Path path = Path();

    // 2. 拼接路径(绘制三角形)
    path.moveTo(center.dx, center.dy - 80); // 移动到三角形顶点
    path.lineTo(center.dx - 80, center.dy + 40); // 连接到左下角
    path.lineTo(center.dx + 80, center.dy + 40); // 连接到右下角
    path.close(); // 闭合路径(连接最后一个点和第一个点)

    // 3. 绘制路径
    canvas.drawPath(path, _paint);

    // 额外示例:绘制一条曲线(贝塞尔曲线)
    final Path curvePath = Path();
    curvePath.moveTo(50, 250); // 起点
    // 二阶贝塞尔曲线:控制点、终点
    curvePath.quadraticBezierTo(
      center.dx, 200, // 控制点(决定曲线弧度)
      250, 250, // 终点
    );
    canvas.drawPath(
      curvePath,
      _paint
        ..color = Colors.blue
        ..style = PaintingStyle.stroke
        ..strokeWidth = 3,
    );
  }

  @override
  bool shouldRepaint(covariant PathPainter oldDelegate) {
    return false;
  }
}

关键说明:

  • Path.moveTo(x, y):将画笔移动到指定坐标,不绘制线条,用于开始一个新的路径。

  • Path.lineTo(x, y):从当前画笔位置绘制一条直线到指定坐标。

  • Path.close():闭合路径,将最后一个点和第一个点连接起来,形成闭合图形(必须调用,否则填充无效)。

  • 贝塞尔曲线:quadraticBezierTo(二阶贝塞尔)、cubicTo(三阶贝塞尔),常用于绘制平滑曲线(比如自定义按钮、波浪效果)。

示例3:绘制文本(Canvas.drawText)

虽然Flutter有Text组件,但有时候需要在自定义图形上叠加文本(比如图表的坐标轴、自定义控件的文字),这时候就需要用Canvas的 drawText 方法。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Canvas文本绘制',
      home: Scaffold(
        appBar: AppBar(title: const Text('文本绘制示例')),
        body: Center(
          child: CustomPaint(
            size: const Size(300, 200),
            painter: TextPainterDemo(),
          ),
        ),
      ),
    );
  }
}

class TextPainterDemo extends CustomPainter {
  // 文本画笔:控制文本样式
  final TextPainter _textPainter = TextPainter(
    textDirection: TextDirection.ltr, // 文本方向(从左到右)
    textAlign: TextAlign.center, // 文本对齐方式
  );

  // 文本样式
  final TextStyle _textStyle = const TextStyle(
    color: Colors.black87,
    fontSize: 20,
    fontWeight: FontWeight.bold,
    fontStyle: FontStyle.italic,
  );

  @override
  void paint(Canvas canvas, Size size) {
    // 1. 准备文本(TextSpan)
    final textSpan = TextSpan(
      text: 'Flutter CustomPaint',
      style: _textStyle,
    );

    // 2. 配置文本绘制参数
    _textPainter
      ..text = textSpan
      ..layout(
        minWidth: 0,
        maxWidth: size.width, // 文本最大宽度(超出会换行)
      );

    // 3. 绘制文本(参数:画布、偏移量)
    _textPainter.paint(
      canvas,
      Offset(
        (size.width - _textPainter.width) / 2, // 水平居中
        (size.height - _textPainter.height) / 2, // 垂直居中
      ),
    );

    // 额外示例:绘制带背景的文本
    final backgroundPaint = Paint()..color = Colors.yellow.withOpacity(0.3);
    // 先绘制矩形背景,再绘制文本(叠加顺序)
    canvas.drawRect(
      Rect.fromLTWH(
        (size.width - _textPainter.width) / 2 - 10,
        (size.height - _textPainter.height) / 2 - 5,
        _textPainter.width + 20,
        _textPainter.height + 10,
      ),
      backgroundPaint,
    );
    // 再次绘制文本(覆盖在背景上)
    _textPainter.paint(
      canvas,
      Offset(
        (size.width - _textPainter.width) / 2,
        (size.height - _textPainter.height) / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(covariant TextPainterDemo oldDelegate) {
    return false;
  }
}

关键说明:

  • 绘制文本需要用 TextPainter,而不是直接用Canvas的drawText(Flutter推荐用法,更灵活)。

  • layout() 方法必须调用,用于计算文本的宽度和高度,否则无法正常绘制。

  • 文本居中技巧:通过计算画布大小和文本大小的差值,设置偏移量,实现水平+垂直居中。

示例4:绘制图片(Canvas.drawImage)

有时候需要在自定义画布上绘制图片(比如自定义头像、背景图、图标),Canvas提供了 drawImage 方法,支持绘制本地图片和网络图片(网络图片需先缓存)。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; // 用于加载本地图片

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Canvas图片绘制',
      home: Scaffold(
        appBar: AppBar(title: const Text('图片绘制示例')),
        body: Center(
          // 注意:加载图片是异步操作,用FutureBuilder包裹
          child: FutureBuilder<ImageProvider>(
            future: _loadLocalImage(), // 加载本地图片
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return CustomPaint(
                  size: const Size(300, 300),
                  painter: ImagePainter(image: snapshot.data!),
                );
              } else {
                return const CircularProgressIndicator(); // 加载中
              }
            },
          ),
        ),
      ),
    );
  }

  // 加载本地图片(需在pubspec.yaml中配置图片路径)
  Future<ImageProvider> _loadLocalImage() async {
    // 1. 加载图片字节数据
    final byteData = await rootBundle.load('assets/images/flutter.png');
    // 2. 转换为Uint8List
    final uint8List = byteData.buffer.asUint8List();
    // 3. 转换为ImageProvider
    return MemoryImage(uint8List);
  }
}

class ImagePainter extends CustomPainter {
  final ImageProvider image;

  ImagePainter({required this.image});

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制背景矩形
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      Paint()..color = Colors.grey[200]!,
    );

    // 绘制图片(异步加载,需用ImageStreamListener监听)
    image.resolve(const ImageConfiguration()).addListener(
      ImageStreamListener(
        (ImageInfo info, bool _) {
          // 绘制图片(参数:图片、目标矩形、源矩形、画笔)
          canvas.drawImageRect(
            info.image, // 加载好的图片
            Rect.fromLTWH(0, 0, info.image.width.toDouble(), info.image.height.toDouble()), // 源矩形(图片本身)
            Rect.fromLTWH(50, 50, 200, 200), // 目标矩形(画布上的位置和大小)
            Paint(),
          );

          // 额外示例:绘制圆形图片(裁剪图片)
          final circlePath = Path()
            ..addOval(Rect.fromLTWH(50, 50, 200, 200)); // 圆形路径
          canvas.save(); // 保存当前画布状态
          canvas.clipPath(circlePath); // 裁剪画布(只显示圆形区域)
          canvas.drawImageRect(
            info.image,
            Rect.fromLTWH(0, 0, info.image.width.toDouble(), info.image.height.toDouble()),
            Rect.fromLTWH(50, 50, 200, 200),
            Paint(),
          );
          canvas.restore(); // 恢复画布状态(避免影响后续绘制)
        },
      ),
    );
  }

  @override
  bool shouldRepaint(covariant ImagePainter oldDelegate) {
    return oldDelegate.image != image;
  }
}

关键说明:

  • 加载本地图片需在 pubspec.yaml 中配置 assets 路径,否则会加载失败。

  • 图片绘制是异步操作,需用 ImageStreamListener 监听图片加载完成,再进行绘制。

  • 圆形图片实现:通过 clipPath 裁剪画布为圆形,再绘制图片,即可实现圆形头像效果。

示例5:绘制渐变与阴影(提升视觉效果)

基础绘制加上渐变和阴影,能让自定义图形更具视觉冲击力。Canvas支持线性渐变、径向渐变、角度渐变,以及阴影效果,只需通过Paint配置即可。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Canvas渐变与阴影',
      home: Scaffold(
        appBar: AppBar(title: const Text('渐变与阴影示例')),
        body: Center(
          child: CustomPaint(
            size: const Size(300, 300),
            painter: GradientShadowPainter(),
          ),
        ),
      ),
    );
  }
}

class GradientShadowPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);

    // 1. 线性渐变(从左到右)
    final linearGradient = LinearGradient(
      colors: [Colors.blue, Colors.purple], // 渐变颜色
      begin: Alignment.centerLeft, // 渐变开始位置
      end: Alignment.centerRight, // 渐变结束位置
    );
    final linearPaint = Paint()
      ..shader = linearGradient.createShader(
        Rect.fromLTWH(50, 50, 200, 80), // 渐变区域
      )
      ..style = PaintingStyle.fill;
    canvas.drawRect(Rect.fromLTWH(50, 50, 200, 80), linearPaint);

    // 2. 径向渐变(从中心向外扩散)
    final radialGradient = RadialGradient(
      colors: [Colors.orange, Colors.red],
      center: center, // 渐变中心
      radius: 80, // 渐变半径
    );
    final radialPaint = Paint()
      ..shader = radialGradient.createShader(
        Rect.fromCircle(center: center, radius: 80),
      )
      ..style = PaintingStyle.fill
      // 阴影配置
      ..shadowColor = Colors.black38
      ..shadowBlurRadius = 10 // 阴影模糊度
      ..shadowOffset = const Offset(5, 5); // 阴影偏移量
    canvas.drawCircle(center, 80, radialPaint);

    // 3. 角度渐变(围绕中心旋转渐变)
    final sweepGradient = SweepGradient(
      colors: [Colors.green, Colors.yellow, Colors.green],
      startAngle: 0, // 开始角度
      endAngle: 2 * 3.1415926, // 结束角度(360度)
    );
    final sweepPaint = Paint()
      ..shader = sweepGradient.createShader(
        Rect.fromCircle(center: center, radius: 40),
      )
      ..style = PaintingStyle.fill;
    canvas.drawCircle(center, 40, sweepPaint);
  }

  @override
  bool shouldRepaint(covariant GradientShadowPainter oldDelegate) {
    return false;
  }
}

关键说明:

  • 渐变通过 Gradient 类实现,需通过 createShader 方法将渐变转换为画笔的shader 属性。

  • 阴影通过Paint的 shadowColor(阴影颜色)、shadowBlurRadius(模糊度)、shadowOffset(偏移量)配置,只有填充模式(fill)下阴影才会生效。

三、进阶实战:示例6——自定义动态进度条(结合动画)

前面的示例都是静态绘制,实际开发中,我们常需要动态绘制(比如进度条、加载动画、图表动效)。这个示例结合Flutter动画,实现一个自定义动态进度条,覆盖“动态绘制+性能优化”,贴近真实开发场景。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态进度条实战',
      home: const Scaffold(
        appBar: AppBar(title: Text('自定义动态进度条')),
        body: Padding(
          padding: EdgeInsets.all(20.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 动态进度条(结合动画)
              CustomProgressBar(progress: 0.7), // 初始进度70%
              const SizedBox(height: 20),
              CustomProgressBar(progress: 0.3, color: Colors.red), // 自定义颜色
            ],
          ),
        ),
      ),
    );
  }
}

// 自定义动态进度条组件(StatefulWidget,支持动画)
class CustomProgressBar extends StatefulWidget {
  final double progress; // 进度(0.0 ~ 1.0)
  final Color color; // 进度条颜色
  final double height; // 进度条高度

  const CustomProgressBar({
    super.key,
    required this.progress,
    this.color = Colors.blue,
    this.height = 10,
  });

  @override
  State<CustomProgressBar> createState() => _CustomProgressBarState();
}

class _CustomProgressBarState extends State<CustomProgressBar>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _progressAnimation;

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1), // 动画时长1秒
    );

    // 进度动画(从0过渡到目标进度)
    _progressAnimation = Tween<double>(
      begin: 0.0,
      end: widget.progress,
    ).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
    )..addListener(() {
        setState(() {}); // 触发重绘
      });

    // 启动动画
    _controller.forward();
  }

  @override
  void didUpdateWidget(covariant CustomProgressBar oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当进度变化时,更新动画
    if (oldWidget.progress != widget.progress) {
      _progressAnimation = Tween<double>(
        begin: _progressAnimation.value,
        end: widget.progress,
      ).animate(
        CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
      )..addListener(() {
          setState(() {});
        });
      _controller.forward(from: 0);
    }
  }

  @override
  void dispose() {
    _controller.dispose(); // 释放动画资源
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(MediaQuery.of(context).size.width - 40, widget.height),
      // 传入当前进度,用于绘制
      painter: ProgressBarPainter(
        progress: _progressAnimation.value,
        color: widget.color,
        height: widget.height,
      ),
    );
  }
}

// 进度条绘制逻辑
class ProgressBarPainter extends CustomPainter {
  final double progress;
  final Color color;
  final double height;

  ProgressBarPainter({
    required this.progress,
    required this.color,
    required this.height,
  });

  // 背景画笔(灰色底色)
  final Paint _backgroundPaint = Paint()..color = Colors.grey[200]!;

  // 进度画笔(自定义颜色,带圆角和阴影)
  late final Paint _progressPaint = Paint()
    ..color = color
    ..style = PaintingStyle.fill
    ..shadowColor = color.withOpacity(0.3)
    ..shadowBlurRadius = 2;

  @override
  void paint(Canvas canvas, Size size) {
    // 1. 绘制进度条背景(圆角矩形)
    final backgroundRect = RRect.fromRectAndRadius(
      Rect.fromLTWH(0, 0, size.width, height),
      Radius.circular(height / 2), // 圆角半径=高度的一半,实现胶囊形状
    );
    canvas.drawRRect(backgroundRect, _backgroundPaint);

    // 2. 绘制进度(根据progress计算宽度)
    final progressWidth = size.width * progress;
    if (progressWidth > 0) {
      final progressRect = RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, progressWidth, height),
        Radius.circular(height / 2),
      );
      canvas.drawRRect(progressRect, _progressPaint);
    }
  }

  // 性能优化:只有进度、颜色、高度变化时,才重绘
  @override
  bool shouldRepaint(covariant ProgressBarPainter oldDelegate) {
    return oldDelegate.progress != progress ||
        oldDelegate.color != color ||
        oldDelegate.height != height;
  }
}

关键说明:

  • 动态绘制的核心是“动画+重绘”:通过AnimationController控制动画,在动画回调中调用setState,触发CustomPaint重绘,从而实现动态效果。

  • 性能优化:重写 shouldRepaint 方法,只有当绘制参数(进度、颜色、高度)变化时,才进行重绘,避免不必要的性能消耗。

  • 胶囊形状进度条:通过 RRect.fromRectAndRadius 绘制圆角矩形,圆角半径等于高度的一半,即可实现胶囊效果。

四、实战总结与避坑指南

1. 核心总结

  • CustomPaint是画布容器,CustomPainter是绘制逻辑载体,Canvas是画笔,三者协同完成自定义绘制。

  • 基础绘制:掌握点、线、矩形、圆形、路径、文本、图片的绘制方法,就能满足大部分简单需求。

  • 进阶技巧:渐变、阴影能提升视觉效果,动画能实现动态绘制,shouldRepaint能优化性能。

  • 实战思路:先确定绘制需求 → 定义Paint(画笔) → 拼接Path(路径,可选) → 调用Canvas绘制方法 → 优化性能。

2. 常见坑点与解决方案

  • 坑点1:绘制的图形不显示? 解决方案:检查CustomPaint的size是否设置(默认size为0,无法显示);检查Paint的style是否正确(stroke描边时,strokeWidth不能为0);检查绘制的坐标是否超出画布范围。

  • 坑点2:图片绘制失败? 解决方案:检查本地图片是否在pubspec.yaml中配置;图片加载是异步的,需用ImageStreamListener监听加载完成后再绘制;避免在paint方法中直接加载图片(会阻塞绘制)。

  • 坑点3:动态绘制卡顿? 解决方案:重写shouldRepaint方法,避免不必要的重绘;减少绘制次数(比如合并路径,避免多次draw操作);避免在paint方法中做耗时操作(比如网络请求、复杂计算)。

  • 坑点4:文本绘制不居中? 解决方案:通过TextPainter的layout方法获取文本宽度和高度,再计算偏移量,实现居中;确保TextPainter的textDirection和textAlign配置正确。

3. 拓展方向

掌握了以上示例,你已经能应对80%的自定义绘制场景,后续可以进一步拓展:

  • 复杂图表:绘制折线图、柱状图、饼图(结合Path和动画)。

  • 自定义控件:绘制自定义按钮、滑块、进度指示器、底部导航栏。

  • 高级动效:绘制粒子动画、波浪动画、路径动画(结合Path和Animation)。

  • 性能优化:使用RepaintBoundary 隔离绘制区域,避免全局重绘;使用PictureRecorder 缓存绘制结果,减少重复绘制。

最后,自定义绘制的核心是“多练”——多尝试修改示例中的参数(比如颜色、尺寸、路径),多结合实际需求写自定义组件,慢慢就能熟练掌握Canvas的各种用法。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐