ユーザーインターフェース

このドキュメントでは、Glass のスタイルに沿って、UI を最適化してユーザー エクスペリエンスを最適化する方法について説明します。以下の UI 要素について説明します。

テーマ

Glass のテーマには、次のような特長があります。

  • アクションバーなしでアクティビティを全画面表示します。
  • 黒一色の背景を適用します。
  • カラーエッジ効果の明るい色を設定します。
  • 白色のテキストの色を適用します。

Glass の推奨テーマ設定は次のとおりです。

 <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
   <item name="android:windowBackground">@android:color/black</item>
   <item name="android:colorEdgeEffect">@android:color/white</item>
   <item name="android:textColor">@android:color/white</item>
 </style>

XML レイアウト

フラグメントにインフレートできる基本的なカード レイアウトには次の 2 つがあります。

メイン レイアウト

このレイアウトでは、カードに推奨される標準のパディングとフッターを定義します。独自のビューは空の FrameLayout に配置します。

中央のボックスは画面内部の大部分を 560 x 240 ピクセルで表示し、下部には 560 x 40 ピクセルの小さなバーを配置しています。
また、40 x 40 ピクセルの小さなブロックが 4 つあり、隅に 1 つずつあります。

XML レイアウトの例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <FrameLayout
      android:id="@+id/body_layout"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_margin="@dimen/glass_card_margin"
      app:layout_constraintBottom_toTopOf="@id/footer"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent">

    <!-- Put your widgets inside this FrameLayout. -->

  </FrameLayout>

  <!-- The footer view will grow to fit as much content as possible while the
         timestamp view keeps its width. If the footer text is too long, it
         will be ellipsized with a 40dp margin between it and the timestamp. -->

  <TextView
      android:id="@+id/footer"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="@dimen/glass_card_margin"
      android:layout_marginEnd="@dimen/glass_card_margin"
      android:layout_marginBottom="@dimen/glass_card_margin"
      android:ellipsize="end"
      android:singleLine="true"
      android:textAppearance="?android:attr/textAppearanceSmall"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toStartOf="@id/timestamp"
      app:layout_constraintStart_toStartOf="parent" />

  <TextView
      android:id="@+id/timestamp"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="@dimen/glass_card_margin"
      android:layout_marginBottom="@dimen/glass_card_margin"
      android:ellipsize="end"
      android:singleLine="true"
      android:textAlignment="viewEnd"
      android:textAppearance="?android:attr/textAppearanceSmall"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

左側の列のレイアウト

このレイアウトでは、ビューを配置できる 2 つの FrameLayout クラスの形式で、左 3 分の 1 の列と右 2 分の 3 の右の列を定義しています。下の図の例をご覧ください。

左側の列は 240 x 360 ピクセルで表示されます。これにより、メイン レイアウトがプッシュされます。サイズはフィットするように縮小され、メイン領域は 330 x 240 ピクセル、小さなバーは 330 x 40 ピクセルです。右の 2 つの角には 40 × 40 ピクセルの小さなボックスが 2 つあり、その他 30 x 40 ピクセルのボックスが 4 つあります。左の列の右下に 2 つ、メイン レイアウトの左側に 2 つ、上部と下部に 1 つずつあります。

XML レイアウトの例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <FrameLayout
      android:id="@+id/left_column"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:background="#303030"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintWidth_percent=".333">

    <!-- Put widgets for the left column inside this FrameLayout. -->

  </FrameLayout>

  <FrameLayout
      android:id="@+id/right_column"
      android:layout_width="0dp"
      android:layout_height="0dp"
      android:layout_marginTop="@dimen/glass_card_two_column_margin"
      android:layout_marginStart="@dimen/glass_card_two_column_margin"
      android:layout_marginBottom="@dimen/glass_card_two_column_margin"
      android:layout_marginEnd="@dimen/glass_card_margin"
      app:layout_constraintBottom_toTopOf="@id/footer"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toEndOf="@id/left_column"
      app:layout_constraintTop_toTopOf="parent">

    <!-- Put widgets for the right column inside this FrameLayout. -->

  </FrameLayout>

  <!-- The footer view will grow to fit as much content as possible while the
         timestamp view keeps its width. If the footer text is too long, it
         will be ellipsized with a 40dp margin between it and the timestamp. -->

  <TextView
      android:id="@+id/footer"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginStart="@dimen/glass_card_margin"
      android:layout_marginEnd="@dimen/glass_card_margin"
      android:layout_marginBottom="@dimen/glass_card_margin"
      android:ellipsize="end"
      android:singleLine="true"
      android:textAppearance="?android:attr/textAppearanceSmall"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toStartOf="@id/timestamp"
      app:layout_constraintStart_toEndOf="@id/left_column" />

  <TextView
      android:id="@+id/timestamp"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="@dimen/glass_card_margin"
      android:layout_marginBottom="@dimen/glass_card_margin"
      android:ellipsize="end"
      android:singleLine="true"
      android:textAlignment="viewEnd"
      android:textAppearance="?android:attr/textAppearanceSmall"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

標準ディメンション

前のレイアウトまたは独自のレイアウトと組み合わせて使用し、Glass' 標準スタイルに準拠したファイルを作成します。このファイルを Android プロジェクトで res/values/dimens.xml として作成します。

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <!-- The recommended margin for the top, left, and right edges of a card. -->
  <dimen name="glass_card_margin">40dp</dimen>

  <!-- The recommended margin between the bottom of the card and the footer. -->
  <dimen name="glass_card_footer_margin">50dp</dimen>

  <!-- The recommended margin for the left column of the two-column card. -->
  <dimen name="glass_card_two_column_margin">30dp</dimen>

</resources>

メニューの作成には RecyclerView を使用することをおすすめします。Android Studio プロジェクト リソースの標準の Android メニュー ファイルに基づいています。Android では、標準のメニュー作成をオーバーライドして、実装に置き換えることができます。方法は次のとおりです。

  1. RecyclerView を使用してレイアウトを作成し、Activity のビューとして設定します。
  2. 新たに作成されたメニュー項目のコレクションを使用するように RecyclerView とそのアダプタを設定します。
  3. onCreateOptionsMenu メソッドをオーバーライドします。
    1. メニューをインフレートして、各メニュー項目のコレクションに新しい要素を追加します。
    2. アダプタの notifyDataSetChanged メソッドを呼び出します。

    Kotlin

        override fun onCreateOptionsMenu(menu: Menu): Boolean {
            val menuResource = intent
                .getIntExtra(EXTRA_MENU_KEY, EXTRA_MENU_ITEM_DEFAULT_VALUE)
            if (menuResource != EXTRA_MENU_ITEM_DEFAULT_VALUE) {
                menuInflater.inflate(menuResource, menu)
                for (i in 0 until menu.size()) {
                    val menuItem = menu.getItem(i)
                    menuItems.add(
                        GlassMenuItem(
                            menuItem.itemId, menuItem.icon,
                            menuItem.title.toString()
                        )
                    )
                    adapter.notifyDataSetChanged()
                }
            }
            return super.onCreateOptionsMenu(menu)
        }
        

    Java

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
          final int menuResource = getIntent()
              .getIntExtra(EXTRA_MENU_KEY, EXTRA_MENU_ITEM_DEFAULT_VALUE);
          if (menuResource != EXTRA_MENU_ITEM_DEFAULT_VALUE) {
            final MenuInflater inflater = getMenuInflater();
            inflater.inflate(menuResource, menu);
    
            for (int i = 0; i < menu.size(); i++) {
              final MenuItem menuItem = menu.getItem(i);
              menuItems.add(
                  new GlassMenuItem(menuItem.getItemId(), menuItem.getIcon(),
                      menuItem.getTitle().toString()));
              adapter.notifyDataSetChanged();
            }
          }
          return super.onCreateOptionsMenu(menu);
        }
        
  4. OnScrollListenerLayoutManager および SnapHelper と併用して、どのオプションが選択されたかを判断します。
  5. TAP ジェスチャーをリッスンして、メニュー項目の選択イベントを処理します。
  6. 選択したメニュー項目に関する情報を使用して Intent を作成します。
  7. このアクティビティの結果を設定して終了します。
  8. メニューを配置するフラグメントまたはアクティビティから startActivityForResult を呼び出します。この目的のために TAP ジェスチャーを使用します。
  9. 呼び出し元のフラグメントまたはアクティビティの onActivityResult をオーバーライドして、選択したメニュー項目を処理します。

ガイドライン

メニュー レイアウトをセットアップする方法のヒントを以下に示します。

次の図は、カスタマイズされたメニュー レイアウトの例です。

黒い背景に「メニュー レイアウト」という単語と黒い背景があり、画面中央に電話マークが隣接しています。

カードサンプルアプリで実装の詳細を確認します。

スワイプ可能なページ

Glass ディスプレイとタッチパッドが連動し、スワイプ可能なカードを便利な方法で表示します。標準の Android ViewPager API を使用すると、アクティビティでスワイプ可能なページを作成できます。

Android ViewPager を使用してカードや画面をスクロールする方法について詳しくは、画面スライドのトレーニング ドキュメントをご覧ください。