定义Layouts

编写: roya 原文:https://developer.android.com/training/wearables/ui/layouts.html

可穿戴设备使用与手持Android设备同样的布局技术,但需要有具体的约束来设计。不要以一个手持app的角度开发功能和UI并期待得到一个好的体验。关于如何设计优秀的可穿戴应用的更多信息,请阅读Android Wear Design Guidelines

当为Android Wear应用创建layout时,我们需要同时考虑方形和圆形屏幕的设备。在圆形Android Wear设备上所有放置在靠近屏幕边角的内容可能会被剪裁掉,所以为方形屏幕设计的layouts在圆形设备上不能很好地显示出来。对这类问题的示范请查看这个视频Full Screen Apps for Android Wear

举个例子,figure 1展示了下面的layout在圆形和方形屏幕上的效果:

定义Layouts - 图1

Figure 1. 为方形屏幕设计的layouts在圆形设备上不能很好显示的示范

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6. <TextView
  7. android:id="@+id/text"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:text="@string/hello_square" />
  11. </LinearLayout>

上述范例的文本没有正确地显示在圆形屏幕上。

Wearable UI库为这个问题提供了两种不同的解决方案:

  • 为圆形和方形屏幕定义不同的layouts。我们的app会在运行时检查设备屏幕形状并inflate正确的layout。

  • 用一个包含在库里面的特殊layout同时适配方形和圆形设备。这个layout会在不同形状的设备屏幕窗口中插入不同的间隔。

当我们希望应用在不同形状的屏幕上看起来不同时,一般会使用第一种方案。当我们希望用一个相似的layout在两种屏幕上且在圆形屏幕上没有视图被边缘剪裁时,可以使用第二种方案。

添加Wearable UI库

当我们使用Android Studio的工程向导时,Android Studio会自动地在wear模块中包含Wearable UI库。为了在工程中编译到这个库,确保 Extras > Google Repository 包已经被安装在Android SDK manager里,下面的依赖被包含在wear模块的build.gradle文件中:

  1. dependencies {
  2. compile fileTree(dir: 'libs', include: ['*.jar'])
  3. compile 'com.google.android.support:wearable:+'
  4. compile 'com.google.android.gms:play-services-wearable:+'
  5. }

要实现以下的布局方法需要用到 'com.google.android.support:wearable' 依赖。

浏览API reference documentation查看Wearable UI库的类。

为方形和圆形屏幕指定不同的Layouts

包含在Wearable UI库中的WatchViewStub类允许我们为方形和圆形屏幕指定不同的layout。这个类会在运行时检查屏幕形状并inflate相应的layout。

为了在我们的应用中使用这个类以应对不同的屏幕形状,我们需要:

  • 添加WatchViewStub作为activity的layout的主元素。
  • 使用rectLayout属性为方形屏幕指定一个layout文件。
  • 使用roundLayout属性为圆形屏幕指定一个layout文件。

类似下面定义activity的layout:

  1. <android.support.wearable.view.WatchViewStub
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:id="@+id/watch_view_stub"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. app:rectLayout="@layout/rect_activity_wear"
  9. app:roundLayout="@layout/round_activity_wear">
  10. </android.support.wearable.view.WatchViewStub>

在activity中inflate这个layout:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_wear);
  5. }

然后为方形和圆形屏幕创建不同的layout文件,在这个例子中,我们需要创建res/layout/rect_activity_wear.xmlres/layout/round_activity_wear.xml两个文件。像创建手持应用的layouts一样定义这些layouts,但需要考虑可穿戴设备的限制。系统会在运行时根据屏幕形状来inflate适合的layout。

取得layout views

我们为方形或圆形屏幕定义的layouts在WatchViewStub检测到屏幕形状之前不会被inflate,所以你的app不能立即取得它们的view。为了取得这些view,需要在我们的activity中设置一个listener,当屏幕适配的layout被inflate时会通知这个listener:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_wear);
  5. WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
  6. stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
  7. @Override public void onLayoutInflated(WatchViewStub stub) {
  8. // Now you can access your views
  9. TextView tv = (TextView) stub.findViewById(R.id.text);
  10. ...
  11. }
  12. });
  13. }

使用感知形状的Layout

包含在Wearable UI库中的BoxInsetLayout类继承自 FrameLayout,该类允许我们定义一个同时适配方形和圆形屏幕的layout。这个类适用于需要根据屏幕形状插入间隔的情况,并让我们容易地将view对其到屏幕的边缘或中心。

定义Layouts - 图2

Figure 2. 在圆形屏幕上的窗口间隔

figure 2中灰色的部分显示了在应用了窗口间隔之后,BoxInsetLayout自动将它的子view放置在圆形屏幕的区域。为了显示在这个区域内,子view需要用下面这些值指定 layout_box属性:

  • 一个topbottomleftright的组合。比如,"left|top"将子view的左和上边缘定位在figure 2的灰色区域里面。
  • all将所有子view的内容定位在figure 2的灰色区域里面。

在方形屏幕上,窗口间隔为0,layout_box属性会被忽略。

定义Layouts - 图3

Figure 3. 同一个layout工作在方形和圆形屏幕上

在figure 3中展示的layout使用了BoxInsetLayout,该layout在圆形和方形屏幕上都可以使用:

  1. <android.support.wearable.view.BoxInsetLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:background="@drawable/robot_background"
  5. android:layout_height="match_parent"
  6. android:layout_width="match_parent"
  7. android:padding="15dp">
  8. <FrameLayout
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:padding="5dp"
  12. app:layout_box="all">
  13. <TextView
  14. android:gravity="center"
  15. android:layout_height="wrap_content"
  16. android:layout_width="match_parent"
  17. android:text="@string/sometext"
  18. android:textColor="@color/black" />
  19. <ImageButton
  20. android:background="@null"
  21. android:layout_gravity="bottom|left"
  22. android:layout_height="50dp"
  23. android:layout_width="50dp"
  24. android:src="@drawable/ok" />
  25. <ImageButton
  26. android:background="@null"
  27. android:layout_gravity="bottom|right"
  28. android:layout_height="50dp"
  29. android:layout_width="50dp"
  30. android:src="@drawable/cancel" />
  31. </FrameLayout>
  32. </android.support.wearable.view.BoxInsetLayout>

注意layout中的这些部分:

  • android:padding="15dp"

这行指定了BoxInsetLayout元素的padding。因为在圆形设备上窗口间隔大于15dp,所以这个padding只应用在方形屏幕上。

  • android:padding="5dp"

这行指定内部FrameLayout元素的padding。这个padding同时应用在方形和圆形屏幕上。在方形屏幕上,按钮和窗口间隔总的padding是20dp(15+5),在圆形屏幕上是5dp。

  • app:layout_box="all"

这行声明FrameLayout和它的子views都被放在圆形屏幕上窗口间隔定义的区域里。这行在方形屏幕上没有任何效果。