定义 Wear 上的布局

Wear OS 应用采用与手持式 Android 设备应用相同的布局技术,但在设计时需要遵循特定约束。请勿移植手持式设备应用中的功能和界面,不要指望这样能带来良好的用户体验。

如需详细了解如何设计出色的穿戴式设备应用,请参阅 Wear OS 设计指南

为 Wear OS 应用创建布局时,您需要考虑到设备会具有方形和圆形屏幕。在圆形 Wear OS 设备上,屏幕角落附近的内容可能会被剪裁掉。因此,为方形屏幕设计的布局在圆形设备上可能会出现显示问题。

例如,图 1 展示了以下布局在方形屏幕和圆形屏幕的显示效果:

图 1. 展示了为方形屏幕设计的布局如何在圆形屏幕上显示效果不佳。

因此,为布局使用以下设置时,文本在具有圆形屏幕的设备上无法正确显示:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_square" />
    </LinearLayout>
    

可以通过两种方法解决此问题:

  1. 对方形设备和圆形设备都使用 Wear 界面库中的布局。
    • BoxInsetLayout - 此布局根据设备屏幕的形状应用不同的窗口边衬区。如果您要对这两种屏幕形状使用相似的布局而又不希望圆形屏幕边缘附近的视图被剪裁掉,则可以使用此方法。
    • 曲线布局 - 当您要显示和操作针对圆形屏幕而优化的垂直项目列表时,可使用此布局。
  2. 按照提供资源指南中所述,为方形设备和圆形设备提供备用布局资源。在运行时,Wear 会检测设备屏幕的形状并加载正确的布局。

如需使用此库编译 Android Studio 项目,请确保在 Android SDK 管理器中安装了 Extras > Google Repository 软件包。此外,还需在 wear 模块的 build.gradle 文件中添加以下依赖项:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:wear:26.0.0'
    }
    

使用 BoxInsetLayout

图 2. 圆形屏幕上的窗口边衬区。

您可以使用 Wear 界面库中的 BoxInsetLayout 类定义同时适用于方形和圆形屏幕的布局。此类会根据屏幕形状应用所需的窗口边衬区,并可让您轻松地在屏幕中心或边缘附近对齐视图。

注意:在穿戴式设备支持库中,BoxInsetLayout 类取代了一个与其相似的已弃用类。

图 2 中的灰色方形区域显示了在应用所需的窗口边衬区后,BoxInsetLayout 可在圆形屏幕上自动放置其子视图的区域。要使子视图显示在此区域内,可通过以下值指定 boxedEdges 属性:

  • topbottomleftright 的组合。例如,"left|top" 值可将子视图的左侧边缘和顶部边缘定位在图 2 中的灰色方形内。
  • "all" 值可确定图 2 灰色方形中所有子视图内容的位置。

在方形屏幕上,窗口边衬区为零,并且会忽略 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">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="5dp"
            app:boxedEdges="all">

            <TextView
                android:gravity="center"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:text="@string/sometext"
                android:textColor="@color/black" />

            <ImageButton
                android:background="@null"
                android:layout_gravity="bottom|left"
                android:layout_height="50dp"
                android:layout_width="50dp"
                android:src="@drawable/ok" />

            <ImageButton
                android:background="@null"
                android:layout_gravity="bottom|right"
                android:layout_height="50dp"
                android:layout_width="50dp"
                android:src="@drawable/cancel" />
        </FrameLayout>
    </androidx.wear.widget.BoxInsetLayout>
    

请注意该布局中以粗体标记的部分:

  • android:padding="15dp"

    此行为 <BoxInsetLayout> 元素指定内边距。圆形设备上的窗口边衬区大于 15dp,所以此内边距仅适用于方形屏幕。

  • android:padding="5dp"

    此行为内部 FrameLayout 元素指定内边距。此内边距同时适用于方形屏幕和圆形屏幕。按钮和窗口边衬区之间的总内边距在方形屏幕上为 20dp (15+5),在圆形屏幕上为 5dp。

  • app:boxedEdges="all"

    此行可确保 FrameLayout 元素及其子项位于圆形屏幕上的窗口边衬区所定义的区域内。此行对方形屏幕没有影响。

使用曲线布局

您可以通过 Wear 界面库中的 WearableRecyclerView 类选择使用针对圆形屏幕进行了优化的曲线布局。要为应用中的可滚动列表启用曲线布局,请参阅创建曲线布局

对方形屏幕和圆形屏幕使用不同的布局

Wear 设备可能具有方形屏幕或圆形屏幕。您的应用需要能够支持任一设备配置。为此,您应提供备用资源。将 -round-notround资源限定符应用于布局、尺寸或其他资源类型。

例如,考虑按如下方式组织布局:

  • layout/ 目录包含同时适用于圆形和方形手表的布局。
  • layout-round/layout-notround/ 目录包含特定屏幕形状的专用布局。

您还可以使用 res/valuesres/values-roundres/values-notround 资源目录。以这种方式组织资源,您可以共享一个布局,仅需根据设备类型更改特定属性即可。

改变值

使用 values/dimens.xmlvalues-round/dimens.xml 可轻松为圆形手表和方形手表构建布局。通过指定不同的内边距设置,您可以使用单个 layout.xml 文件和两个 dimens.xml 文件创建以下布局:

    <dimen name="header_start_padding">36dp</dimen>
    <dimen name="header_end_padding">22dp</dimen>
    <dimen name="list_start_padding">36dp</dimen>
    <dimen name="list_end_padding">22dp</dimen>
    
使用 values-round/dimens.xml

图 4. 使用 values-round/dimens.xml

    <dimen name="header_start_padding">16dp</dimen>
    <dimen name="header_end_padding">16dp</dimen>
    <dimen name="list_start_padding">10dp</dimen>
    <dimen name="list_end_padding">10dp</dimen>
    
使用 values/dimens.xml

图 5. 使用 values/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_favourite"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:layout_gravity="bottom"
        .../>
    </FrameLayout>
    

如果不采取任何措施,部分设计会被下巴挡住。

简单的心形设计

图 7. 未加以修正。

您可以使用 fitsSystemWindows 属性设置内边距来避开下巴。以下 activity_main.xml 代码段展示了 fitsSystemWindows 的用法:

    <ImageButton
      android:id="@+id/lovebtn"
      android:src="@drawable/ic_favourite"
      android:paddingTop="5dp"
      android:paddingBottom="5dp"
      android:fitsSystemWindows="true"
      .../>
    
使用 fitsSystemWindows

图 8. 使用 fitsSystemWindows 属性。

请注意,您定义的上下内边距值会被替换,以使所有内容都能容纳在系统窗口中。解决此问题的方法是使用 InsetDrawable 替换内边距值。

创建 inset_favourite.xml 文件来定义内边距值:

    <inset
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:drawable="@drawable/ic_favourite"
      android:insetTop="5dp"
      android:insetBottom="5dp" />
    

activity_main.xml 中移除内边距:

    <ImageButton
      android:id="@+id/lovebtn"
      android:src="@drawable/inset_favourite"
      android:paddingTop="5dp"
      android:paddingBottom="5dp"
      android:fitsSystemWindows="true"
      .../>
    
使用 InsetDrawables

图 9. 使用 InsetDrawables

以编程方式管理下巴

如果您需要获得比使用 XML 以声明方式定义布局时更多的控制权限,可以通过编程方式调整布局。要获取下巴的尺寸,您应将 View.OnApplyWindowInsetsListener 附加到布局的最外层视图。

将以下内容添加到 MainActivity.java

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 which 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 which react to insets
                v.onApplyWindowInsets(insets);
                return insets;
            }
        });
    }