2013年5月31日金曜日

Android : Navigation Drawer を使う

画面の左側にオーバーレイでアプリの主なオプションを表示するパネル。
通常は隠れていて、画面の左端からスワイプするか、トップレベルにいるならアクションバーのアイコンをタップすることで表示される。


http://developer.android.com/design/patterns/navigation-drawer.html より

Navigation Drawer を使う前に、Navigation Drawer デザインガイドにあるこのパターンのユースケースとデザイン原則をきちんと理解すること。


Drawer Layout を作成する

support package にある DrawerLayout を利用する。

DrawerLayout をルートビューとし、その中にメインのコンンテンツを表示するビューと、NavigationDrawer として利用するビューを入れる。
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- The main content view --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- The navigation drawer --> <ListView android:id="@+id/left_drawer" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="start" android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp" android:background="#111"/> </android.support.v4.widget.DrawerLayout>
DrawerLayout を利用するうえでいくつか注意点がある
  • メインのコンテンツ用のビュー(上の例だとFrameLayout)は、DrawerLayout の最初のビューでなければならない
  • メインのコンテンツ用のビュー の layout_width と layout_height は match_parent にする
  • NavigationDrawer 用のビュー(上の例だとListView)は、layout_gravity で horizontal gravity を指定しなければならない
    RTL言語をサポートするなら left ではなく start を使う
  • NavigationDrawer 用のビューは dp 単位で幅を指定し、縦は patch_parent にする
    横幅は 320dp 以上にはしない



Drawer List を初期化する

Activity で最初に行うことは、Navigation Drawer のアイテムの初期化。
例えば ListView を利用するなら Adapter をセットする。

public class MainActivity extends Activity { private String[] mPlanetTitles; private ListView mDrawerList; private DrawerLayout mDrawerLayout; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mPlanetTitles = getResources().getStringArray(R.array.planets_array); mDrawerList = (ListView) findViewById(R.id.left_drawer); // Set the adapter for the list view mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles)); // Set the list's click listener mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); ... } }


Navigation Drawer アイテムのクリックを処理する

Navigation Drawer アイテムがクリックされたときに何をするかはアプリの構造によるが、ここではメインコンテンツ用のビューにセットする Fragment を切り替えている。

private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); } } /** Swaps fragments in the main content view */ private void selectItem(int position) { // Create a new fragment and specify the planet to show based on position Fragment fragment = new PlanetFragment(); Bundle args = new Bundle(); args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); fragment.setArguments(args); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment) .commit(); // Highlight the selected item, update the title, and close the drawer mDrawerList.setItemChecked(position, true); setTitle(mPlanetTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); } @Override public void setTitle(CharSequence title) { mTitle = title; getActionBar().setTitle(mTitle); }


オープン・クローズイベント時に処理を行う

Drawer が開いたり閉じたりしたのを検知するには、DrawerLayout の setDrawerListener() を使って DrawerLayout.DrawerListener をセットする。
Drawer の開閉時にはそれぞれリスナーの onDrawerOpened() と onDrawerClosed() が呼ばれる。

アプリに ActionBar があるなら、DrawerLayout.DrawerListener よりもそれを implements した ActionBarDrawerToggle が便利。

Navigation Drawer デザインガイドにあるように、Drawer が開いている状態では、タイトルを変えたり、メインコンテンツに関係する Action Item を削除したりすべき。

以下では、ActionBarDrawerToggle を使って、タイトルと Action Item を編集している。
また、利用している R.drawable.ic_drawer 画像は Android_Navigation_Drawer_Icon_20130516.zip からダウンロードできる

public class MainActivity extends Activity { private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private CharSequence mDrawerTitle; private CharSequence mTitle; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... mTitle = mDrawerTitle = getTitle(); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { setTitle(mTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { setTitle(mDrawerTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } }; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); } /* Called whenever we call invalidateOptionsMenu() */ @Override public boolean onPrepareOptionsMenu(Menu menu) { // If the nav drawer is open, hide action items related to the content view boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); return super.onPrepareOptionsMenu(menu); } }


アプリアイコンでオープン・クローズする

ActionBar のアプリアイコンをタッチして Navigation Drawer を開閉させるには ActionBarDrawerToggle を使えば簡単にできる。
ActionBarDrawerToggle のコンストラクタでは以下の5つが必要。
  • Drawer を持っている Activity
  • DrawerLayout
  • Drawer アイコン(UPアイコンの代わりに表示される)の drawable リソースID
  • Drawer を開くというアクションの説明(アクセシビリティ用)の string リソースID
  • Drawer を閉じるというアクションの説明(アクセシビリティ用)の string リソースID
public class MainActivity extends Activity { private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; ... public void onCreate(Bundle savedInstanceState) { ... mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle( this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ R.string.drawer_open, /* "open drawer" description */ R.string.drawer_close /* "close drawer" description */ ) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { setTitle(mTitle); } /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { setTitle(mDrawerTitle); } }; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle your other action bar items... return super.onOptionsItemSelected(item); } ... }


Action Bar Sherlock で使う

MainActivity.java の変更
  • invalidateOptionsMenu() の代わりに supportInvalidateOptionsMenu() を使う
  • getActionBar() の代わりに getSupportActionBar() を使う
  • MenuItem のインポートを Action Bar Sherlock のものにする


ライブラリの変更
  • extras/android/support/v4/src/java/android/support/v4/app/ActionBarDrawerToggle.java
  • extras/android/support/v4/src/honeycomb/android/support/v4/app/ActionBarDrawerToggleHoneycomb.java
をコピーして、

ActionBarDrawerToggle.java の変更
  • IMPL を常に ActionBarDrawerToggleImplHC を使うように変更
  • ActionBarDrawerToggleImpl の setActionBarUpIndicator() と setActionBarUpIndicator() の Activity を SherlockActivity や SherlockFragmentActivity に変更
  • ActionBarDrawerToggleImplHC の setActionBarUpIndicator() と setActionBarUpIndicator() も同様
  • ActionBarDrawerToggle のコンストラクタの Activity も同様
  • MenuItem のインポートを Sherlock のものに変更
  • public boolean onOptionsItemSelected(MenuItem item) { if (item != null && item.getItemId() == ID_HOME && mDrawerIndicatorEnabled) { if (mDrawerLayout.isDrawerVisible(GravityCompat.START)) { mDrawerLayout.closeDrawer(GravityCompat.START); } else { mDrawerLayout.openDrawer(GravityCompat.START); } } return false; } public boolean onOptionsItemSelected(MenuItem item) { if (item != null && item.getItemId() == ID_HOME && mDrawerIndicatorEnabled) { if (mDrawerLayout.isDrawerVisible(GravityCompat.START)) { mDrawerLayout.closeDrawer(GravityCompat.START); } else { mDrawerLayout.openDrawer(GravityCompat.START); } return true; } return false; } にする(最初の if に入ったら true を返す)

    # サンプルで @Override public boolean onOptionsItemSelected(MenuItem item) { // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle your other action bar items... return super.onOptionsItemSelected(item); } # としているのに1つ目の if は常に false とかひどいバグでござる。。。


ActionBarDrawerToggleHoneycomb.java の変更
  • setActionBarUpIndicator() と setActionBarUpIndicator() の Activity を SherlockActivity や SherlockFragmentActivity に変更
  • activity.getActionBar() を activity.getSupportActionBar() に変更





2013年5月30日木曜日

Google I/O 2013 - Android : Structure in Android App Design

Structure in Android App Design

我々が"構造"について話すとき、それはどういう意味なのか。みなさんが最初に思いつくのは建築の図面ではないでしょうか?ここでの構造は、各スペースが何を意図しているものか決め、また行いたい活動をどうサポートするか提供します。構造は直接見えるものだけではありません。骨は私たちの体の構造を提供しています。骨により筋肉や内蔵を適切な場所に収めることができます。

"Does your app have good bones?"

我々のアプリケーションはこれらの例両方に似ています。空間を定義し、適切な場所に機能を取り付ける必要があります。

よく構造化されたアプリは少なくとも次の3つの特徴があります。
  • 何かを行うのが簡単かつ効果的
    家ではドアの幅は出入りするのに適切なサイズだし、床は硬いし、何かを行うのに適切な光量がある

  • 関連する項目は一緒にし、関連しない項目は離す
    近接の効果を利用する。台所とダイニングは繋がっていて、ガレージで区切られていたりはしない

  • 積極的に一貫性のあるナビゲーションを通して複雑さを管理する
    フロントの部屋はすべて互いに繋がっているが、客室は共通の廊下につながっている
"How do you find your app's natural structure?"

どうやってアプリケーションのための自然な構造を見つけるのか。その説明として Barkeeper という近くのバーを探せるアプリのデザインを例に説明します。

"What does my app do?"

形は機能に従うという何度も繰り返された教訓を聞いたことがあると思います。まず、"このアプリは何をするのか?"をはっきりさせましょう。これに答えることによって、アプリでの作業を支えるために必要な構造を考えやすくなります。

この答えはバズワードで飾られたエレベータピッチのようなものではありません。また、機能のリストでもありません。機能のリストは低レベルすぎてユーザーのゴールからかけ離れています。この2つのちょうど中間のものが必要です。幸いなことにモデリングテクニックがあります。

ユースケース : あるタスクを達成するためのシステム(= アプリ)とアクター(= ユーザー)間のインタラクションの流れ

物語の説明としてユースケースを記述するとこんな感じになります。



BarKeeper にはバーを見つけるというユースケースがあります。これに入る前に、このユースケースの主なアクターが誰で、どのようなことが起こらなければいけないのか識別しておきます。ここでのユースケースのコアはアクターとシステム間の対話です。 これをいろいろ拡張できます。このタスクのバリエーション、例えば近くのバーを見つける、を考えたり、成功や失敗などの事後条件を考えたり。とてもシンプルの保つのがいいと思います。これの価値はフォーマットではなくコンテンツにあります。


ユースケースダイアグラム

アプリケーションの構造にとても役立つのですが、しばしば見落とされることがあります。ダイアグラムのコンポーネントは、アクター、ユースケース、関係を表す矢印、の3つだけです。



BarKeeper のユースケースダイアグラムはこんな感じになります。



ユースケースダイアグラムはとても簡単に描けます。これを使うととても早く概要を把握することができます。 ユースケースダイアグラムを描くステップ
  • 1. Inventory
    必要なアクターとユースケースを用意する。物語としての説明はいらない。ユーザーの観点からアプリにさせたいことが必要。

  • 2. Prioritize
    大雑把な優先順位をつける。High, Medium, Low でいい。ユースケースの優先順位付けで重要なのは、どれが一番重要なユースケースで、どれがそれほどでもないのかを見つけること。アプリでもっとも頻繁にすることは何か、最も重要なことはなにか、という視点から始めるとよい。

  • 3. Sequence
    個別のアクターからどのユースケースに直接行きたいのか、どのユースケースは他のユースケースの拡張なのかを順序付ける。



  • 4. Decompose
    幅広い、漠然としている、多くの基礎をカバーしている、と感じるユースケースがあるなら、それを分解する。



優先度に応じてストロークを変えてみます。優先度の高いものは太く、優先度の低いものは点線にします。アプリのキーになるパスがわかるようになります。



階層については何か言えるだろうか?

"Breadth"

アクターがいくつもの同じ優先度のユースケースに繋がっていて、それらがすべて合理的な開始ポイントであるなら、ある開始ポイントから他の開始ポイントへ簡単に遷移できるようにしたいです。

"Focus"

一方、ユーザーケースの中でよりフォーカスされたもの、他のより重要なものが1つだけある場合、他のユースケースは少しアクセスしにくくします。

"Statefulness"

ユーザーによって、優先度の高いユースケースが異なる場合があります。例として Android の Phone アプリがあります。起動した画面に Dialer, Call Log, Favorite の3つのステートがあります。これらは異なるタイプのユーザーの振る舞いと紐付いています。

"Depth"

完全に分離されたユースケースの深いつながりがある場合、特に重要です。



ユーザーが他のセクションに簡単に移動したい場合があるなら、アプリはそれを考慮しないといけません。

"Interconnectedness"



ハブのような複数のスレッドから集まるユースケースがある場合、それはとても重要な機能です。

ユースケースダイアグラムを90度回転し、それぞれのユースケースを画面に見立てます。アプリの階層を考えるうえでいいスタートポイントですが完璧ではありません。ユースケースの詳細度によって、1画面にとっては詳細すぎたり、大雑把すぎたりします。


階層のパターンについて

一般的なブループリントの例



トップレベル



アプリのエントリーであり、アプリに応じて1スクリーンだったり、マルチスクリーンだったりします。アプリの主なユースケースが何か、いくつあるのかに依存します。重要なことは、ユーザはここでカバーされている主なユースケースを見つけ、あるタスクを完了するための次のステップに導かれるということです。

トップレベルは、アプリの広い機能について示し、アプリのアイデンティティを構築します。 例を見るとわかるように、ひと目で "このアプリが何なのか" 理解することができます。



カテゴリーは階層や機能により深く入るために、トップレベルとボトムレベルをつなぐ部分です。 アルバムやフォルダーなど組織的な階層です。





詳細はタスクを完了したり、データを消費したりする部分です。あつかうデータタイプによって様々な画面になります。





構造を決定する上でトップレベルが一番重要です。

ここでの決定によって、ユーザーがここでカバーされている主なユースケースをうまく見つけられるか、次に進もうと思うようにユーザーを満足させられるかが変わってきます。

トップレベルはユーザーとのコミュニケーションであり、アプリの機能についてユーザーにエレベータピッチするところです。このアプリの目的のみならず、機能の広がり(他に何ができるのか)も紹介します。

Phone アプリでは、電話をかけることができるという主な機能の他に、Call Log と Favorites があることをタブで示しています。

このような情報は可能な限り早く開示し、ここでのコミュニケーションは簡潔にする必要があります。 トップレベルはユーザーとの対話であるということを心に留めておいてください。



ユーザーがトップレベルの機能を探せるようにすることは、全てのユースケースがカバーされていることを確認するうえで決定的です。
Android では、このようなアプリの異なる面に切り替えるためのいくつかの確立したパターンを提供しています。


Six-pack



階層の一番上の単一画面で、本質的にはメニュー画面です。ユーザーはメニュー画面を通して深いレベルに入っていきます。常にこの画面を介さないといけないので、複数の面を探したり、自分のユースケースのためにはどこに行けばいいかわからないときに一つづつ試すことができます。

利点
・直線的でシンプル
・全ての面のトップレベルが並んで見えている

欠点
・見てもあまり面白くない
・データがない
・ナビゲーションの努力がユーザーに必要

時間がたてばユーザーは構造を理解するので、タスクに入るまでのジャンプが余分になります。また、ある面から別の面に移動するにはここまで戻ってくる必要があるので、ユーザーがたくさんナビゲーションを行わなければなりません。


Fixed-tabs



Android では一般的なパターンです。横に並んだ3つのトップ画面があります。素早く効果的に別の面に移動できます。



トップレベルを簡単に素早く切り替えられるように、サイドスワイプでタブを切り替えるようにしてください。

利点
・全てのトップレベルが並んで表示される=他の選択肢が見つけやすい
・ものすごく効率的

欠点
・3つまでに制限される
・縦の領域を専有する
・階層の下のレベルからアクセスできない


Spinner



スピナーはより多くのアクションがある場合にタブの代わりに利用します。タブでは2つか3つまでですが、それ以上の選択肢を与えられます。ActionBar の中にスピナーが入るのでタブが専有する部分がなくなります。 一方で、スピナーが閉じている状態ではアプリの構造があまり明らかになっていません。

利点
・3つより多くのトップレベルを持てる
・コンパクト

欠点
・トップレベルが並んで表示されない
・階層の下のレベルからアクセスできない


Navigation Drawer

いくつかのアプリはすでにこれを実装しており、これまではコミュニティドリブンのパターンでしたが、Navigation Drawer を正式に Android Design Guide の一部にしました。

Navigation Drawer : Android Design

Creating a Navigation Drawer : Android Training



Navigation Drawer はアプリの最も重要な画面へのメニューパネルです。左上の Navigation Drawer アイコンをタップするか左端からスワイプすると、スライドしてきてコンテンツの上にオーバーレイされます。Navigation Drawer は "navigation hubs" を含み、ユーザーは頻繁にここにくるでしょう。ハブだけでなく下位階層へのナビゲーションも含めることができます。



下の階層の画面からでもトップレベルにアクセスできますし、別の面の下位の階層にアクセスすることもできます。 トップレベルでは ActionBar のアプリケーションアイコンの左に、ーが縦に3つ並んだ Navigation Drawer Icon が表示され、下位の階層では UP アイコンが表示されます。



Navigation Drawer のいいところは、コンテンツを作るためのスペースがあることです。アイコンを表示したり、ディバイダーを使ってセグメントを分けたり、アップデートなどのカウンターを表示したり、下位レベルのコンテンツをグルーピングしたりできます。

利点
・多くのビューを表示できる
・コンパクトで閉じることができる
・下位の画面への遷移も入れることができる
・どの画面からでもアクセスできる

欠点
・トップレベルが並んで表示されない



どのパターンを使うべきか - 実例

例1 - Calendar

現在カレンダーはスピナーを使っています。日、週、月、予定の関係をユーザーはしっかり理解しています。コンパクトで、上部にきちんと収まっていて、タイトルに合っています。



Navigation Drawer も可能です。表示するための特定のタイトルも提供できます。ただし、4つ以上のオプションが増えることはなさそうなので、タブレットなどのデバイスでは空間が目立つでしょう。



タブはよくない選択です。 タブは頻繁に切り替えるようなアプリではいいのですが、カレンダーではある人はよく1日のビューを使い、ある人はよく1週間のビューを使っているのではないでしょうか。さらに、タブによってカレンダーの領域が狭くなってしまいます。一番重要なことは、望ましくないジェスチャーを紹介しないといけないことです。ユーザーはスワイプによって日を切り替えられると期待しますが、タブを使うと日の切り替えとタブの切り替えが衝突してしまいます。



six-pack も同じように良くない選択です。毎回データを見るまでトップ画面を介さなければならないですし、カテゴリーを変更するために毎回トップに戻らなければなりません。




例2 - Clock

Clock は他の機能面とうまくコミュニケーションするためにタブを利用しています。ただの時計だけでなく、タイマーとストップウォッチがあることをほのめかしています。すぐに時計だとわかるのでアプリの名前などを表示する ActionBar をなくすかわりに、時計画面では split action bar を使っています。縦の領域は問題ではないので split action bar は適切な選択です。



スピナーを使ってもいいでしょう。ここでは時計画面にあったアラームを4番目のアイテムとして置くことができます。他の面への移動は少し操作が増えます。タイトル部分が増えるので、もとのデザインのシンプルさが少し損なわれます。



Navigation Drawer はスピナーと同じような感じです。



ナビゲーションとしては3つのパターンが使えますが、ビジュアル的なシンプルさではタブが一番いいでしょう。


例3 - Gallery

ギャラリーはスピナーを使っています。カレンダーとは少し異なる方法です。ここでは"同じデータをどのような方法で表示するか"を選択するために使っています。



Navigation Drawer でもいいでしょう。例えば下位階層として個別のアルバムへの遷移を入れることもできるでしょう。ただし、ここにはアルバムのサムネイルが表示されないため、有効な手段かどうかは疑問が残ります。



タブはだめです。タブの多すぎるし、カレンダーと同じスワイプジェスチャーの問題があります。




例4 - Drive

Drive ではアカウントの切り替えようにスピナーを使っていました。アプリ内でアカウント切り替えが頻繁にある場合、スピナーで行うことはいい選択です。ただし、Drive ではトップレベル切り替えの問題があり、コンテンツ領域のトップレベルにファイル階層を持ってくることでそれを解決していました。正確にはファイル階層ではなくスタティックリンクですが。本質的にはカスタマイズした six-pack です。



アプリを開いたときに直接データにアクセスするために、例えばスピナーをカスタマイズするという方法があります。Gmail では同じような方法をとっています。この方法は、スピナーが本来どうあるべきかを薄めてしまいます。スピナーは本来ある1つの項目を選択するためのものです。



Navigation Drawer は Drive にとって素晴らしい選択です。すぐにデータを表示でき、アカウントの選択もでき、トップレベルの切り替えもできます。




Shifting structure

アプリの進化よって直面する挑戦があります。Play Store にアプリを公開しているなら、アプリのライフスパンに応じて多くのデータや機能を追加するでしょう。その場合、常にアプリの構造が適しているか気に留めてください。

例えば、スポーツの結果を表示するシンプルなアプリがあるとします。公開したときに開発者が興味をもっている US で人気のある3つのスポーツに特化しています。





人々はこのアプリのシンプルさを気に入りますが、変更も頼んできます。一度アプリを公開するとフィードバックが来ます。例えば「ホッケーはないの?」とか。

そこで、ホッケーとサッカーを入れたり、Notification を追加したり、お気に入りのスポーツを登録できたりするようにしようとします。



最初に考えるのが、すでにあるトップナビゲーションに追加することです。なぜなら、ユーザーはすでにその方法に慣れ親しんでいるからです。しかし、この場合タブにこれ以上アイテムを追加するのはいい方法ではありません。

Navigation Drawer を使うと、多くのスポーツへのトップレベルを提供でき、設定画面への Overflow メニューも用意できます。




まとめ
  • 適切な構造を選択することはアプリの体験にとって非常に重要
  • "このアプリは何なのか"を理解し、異なるユーザーにどのように提供するのか
  • ナビゲーションパターンを選択するときは全てのオプションを考える



2013年5月28日火曜日

Android : Mobile Backend Starter のサンプルを試す

Mobile backend starter はすぐにデプロイして使うことができるサンプル。
これを使うと、Android クライアント用のクラウドバックエンドを簡単に構築することができる。
また、バックエンドとやり取りする Android クライアント用のフレームワークと、それを利用したサンプルアプリも提供されているので、バックエンドとのやりとりをすぐ試すことができるし、フレームワークをどう使えばいいかもわかるので、自分の Android アプリに組み込む方法もわかる。

バックエンド側のコードはいっさい書かなくていいし、バックエンドのアクセス部分のコードはフレームワークとして提供されているので、アプリの機能や UI に集中できるというのが利点。

クライアント側のサンプルアプリとして GuestBook と SocialTalk というのがある。
このサンプルでは、Google Cloud Messaging を使用して、同じアプリをインストールしている他のデバイスにメッセージを送信することができる。複数人チャットみたいなことができる。

Mobile Backend Starter - Google Cloud Platform — Google Developers に沿ってサンプルを試してみたのでそのメモ。





Getting Started

警告:Prospective Search 機能はこのサンプルであっても実験的であり、自身の責任で使用すること。

必要なもの
  • Google Account
  • Eclipse 3.8 or 4.2 で Android アプリを開発できる環境
  • 4.0.3 (API Level 15)以上の Android SDK と Google APIs
ドキュメントには以下の項目も必要とあるが無くても動いた
  • Google Plugin for Eclipse (GPE) 3.8/4.2 を Eclipse のリンク先の指示通りに、以下の項目をインストールする
    • Developer Tools > Android Developer Tools
    • Google App Engine Tools for Android
    • Google Plugin for Eclipse
    • SDKs > Google App Engine Java SDK
  • Extras の中の Google Cloud Messaging for Android




デプロイ・ダウンロード・サンプルの実行

やること
・サンプルのためのプロジェクトを作成する
・プロジェクトにバックエンドをデプロイする
・Android クライアントアプリのサンプルをダウンロードし、必要な ID をコードに記入しビルドして実行する


サンプルのためのプロジェクトを作成する

1. Google Account にサインインする

2. https://cloud.google.com/solutions/mobile に行く

3. Try it now をクリックする



はじめて Google App Engine を使う場合は認証コードによる確認が入ります

New Project のフォームが開かない場合は Create Project ボタンを押す

4. プロジェクト名とプロジェクトIDを指定してプロジェクトを作成する

プロジェクトIDは全世界で一意である必要がある。プロジェクトIDのテキストボックス内の更新ボタンを押すと利用できるプロジェクトIDを適当に生成してくれる。
このプロジェクトIDは Google App Engine のコンソールGoogle APIs のコンソールでプロジェクトを識別するのに使われる。


プロジェクトにバックエンドをデプロイする

1. プロジェクトの Cloud Console 画面に行く

プロジェクトを新規作成したあとに遷移する画面 Google Cloud コンソール で作成したプロジェクトを選択しても行ける

2. 上側の2つのドットの2番目をクリックして Mobile Backend App を表示する



3. Deploy ボタンを押す



Deploy 中はポップアップが出るので、Details を押せばどこまで進んでいるのか見れる



Deploy が終わったら、Deploy ボタンの表示が "Deployed on [日付]" になる

4. 2の Settings ボタンを押す



5. Mobile Backend settings の Authentication/Authorization を Open にして Save をクリックする(この時点では他の値はいじらない)



保存できたら Done というポップアップが表示される

重要: Open 設定は開発時向けであり、プロジェクトIDを知っている人であればだれでもアクセスできる。アプリ開発時でもアクティブに使用しないときは Locked Down に変更すること!


Android クライアントアプリのサンプルをダウンロードし、必要な ID をコードに記入しビルドして実行する

1. zip ファイルをダウンロードする

2. zip ファイルを解凍する

3. Android プロジェクトを Eclipse に Import する

Eclipse を立ち上げて File > Import > Android > Existing Android Code Into Workspace を選択



Root Directory に展開したプロジェクトのディレクトリ(solutions-mobile-backend-starter-android-client-master)を指定して Finish をクリック



4. プロジェクトのプロパティの Android でビルドターゲットが API Level 15 以上の Google APIs になっていることを確認する



5. src/com.google.cloud.backend.android の Consts.java を開く



Google APIs Console に行って、Project ID を調べ Consts.java にセットする。(Project Number は GCM を利用する場合に必要になる、あとで説明する)



6. Android アプリをビルドし、Android 4.0.3 以上の実機、もしくはエミュレータで実行する

メッセージを送ると、タイムスタンプとともに戻ってきて表示される

7. バックエンドに保存されているデータを見る

Google Cloud Console のプロジェクトから App Engine をクリックして、Cloud Datastore をクリックする



左側の Query を選択し、Kind で GuestBook を選択して RunQuery ボタンを押すと、保存されているデータが表示される



チェックボックスでチェックしたデータを削除することもできる





サンプルで Google Cloud Messaging を使うようにする

1. Google API Console に行く

2. 左側の Services を選択する



Google Cloud Messaging for Android を ON にする。必要に応じて利用規約確認する。



3. 左側の API Access を選択する



Create new Server key... ボタンをクリックする



Create ボタンを押す



IP アドレスは空で OK(server key が IP アドレスのホワイトリストを使えるようにする場合に利用する機能)

Key for server apps という項目が追加される



4. Cloud Console の Mobile Backend App の Settings ボタンを押す



Google Cloud Messaging を Enable にする



Google Cloud Messaging API key の部分にさっきの Key for server apps の API key を入れて Save ボタンを押す

保存できたら Done というポップアップが表示される

5. src/com.google.cloud.backend.android の Consts.java を開く



Google APIs Console に行って、Project Number を調べ Consts.java にセットする。



6. Android アプリをビルドし、Android 4.0.3 以上の実機、もしくはエミュレータで実行する

エミュレータの場合は Target: を Google APIs にする



メッセージを送ると、タイムスタンプとともに戻ってきて表示され、別のデバイスの同じアプリにも自動でプッシュされる

7. Mobile Backend settings の Send Cloud Message で topicId を _breadcast のまま send ボタンを押すと、message= で指定したメッセージが Android アプリ側にトーストで表示される







サンプルに認証を追加する

ドキュメントには「認証を追加したサンプルはエミュレーターでは実行できず、実際のデバイスを使う必要がある」とあるが、Target に Google APIs を指定したエミュレータなら実行できます

モバイルバックエンドサンプルに認証を追加するには以下の手順が必要
  • 1. web client ID を作成する
  • 2. Android client ID を作成する
  • 3. web client ID と Android client ID をモバイルバックエンドの設定画面でセットする
  • 4. web client ID を Android クライアントアプリの Consts.java に記入する


Web Client ID を作る

1. Google API Console にアクセスし、左側の API Access を選択する



2. Create an OAuth 2.0 client ID ボタンもしくは Create another client ID ボタンをクリックする



3. Create Client ID ダイアログで
・Application type を web application にする
・Your site or hostname のスキーマ部分を http:// にする
・Your site or hostname のアドレス部分を www.[プロジェクト名].appspot.com にする



Create client ID ボタンをクリックする

Client ID for web applications という項目が作成される




Android client ID を作成する

Android Client ID を作成するには、署名キーの SHA1 fingerprint が必要
アプリをリリースするときはリリース用の署名キーで Android Client ID を作成する!
ここでは debug.keystore を使う

1. 署名キー の SHA1 fingerprint を取得する

$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1
パスワードは android

fingerprint が表示されるのでメモしておく

2. Google API Console にアクセスし、左側の API Access を選択する



3. Create an OAuth 2.0 client ID ボタンもしくは Create another client ID ボタンをクリックする



4. Create Client ID ダイアログで
・Application type を Installed application にする
・Installed application type を Android にする
・Package name: にアプリのパッケージ名(サンプルだと com.google.cloud.backend.android)を入力する
・Signing certificate fingerprint (SHA1): に 1. の fingerprint を入力する



Create client ID ボタンをクリックする

Client ID for installed applications という項目が作成される




web client ID と Android client ID をモバイルバックエンドの設定画面でセットする

1. Google Cloud Console でモバイルバックエンドプロジェクトを開く

2. Cloud Console の Mobile Backend App の Settings ボタンを押す



3. Authentication/Authorization を Secured by Client IDs に変更し、Web Client ID を Android Client ID を入力し、Save を押す




web client ID を Android クライアントアプリの Consts.java に記入する

1. src/com.google.cloud.backend.android の Consts.java を開く

Web Client ID を WEB_CLIENT_ID 定数にセットし、IS_AUTH_ENABLED 定数を true にする



アプリを実行すると、最初に使用する Google アカウントを聞かれるようになる
メッセージを送ると <anonymous> だった部分が Goole アカウント名になる





# おおお、チャットできるぞい!いやー、サーバー側のコード全く書いてないのにすごいねぇ!