Android App 的 Edge-to-Edge UI 指南 Part 2:軟體鍵盤 IME 動畫處理

工程師 Hiking
Dcard Tech Blog
Published in
5 min readMay 27, 2022

上一篇 Part 1 的介紹裡,我們看見了軟體鍵盤,或是包含手寫或語音輸入等輸入方式,以下統稱 IME(輸入法,Input Method Editor),其所佔的空間也可以從 Window Insets 裡取得,因此當 IME 彈出時,app layout 可以因應調整。

下面動畫的左邊部分,是我們在 Part 1 時的進度,可以看見 IME 出現時,輸入框往上位移以避開被鍵盤蓋住,但是輸入框的位移並沒有跟 IME 動畫同步;而本文希望做到的是右邊的樣子,讓 app layout 可以配合 IME 動畫做調整。

軟體鍵盤動畫,左:未同步/右:同步(0.2X 慢動作)

先觀察一下 API 的設計

這次除了 OnApplyWindowInsetsListener 以外,我們額外再註冊一個 WindowInsetsAnimationCompat.Callback,印些 log 來觀察一下:

當點擊輸入框彈出 IME 時,得到的結果是:

onApplyWindowInsets, bottom = 0onPrepareonApplyWindowInsets, bottom = 817onStart, bounds = Bounds{lower=Insets{left=0, top=0, right=0, bottom=0} upper=Insets{left=0, top=0, right=0, bottom=817}}onProgress, bottom = 0(多個 onProgress,0~817)onProgress, bottom = 817onEnd

之前的做法我們只聽得到 onApplyWindowInsets ,所以 IME 彈出瞬間直接設了 817px 的 padding 上去,輸入框瞬間位移。

但是如果我們在 onApplyWindowInsetsonProgress 時都調整 layout,聽到的數值會是 0、817、0、1、2……816、817,輸入框還是會先閃現在終點,才又回到起點開始動畫。

當試著切換 IME,例如從英文鍵盤換到注音鍵盤時,由於注音鍵盤較高,高度改變時會發生:

onApplyWindowInsets, bottom = 930

IME 切換時是沒有動畫的,所以只有 onApplyWindowInsets 會被呼叫,因此我們也不能忽略 onApplyWindowInsets 只管 onProgress

實作

透過上面的觀察,我們可以在 onPrepare 的時候視為動畫開始進行,而動畫進行中時忽略 onApplyWindowInsets,只處理 onProgress。

有了上面的實作,只要將 Part 1 實作裡的

ViewCompat.setOnApplyWindowInsetsListener(view)

換成

view.doOnWindowInsetsChanged(listenToAnimation = true)

就可以讓 UI 與 IME 動畫同步了。

包裝成 extension function 的做法整合了OnApplyWindowInsetsListenerWindowInsetsAnimationCompat.Callback ,讓使用端寫起來更簡潔,也可以透過 listenToAnimation 控制要不要聽取動畫進度。

相容性

Window Insets Animation 從 Android 11(API level 30)開始支援,但這裡的實作使用的都是 AndroidX 的 ViewCompatWindowInsetsCompat 的相容實作,因此雖然舊版系統不會因此實現 IME 動畫同步,但你無需為了相容舊裝置實作額外的邏輯。

未來展望

你可能已經注意到,雖然上述的 API 就目前來說,使用情境就只有處理 IME 的動畫,但 API 的設計本身並不是針對 IME,而是各種 insets 都有實作動畫的空間。

撰稿期間正好碰上 Android 12.1(12L)剛發布不久,其中底部的工作列(taskbar)對 app 來說也是一種 system bar inset。將來若有 IME 以外的 insets 在出現、隱藏或改變大小時回報動畫給 app,也是相當合理;而本文介紹的實作有鑑於此,也並非只有針對 IME 或 bottom inset 做處理。

Android 12.1 (12L) 的 UI

Dcard Android 職缺看這裡

Senior Android Developer 👉 https://dcard.link/PnSmCJ
Senior Android Developer, Advertising 👉 https://dcard.link/d6ZH2s
Junior Android Developer 👉 https://dcard.link/hU0Cd9

--

--