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

このドキュメントでは、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×240 ピクセルです。下部には 560×40 ピクセルの小さなバーがあります。また、40 x 40 ピクセルの小さなブロックが 4 つあり、それぞれが四隅に配置されています。

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 の左側の列と幅 3 分の 2 の右側の列を定義します。ビューをこれらのクラスに配置できます。次の図を参照してください。

左側の列を 240 × 360 ピクセルで表示し、メイン レイアウトを右に移動します。サイズは収まるように縮小されます。メインエリアは 330 x 240 ピクセルで、下部の小さなバーは 330 x 40 ピクセルです。右側の 2 つの角には 40×40 ピクセルの小さなボックスが 2 つあり、左側の列の下の角に 2 つ、メイン レイアウトの左側に 2 つ(上と下に 1 つずつ)の 30×40 ピクセルのボックスが 4 つあります。

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. OnScrollListener LayoutManagerSnapHelper を組み合わせて、どのオプションが選択されたかを判断します。
  5. TAP ジェスチャーをリッスンして、メニュー アイテム選択イベントを処理します。
  6. 選択したメニュー項目に関する情報を含む Intent を作成します。
  7. このアクティビティの結果を設定して終了します。
  8. メニューを表示するフラグメントまたはアクティビティから startActivityForResult を呼び出します。この目的には TAP ジェスチャーを使用します。
  9. 呼び出し元のフラグメントまたはアクティビティで onActivityResult をオーバーライドして、選択されたメニュー項目を処理します。

ガイドライン

メニュー レイアウトの設定に関する提案は次のとおりです。

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

このシンプルな画像は、黒い背景の中央に「MENU layout」という文字が表示され、その横にスマートフォンのシンボルが表示されている様子を示しています。

実装の詳細については、カードのサンプルアプリをご覧ください。

スワイプ可能なページ

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

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