應用程式基礎

使用 Jetpack Compose 擴充觸控螢幕以外的輸入互動

喇叭

  • Alex Vanyo

    軟體工程師

  • Jeremy Walker

    Compose 軟體工程師

關於

講座簡介

隨著新板型規格超越觸控螢幕,Compose 的輸入系統也與時俱進,以因應挑戰。其分層架構奠定了基礎,可完美支援從進階觸控板手勢到新穎硬體輸入等各種功能。

本課程提供清楚的架構細目和實際程式碼範例,帶您瞭解輸入事件的生命週期,從 OS 平台層一路到 Compose 手勢偵測工具和 UI 元件。您將瞭解如何追蹤整個堆疊中的事件、為新興輸入類型套用重要架構模式,以及在自己的應用程式中自信地偵錯複雜的手勢互動。

摘要

  • Jetpack Compose 會透過六階段管道處理傳入的 Android 指標事件。這個管道會維護命中路徑樹狀結構,並在 UI 節點中分三次調度事件。
  • Compose 中的觸控板輸入現在會對應至滑鼠事件,而非觸控事件。Compose 也會將平台假手指事件轉換為專屬平移和縮放指標事件。
  • 智慧眼鏡觸控板採用間接輸入類型,而非螢幕座標觸控。這類元件會使用主要方向動作軸,將水平滑動手勢對應至垂直方向的「下一個」或「上一個」導覽動作。
  • 測試支援已擴展至觸控以外的範圍,並提供 performTrackpadInput 等新方法。這些 API 提供熟悉的測試介面,可驗證應用程式是否正確處理非觸控裝置。

Gemini Notebook

主題

  • jetpack-compose
  • pointer-input
  • 觸控板
  • 手勢
  • intelligent-eyewear
  • 無障礙設定

轉錄稿

簡介和輸入理念

  • 大家好!

  • 希望您在 Android 開發人員高峰會上收穫滿滿,也感謝您參加我們的講座。

  • 我是 Compose 團隊的軟體工程師 Alex Vanyo,今天與我一起的是 Compose 團隊的另一位軟體工程師 Jeremy。

  • 我們一直致力於強化 Jetpack Compose 的輸入系統,今天將說明如何將系統擴充至觸控螢幕以外的裝置,以便輕鬆支援非觸控輸入。

  • 廣義來說,輸入是指使用者與應用程式互動的任何方式。

  • 提到行動應用程式,您可能首先會想到觸控螢幕,但這絕對不是唯一的輸入類型。

  • 其他類型的輸入方式包括實體鍵盤、滑鼠、觸控板、觸控筆、控制器和旋轉裝置。

  • 我們也看到新的輸入類型,例如智慧眼鏡上的觸控板、XR 中的眼球和手勢追蹤,以及 Wear 上的手指和手腕手勢。

  • 如果想進一步瞭解,還有其他類型的輸入方式,例如用於文字編輯的軟體輸入法編輯器、麥克風、相機和其他裝置感應器。

  • 您必須預期應用程式會與各種輸入類型搭配使用。

  • 其中一個原因是,應用程式執行的裝置種類越來越多,且這些裝置的適應性也越來越高。

  • 裝置板型規格可能有最常見的輸入法,但請務必不要假設特定裝置板型規格代表使用特定輸入類型。

  • 雖然 Chromebook 內建觸控板和鍵盤,但這並不代表這些輸入類型是該板型規格獨有。

  • 您應該預期實體鍵盤、觸控板和滑鼠也會在手機、平板電腦和電視上使用,有些電視甚至有觸控螢幕。

  • 預期並支援多種輸入類型的第二個重要原因,是無障礙功能。

  • 允許使用者使用自選的輸入裝置,並允許他們視情況切換輸入裝置,是提高應用程式無障礙程度和可用性的正確做法。

  • 此外,根據管轄區的不同,法律可能要求應用程式必須針對這些輸入類型 (尤其是鍵盤和滑鼠等) 採取特定、相容的行為。

  • 簡而言之,無論板型規格為何,您都應建構對不同輸入類型的支援。

  • 接著,我們來看看 Jetpack Compose 如何協助應用程式滿足這些使用者需求。

  • 一般來說,Compose 的理念是,這些不同的輸入類型應能與標準元件搭配使用,且不需進行任何特殊操作。

  • 接著,您應該要有能自訂及調整行為的工具,以便視需要進行調整。

  • 因此,除了這些初始輸入類型外,隨著基礎平台支援更多輸入類型,我們也希望在 Compose 元件中加入對這些類型的支援,只要更新 Compose 版本,就能改善應用程式的行為。

  • 我們將在本次演講中介紹兩個這類範例,兩者都是 Compose 最近新增的功能。

  • 巧合的是,這兩者都是類似觸控板的裝置,但使用方式截然不同。

  • 首先,我們來看看最近在筆電式觸控板上進行的一些工作。

  • 觸控板是以指標為基礎的輸入裝置。

  • 具體來說,使用觸控板時,您會取得二維座標。

  • 但與觸控螢幕不同的是,觸控板上的手指座標不會直接位於 UI 的座標空間中。

  • 如果是筆電觸控板,則會轉譯為控制游標。

  • 在這個轉譯過程中,會進行許多複雜的平台轉換,例如游標加速和手掌誤觸抑制。

  • 因此,在 Compose 端,您會看到與游標位置對應的指標,其行為通常類似滑鼠輸入。

  • 我們從 Android 平台取得懸停資訊、點擊 (包括次要或第三次點擊),並將所有這些資訊包裝成 MotionEvent。

  • 為了瞭解我們如何改善最新版本 Compose 的行為,首先來概略瞭解 Compose 如何更密切地處理這些指標事件。

指標輸入處理的六個階段

  • 我會將這個過程分為六個階段,並逐一說明。

  • 第一階段是將 MotionEvents 移至 Compose。

  • 因此,Compose 實際上只是一個檢視畫面:一個非常複雜、能執行許多操作的檢視畫面,但它只是一個檢視畫面,這意味著它需要在整個 Android 系統中運作,才能處理指標輸入。

  • 因此,在 Compose 中,這表示 Compose 位於一般活動的一般視窗中,會取得 MotionEvent:Android 平台的 MotionEvent。

  • 接著,Compose 必須處理這些事件,並在 Compose 中執行您想執行的所有指標輸入相關操作。

  • 第一個階段是將這些 MotionEvent 轉譯為 Compose 中與平台無關的內部表示法,我們稱為 PointerInputEvent。

  • 這是內部類型,因此您不會直接看到。

  • 如果想查看這裡發生的所有詳細資料,可以查看 MotionEventAdapter,但我們在這裡會略過這些詳細資料。

  • 現在,我們在 Compose 中有了與平台無關的 MotionEvent 表示法。

  • 接下來,輸入通常會隨著時間發生。

  • 您會陸續收到許多輸入事件,因此實際瞭解一段時間內發生的情況非常實用。

  • 手勢、速度追蹤、斜率偵測等,都需要瞭解每個時間點發生的情況。

  • 因此,這個處理管道的下一個階段是加入時間資訊,並根據連續的指標事件進行一些預先處理。

  • 在程式碼中,您可以將其視為採用目前的 PointerInputEvent,並與前一個事件發生的情況結合,然後建立時間感知 InternalPointerEvent。

  • 顧名思義,這也是另一種內部類型,但您可以將其視為不同指標 ID 與指標輸入變更的對應,而 PointerInputChanges 是公開類型。

  • 這是第一個。

  • 思考不同指標 ID 時,您可能會注意到,既然我們知道追蹤時間,也可以追蹤指標 ID 的出現和消失時間。

  • 不只一個。

  • 如果觸控螢幕上有多根手指,可能同時會有多個指標來來去去,我們需要處理這種情況。

  • 同樣地,如果您想深入瞭解細節,可以查看 PointerInputChangeEventProducer,它會執行所有這些操作,但我們會略過。

  • 還有很多內容要介紹。

  • 現在,我們有了輸入內容的 Compose 表示法,其中包含時間資訊。

  • 下一步是實際找出如何處理該事件。

  • 我們該寄到哪裡?

  • 這時就要進行命中路徑測試。

  • 假設我們傳送 MotionEvent,而這個 ComposeView 填滿整個視窗,甚至是整個螢幕。

  • 我們需要找出傳送目的地。

  • Compose 只是檢視畫面,但 Compose 內有許多內容,包括各種節點、按鈕、互動項目,因此我們必須在 Compose 中完成所有工作,找出要將特定位置發生的事件傳送至何處,並找出實際對該事件感興趣的節點。

  • 這些版面配置節點有座標、邊界和形狀,因此我們需要找出是否有任何節點被點擊。

  • 但這也不是最通用的觀點。

  • 我們喜歡將可互動的項目放在其他可互動的項目中,例如在清單中加入按鈕。

  • 我們經常這麼做。

  • 因此,這裡的一般情況是,特定輸入事件會建立完整的命中路徑,該路徑會通過輸入事件座標命中的每個可互動節點。

  • 因此,在這個例子中,巢狀階層中會有三個。

  • 因此,我們有 Hit Path,但這還不夠。

  • 還記得我說過,我們有多個指標輸入事件,以及多個可能按下的指標嗎?

  • 我們需要完整的命中路徑樹狀結構。

  • 如果有多個指標,就不會只有一個命中路徑。

  • 因此,下一個階段是建立及更新這個命中路徑樹狀結構。

  • 接著,我們來進一步瞭解實際情況。

  • 左側是視覺化呈現的節點,左下方則是階層式表示法,顯示哪些節點位於其他節點內,以及哪些節點是彼此的上層或下層節點。

  • 當指標向下移動時,系統會追蹤該命中路徑 (即通過樹狀結構的完整路徑),然後取得該特定指標的命中路徑。

  • 當我們取得第二個指標時,這個命中路徑就會變成樹狀結構。

  • 它可能與另一個網路的某些節點交集,但也可能與其他節點互動。

  • 右側是我們目前維護的命中路徑樹狀結構。

  • 指標可能會隨時變動,因此我們需要自行維護這項資訊,確保能取得目前對每個進行中事件感興趣的完整節點清單。

  • 現在我們來做個小測驗

  • 現在,我們已為這些輸入事件維護命中路徑樹狀結構,這表示我們已進入下一個階段。

  • 現在,我們知道要將這些指標輸入事件傳送到何處,以及哪些節點對每個特定事件感興趣。

  • 瞭解這點後,我們就能進行座標轉換。

  • 如果節點在本機座標空間內接收輸入事件,您就不必自行進行大量座標數學運算,這點非常實用。

  • 因此,一旦我們知道這一點,就可以取得指標輸入事件,並將其與感興趣的節點座標結合,然後最終建立公開的 PointerEvent,並將其分派給所有人和指標輸入修飾符。

  • 最後,在第 6 階段,我們終於要實際傳送事件。

  • 這是指標輸入事件,事件發生後會傳送至節點,讓節點執行所需操作。

  • 我們分三次完成這項作業。

  • 包括初步、主要和最終階段。

  • 之所以有三個不同的階段,是為了讓節點彼此通訊,並傳達對特定指標輸入事件特定部分的興趣,然後在分派過程中取用這些事件的不同部分。

  • 因此,第一個階段 (初始階段) 會從祖系到子系。

  • 舉例來說,如果某個外部版面配置節點可能是捲動容器,且知道自己已開始捲動,並想停止所有指標事件到達任何子項,這項功能就非常實用。

  • 這樣一來,在初始傳遞時,系統就會允許它使用,然後子項就不會再收到任何事件。

  • 第二個票證是主要票證。

  • 這是唯一從子系到祖系的關係。

  • 顧名思義,這是執行作業的主要階段。

  • 舉例來說,如果後續事件會消耗按下事件,並開始處理點擊,那麼在巢狀可點擊項目中,後續事件就有機會在任何父項之前執行這項操作。

  • 最後一個階段 (最終階段) 與初始階段相同,都是從祖系到子系。

  • 這樣一來,子項就能通知父項,表示父項已取用部分事件。

  • 因此,如果子項開始處理點擊,但父項開始捲動,子項可以偵測到父項的動作,並取消已開始處理的點擊。

  • 以上就是三個階段和六個步驟。

  • 這些是游標輸入系統上所有內容的基礎。

  • 所有手勢處理、可捲動、可拖曳、可點擊和任何自訂指標輸入,都是以這個基礎為基礎。

觸控板更新和多指手勢

  • 現在,我們可以進一步討論 Compose 如何改善觸控板行為支援。

  • 因此,在 Compose 1.12 之前,觸控板輸入事件會回報為觸控指標類型,因為使用者是以手指實際與觸控板互動來控制游標。

  • 不過,使用者期望這種輸入類型的運作方式更像實體滑鼠。

  • 因此,我們將指標類型從 PointerType.Touch 變更為 PointerType.Mouse。

  • 這項異動可讓使用者在任何查看這項資訊的元件上,享有更一致的滑鼠和觸控板體驗。

  • 舉例來說,工具提示行為:您可能希望觸控板和滑鼠的工具提示行為相同。

  • 我們對觸控板所做的第二項重大變更,與觸控板上的多指手勢有關。

  • 先前,Compose 會透過 Android 平台巧妙但有點詭異的假手指概念。

  • 我們來看看這代表什麼意思。

  • 在觸控板上用兩指平移時,Android 會產生一個虛擬觸控螢幕手指,在游標所在位置按下。

  • 接著,當你在觸控板上移動手指時,觸控螢幕上的假手指會移動正確的距離和方向,模擬所需行為,然後放開。

  • 因此,在觸控板上向上移動兩指,會轉譯為觸控螢幕上的單一假手指,也會向上移動,進而導致向下捲動。

  • 有趣的是,如果你和我一樣使用反向觸控板捲動功能,就會希望捲動方向相反。

  • 做法是翻轉假手指的方向。

  • 現在,在觸控板上向上移動兩指,會轉譯為在觸控螢幕上向下移動一根假手指,進而導致向上捲動,與先前的結果相反。

  • 資源調度也類似。

  • 在觸控板上使用雙指縮放時,Android 會產生兩根虛擬觸控螢幕手指,並根據手勢靠近或分開,模擬放大或縮小的行為。

  • 因此,自 Compose 1.12 (平移) 和 Compose 1.13 (縮放) 起,我們現在會攔截這些假手指。

  • 我們不會直接傳送平台提供的事件,而是會重新解讀為新的指標事件類型:PanStart、PanMove 和 PanEnd 用於平移,ScaleStart、ScaleChange 和 ScaleEnd 用於縮放。

  • 我們會在先前介紹的 Android 到 Compose 事件轉換階段執行這項操作。

  • 因此,我們會將假手指事件轉換為適當的類型,然後透過這個平台定義的手勢,傳送至與游標對應的命中路徑。

  • 接著來看看這項功能的重要性。

  • 為什麼要這麼做?

  • 假設這個方塊是一張精美的圖片,我們想要放大。

  • 在觸控板上使用兩指縮放時,其中一指可能會錯過元素。

  • 其命中路徑不會與圖片相交,且會從邊界外開始。

  • 因此,這會導致圖片捲動,而不是縮放。

  • 如果圖片外還有其他可捲動的元素,可能也會一起捲動。

  • 這不符合使用者需求。

  • 他們希望擴大業務規模。

  • 因此,我們現在不會傳送這些假手指,而是只將縮放手勢傳送至游標位置下方的元素,這樣就能產生正確的行為。

  • 無論游標位於何處,或與要縮放的元素邊緣有多近,現在都能正確縮放。

  • 好消息是,如果您使用這些基本建構元素,可捲動和可轉換等元件預設會使用這些重新解讀的輸入事件,為觸控板輸入提供更高傳真的行為。

  • 如果您使用以這些元件為基礎建構的任何元件,只要將 Compose 更新至最新版本,就能享有更優異的行為。

  • 如果您正在編寫自訂指標輸入修飾符,建議您查看這些新的 API,並確保支援這些 API,為使用者提供最佳體驗,無論他們使用哪種輸入裝置。

  • 當然,我們也新增了測試 API,您也可以檢查應用程式是否正確處理所有這些不同的輸入類型。

  • 因此,performTrackpadInput 看起來與現有的滑鼠和觸控測試 API 非常相似。

    onNodeWithContentDescription("Photo")
        .performTrackpadInput {
            click(center)
    
            pan(Offset(0f, 100f))
    
            scale(0.9f)
        }
  • 無論使用者在裝置上使用何種輸入裝置,您都可以使用上述所有方法,驗證應用程式是否正常運作。

  • 希望這能讓您更清楚瞭解指標事件如何從觸控板上的手指,一路傳遞到應用程式。

  • 接下來,我將交棒給 Jeremy,他會介紹觸控板的另一種用法。

間接輸入簡介

  • 謝謝。

  • 現在我們來做個小測驗

  • 大家好,我是 Jeremy Walker。

  • 我經常處理輸入內容,因此想與您分享我們在 Compose 輸入內容方面的一些新功能,以及這些功能對您的影響。

  • 尤其是這種間接輸入類型,這是因為智慧眼鏡這種新板型規格而產生的新輸入方式。

  • 因此,當你從眼鏡取得輸入內容時,可以透過幾種方式:你可以看著某個東西、使用語音、使用手勢,但我會特別談論觸控板。

  • 這些與 Alex 提到的有點不同,因為它們位於頭部兩側,垂直、非常寬且極短。

  • 因此,當我們思考這項功能在 Compose 中的運作方式時,如果想像眼前有一個小按鈕,而您移動游標嘗試觸碰它,這並不是很好的體驗,對吧?

  • 那麼,我們要如何支援這項功能?

  • 提醒您,這是 Glasses UI 的外觀。

  • 這款眼鏡採用獨特設計,可將內容完美疊加在視野中。

  • 但我們盡量保持簡單。

  • 從這個範例可以看到,這是一份清單。

  • 聚焦於中央的特定元素。

  • 我們真正想做的是什麼?

  • 我們想選取這個項目,或移至上一個或下一個項目。

  • 不一定要提供螢幕位置。

  • 這個稍微複雜一點,但基本上有三個選項:掛斷、取消靜音或關閉視訊。

  • 但實際上,您需要選取該項目,或移至其他項目並選取。

  • 因此,我們認為不需要直接對應到畫面上的位置,或是在這個情況下,你的眼睛。

  • 這就是實際的樣子。

  • 舉例來說,您可以在這裡看到,我只是在元素之間移動,這是在眼鏡 UI 上,這符合我們的需求。

  • 輕觸即可。

  • 因此,我們研究了 Android,思考有哪些現有功能可以支援這項功能。

  • 如果各位有,抱歉。

  • 這裡會跳過。

  • 讓我回去。

  • 有了

  • 現在我們來做個小測驗

  • 如果各位曾使用過這類裝置,並與 Android 裝置連結,就會知道可以輕鬆在不同裝置的 UI 元素之間切換。

  • 舉例來說,如果遙控器已連結 Google TV,畫面上會顯示一整排電影,你可以按下方向鍵,找到想看的電影,然後按下「確定」按鈕選取。

  • 如果你有遊戲選單和這個控制器,可以使用 D-pad 或搖桿移動到 UI 元素 (例如選單),然後選取該元素。

  • 鍵盤也一樣。

  • 這與我們在 Compose 中想要達成的效果相符。

  • 再次提醒,以下是相關資訊,方便您瞭解。

  • 如果再往前追溯,其實有一種觸控板類型是與電視配對,並使用一種稱為導覽觸控的功能。

  • 這個做法完全不涉及 X 和 Y 座標,與螢幕完全無關。

  • 僅用於偵測手勢。

  • 因此,向右滑動就像是按下「喜歡」或向右箭頭。

  • 向左滑動,則會向左、向上和向下移動。

  • 這樣一來,我們就能大致取得所需資訊。

  • 您只需要使用 x 和 y 進行速度追蹤,即可找出拖曳和滑動等動作,因此這能提供我們在 Compose 上開始所需的大部分內容。

  • 這並非精確的對應,因為它有點對應到電視,所以我們需要確保不走電視路線,不弄亂電視,所以我們需要確保將它們分開,但這是我們稍後會討論的事情。

  • 但至少能做為起點。

  • 這樣我們就能使用這類觸控導覽。

間接輸入和新板型規格的 API 設計

  • 好的。

  • API 設計會是什麼樣子?

  • 在 Compose 中,最常見或最普遍的輸入方式是觸控,對吧?

  • 因此,我們在這方面做了很多工作。

  • 因此,我們必須設法找出兩者之間的關聯。

  • 提醒您,PointerEvent 的結構如下。

  • 您可以在這裡看到類型,也就是向上移動的向下類型,然後您有這個 PointerInputChange,這只是一個清單,您可以將其視為手指。

  • 如果你放下三根手指,清單中就會有三個項目。

  • 因此我們希望它看起來像這樣。

  • 這不是 UI 模組,而是比多數開發人員處理輸入時使用的模組更低階的模組。

  • 這些服務會位於基礎層上方。

  • 所有手勢偵測工具都在這裡。

  • 因為身為開發人員,您不會在意這些細微的輸入。

  • 您只需要知道是否發生拖曳、點擊或滑動,然後根據該動作執行操作。

  • 因此,我們提供了一堆程式碼,可為您執行這項操作,並由這些程式碼在幕後提供支援。

  • 因此,我們開始思考,如何讓基礎中的現有程式碼盡可能輕鬆地支援這些新類型。

  • 因此,我們建立了一個看起來幾乎相同的新類型。

  • 只是稱為間接。

  • 「間接」是指不直接觸碰螢幕。

  • 因此,這項功能會讓您知道,這項功能即將推出,請不要從觸控的角度思考。

  • 請注意,我們在這裡只是處理手勢,以及在 UI 元素之間瀏覽。

  • 類型相同。

  • 其中包含手指清單。

  • 在本例中,是 IndirectPointerInputChange。

  • 我們確實這麼做了,而且效果很好。

  • 但我們還發現另一個項目,稱為「主要方向動作軸」。

  • 這聽起來有點複雜,但等我說明後,你就會明白,因為它的由來很有趣。

  • 這項功能是如何誕生的?

  • 同樣地,眼鏡側邊有觸控板,寬度較寬,長度較短。

  • 因此,我們當時的想法是,既然有了觸控導覽,

  • 所以我們可以左右滑動。

  • 效果很好。

  • 不過,如果仔細審視現有的觸控板

  • 向左或向右移動時,觸控板上的 UI 內容也會向左或向右移動。

  • 如果向上或向下移動,清單就會向上或向下捲動。

  • 如果戴著眼鏡,就像這樣,其實還不錯,對吧?

  • 但如果這樣做,體驗就不太好。

  • 我們發現最自然的動作顯然是輕觸,但要沿著眼鏡側邊滑動。

  • 因此,我們對使用者進行了大量研究,發現他們實際上預期在水平滑動時,內容不會水平移動,而是垂直移動。

  • 你可以想像一下,當你在這裡滑動時,如果面前有一份清單,你可以向前瀏覽清單,也可以向後瀏覽清單。

  • 因此,我們採用二維輸入,並嘗試以一維方式思考。

  • 這就是主要動作軸概念的由來,你可以水平移動,它會垂直移動。

  • 因此,您可以在裝置上指定。

  • 你可以說,這不是一般的觸控板。

  • 我想將主要方向設為水平,這也是我唯一想沿著移動的主軸。

  • 這就是這項功能的由來。

  • 好的。

  • 因此,我們對這方面很有信心。

  • Compose 會處理所有作業。

  • 您也可以採用這種直向實作方式。

  • 你可以考慮一下。

  • 因此,我們對應的方式是,現在您有了水平定義,我們只有前進和後退。

  • 我們沒有上、下、左、右。

  • 我們只有前進和後退。

  • 如果您熟悉焦點,可以使用下一個和上一個來對應。

  • 現在,我們只要移至下一個和上一個。

  • 這對直向影片來說非常實用。

  • 而且更棒的是,

  • 這項功能可直接用於橫向模式,對吧?

  • 因為你只有一堆聚焦項目。

  • 只要前往下一頁或上一頁,兩者都能正常運作。

  • 因此,在 Compose 中,我們定義並解決了這個問題,但如果裝置本身能說出,因為它知道它是否是奇怪的形狀,並且知道它是否只需要這個主軸,但它沒有辦法定義。

  • 因此,我們必須將這個概念帶回平台,並說,好吧,我們需要在平台中定義這個概念,以便將其標準化,而這些裝置本身可以指定它,這樣 Compose 就可以完全支援它。

  • 因此,我們在 Compose 中進行開發,並在 Compose 中向前和向後開發,並將其帶入。

  • 這正是我們採取的做法。

  • 我們建立了這個新 API,讓您或裝置可以指定。

    InputDevice.ViewBehavior
    .getPrimaryDirectionalMotionAxis()
  • 還記得我先前談到觸控輸入或觸控導覽時,曾說過這主要是用於電視。

  • 我們不希望破壞這些實作。

  • 因此,我們新增了另一個欄位,用來指定這種新的玻璃輸入類型。

  • 好的。

  • 這對您有什麼影響?

  • 這是手勢偵測工具的更具體版本。

    // Glimmer Library
    Box(
        modifier =
            Modifier.fillMaxSize()
                .onIndirectPointerGesture(
                    enabled = true,
                    onSwipeForward = { /* onSwipeForward */ },
                    onSwipeBackward = { /* onSwipeBackward */ },
                    onClick = { /* onClick */ },
                )
                .focusTarget()
    ) {
        // App()
  • 如果您使用 Foundation,則可直接搭配手勢偵測工具使用。

  • 我們已為您完成這項作業,您不必採取任何行動。

  • 但如果你想為眼鏡製作應用程式,可以使用我們的 Glimmer 程式庫,然後我們有一個特殊的手勢偵測器,它只會給你,你知道,所以你不必將它們結合起來,你可以只使用這一個,獲得點擊,前進框架,和後退。

測試間接指標輸入

  • 好的。

  • 我們已介紹 API 在平台和 Compose 中的運作方式。

  • 測試

  • 這就是觸控測試的畫面。

    @Test
    clickTest {
    
    rule.onNodeWithTag(defaultTag)
    .performTouchInput
    { click(center) }
    
    }
  • 只要說出「performTouchInput」,然後做出手勢即可。

  • 手勢有很多種,我們需要建構所有小事件,才能讓手勢發生。

  • 概略運作方式為何?

  • 您有 Compose UI 模組和 UI 測試模組,兩者會透過 MotionEvent 進行通訊,至少在輸入方面是如此。

  • 此外,UI 測試模組也使用佇列機制,全部以 MotionEvent 為基礎。

  • 為什麼要告訴你這些?

  • 因為您無法以程式輔助方式建立類似間接輸入事件的 MotionEvent,這對我來說是件好事,因為這表示我必須重寫模組和佇列系統之間的所有通訊。

  • 但我做到了。

  • 這對您有什麼影響?

  • 如果你習慣觸控輸入測試,這就是間接輸入的結果。

  • 只要在名稱中加入「indirect」即可。

    @Test
    clickTest {
    
    rule.onNodeWithTag(defaultTag)
    // .performTouchInput
    .performIndirectPointerInput (
    IndirectPointerEventPrimaryDirectionalMotionAxis.X,
    inputDeviceSize,
    )
    { click(center) }
    }
  • 現在,我確實傳遞了一些額外參數。

  • 您可能認得第一個,那是指定水平位置的地方。

  • 輸入大小則顯然是觸控板的大小。

  • 這項設定不會造成太大影響。

  • 因此,如果查看原始碼,您會發現我只是編寫了擴充功能函式,看起來就像這樣。

    @Test
    clickTest {
    
    rule.onNodeWithTag(defaultTag)
    // .performTouchInput
    .performIndirectPointerInput
    { click(center) }
    
    }
  • 好的。

  • 這應該能讓大家瞭解,我們為了在鏡框或智慧眼鏡上支援觸控板,做了多少努力。

  • 但我們正在進行這些,我也在進行這些眼部和手勢追蹤的練習,所以還有很多工作要做。

  • 總而言之,我希望大家記住什麼?

摘要和重點

  • 因此,無論您是使用手機、眼鏡或其他輸入裝置搭配觸控板,我們都會考量到這一點,並試著提前做好準備。

  • 因此,如果您使用這些 Foundation 手勢偵測工具,就能直接使用。

  • 這是第一點。

  • 第二,Compose 是主要的 UI 工具包,我們會繼續在平台和工具包中探索及支援新的輸入類型,就像我剛才示範的那樣。

  • 此外,在測試 API 時,我希望維持熟悉的介面和模式,讓您能盡可能輕鬆地遷移及測試這些新類型。

  • 謝謝,希望以上說明對你有幫助。

程式碼

程式碼片段

「觸控板更新和多指手勢」影片 15:47 處的 Kotlin 程式碼片段
onNodeWithContentDescription("Photo")
    .performTrackpadInput {
        click(center)

        pan(Offset(0f, 100f))

        scale(0.9f)
    }
「API design for indirect input and new form factors」中 25:08 處的 Kotlin 程式碼片段
InputDevice.ViewBehavior
.getPrimaryDirectionalMotionAxis()
「API design for indirect input and new form factors」影片 25:32 處的 Kotlin 程式碼片段
// Glimmer Library
Box(
    modifier =
        Modifier.fillMaxSize()
            .onIndirectPointerGesture(
                enabled = true,
                onSwipeForward = { /* onSwipeForward */ },
                onSwipeBackward = { /* onSwipeBackward */ },
                onClick = { /* onClick */ },
            )
            .focusTarget()
) {
    // App()
「測試間接指標輸入」中的 Kotlin 程式碼片段 (26:11)
@Test
clickTest {

rule.onNodeWithTag(defaultTag)
.performTouchInput
{ click(center) }

}
「Testing indirect pointer input」中 27:08 的 Kotlin 程式碼片段
@Test
clickTest {

rule.onNodeWithTag(defaultTag)
// .performTouchInput
.performIndirectPointerInput (
IndirectPointerEventPrimaryDirectionalMotionAxis.X,
inputDeviceSize,
)
{ click(center) }
}
「測試間接指標輸入」中 27:23 的 Kotlin 程式碼片段
@Test
clickTest {

rule.onNodeWithTag(defaultTag)
// .performTouchInput
.performIndirectPointerInput
{ click(center) }

}