Wear OS 中的應用程式使用的版面配置技巧與其他 Android 裝置相同,但必須根據特定手錶的限制來設計。
注意:請勿將整個功能和 UI 從行動應用程式轉移至 Wear OS,這麼做無法帶來良好的使用者體驗。
如果您是針對方形手錶設計應用程式,則圓形手錶上螢幕角落的內容可能會遭到裁切。如果您使用可捲動的垂直清單,那麼受到的影響可能會較小,因為使用者能夠捲動畫面將內容置中。不過,如果是單螢幕畫面,可能會導致使用者體驗不佳。
圖 1 顯示一個版面配置在正方形和圓形螢幕上顯示的外觀:

圖 1. 專為正方形螢幕設計的版面配置在圓形螢幕上可能無法正確顯示。
如果您為版面配置使用下列設定,文字會在圓形螢幕的裝置上會無法正確顯示:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/text" android:layout_width="0dp" android:layout_height="0dp" android:text="@string/very_long_hello_world" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
解決問題的方法有兩種,詳情請見後續章節:
- 對正方形和圓形裝置一併使用 Wear OS UI 程式庫中的版面配置。
- 您可以使用
BoxInsetLayout
,根據裝置螢幕形狀套用不同的視窗插邊。如果您想在這兩種螢幕形狀上使用類似的版面配置,但不想裁切到圓形螢幕接近邊緣的畫面,請使用這個方法。 - 如果您想要顯示並操控針對圓形螢幕進行最佳化的項目垂直清單,可以使用
WearableRecyclerView
建立曲線版面配置。
- 您可以使用
- 按照提供替代資源指南所述,為正方形和圓形裝置提供替代版面配置資源。在執行階段,Wear OS 會偵測裝置螢幕的形狀,並載入正確的版面配置。
如要進一步瞭解如何設計應用程式,請參閱「Wear OS 設計指南」。
使用 BoxInsetLayout

圖 2. 圓形螢幕上的視窗插邊。
Wear OS UI 程式庫中的 BoxInsetLayout
類別可讓您定義同時適用於正方形和圓形螢幕的單一版面配置。這個類別會根據螢幕形狀套用必要的視窗插邊,方便您以檢視畫面的中央或畫面邊緣對齊。
圖 2 中的灰色正方形會顯示 BoxInsetLayout
在套用必要視窗插邊後,將子檢視畫面自動顯示在圓形螢幕上的畫面。如要在這個區域中顯示,子檢視畫面會使用下列值指定 layout_boxedEdges
屬性:
top
、bottom
、left
和right
的結合。例如,"left|top"
值會將子項的左側和頂端邊緣置於圖 2 的灰色正方形中。"all"
值會將子項中的所有內容置於圖 2 的灰色正方形中。這是最常見的ConstraintLayout
方法。
正方形螢幕的視窗插邊為 0,因此系統會忽略 layout_boxedEdges
屬性。

圖 3:同時適用於正方形和圓形螢幕的版面配置定義。
圖 3 所示的版面配置使用 <BoxInsetLayout>
元素,並適用於正方形和圓形螢幕:
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" android:layout_width="match_parent" android:padding="15dp"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="5dp" app:layout_boxedEdges="all"> <TextView android:layout_height="wrap_content" android:layout_width="match_parent" android:text="@string/sometext" android:textAlignment="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageButton android:background="@android:color/transparent" android:layout_height="50dp" android:layout_width="50dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" android:src="@drawable/cancel" /> <ImageButton android:background="@android:color/transparent" android:layout_height="50dp" android:layout_width="50dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" android:src="@drawable/ok" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.wear.widget.BoxInsetLayout>
請注意,版面配置的部分會以粗體標示:
-
android:padding="15dp"
此行會將邊框間距指派給
<BoxInsetLayout>
元素。 -
android:padding="5dp"
此行會將邊框間距指派給內部
ConstraintLayout
元素。 -
app:layout_boxedEdges="all"
此行可確保
ConstraintLayout
元素及其子項在圓形螢幕上的視窗插邊定義區域內。此線條在正方形螢幕不會影響。
使用曲線版面配置
您可以透過 Wear OS UI 程式庫中的
WearableRecyclerView
類別,選擇採用針對圓形螢幕進行最佳化調整的曲線版面配置。如要為應用程式中的可捲動清單啟用曲線版面配置,請參閱「在 Wear OS 上建立清單」。
針對正方形和圓形螢幕使用不同的版面配置
另一個同時支援正方形和圓形螢幕的方法,是為不同的螢幕形狀提供替代資源。針對版面配置、維度或其他資源類型,將資源限定詞設為 round
或 notround
。
例如,您可以考慮採用以下方式安排版面配置:
-
針對適用於圓形與正方形手錶的版面配置,使用
layout/
目錄。 -
針對適用於特定螢幕形狀的版面配置,使用
layout-round/
和layout-notround/
目錄。
您也可以使用 res/values
、res/values-round
和 res/values-notround
資源目錄。以這種方式整理資源,可以共用一個版面配置,只須根據裝置類型變更某些特定屬性。
改變值
為圓形和方形手錶設計版面配置的其中一種方法是使用 values/dimens.xml
和 values-round/dimens.xml
。您可以透過指定不同的邊框間距設定,使用單一 layout.xml
檔案和兩個 dimens.xml
檔案建立以下版面配置:
嘗試不同值,看看哪個值成效最佳。
使用 XML 填補下巴
某些手錶圓形螢幕上會有插邊 (也稱為「下巴」)。如果不填補下巴,部分設計可能會遭到遮擋。
舉例來說,假設您採用以下設計:

圖 6. 基本的心形設計。
這個 activity_main.xml
程式碼片段定義了其版面配置。
<FrameLayout ... <androidx.wear.widget.RoundedDrawable android:id="@+id/androidbtn" android:src="@drawable/ic_android" .../> <ImageButton android:id="@+id/lovebtn" android:src="@drawable/ic_favorite" android:paddingTop="5dp" android:paddingBottom="5dp" android:layout_gravity="bottom" .../> </FrameLayout>
如果未採取任何動作,部分設計會遭下巴擋住。

圖 7. 未套用修正。
您可以使用
fitsSystemWindows
屬性設定邊框間距,以避免 chin。下列 activity_main.xml
程式碼片段顯示使用 fitsSystemWindows
:
<ImageButton android:id="@+id/lovebtn" android:src="@drawable/ic_favorite" android:paddingTop="5dp" android:paddingBottom="5dp" android:fitsSystemWindows="true" .../>

圖 8. 使用 fitsSystemWindows
屬性。
現在系統會覆寫您定義的頂端和底部邊框間距值,讓所有項目都能納入系統視窗中。如要修正這個問題,請使用 InsetDrawable
取代邊框間距值。
建立 inset_favorite.xml
檔案來定義邊框間距值:
<inset xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_favorite" android:insetTop="5dp" android:insetBottom="5dp" />
移除 activity_main.xml
檔案中的邊框間距:
<ImageButton android:id="@+id/lovebtn" android:src="@drawable/inset_favorite"android:paddingTop="5dp"android:paddingBottom="5dp"android:fitsSystemWindows="true" .../>

圖 9. 使用 InsetDrawable
。
透過程式輔助管理 chin
如果您需要使用 XML 以宣告式方式進行更多控制,可以以程式化方式調整版面配置。如要取得下巴的大小,請將 View.OnApplyWindowInsetsListener
附加至版面配置最外層的檢視畫面。
請將以下內容新增到 activity_main.xml
檔案中:
Kotlin
private var chinSize: Int = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Find the outermost element findViewById<View>(R.id.outer_container).apply { // Attach a View.OnApplyWindowInsetsListener setOnApplyWindowInsetsListener { v, insets -> chinSize = insets.systemWindowInsetBottom // The following line is important for inner elements that react to insets v.onApplyWindowInsets(insets) insets } } }
Java
private int chinSize; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Find the outermost element final View container = findViewById(R.id.outer_container); // Attach a View.OnApplyWindowInsetsListener container.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { chinSize = insets.getSystemWindowInsetBottom(); // The following line is important for inner elements that react to insets v.onApplyWindowInsets(insets); return insets; } }); }