Last active
December 20, 2024 02:26
-
-
Save CaiJingLong/9cbd4a968512446400f4fb2a3e2ef999 to your computer and use it in GitHub Desktop.
拼音模糊匹配的方法,需要导入 pinyin 库 flutter pub add pinyin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:pinyin/pinyin.dart'; | |
/// 拼音比较结果类 | |
/// | |
/// 用于存储拼音匹配的结果信息,包括是否匹配、拼音字符串和匹配位置信息 | |
/// | |
/// 主要功能: | |
/// 1. 存储匹配结果 | |
/// 2. 提供构建富文本的方法,可以高亮显示匹配的字符 | |
/// | |
/// 示例: | |
/// ```dart | |
/// // 1. 基本使用 | |
/// final result = PinyinUtils.checkPinyin( | |
/// text: '小明同学', | |
/// inputPinyin: 'xm', | |
/// ); | |
/// | |
/// print(result.isMatch); // true | |
/// print(result.pinyin); // xiaomingtongxue | |
/// print(result.matchIndex); // {0: true, 1: true} | |
/// | |
/// // 2. 在 UI 中显示高亮效果 | |
/// RichText( | |
/// text: TextSpan( | |
/// children: result.buildRich( | |
/// matchColor: Colors.red, // 匹配字符的颜色 | |
/// normalColor: Colors.grey, // 普通字符的颜色 | |
/// fontSize: 16, // 字体大小 | |
/// ), | |
/// ), | |
/// ) | |
/// | |
/// // 3. 自定义样式 | |
/// final spans = result.buildRich( | |
/// matchColor: Colors.blue, | |
/// normalColor: Colors.black87, | |
/// fontSize: 14, | |
/// ); | |
/// ``` | |
class PinyinCompareResult { | |
/// 是否匹配成功 | |
final bool isMatch; | |
/// 原文本转换后的拼音字符串 | |
final String pinyin; | |
/// 匹配位置信息 | |
/// | |
/// key: 字符在拼音中的位置 | |
/// value: true 表示该位置的字符匹配成功 | |
final Map<int, bool> matchIndex; | |
PinyinCompareResult({ | |
required this.isMatch, | |
required this.pinyin, | |
this.matchIndex = const {}, | |
}); | |
/// 构建富文本,用于显示匹配高亮效果 | |
/// | |
/// 参数: | |
/// * [matchColor] - 匹配字符的颜色,默认为红色 | |
/// * [normalColor] - 普通字符的颜色,默认为灰色 | |
/// * [fontSize] - 字体大小,默认为 13 | |
/// | |
/// 返回值: | |
/// * 返回 [TextSpan] 列表,可以直接用于 [RichText] 或 [Text.rich] | |
List<TextSpan> buildRich({ | |
Color matchColor = Colors.red, | |
Color normalColor = Colors.grey, | |
double fontSize = 13, | |
}) { | |
final normalStyle = TextStyle(color: normalColor, fontSize: fontSize); | |
final matchStyle = TextStyle(color: matchColor, fontSize: fontSize); | |
final List<TextSpan> textSpans = [ | |
TextSpan( | |
text: ' ', | |
style: normalStyle, | |
), | |
]; | |
var start = 0; | |
matchIndex.forEach((key, value) { | |
if (start < key) { | |
textSpans.add( | |
TextSpan( | |
text: pinyin.substring(start, key), | |
style: normalStyle, | |
), | |
); | |
} | |
textSpans.add( | |
TextSpan( | |
text: pinyin[key], | |
style: value ? matchStyle : normalStyle, | |
), | |
); | |
start = key + 1; | |
}); | |
if (start < pinyin.length) { | |
textSpans.add( | |
TextSpan( | |
text: pinyin.substring(start), | |
style: normalStyle, | |
), | |
); | |
} | |
return textSpans; | |
} | |
} | |
class PinyinUtils { | |
PinyinUtils._(); | |
/// 批量检查拼音匹配,支持任意类型的数据列表 | |
/// | |
/// 示例: | |
/// ```dart | |
/// // 1. 简单的字符串列表匹配 | |
/// final names = ['小明', '小红', '小张']; | |
/// final matches = PinyinUtils.batchCheckPinyin( | |
/// items: names, | |
/// inputPinyin: 'xm', | |
/// textExtractor: (name) => name, | |
/// ); | |
/// | |
/// // 2. 对象列表匹配 | |
/// class User { | |
/// final String name; | |
/// final String phone; | |
/// User(this.name, this.phone); | |
/// } | |
/// | |
/// final users = [ | |
/// User('张三', '13800138000'), | |
/// User('李四', '13900139000'), | |
/// ]; | |
/// | |
/// // 按名字搜索 | |
/// final nameMatches = PinyinUtils.batchCheckPinyin( | |
/// items: users, | |
/// inputPinyin: 'zs', | |
/// textExtractor: (user) => user.name, | |
/// ); | |
/// | |
/// // 按电话搜索 | |
/// final phoneMatches = PinyinUtils.batchCheckPinyin( | |
/// items: users, | |
/// inputPinyin: '138', | |
/// textExtractor: (user) => user.phone, | |
/// ); | |
/// | |
/// // 3. 在UI中使用匹配结果 | |
/// ListView.builder( | |
/// itemCount: matches.length, | |
/// itemBuilder: (context, index) { | |
/// final match = matches[index]; | |
/// return ListTile( | |
/// title: RichText( | |
/// text: TextSpan( | |
/// children: match.result.buildRich(), | |
/// ), | |
/// ), | |
/// ); | |
/// }, | |
/// ) | |
/// ``` | |
/// | |
/// 参数说明: | |
/// * [items] - 要搜索的数据列表,可以是任意类型 | |
/// * [inputPinyin] - 输入的拼音字符串,支持全拼和首字母,如 'xiaoming' 或 'xm' | |
/// * [textExtractor] - 从数据项中提取要匹配的文本的函数 | |
/// | |
/// 返回值: | |
/// * 返回 [PinyinMatchItem] 列表,包含匹配的原始数据和匹配结果 | |
/// * 只返回匹配成功的项目 | |
static List<PinyinMatchItem<T>> batchCheckPinyin<T>({ | |
required List<T> items, | |
required String inputPinyin, | |
required String Function(T item) textExtractor, | |
}) { | |
final results = <PinyinMatchItem<T>>[]; | |
for (final item in items) { | |
final text = textExtractor(item); | |
final matchResult = checkPinyin( | |
text: text, | |
inputPinyin: inputPinyin, | |
); | |
if (matchResult.isMatch) { | |
results.add(PinyinMatchItem( | |
item: item, | |
result: matchResult, | |
)); | |
} | |
} | |
return results; | |
} | |
/// 检查单个文本是否匹配输入的拼音 | |
/// | |
/// 支持的匹配方式: | |
/// 1. 连续匹配: 'xiaoming' 匹配 'xm' | |
/// 2. 顺序匹配: 'xiaoming' 匹配 'xming' | |
/// 3. 完整匹配: 'xiaoming' 匹配 'xiaoming' | |
/// | |
/// 示例: | |
/// ```dart | |
/// final result = PinyinUtils.checkPinyin( | |
/// text: '小明', | |
/// inputPinyin: 'xm', | |
/// ); | |
/// | |
/// if (result.isMatch) { | |
/// print('匹配成功'); | |
/// // 使用 buildRich() 显示高亮的匹配结果 | |
/// RichText( | |
/// text: TextSpan( | |
/// children: result.buildRich(), | |
/// ), | |
/// ); | |
/// } | |
/// ``` | |
/// | |
/// 参数: | |
/// * [text] - 要匹配的原始文本 | |
/// * [inputPinyin] - 输入的拼音 | |
/// | |
/// 返回值: | |
/// * 返回 [PinyinCompareResult],包含匹配结果和匹配位置信息 | |
static PinyinCompareResult checkPinyin({ | |
required String text, | |
required String inputPinyin, | |
}) { | |
if (text.trim().isEmpty) { | |
return PinyinCompareResult(isMatch: false, pinyin: ''); | |
} | |
final textPinyin = PinyinHelper.getPinyin(text, separator: ''); | |
if (inputPinyin.isEmpty) { | |
return PinyinCompareResult(isMatch: false, pinyin: textPinyin); | |
} | |
inputPinyin = inputPinyin.toLowerCase(); | |
final matchIndex = <int, bool>{}; | |
if (textPinyin.contains(inputPinyin)) { | |
final index = textPinyin.indexOf(inputPinyin); | |
for (var i = 0; i < inputPinyin.length; i++) { | |
matchIndex[index + i] = true; | |
} | |
return PinyinCompareResult( | |
isMatch: true, pinyin: textPinyin, matchIndex: matchIndex); | |
} | |
// 双指针查找法,查找是否包含输入的拼音 | |
// 比如 xiaomishouji 包含 xmsj、也包含 xmiouji ,只要顺序不错,即可匹配成功 | |
// 被查找的拼音指针 | |
var tPosition = 0; | |
// 输入的拼音指针 | |
var iPosition = 0; | |
for (; tPosition < textPinyin.length; tPosition++) { | |
if (textPinyin[tPosition] == inputPinyin[iPosition]) { | |
iPosition++; | |
matchIndex[tPosition] = true; | |
} | |
if (iPosition == inputPinyin.length) { | |
return PinyinCompareResult( | |
isMatch: true, | |
pinyin: textPinyin, | |
matchIndex: matchIndex, | |
); | |
} | |
} | |
return PinyinCompareResult(isMatch: false, pinyin: textPinyin); | |
} | |
} | |
/// 拼音匹配结果项 | |
/// | |
/// 包含原始数据项和对应的匹配结果,用于批量匹配场景 | |
/// | |
/// 泛型 [T] 可以是任意类型,方便处理不同类型的数据 | |
/// | |
/// 示例: | |
/// ```dart | |
/// final match = PinyinMatchItem( | |
/// item: user, // 原始数据 | |
/// result: compareResult, // 匹配结果 | |
/// ); | |
/// | |
/// // 访问原始数据 | |
/// print(match.item.name); | |
/// | |
/// // 使用匹配结果 | |
/// RichText( | |
/// text: TextSpan( | |
/// children: match.result.buildRich(), | |
/// ), | |
/// ); | |
/// ``` | |
class PinyinMatchItem<T> { | |
/// 原始数据项 | |
final T item; | |
/// 拼音匹配结果 | |
final PinyinCompareResult result; | |
PinyinMatchItem({ | |
required this.item, | |
required this.result, | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment