教學 | 2026/9/22 | 約 41 分鐘閱讀

【BOOX 開發篇】Vibe Coding 一台電子紙手寫 App 的實戰筆記:我們用了哪些 SDK,又被電子紙教了哪些事

作者:Read with Pro 編輯部

在電子紙裝置上打字、閱讀的情境
圖:在電子紙裝置上寫程式與閱讀——vibe coding 一個電子紙 App,門檻比想像低。(圖:文石 BOOX 官方情境圖) 來源:文石 BOOX 官方網站情境圖

分享用 vibe coding 做電子紙手寫 App 的實戰經驗:實際用到的 SDK(Onyx onyxsdk-pen/device、ML Kit Digital Ink、MNN 離線 AI)、低延遲雙軌筆跡、掌壓防誤觸、中英混寫辨識,以及電子紙殘影控制(局部刷新、dirty rect、REGAL/GC、面板節奏解耦)與各機型刷新差異。

#Vibe Coding#電子紙#電子墨水#BOOX#Onyx SDK#手寫辨識#ML Kit#EPD#局部刷新#Android#ADB#側載 APK#Supernote#Meebook#HyRead#Pubook#開發筆記#閱讀器 App

相關品牌
BOOX 文石主要品牌HyRead 凌網主要品牌Meebook主要品牌Pubook / Pubu主要品牌Supernote主要品牌

主要來源:參考連結

分享文章把這篇整理傳給正在挑閱讀器的人
FacebookXLINEThreadsPlurk

最近好多人開始「vibe coding」——不寫規格、邊聊邊生、快速把想法變成一個能跑的 App。做閱讀器相關的小工具尤其熱門。我自己也用這種方式,做了一個展示用的手寫 App,跑在 BOOX 這類電子紙裝置上:可以手寫、辨識繁中英文,還會用「一筆一畫」的方式把 AI 回答寫回紙面。

為了不暴露專案,App 的名字這裡先不寫;你可以把它想成哈利波特裡那本會回話的魔法日記——你在紙面上寫字,它會用手寫回你話。

這篇想分享的,是我們實際用了哪些 SDK 與工具,以及在電子紙上踩到、最後學會的一些事。先講一句最重要的心得:

在一般 Android 平板上,畫筆跡就是 MotionEvent → Canvas → invalidate();但電子紙不是 60/120Hz 的 LCD。Android 這邊畫好了,不代表電子紙面板已經更新。 更新太快會塞車、太慢會卡頓、用錯波形會殘影、閃黑或缺筆。

注:文中會出現一些術語,我會在旁邊用括號補一句白話。想動手做的人可以照著 SDK 與程式碼走;只想看熱鬧的讀者,抓「電子紙為什麼難搞」的觀念就夠了。

先說結論:把它想成四種不同的工作

這個 App 最後採用的是一個分層架構:

  1. BOOX 原生筆跡層:處理低延遲書寫(筆尖跟得上手)。
  2. Android 向量筆跡層:保留每一筆,供辨識、淡出、局部清除,以及非 BOOX 裝置的備援。
  3. Google ML Kit Digital Ink Recognition:把手寫軌跡辨識成繁中、英文與混寫文字。
  4. BOOX EPD Device SDK:決定哪些區域用快速刷新、哪些用較乾淨的品質刷新。
  5. MNN 本機推論:在裝置上離線跑語言模型;題庫、規則與網路資料則作為可控的補充來源。

最重要的經驗,不是「挑一個最快的刷新模式」,而是:

把書寫、動畫、靜態文字、深度清除視為四種不同的工作,分別安排刷新區域、波形與節奏。

我們實際用了哪些 SDK 與工具

專案以 Java 17、compileSdk 35、minSdk 24、targetSdk 35 建置。核心清單如下:

類別 實際使用 版本/方式 用途
Android Android SDK API 24–35 Activity、View、Canvas、Path、MotionEvent、LocationManager、SharedPreferences、HttpURLConnection
手寫辨識 Google ML Kit Digital Ink Recognition 19.0.0 繁中、英文、混寫手寫軌跡辨識
BOOX 筆跡 onyxsdk-pen 1.5.4 TouchHelper、RawInputCallback、低延遲原生筆跡
BOOX 裝置/電子紙 onyxsdk-device 1.3.4 EpdController、刷新模式、局部面板更新,以及可選的前光 API
BOOX 初始化 ResManager、RxBaseAction 隨 Onyx SDK 初始化 BOOX SDK 所需資源與基礎動作
隱藏 API 相容 HiddenApiBypass 4.3 讓 BOOX 裝置 SDK 在新版 Android 上存取部分廠商/隱藏 API
離線 AI Alibaba MNN/MNN LLM native runtime JNI .so + Java wrapper 在裝置 CPU 上執行打包於 App 的本機語言模型
天氣 Open-Meteo HTTPS JSON,無專用 SDK 以座標查即時天氣,不需 API Key
網路查證 MediaWiki API HTTPS JSON,無專用 SDK 普通 AI 模式下,處理明確要求上網查證的問題
字型 LXGW WenKai TC、Kalam 等 App 內嵌字型 中文閱讀字體、英文手寫感字體

Gradle 的核心依賴:

// 1) 先在 repositories 加入 Onyx 的來源(settings.gradle 或 project build.gradle)
maven { url 'https://jitpack.io' }
maven { url 'http://repo.boox.com/repository/maven-public/' }

// 2) 模組 build.gradle 的依賴
implementation 'com.google.mlkit:digital-ink-recognition:19.0.0'
implementation 'com.onyx.android.sdk:onyxsdk-pen:1.5.4'
implementation 'com.onyx.android.sdk:onyxsdk-device:1.3.4'
implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'

Onyx 的 SDK 由上面兩個 Maven repository 取得(jitpack.io 與 repo.boox.com);MNN 不是一般 Maven 依賴,而是把 arm64 native library、Java JNI wrapper 與轉換後的模型檔一起包進專案。

各 SDK 的官方位置(想自己接,從這裡開始):

一個必須誠實的提醒:HiddenApiBypass 與 BOOX 廠商 API 都要當成「裝置專用整合」看待。 它們讓你在 BOOX 上拿到原生能力,但若要上架一般應用商店,得另外檢查隱藏 API、裝置相容性與商店政策——別把它們當成「所有 Android 閱讀器都保證支援」的通用介面。

哪些功能不需要 API Key?

  • 離線題庫、MNN 離線 AI:不需要。
  • ML Kit 手寫模型:下載後可離線辨識;但第一次下載語言模型時要連網。
  • Open-Meteo 天氣、MediaWiki 查證:目前端點不需要 Key。

但「不需要 Key」不等於「可以永久無限用」。公開端點仍該設逾時、節流、快取與離線 fallback,也要留意日後服務條款或政策改變。

那其他家閱讀器呢?Supernote/Meebook/HyRead/Pubook 有 SDK 嗎

很多人會問:一定要 BOOX 嗎?我查了一輪公開資訊,簡單講——目前對開發者最友善、而且有「原廠公開 SDK」的,就是 BOOX(Onyx)。其他家大多是「開放 Android、可以側載 App,但沒有原廠的電子紙/筆 SDK」,電子紙優化得自己用標準 Android API 硬做。

品牌 原廠公開 SDK? 能裝自己的 App 嗎 備註
BOOX(Onyx) ✅ 有(pen/device/scribble/base) 可(開放 Android) 有 TouchHelper、EpdController、前光 API 與完整文件,本專案就用這套
Supernote(Ratta) ❌ 沒有第三方裝置 SDK 較封閉、不易自由側載 系統偏封閉;社群有非官方檔案工具(解析 .note),但那不是 App 開發 SDK
Meebook ❌ 沒查到原廠 SDK 多為開放 Android,可側載 能裝 APK,但低延遲手寫/刷新/筆得靠標準 Android API 自己處理
HyRead Gaze ❌ 沒查到原廠 SDK 「開放系統」機型可側載;另有 HyRead 專屬封閉版 買前先確認是「開放 Android」版;封閉版裝不了自己的 App
Pubook(Pubu) ❌ 沒查到原廠 SDK 開放 Android(如 Pubook Pro 10.3),可側載 開放系統、能裝 APK,同樣沒有原廠 EPD/筆 SDK

兩個誠實但書:一是「沒查到公開 SDK ≠ 一定沒有」,有些廠商可能有 NDA/合作性質的私有 SDK,只是不對外;二是「能側載 App」和「有好用的電子紙 SDK」是兩回事——沒有原廠 SDK 時,App 一樣能在開放 Android 機上跑,只是低延遲手寫、局部刷新、前光控制這些,多半只能用通用 Android 做法,效果不一定比得上原廠 API。

一句話結論:想玩「深度電子紙控制」,BOOX 目前最好上手;只想把一般 Android App 跑在電子紙上,開放系統的 Meebook/HyRead 開放版/Pubook 也都能側載來玩。

先分清楚:Wacom EMR、InkSense、Onyx Pen SDK 是三件事

很多人一開始會把這三個混成一團,但它們分屬不同層級:

  • Wacom EMR:筆與面板之間的硬體感應技術(電磁式)。筆通常不用電池,靠面板去感應筆的位置、壓力,部分裝置還有傾斜。
  • BOOX InkSense Plus:BOOX 特定機型採用的主動式筆方案,需要相容的主動筆。
  • Onyx Pen SDK:App 真正呼叫的軟體 API,把裝置韌體提供的原生墨水能力,暴露成 TouchHelper 與 raw drawing callback(原始筆點回呼)。

這個專案沒有直接整合 Wacom SDK,也沒有另一套獨立的 InkSense SDK。策略是這樣分工:

裝置/筆 硬體筆技術 App 優先路徑 備援路徑
BOOX Note Air 5C EMR BOOX TouchHelper raw drawing Android MotionEvent stylus
BOOX Note Max EMR BOOX TouchHelper raw drawing Android MotionEvent stylus
BOOX Palma 2 Pro InkSense Plus 主動筆 仍先試 BOOX TouchHelper(實際能力看該機韌體) Android MotionEvent stylus
一般 Android 裝置 視硬體而定 不使用 BOOX raw drawing Android MotionEvent

所以可攜性的關鍵,不是在程式裡判斷「這是 EMR 還是 InkSense」,而是:

  1. 製造商是 ONYX 時,嘗試建立 BOOX 原生筆跡層。
  2. 建立失敗、或不是 BOOX 時,自動退回標準 Android 輸入。
  3. 所有筆畫都轉成自己的共同資料格式,後面的辨識與特效不直接依賴筆的硬體種類。

好處是同一套上層邏輯能服務多台裝置;限制是不同韌體對 hover、橡皮擦、壓感、掌壓與原生 front buffer 的實作仍可能不同——不能因為 API 名字一樣,就假設體驗完全一致。

低延遲手寫:一個「雙軌」設計

1. BOOX 原生筆跡層

在 ONYX 裝置上,對書寫的 View 建立 TouchHelper:

touchHelper = TouchHelper.create(inkView, rawCallback());
touchHelper.setLimitRect(new Rect(0, 0, width, height), new ArrayList<>())
        .setStrokeWidth(6f)
        .openRawDrawing();
touchHelper.setRawDrawingEnabled(true);

RawInputCallback 會收到 down、move、up 的 TouchPoint。這條路徑由 BOOX 原生墨水層直接配合電子紙顯示,比只靠 Android View 重繪更接近筆尖(延遲更低)。

2. 同時保存一份 App 自己的筆跡

每一筆會同步存成兩種形式:

  • Android Path:供畫面重繪、淡出、局部擦除,以及非 BOOX 的 fallback。
  • ML Kit Ink.Point:保存 x、y、timestamp,交給手寫辨識模型。

這個「雙軌」很重要。BOOX raw layer 很快,但它不是 App 可以任意重播、做動畫的普通 Canvas;如果沒有另外留一份自己的 Path,後面要做「墨水消失」「局部清除」「旋轉後重繪」就會很難。

3. 標準 Android fallback

若 BOOX raw drawing 無法初始化,就走 InkView.onTouchEvent(),處理 TOOL_TYPE_STYLUS、TOOL_TYPE_ERASER、SOURCE_STYLUS 與 MotionEvent 的 historical samples(同一次事件裡夾帶的多個取樣點)。這讓 App 在其他 Android 裝置上仍可運作,也方便用測試工具模擬筆跡——只是通常無法完全複製 BOOX 原生那條低延遲 front buffer。

4. 壓感的現況

目前 raw drawing 的顯示寬度固定為 6px,辨識主要靠軌跡的幾何與時間;Android 路徑雖然能讀到 pressure/tilt,但還沒有把壓力對應成動態筆寬。

這是刻意的展示版取捨:固定粗度在電子紙上比較容易得到一致的辨識與淡出效果。 若未來要做更自然的書法筆觸,應先把壓感正規化,再測不同機型的回報範圍——不能假設所有裝置都是同一條 0–1 曲線。

掌壓防誤觸:硬體有幫忙,App 還是要做第二道防線

展示時最常出包的,其實不是辨識錯,而是使用者手腕一放上去,工具列、翻頁或功能鍵就被誤觸。所以 App 另外做了一套輸入政策:

  • 每次偵測到筆後,啟動約 1.4 秒的 stylus palm guard(這段時間內把手掌類接觸擋掉)。
  • 手指接觸的 touchMajor 大於約 52dp,或 normalized size 大於 0.22,視為手掌、直接吞掉。
  • 優先相信 MotionEvent.getToolType(),不要只信 source bit。
  • BOOX raw drawing 啟用時,筆交給原生墨水層;手指只保留受控的翻頁、關書與手勢。
  • 多指事件與手掌事件,不送進書寫 View。

這裡有個實機踩坑:Note Max 的一般手指事件,有時會帶著看似 stylus 的 source bit。 如果只用 SOURCE_STYLUS 判斷,手指會被誤認成筆——所以明確的 TOOL_TYPE_FINGER 必須優先。

掌壓防護也不該只靠「關掉手指」。展示 App 仍需要手指操作,比較好的做法是把「筆」「單指功能」「多指提示」「大面積接觸」拆成不同狀態機分開處理。

手寫辨識:繁中、英文、混寫,還有小朋友的注音

基礎模型

App 用 ML Kit Digital Ink Recognition 的 zh-Hani-TW 與 en-US 兩個模型。首次啟動會下載模型、並用一小段假筆跡 warm-up(暖機),之後可在裝置上離線辨識。

中英文混寫

整頁同時送進中、英文模型只是第一層。遇到「你好我是 Seal」或「Today 天氣如何」這種混寫,App 會找筆畫之間明顯的水平空隙,把左右區塊分開,再各自跑一次中、英文模型,最後用候選分數、字形與語境合併:

原始 strokes
   ├─ 整頁中文模型
   ├─ 整頁英文模型
   └─ 若偵測到大型水平間距
        ├─ 左區:中文 + 英文
        └─ 右區:中文 + 英文
                ↓
         候選修復與語境合併

辨識範圍不能用整個螢幕

ML Kit 的 WritingArea(辨識範圍)會影響結果。如果小朋友只在大頁面角落寫幾個注音,卻把整個全螢幕當成 writing area,符號很容易被判成標點或碎片。所以本專案會依實際筆畫的 bounds 建一個較緊的辨識區域,再正規化筆畫。

注音模式要獨立

全注音、中文與中英混寫如果永遠同時競爭,候選會互相污染。因此改成讓「使用情境」決定等待時間與候選修復:

  • 一般模式:約 2.8 秒沒有新筆畫就辨識。
  • 兒童雙語模式:約 4.8 秒。
  • 全注音模式:約 6 秒(小朋友通常一個符號一個符號慢慢寫)。
  • 展示用 Promo 模式:約 2.2 秒,而且只跑中文模型,換取更快、更穩定的三題辨識。

亂塗、畫畫、寫錯字也不該立刻送 AI。App 會先判斷「密集來回的 scribble」或「停頓後的一橫」是不是塗掉的動作,刪掉相交的舊筆畫、局部刷新,然後等使用者重寫。

殘影控制的核心:局部刷新,不是一直全刷

「局部刷新」=只更新螢幕上真的有變動的那一小塊,而不是整頁重畫。這是電子紙不殘影的關鍵。

1. App 級的品質基線

BOOX 使用者可能在系統最佳化裡切到 Speed/Extreme。為避免 App 下次啟動時品質突然掉一截,App 會在啟動、回前景、重新取得焦點時,重新套用 app 範圍的 REGAL 基線並開啟 Regal(REGAL 是 BOOX 一種抑制殘影的局部刷新波形):

EpdController.applyAppScopeUpdate(
        profileName, true, true,
        UpdateMode.REGAL, Integer.MAX_VALUE);
EpdController.enableRegal();

這是應用範圍內的 best effort,不是繞過韌體的絕對鎖定。系統版本、裝置最佳化設定與廠商策略,仍可能影響最終面板行為。

2. 不同內容用不同 update mode

以下是本專案實測後的分工(不是宣稱所有 BOOX 機型都得照抄):

工作 使用模式 原因
即時手寫 BOOX raw drawing;View 備援用 GU_FAST/GU 以低延遲優先
回答逐筆浮現 過程用 GU 或 ANIMATION_QUALITY 允許局部動態更新
翻頁/淡出 ANIMATION_QUALITY;Note Max 淡出偏 REGAL 避免把每個中間灰階都殘留在面板
回答完成 REGAL/REGAL_PLUS 做一次乾淨的局部 settle(收尾)
深度清理 謹慎用 GC;Note Max 改用兩次局部 REGAL GC(整頁清除)可能造成明顯全螢幕閃爍
文字 View 停用 A2 A2 雖快,但畫質與殘影不適合書頁文字

3. Dirty rectangle 是最重要的資料

每次刷新前,先算出實際變動範圍(dirty rect,「髒矩形」):

  • 手寫區:所有 Path bounds 的聯集,再加約 36px padding。
  • 回答動畫:只送出本幀新增筆畫的 dirty rect。
  • 回答完成:只清理回答文字 content bounds 周圍。
  • 塗掉重寫:只重繪「擦除區」與「仍存在筆畫」的聯集。
EpdController.refreshScreenRegion(
        view, left, top, width, height, updateMode);

這比對整個 root view 呼叫刷新重要太多。大區域的品質刷新,很容易被韌體升級成一次明顯的全刷;小區域則比較能維持「紙面只有局部變化」的錯覺。

4. Android 的 frame 與 E Ink 面板的 frame 必須解耦

回答的手寫動畫在 Android 端大約每 44ms 更新一次 Path,但不會每 44ms 都要求面板更新。實際送往面板的節奏約為:

  • Promo:145ms
  • Note Air 5C/一般 BOOX:165ms
  • Note Max:200ms

也就是 Android 可以先累積幾小段線,再一次刷新它們的 dirty rect。如果每次 invalidate() 都立刻要求電子紙刷新,更新會排隊,最後看到的反而是卡頓、缺字或中段沒正常顯示。

5. 要等面板真的完成

品質 settle 前,會用背景執行緒呼叫 EpdController.waitForUpdateFinished(),避免最後一個 REGAL 收尾插隊、蓋掉還沒畫完的 GU 幀;同時設 watchdog(看門狗計時器),因為部分韌體可能會漏掉「完成」訊號,不能讓回答永遠卡在等待。

手寫消失特效:不能照手機動畫的思維做

早期如果把字拆成粒子、透明度碎片或大量移動點,在 LCD 上可能很好看,在電子紙上卻會留下一堆中間狀態,像數位雜訊,而不是「墨水被紙吸走」。

現在的做法是:

  1. 先用 App 保存的完整向量 Path,覆蓋在 BOOX raw layer 下方。
  2. 等這個向量版本確實送到面板。
  3. 再關掉 raw drawing,避免原生墨水層突然消失、閃一幀空白。
  4. 前半段:讓完整字形以不透明灰階逐步靠近紙張白色。
  5. 後半段:才在已經很淡的線條上,加入位置固定的白色「紙纖維孔洞」。
  6. 最後只對筆畫 bounds 做 REGAL/REGAL_PLUS 局部清除。

紙纖維的雜訊圖樣(hash)每一幀都固定,孔洞不會亂跑,所以視覺上比較像「紙慢慢吸墨」,而不是字被數位化拆散。

不同面板還要用不同節奏:

  • Note Max(大尺寸黑白):5 個較慢步驟,每步約 420ms,過程以 REGAL 為主。
  • 彩色 BOOX:10 個步驟,每步約 260ms,用 ANIMATION_QUALITY 呈現較連續的淡出,完成後再品質清理。

這不是因為 EMR 筆會製造殘影,而是「大尺寸黑白面板、彩色濾光層、刷新波形、動畫中間狀態」的組合不同。

AI 回答怎麼做出「一筆一畫寫上去」的效果

回答不是把 TextView 一個字一個字顯示。HandwrittenReplyView 的流程是:

  1. 用指定字型把 glyph(字形)rasterize 成 bitmap。
  2. 對黑色遮罩做 Zhang–Suen thinning(骨架化,把粗筆畫縮成一像素寬的骨架)。
  3. 從端點追蹤骨架,轉成一段段 Path。
  4. 按節奏逐段加入 retained path。
  5. 每次只把新增筆畫的 dirty rect 交給 BOOX 局部刷新。

於是視覺上像筆尖逐步寫出文字。但這裡要誠實標註:

這是「視覺上的手寫路徑模擬」,不是正確的國字教學筆順。

若產品要用於識字或筆順教育,就需要真正的中文字形筆順資料,不能只靠骨架追蹤。

Note Max 為什麼需要獨立的刷新設定

本專案目前做兩種刷新 profile:

  • NoteMax:大尺寸黑白面板,回答較慢、淡出以 REGAL 為主、每幾次回答安排一次局部品質清理。
  • BooxColor:Note Air 5C/其他彩色 BOOX,書寫用 GU_FAST、動畫用 ANIMATION_QUALITY、完成後用 REGAL_PLUS settle。

Note Max 上若對大 parent view 用 GC,有機會被韌體表現成整頁閃黑。所以「深度清理」改成對目前可見的小範圍連續做兩次 REGAL,中間留一點間隔。

這也說明電子紙最佳化不該只問「哪個模式最好」,真正要問的是:

  • 哪一塊畫面變了?
  • 這一塊是動畫中間幀,還是最後結果?
  • 使用者此刻更在意低延遲,還是乾淨文字?
  • 面板這一分鐘累積了多少中間狀態?

前光控制:SDK 做得到,但這一版沒有接管

目前這個 App 沒有主動調前光。Note Air 5C 與 Palma 2 Pro 的前光仍由 BOOX 系統/使用者控制;Note Max 本身沒有前光。

這是刻意的產品選擇:前光是裝置層級的狀態,不是 App 裡的一個 View 屬性。 展示 App 若擅自改亮度或色溫、離開又沒復原,會影響使用者後續閱讀。

BOOX Device SDK 的範例顯示,可以先判斷前光類型:

switch (BrightnessController.getBrightnessType(context)) {
    case FL:
        // 單一前光
        break;
    case WARM_AND_COLD:
        // 暖光、冷光分開控制
        break;
    case CTM:
        // 亮度 + 色溫
        break;
    case NONE:
        // 此裝置沒有前光,例如 Note Max
        break;
}

取得對應的 BaseBrightnessProvider 後,可以用 getIndex()/getMaxIndex()、setIndex(...)、toggle()、isLightOn()(依 provider 版本能力而定)。常見 light type 包括 LIGHT_TYPE_FL、LIGHT_TYPE_CTM_WARM、LIGHT_TYPE_CTM_COLD、LIGHT_TYPE_CTM_BRIGHTNESS、LIGHT_TYPE_CTM_TEMPERATURE;也可以廣播 action.show.brightness.dialog,讓 BOOX 自己跳亮度設定介面。

若下一版要加入前光控制,建議的流程是:

  1. 先偵測 BrightnessType,沒有前光就隱藏功能。
  2. 進 App 時記住原本的 on/off、亮度與色溫。
  3. 只在使用者明確啟用時才調整,避免每次啟動就強改全機設定。
  4. App 離開或展示模式結束時,把原值復原。
  5. 對 Note Max 顯示「此裝置沒有前光」,不要去呼叫空的 provider。

離線 AI 與資料來源

App 內的語言模型用 MNN native runtime(阿里巴巴的端側推論框架),在 arm64 裝置上以 CPU、2 threads、low precision、low memory 執行。這讓離線 AI 功能不必依賴雲端 API Key,但小模型的知識、推理與速度都有限,所以實際採混合架構:

  • 高命中、展場重要問題:離線題庫直接回答。
  • 人格與世界觀:用結構化的 Knowledge Pack 與 Evidence Map 約束回答。
  • 開放式生成:MNN 離線模型。
  • 天氣:Open-Meteo。
  • 明確的網路查證:MediaWiki。
  • 逾時或模型卡住:watchdog + 可控 fallback,不能讓畫面永遠停在「思考中」。

補一個容易誤會的點:資料(例如幾本小說)不是每次回答都整本塞進 prompt,也不是把大量原文輸出給使用者;它們主要拿來離線整理譯名、事件、角色知道什麼、角色願意透露什麼,以及可追溯的證據摘要。

如果你也要 vibe coding 這種 App,幾個值得保留的方法

1. 每一台機器都當成不同的顯示器

不要只在模擬器驗證。至少記錄下:Build.MODEL、Android/韌體版本、用的 refresh profile、每次辨識耗時與最終選擇、AI 載入與生成時間、是否觸發 watchdog、局部刷新矩形與 update mode。

2. 先建立「可重播測試」,再調動畫

手寫問題如果只能請真人反覆寫,很難分辨是辨識、輸入還是刷新退步。把 stroke 的 x/y/time 存下來,就能在測試環境重播同一份筆跡,分別測:ML Kit 候選有沒有變、混寫切分對不對、塗除有沒有誤判、回答區有沒有缺幀、Note Max 長時間用會不會愈來愈慢。

3. 把「刷新」當成排程器,而不是工具函式

成熟一點的版本,可以把所有 EPD 更新集中到一個 coordinator:合併相近的 dirty rect、限制同時只有一個品質刷新、動畫結束後統一 settle、記錄每次面板等待時間。這比讓各個 View 到處直接呼叫 refreshScreenRegion() 好維護太多。

4. 不要把展示流程綁在最慢的元件上

冷啟動時,ML Kit 模型、MNN 模型與電子紙清理都可能要時間。使用者看到的「開書動畫」不是純裝飾,而是把初始化狀態變成一段可理解的過場——但仍要有上限與 watchdog,動畫不能拿來掩蓋真正的死鎖。

Android 的另一個好處:AI 可以用 ADB 自己測、APK 直接裝

做電子紙 App 時,Android(相對於封閉系統)有兩個對「vibe coding」特別友善的地方。

一、AI 可以透過 ADB 直接操作真機、自己測。 把閱讀器用 USB 接上(或開無線 ADB),AI 就能用指令安裝、啟動、點擊、輸入、截圖,形成「改程式 → 裝上真機 → 截圖看結果 → 再改」的自動迴圈,不用你每一輪都手動操作:

adb install -r app-debug.apk               # 安裝/覆蓋安裝
adb shell am start -n 你的套件名/.MainActivity   # 啟動 App
adb shell input tap 500 800                # 模擬點擊
adb shell input swipe 300 900 300 300      # 模擬滑動
adb exec-out screencap -p > shot.png       # 截圖回存,讓 AI「看」結果

(想做更完整的 UI 自動化,可再搭配 uiautomator 或用 scrcpy 投影操作。)電子紙有個現實:截圖看到的畫面,和你眼睛看到的殘影/刷新品質不完全一樣,所以 ADB 自動測適合驗證「流程對不對、有沒有崩」,最後的刷新手感還是得真人盯著面板看。

二、做好的 APK 可以直接裝,不必上架商店。 開放式 Android 閱讀器(BOOX、Meebook、HyRead 開放版、Pubook 等)允許側載(sideload):把 .apk 丟上去、或用 adb install,就能直接跑自己的 App,不必送 Google Play 審核、也不必公開發佈。對「做給自己用」「展場 demo」「小圈子分享」來說,這個門檻低到幾乎沒有——這也是為什麼 vibe coding 一個電子紙 App,比你想像的更容易起步。

但別忘了前面那個誠實提醒:用到 BOOX 廠商 API/HiddenApiBypass 的部分是裝置專用的;自用、側載沒問題,但哪天要正式上架一般商店,隱藏 API、裝置相容性與商店政策都得再處理。

想自己動手?兩段可以直接用的 Prompt

如果你也想試,這裡給兩段可以直接丟給 AI(Claude/Codex 等)的起手 Prompt:一段先求「最小可跑」,一段再加「電子紙優化」。

Prompt 1:先做出一個能手寫的最小版

幫我做一個 Android(Java,minSdk 24、targetSdk 35)電子紙手寫 App 的最小可跑版本。需求:
1. 一個全螢幕手寫 View:在 BOOX(製造商為 ONYX)裝置上,用 Onyx 的 onyxsdk-pen(TouchHelper + RawInputCallback)做低延遲書寫;
   若初始化失敗或非 BOOX,退回標準 Android MotionEvent(TOOL_TYPE_STYLUS)。
2. 每一筆同時保存兩份:Android Path(畫面用)與一組 (x, y, timestamp) 點列(之後給辨識用)。
3. 一顆「清除」按鈕。
請附上 build.gradle 要加入的 Maven repository 與依賴,並在關鍵處註解說明為什麼要「雙軌」保存筆跡。

Prompt 2:把辨識與電子紙刷新加上去

延續上一個手寫 App,加上:
1. 用 Google ML Kit Digital Ink Recognition,模型用 zh-Hani-TW 與 en-US 離線辨識;停筆約 2.5 秒後才辨識。
2. 電子紙刷新只刷「有變動的區域」:每次更新前計算 dirty rectangle(所有變動 Path bounds 的聯集加 padding),
   用 onyxsdk-device 的 EpdController.refreshScreenRegion() 局部刷新;書寫用快速模式、收尾用 REGAL 做一次乾淨清除。
3. 把「Android 重繪節奏」和「送去電子紙面板的節奏」分開,不要每次 invalidate 都要求面板刷新。
請解釋你選的 update mode,以及為什麼不要一直對整頁做全刷。

小提醒:AI 生出來的第一版通常「能跑但不夠順」。真正的功夫,是拿到真機上一輪一輪調刷新節奏——也就是前面整篇在講的東西。

實作檢查清單(可直接照抄)

輸入

  • BOOX raw drawing 失敗時可退回 Android MotionEvent。
  • 所有筆畫同時保留 Path 與辨識點列。
  • 明確區分 finger、stylus、eraser 與 palm。
  • 不只依賴 source bit 判斷筆。
  • 橡皮擦、塗掉與普通橫線有不同規則。

辨識

  • 語言模型有下載、warm-up 與失敗提示。
  • 繁中使用 zh-Hani-TW。
  • 中英混寫不只跑單一整頁模型。
  • 全注音/兒童模式有較長的 idle window。
  • 亂塗、未寫完、低信心輸入不直接丟給 AI。

電子紙

  • 所有動畫都計算 dirty rectangle。
  • Android animation cadence 與 panel cadence 分離。
  • 最後一幀後才做 REGAL/REGAL_PLUS settle。
  • 不對大 root view 頻繁 GC。
  • 切前景、轉向、焦點恢復時,重新套用 refresh profile。
  • 面板等待有 watchdog。

前光

  • 先偵測裝置是否支援。
  • 修改前先保存原狀態。
  • 使用者明確操作才修改。
  • 離開 App 時復原。
  • Note Max 等無前光裝置不顯示無效控制。

結語

Vibe coding 可以很快做出「能跑」的 Android App,但電子紙會逼你面對另一層現實:畫面不是畫完就結束,筆跡也不是收到座標就完成。 真正的體驗,來自輸入技術、保留筆跡、辨識模型、面板波形、局部刷新與產品流程一起工作。

這個專案最後得到的,不是一個神奇的「最佳刷新模式」,而是一套協作原則:

筆尖要快、動畫要節制、文字要乾淨、清理要局部;硬體差異交給 adapter,使用者等待則交給有意義的故事與明確狀態。

這也是把一個原型,變成能在 Note Air 5C、Note Max、甚至不同筆技術裝置上都展示得起來的關鍵。如果你也在 vibe coding 自己的閱讀器 App,希望這份踩坑筆記能幫你少繞幾個彎。

參考資料與 SDK 連結

本文用到的 SDK/工具

其他閱讀器的 SDK/開放性參考

  • 只有 BOOX(Onyx)有原廠公開 SDK(見上方連結)。
  • Supernote、Meebook、HyRead、Pubook:以公開資訊查無原廠 EPD/筆 SDK;開放 Android 機型可側載 App(HyRead 有開放版與封閉版之分,購買前請確認)。「查無」不代表一定沒有,可能有 NDA/私有 SDK。

延伸閱讀

Reader Contribution

讀者補充與資料修正

提供補充或修正

如果你對本文主題有實際經驗,或發現資料來源、時間點、連結需要更新,歡迎提供補充。Read with Pro 會先人工確認內容,再整理進站內資料;未經確認的回報不會直接公開。

購買通路與年份使用的書城、圖書館或閱讀 App電池、螢幕、翻頁、手寫、系統穩定度等實際體驗售後、維修、保固或退換貨經驗規格、價格、上市狀態或連結錯誤修正

請盡量附上來源、截圖或公開連結,方便我們確認。涉及個人資料、訂單資訊或私人對話時,請先遮蔽敏感內容。