MLKitDocScanner 拓展
调起系统级的文档扫描界面:对着纸张一拍,自动找到四个角、纠正透视变形,还能裁剪、加黑白/增强滤镜、去阴影污点,最后导出 JPG 图片和 / 或 PDF 文件。整套扫描 UI 由 Google 提供,我们不用自己做取景框和边缘检测。
为什么选它:
- 不用写”初始化”块。扫描参数全是设计器属性,拖进来设好就能直接调
开始扫描。 - 返回的是能直接用的文件路径。扫描器原本给的是临时
content://地址,在 App Inventor 里几乎没法用;本拓展统一复制到应用自己的目录,返回的路径可以直接喂给「图像」组件显示、「分享」组件发送、「文件」组件读取。 - 模式用词不用数字。
base/filter/full,而不是记不住的 1 / 2 / 3。 - 不重复造轮子。没有自带”分享”方法 —— App Inventor 本来就有「分享」组件,用它的
分享文件块即可。
重要限制:文档扫描界面由 Google Play 服务提供,ML Kit 没有离线版本。 因此设备必须装有 Google Play 服务,且首次使用需要联网下载扫描模块(约十几 MB,之后可离线)。 没有 GMS 的设备调用
开始扫描会触发ScanFailed事件并说明原因。 这一点和本仓库其它 ML Kit 拓展(条码、文字识别用的是离线捆绑模型)不同,请务必在文档里向用户交代清楚。
不需要申请摄像头权限 —— 拍照在 Google 的扫描界面里完成,不经过我们的 App。
- .aix 拓展下载:
cn.fun123.MLKit.DocScanner.aix
快速上手
一、扫一页并显示出来
设计器里放一个 MLKitDocScanner、一个按钮、一个「图像」组件。
when 按钮_扫描.Click() {
DocScan1.Scan()
}
when DocScan1.GotScanResult(pageCount, imagePaths, pdfPath) {
标签1.Text = join("扫描完成,共 ", pageCount, " 页")
if length(imagePaths) > 0 {
图像1.Picture = imagePaths[1]
}
}
when DocScan1.ScanCancelled() {
标签1.Text = "已取消扫描"
}
when DocScan1.ScanFailed(errorMessage) {
标签1.Text = errorMessage
}
二、扫多页导出 PDF 并分享
设计器里再放一个「分享」组件(Sharing)。把 输出格式 属性设为 pdf(或保持默认 both)。
global lastPdf = ""
when 按钮_扫描.Click() {
DocScan1.PageLimit = 20
DocScan1.ResultFormat = "pdf"
DocScan1.Scan()
}
when DocScan1.GotScanResult(pageCount, imagePaths, pdfPath) {
lastPdf = pdfPath
标签1.Text = join("已生成 ", pageCount, " 页 PDF")
按钮_分享.Enabled = true
}
when 按钮_分享.Click() {
信息分享器1.ShareFile(lastPdf)
}
三、把每一页都显示在列表里
when DocScan1.GotScanResult(pageCount, imagePaths, pdfPath) {
列表显示框1.ElementsFromString = ""
列表显示框1.Elements = imagePaths
标签1.Text = join("共 ", length(imagePaths), " 张图片")
}
四、只要单页、简洁模式(比如拍身份证)
when Screen1.Initialize() {
DocScan1.PageLimit = 1
DocScan1.ResultFormat = "jpeg"
DocScan1.ScannerMode = "base"
DocScan1.AllowGalleryImport = false
}
五、用完清理占用的空间
扫描结果会一直留在应用目录里,做成”清空历史”功能时调用:
global removed = 0
when 按钮_清除.Click() {
removed = DocScan1.ClearCache()
标签1.Text = join("已清理 ", removed, " 个文件")
}
属性
- PageLimit
- 一次最多扫描多少页。至少 1 页,默认 10 页。拍身份证、发票这类单页场景设为 1,用户扫完一页就直接进入确认界面。
- AllowGalleryImport
- 是否允许用户在扫描界面里从相册选图片(而不是只能现场拍)。默认允许。 需要保证是现场拍摄(例如考勤、核验场景)时设为「假」。
- ResultFormat
- 输出格式。
jpeg— 只出图片pdf— 只出 PDFboth— 两种都出(默认)
- ScannerMode
- 扫描界面的功能档次。
base— 只有基础的边缘检测和裁剪,界面最简单filter— 增加黑白 / 增强滤镜full— 再增加去阴影去污点、重新拍摄单页等完整能力(默认)
- CaptureMode
- 拍摄方式。
auto检测到纸张就自动拍(默认),manual由用户手动按快门。 - Scanning
- 只读。扫描界面是否正在进行中。
事件
- GotScanResult(pageCount,imagePaths,pdfPath)
- 扫描完成(用户在扫描界面点了「完成 / 保存」)。
pageCount— 页数。imagePaths— 每页 JPG 图片的文件路径列表,输出格式含jpeg时才有内容。 列表里的路径可以直接给「图像」组件的图片属性。pdfPath— PDF 文件路径,输出格式含pdf时才有内容。可以用「分享」组件的分享文件发送。
- ScanCancelled()
- 用户取消了扫描(例如按了返回键)。这不是错误,一般不用提示什么。
- ScanFailed(errorMessage)
- 扫描失败。最常见的两个原因:设备没有 Google Play 服务,或首次使用时无法联网下载扫描模块。 错误文本里已经带了可操作的提示,直接显示给用户即可。
方法
- Scan()
- 打开扫描界面开始扫描。
用户扫完点确认 →
GotScanResult;按返回键取消 →ScanCancelled;起不来 →ScanFailed。 - ClearCache()
- 清空本拓展生成的所有扫描文件,返回删除的文件个数。
文件放在哪里
扫描结果存在应用私有的外部目录下的 MLKitDocScanner/ 子目录,形如:
/storage/emulated/0/Android/data/<你的包名>/files/MLKitDocScanner/
page_1751234567890_1.jpg
scan_1751234567890.pdf
这个位置的好处:不需要申请存储权限、卸载 App 自动清理、路径可以直接给其它组件用。 文件名带毫秒时间戳,多次扫描不会互相覆盖。
常见问题
调用 开始扫描 立刻触发 ScanFailed。
九成是设备没有 Google Play 服务(国产 ROM、部分海外定制机),或者 Play 服务版本太旧。
请在应用里对用户说明清楚,或改用别的方案(例如本仓库的 MLKitCodeScanner 是完全离线的,但它做的是条码而不是文档)。
首次扫描要等一会儿,还提示下载。
Google 的扫描模块是按需下载的(十几 MB),首次使用需要联网。下载完之后再用就不需要网络了。
可以在应用启动时先调一次 开始扫描,让用户尽早完成下载。
GotScanResult 里 imagePaths 是空的。
输出格式 设成了 pdf。想要图片就设 jpeg 或 both。
「图像」组件显示不出扫描结果。
确认传的是 imagePaths 里列表的某一项(如 选择列表项 imagePaths 1),不是整个列表。
能不能自己做扫描界面(不用 Google 那套)? 本拓展的价值恰恰在于复用 Google 那套成熟 UI(边缘检测、透视纠正、滤镜都是现成的)。 自己做需要 OpenCV 级别的图像处理,不在本拓展范围内。
扫码添加客服咨询