Flutter手势识别插件one_dollar_unistroke_recognizer的使用

发布于 1周前 作者 itying888 来自 Flutter

Flutter手势识别插件one_dollar_unistroke_recognizer的使用

Flutter插件one_dollar_unistroke_recognizer是一个Dart版本的$1单笔画手势识别器,它基于华盛顿大学的$1 Unistroke Recognizer,并添加了一些增强功能。此插件能够识别基本的几何形状如线、圆、矩形和三角形,同时支持自定义单笔画模板的识别。

主要特点

  • Protractor增强:默认启用,可以禁用。
  • 获取完美的(标准)形状:从用户绘制的手势中提取出最接近的标准形状。
  • 更适合检测直线的算法:改进了对直线的识别能力。
  • 类型安全:通过DefaultUnistrokeNames枚举或其他自定义类型来保证类型安全。

使用方法

基本使用

final points = <Offset>[...]; // 用户绘制的手势点集合
final recognized = recognizeUnistroke(points);
if (recognized == null) {
  print('No match found');
} else {
  print('Stroke recognized as ${recognized.name}');
}

禁用Protractor增强

final recognized = recognizeUnistroke(
  points,
  useProtractor: false,
);

获取“完美”的标准形状

可以通过调用RecognizedUnistroke对象上的不同方法来获得用户绘制的手势对应的理想形状:

  • convertToCanonicalPolygon(): 返回与输入手势最匹配的模板多边形。
  • convertToLine(): 返回第一个和最后一个输入点,用于表示一条线段。
  • convertToCircle(): 返回最佳拟合圆的中心和半径。
  • convertToOval(): 类似于convertToCircle()但不取宽高平均值。
  • convertToRect(): 返回最佳拟合矩形(边界框),并可以选择将其角部圆滑处理。

例如,根据识别结果在Canvas上绘制图形:

switch (recognized?.name) {
  case DefaultUnistrokeNames.line:
    final (start, end) = recognized!.convertToLine();
    canvas.drawLine(start, end, paint);
  case DefaultUnistrokeNames.circle:
    final (center, radius) = recognized!.convertToCircle();
    canvas.drawCircle(center, radius, paint);
  case DefaultUnistrokeNames.rectangle:
    final rect = recognized!.convertToRect();
    if (youWantARoundedRectangle) {
      canvas.drawRRect(
        RRect.fromRectAndRadius(rect, Radius.circular(10)),
        paint,
      );
    } else {
      canvas.drawRect(rect, paint);
    }
  case DefaultUnistrokeNames.triangle:
  case DefaultUnistrokeNames.star:
    final polygon = recognized!.convertToCanonicalPolygon();
    canvas.drawPoints(PointMode.polygon, polygon, paint);
  default:
    break;
}

使用自定义单笔画模板

如果你想要识别特定的手势,可以设置referenceUnistrokes列表以包含自定义的手势模板。这将覆盖默认的模板。

enum MyUnistrokeNames {
  circle,
  rectangle,
  triangle,
  leaf,
}

// 定义你的自定义模板
referenceUnistrokes = <Unistroke<MyUnistrokeNames>>[
  Unistroke(MyUnistrokeNames.circle, [...]),
  Unistroke(MyUnistrokeNames.rectangle, [...]),
  Unistroke(MyUnistrokeNames.triangle, [...]),
  Unistroke(MyUnistrokeNames.leaf, [...]),
];

// 使用自定义模板进行识别
final recognized = recognizeCustomUnistroke<MyUnistrokeNames>(points);

对于直线这样的特殊情况,确保你的模板只有两个不同的点。

referenceUnistrokes = <Unistroke<MyUnistrokeNames>>[
  Unistroke(MyUnistrokeNames.line, [
    Offset(0, 0),
    Offset(0, 100),
  ]),
  // 其他模板...
];

示例代码

下面是一个完整的示例程序,展示了如何结合Flutter应用使用one_dollar_unistroke_recognizer插件来创建一个简单的手势识别界面。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '\$1 Unistroke Recognizer Demo',
      theme: ThemeData(useMaterial3: true),
      home: const GestureRecognizerPage(),
    );
  }
}

class GestureRecognizerPage extends StatefulWidget {
  const GestureRecognizerPage({super.key});

  @override
  State<GestureRecognizerPage> createState() => _GestureRecognizerPageState();
}

class _GestureRecognizerPageState extends State<GestureRecognizerPage> {
  final recognized = ValueNotifier<RecognizedUnistroke?>(null);
  Timer? pointDebounce;

  void onDraw(List<Offset> points) {
    if (pointDebounce == null || !pointDebounce!.isActive) {
      pointDebounce = Timer(const Duration(milliseconds: 100), () {
        setState(() {
          recognized.value = recognizeUnistroke(points);
        });
      });
    }
  }

  void onDrawEnd(List<Offset> points) {
    pointDebounce?.cancel();
    pointDebounce = null;
    setState(() {
      recognized.value = recognizeUnistroke(points);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: ValueListenableBuilder(
          valueListenable: recognized,
          builder: (context, recognized, child) {
            return Text(
              recognized == null
                  ? 'Draw below to detect a shape'
                  : 'Detected "${recognized.name}" with score '
                      '${recognized.score.toStringAsFixed(2)}',
            );
          },
        ),
      ),
      body: Column(
        children: [
          Expanded(
            child: GestureDetector(
              onPanUpdate: (details) {
                final RenderBox renderBox = context.findRenderObject() as RenderBox;
                final Offset localPosition = renderBox.globalToLocal(details.globalPosition);
                onDraw([localPosition]);
              },
              onPanEnd: (details) {
                onDrawEnd([]);
              },
              child: CustomPaint(
                size: Size.infinite,
                painter: GesturePainter(recognized.value),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class GesturePainter extends CustomPainter {
  final RecognizedUnistroke? recognized;

  GesturePainter(this.recognized);

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 2
      ..style = PaintingStyle.stroke;

    switch (recognized?.name) {
      case DefaultUnistrokeNames.line:
        final (start, end) = recognized!.convertToLine();
        canvas.drawLine(start, end, paint);
        break;
      case DefaultUnistrokeNames.circle:
        final (center, radius) = recognized!.convertToCircle();
        canvas.drawCircle(center, radius, paint);
        break;
      case DefaultUnistrokeNames.rectangle:
        final rect = recognized!.convertToRect();
        canvas.drawRect(rect, paint);
        break;
      case DefaultUnistrokeNames.triangle:
      case DefaultUnistrokeNames.star:
        final polygon = recognized!.convertToCanonicalPolygon();
        canvas.drawPoints(PointMode.polygon, polygon, paint);
        break;
      default:
        break;
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

以上就是关于one_dollar_unistroke_recognizer插件的详细介绍及示例代码,希望对你有所帮助!


更多关于Flutter手势识别插件one_dollar_unistroke_recognizer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter手势识别插件one_dollar_unistroke_recognizer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中使用one_dollar_unistroke_recognizer插件来识别手势的示例代码。这个插件可以帮助你识别用户绘制的手势,并将它们与预定义的手势进行匹配。

首先,你需要在你的pubspec.yaml文件中添加这个插件的依赖项:

dependencies:
  flutter:
    sdk: flutter
  one_dollar_unistroke_recognizer: ^最新版本号  # 请替换为插件的最新版本号

然后,运行flutter pub get来安装依赖项。

接下来,在你的Flutter应用中,你可以按照以下步骤实现手势识别:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
  1. 创建手势识别器并定义预定义手势
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Gesture Recognition'),
        ),
        body: GestureRecognitionScreen(),
      ),
    );
  }
}

class GestureRecognitionScreen extends StatefulWidget {
  @override
  _GestureRecognitionScreenState createState() => _GestureRecognitionScreenState();
}

class _GestureRecognitionScreenState extends State<GestureRecognitionScreen> {
  final recognizer = UniStrokeRecognizer();
  List<List<Offset>> predefinedGestures = [
    [
      Offset(50, 50),
      Offset(100, 50),
      Offset(100, 100),
    ],
    [
      Offset(50, 50),
      Offset(50, 100),
      Offset(100, 100),
    ],
  ];

  List<Offset> currentGesture = [];

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: GesturePainter(currentGesture, recognizer, predefinedGestures),
      size: Size.infinite,
      child: GestureDetector(
        onPanStart: (details) {
          setState(() {
            currentGesture.add(details.localPosition);
          });
        },
        onPanUpdate: (details) {
          setState(() {
            currentGesture.add(details.localPosition);
          });
        },
        onPanEnd: (details) {
          setState(() {
            final result = recognizer.recognizeGesture(currentGesture, predefinedGestures);
            print('Recognized gesture index: ${result?.index}');
            currentGesture.clear(); // Clear the current gesture for the next draw
          });
        },
      ),
    );
  }
}

class GesturePainter extends CustomPainter {
  final List<Offset> currentGesture;
  final UniStrokeRecognizer recognizer;
  final List<List<Offset>> predefinedGestures;

  GesturePainter(this.currentGesture, this.recognizer, this.predefinedGestures);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 4.0
      ..style = PaintingStyle.stroke;

    // Draw predefined gestures
    for (var gesture in predefinedGestures) {
      Path path = Path();
      for (var point in gesture) {
        if (path.isEmpty) {
          path.moveTo(point.dx, point.dy);
        } else {
          path.lineTo(point.dx, point.dy);
        }
      }
      canvas.drawPath(path, paint);
    }

    // Draw current gesture
    if (currentGesture.isNotEmpty) {
      Path path = Path();
      for (var point in currentGesture) {
        if (path.isEmpty) {
          path.moveTo(point.dx, point.dy);
        } else {
          path.lineTo(point.dx, point.dy);
        }
      }
      paint.color = Colors.red;
      canvas.drawPath(path, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

在这个示例中,我们创建了一个GestureRecognitionScreen,它使用GestureDetector来捕获用户的绘制手势。onPanStartonPanUpdateonPanEnd回调用于收集用户绘制的点。当用户完成绘制时,onPanEnd回调会触发手势识别,并打印出识别到的预定义手势的索引。

GesturePainter类负责在屏幕上绘制预定义的手势和当前的手势。预定义的手势使用黑色绘制,而当前的手势使用红色绘制。

请注意,这只是一个基本示例,你可能需要根据实际需求对代码进行调整和扩展。

回到顶部