参考:UI デザインとしてのAction Bar | Android Developers
参考:プログラミングとしてのAction Bar | Android Developers
目次
ActionBar
Android の公式な UI デザインの中でも、特に重要な役割をもっているものが ActionBar です。
ユーザを適切にナビゲーションし、アプリの中での目的を達成するには、ActionBar の知識と実装は必須のものとなっています。
ActionBar の基本
ActionBar とは、アプリにある複数の画面で恒久的に表示される、主にナビゲーションと適切なアクションの提示のために利用される、重要な UI コンポーネントです。
ActionBar には、以下の 4 つのコンポーネントがあります。
- App Icon
- アプリのアイコンです。単なるアイコンとしての機能だけでなく、ホームに戻る、や、階層を 1 つ戻る、などの役割を持つこともできます。
- View Control
- 異なる View に様々なデータを表示するような場合に、ユーザが素早く View を切り替えるためのコンポーネントです。右下に三角形のマークが付いているものは、ドロップダウンリストのメニューが表示されます。これ以外に、タブによる切り替えも用意されています。
- ActionItem Buttons(Action Buttons)
- ここに配置されるボタン類は、アプリやその画面のなかで最も重要なアクションを促すために配置されます。画面幅などでこのエリア内に収まらないボタンは、自動的に、4 の Action Overflow に移されます。Action Buttons は、ActionBar を分割し、Action Buttons を画面下部に表示させることもできます。
- Action Overflow
- 画面に収まらなかったり、意図的に Action Overflow に入るよう設定した Action Buttons がここにまとめられます。ここに収める Action Buttons は優先順位の低いものとして位置づけられます。
また、ActionBar を導入するにあたって、以下に上げる重要な要素があります。
NavigationMode
ActionBar には、3 つのナビゲーションモードが用意されています。 1 つの Activity で選択できるモードは、このうち 1 つだけです。
Standard Navigation
デフォルトのナビゲーションモードです。
List Navigation
ドロップダウンのリストから、項目を 1 つ選択してコンテンツを切り替えるタイプのナビゲーションモードです。
// Honeycomb 以降向けのコード
public class ListNavigationActivity extends Activity implements OnNavigationListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
// List Navigation のリストに表示するもの
List<String> list = new ArrayList<String>();
list.add("Navi Menu 1");
list.add("Navi Menu 2");
list.add("Navi Menu 3");
// ナビゲーションモードを List Navigation に設定
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
// List Navigation の準備
// ArrayAdapter は一覧表示のための、データと View をひもづけるためのクラス。詳細は別の章。
getActionBar().setListNavigationCallbacks(
new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
android.R.id.text1, list),
this);
// タイトルを表示しないようにする
getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
}
// List Navigation の一覧から項目を選択したら呼び出されるコールバック
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
// ここで、どの項目が選択されたかを検知し、適切なイベント処理を行う
// イベント処理が実行されたら、true を返すようにする
return false;
}
}
Tab Navigation
複数のタブから 1 つを選択してコンテンツを切り替えるタイプのナビゲーションモードです。
タブの位置は、画面の大きさによって決定されます。
横幅の狭い画面では、アクションバーの直下に表示されますが、広い画面では、アクションバーのタイトル部分の右横にタブが統合されます。
これは、端末の画面回転時にも判定が行われるため、縦画面と横画面で動的にアクションバーのレイアウトが切り替わることになります。
// Honeycomb 以降向けのコード
public class TabNavigationActivity extends Activity implements TabListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sample);
// タブナビゲーションモードに設定
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// タブを作成して追加。タブの選択・解除・再選択をハンドリングするコールバックの TabListener をセットしないと実行時例外でクラッシュする
getActionBar().addTab(getSupportActionBar().newTab().setText("Tab1").setTabListener(this));
getActionBar().addTab(getSupportActionBar().newTab().setText("Tab2").setTabListener(this));
getActionBar().addTab(getSupportActionBar().newTab().setText("Tab3").setTabListener(this));
}
// タブナビゲーションの Tab が選択された時のコールバック
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
}
// タブナビゲーションの Tab が選択解除された時のコールバック
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
// タブナビゲーションの Tab が再度選択された時のコールバック
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
}
ActionItem
これまでの OptionsMenu(メニューキーを押して表示するメニュー)の代替として、アクションバーに含められるメニューです。
リソースは全て OptionsMenu のものを流用することが可能ですが、一部属性がアクションバー向けに追加されています。
android:showAsAction
-
アクションバーへの出し方を決定するための属性です。
値 意味 always 常に表示します。最も重要度の高いメニューとして扱います。 ifRoom 他の ActionBar のコンポーネントが占める領域を見て、空きがあれば表示します。 never 常に ActionBar 上には表示せず、その他のメニュー(3 つの点のアイコン)の中にまとめられます。最も重要度が低い項目に適用します。 withText 横幅の広い画面において、アイコンだけでなく、文言も一緒に表示するオプションです。
always
、ifRoom
、never
は一緒に指定することができません。必ずこの 3 つのどれかを選択します。
ifRoom
指定して画面に入りきらなかったものと、never
指定したものは、下記のように Overflow にまとめられます。
また、アイコンを設定せず、ラベルのみを設定した場合で、Action Item に並べられるときは、全て大文字になって表示されます。
端末の画面サイズに応じて、ActionBar が自動で Action Item の表示をハンドリングしてくれます。
タブレット型のような大きなディスプレイでは、アイコンとラベルが同時に表示されますが、モバイル向けの端末では、横幅によってアイコンのみの場合と、ラベルとアイコンが同時に表示される場合とがあります。
横画面にしても、横幅が 480dp に満たない端末では、常にどちらかだけになります。
App Icon Navigation
標準で、アクションバーの左端には、アプリのアイコンが表示されています。
このアイコンを、ナビゲーションのための UI として機能させることができます。
Android の流儀としての使い方として、下記の 2 つが想定されています。
- アプリのホーム画面(アプリの起動直後の画面)へ戻る、ホームボタンの役割
- アプリのナビゲーションの階層構造を上に戻る、戻るボタンの役割
この 2 つの違いは、アイコンの表示にも影響します。
左が、ホームボタンとしての役割の時、右が、戻るボタンとしての役割の時の表示です。
戻るボタンとして機能させるときには、左向きの矢印が表示されます。
アイコンがタップされた時の制御については、後述するインタラクション制御で説明します。
Split ActionBar
Action Item を、画面上部の ActionBar から切り離し、画面下部に 1 列に表示するモードです。
AndroidManifest において、<activity>
の属性として、android:uiOptions
にsplitActionBarWhenNarrow
を設定することで実現出来ます。
ActionBar の見た目を変える
Android Action Bar Style Generator などで、簡単に Drawable Resource や Style Resource を生成できるようになっています。
以下のように、Style Resource として各種 Drawable を利用してカスタマイズしていきます。
<resources>
<!-- アクションバー の各種スタイルの設定をまとめた、テーマスタイル -->
<style name="Theme.Example" parent="@style/Theme.AppCompat.Light">
<!-- アクションバーの背景 Drawable -->
<item name="actionBarItemBackground">@drawable/selectable_background_example</item>
<!-- ポップアップで表示されるメニュー項目の Style -->
<item name="popupMenuStyle">@style/PopupMenu.Example</item>
<item name="dropDownListViewStyle">@style/DropDownListView.Example</item>
<item name="actionBarTabStyle">@style/ActionBarTabStyle.Example</item>
<item name="actionDropDownStyle">@style/DropDownNav.Example</item>
<item name="actionBarStyle">@style/ActionBar.Solid.Example</item>
<item name="actionModeBackground">@drawable/cab_background_top_example</item>
<item name="actionModeSplitBackground">@drawable/cab_background_bottom_example</item>
<item name="actionModeCloseButtonStyle">@style/ActionButton.CloseMode.Example</item>
</style>
<style name="ActionBar.Solid.Example" parent="@style/Widget.AppCompat.Light.ActionBar.Solid">
<item name="background">@drawable/ab_solid_example</item>
<item name="backgroundStacked">@drawable/ab_stacked_solid_example</item>
<item name="backgroundSplit">@drawable/ab_bottom_solid_example</item>
<item name="progressBarStyle">@style/ProgressBar.Example</item>
</style>
<style name="ActionBar.Transparent.Example" parent="@style/Widget.AppCompat.Light.ActionBar">
<item name="background">@drawable/ab_transparent_example</item>
<item name="progressBarStyle">@style/ProgressBar.Example</item>
</style>
<style name="PopupMenu.Example" parent="@style/Widget.AppCompat.Light.ListPopupWindow">
<item name="android:popupBackground">@drawable/menu_dropdown_panel_example</item>
</style>
<style name="DropDownListView.Example" parent="@style/Widget.AppCompat.Light.ListView.DropDown">
<item name="android:listSelector">@drawable/selectable_background_example</item>
</style>
<style name="ActionBarTabStyle.Example" parent="@style/Widget.AppCompat.Light.ActionBar.TabView">
<item name="android:background">@drawable/tab_indicator_ab_example</item>
</style>
<style name="DropDownNav.Example" parent="@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar">
<item name="android:background">@drawable/spinner_background_ab_example</item>
<item name="android:popupBackground">@drawable/menu_dropdown_panel_example</item>
<item name="android:dropDownSelector">@drawable/selectable_background_example</item>
</style>
<style name="ActionButton.CloseMode.Example" parent="@style/Widget.AppCompat.Light.ActionButton.CloseMode">
<item name="android:background">@drawable/btn_cab_done_example</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style name="Theme.Example.Widget" parent="@style/Theme.AppCompat">
<item name="popupMenuStyle">@style/PopupMenu.Example</item>
<item name="dropDownListViewStyle">@style/DropDownListView.Example</item>
</style>
</resources>
インタラクション制御
Activity や Fragment のコールバックメソッド
Activity と Fragment には、メニューの構成と表示について、下記のような 2 種類のメニューを扱うコールバックメソッドが用意されています。
OptionsMenu
メニューキーで表示されるメニュー、また ActionBar に表示される ActionItem の構成を扱います。
下記のコールバックメソッドが用意されています。
メソッド名 | 意味 |
---|---|
onPrepareOptionsMenu | OptionsMenu を表示する前に、メニュー要素の状態を管理するために呼び出される |
onCreateOptionsMenu | OptionsMenu の構成を初期化するために、Activity や Fragment のライフサイクルの中で 1 度だけ呼ばれる |
onOptionsItemSelected | OptionsMenu の選択状況を見て、適切なアクションを起こすために呼び出される |
public class MainActivity extends Activity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// ここで、状態に応じてメニューの有効・無効を切り替えたりなどの処理をする
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// ここで、この Activity で利用するメニューのリソースを読み込む
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 選択されたメニューに対応するイベント処理をここで実行する
return super.onOptionsItemSelected(item);
}
}
ContextMenu
View の長押しで表示されるメニューの構成を扱います。
下記のコールバックメソッドが用意されています。
メソッド名 | 意味 |
---|---|
onCreateContextMenu | ContextMenu の構成を初期化する |
onContextItemSelected | ContextMenu の選択状況を見て、適切なアクションを起こすために呼び出される |
public class MainActivity extends Activity {
@Override
protected void onStart() {
super.onStart();
// View に長押しメニュー用のコールバックを設定する
// 実際には、View に OnCreateContextMenuListener を設定するだけ
// 登録処理を Activity が肩代わりしている
View helloWorld = findViewById(R.id.HelloWorld);
registerForContextMenu(helloWorld);
}
@Override
protected void onStop() {
// View に設定した長押しメニュー用のコールバックを解除する
View helloWorld = findViewById(R.id.HelloWorld);
unregisterForContextMenu(helloWorld);
super.onStop();
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
// ここで、長押しメニューで利用するメニューリソースを読み込む
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// ここで、選択されたメニューリソースに対応するイベント処理を実行する
return super.onContextItemSelected(item);
}
}
View の状態とコールバックメソッド
View には、以下のような「状態(State)」が定義されています。
View の種類によって、取り得る状態は様々です。
状態名 | 意味 |
---|---|
normal | 何もしていない |
enabled | View がユーザ操作を受け付ける状態 |
focused | View がユーザ操作によってフォーカスを持っている状態 |
selected | View がユーザ操作によって選択されている状態 |
checked | View がユーザ操作によってチェックされている状態 |
pressed | View がユーザ操作によって押されている(クリックされている・タップされている)状態 |
これらそれぞれの状態について、Observer パターンでイベントを監視するための仕組みが用意されています。
監視するためのオブジェクトの登録方法によっては、Activity のライフサイクルに合わせて監視オブジェクトの管理を剃る必要のあるものがあることに注意してください。
OnClickListener
View をタップした時に呼び出されるイベントを拾うための Observer です。
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
OnLongClickListener
View を長押しした時に呼び出されるイベントを拾うための Observer です。
長押しした時のイベント処理が適切に実行されたら、true
を返します。
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
OnFocusChangedListener
View のフォーカスが移動した時のイベントを拾うための Observer です。
view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
}
});
OnCheckedChangeListener
CheckBox などで、チェック状態が変化した時のイベントを拾うための Observer です。
CompoundButton checkBox = (CompoundButton) findViewById(R.id.HelloCheck);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
}
});
TextWatcher
EditText などで、入力テキストが変更された時のイベントを拾うための Observer です。 こちらは、Activity や Fragment のライフサイクルに合わせて、適宜 View への登録と解除を行う必要があります。
public class MainActivity extends Activity {
private TextWatcher mTextWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {}
};
@Override
protected void onStart() {
super.onStart();
// ライフサイクルに合わせて、Observer オブジェクトを登録する
TextView helloEdit = (TextView) findViewById(R.id.HelloEdit);
helloEdit.addTextChangedListener(mTextWatcher);
}
@Override
protected void onStop() {
// ライフサイクルに合わせて、Observer オブジェクトを解除する
TextView helloEdit = (TextView) findViewById(R.id.HelloEdit);
helloEdit.removeTextChangedListener(mTextWatcher);
super.onStop();
}
}
実習・課題
プロジェクトの開き方は課題プロジェクトの開き方を参照してください。
実習
この実習では 1-5-ActionBarPractice
を使用します。
ActionBar
- ActionbarActivity の ActionBar で TabNavigation を実現し、3 つのタブを持つ画面を作成してください
- 1でTabNavigationを持つ画面を作成後、ActionbarActivity で TabNavigation で、タブを選択された時に、どのタブが選択されたかを
Toast
で表示するようにしてください
インタラクション制御
- InteractionActivityのOptionsMenu に、以下の 3 つの項目を表示し、それぞれ選択時に、どのメニューが選択されたかを
Toast
で表示するようにしてください- Settings と表示するもの
- Login と表示するもの
- Refresh と表示するもの
- InteractionActivity で OptionsMenu を扱った場合の、OptionsMenu の表示のされ方の違いを、エミュレータを用いて、画面の大きさを元に考察してレポートに記述してください(
android:showAsAction
にwithText
をつけると違いが出てきます。)
課題
この課題では 1-5-ActionBarAssignment
を使用します。
ActionBar
- Actionbar1Activity の中で App Icon Navigation を有効にした上で、階層を戻るボタンとしての役割を実装してください (Actionbar1Activityの上の階層はMainActivityとしてください)
- 3 つのタブと、2 つの ActionItem を持つ ActionBar を Actionbar2Activity に実装してください。ActionItem のうち、片方は新しい Activity を起動するように、もう片方は Actionbar2Activity を終了するようにイベントをハンドリングしてください。
インタラクション制御
- Interaction1Activity の OptionsMenu に、以下の 2 つの項目を表示し、そのうち一方の項目の有効/無効の状態を交互に切り替える処理を実装してください。
- Settings と表示するもの
- Refresh と表示するもの
- Interaction2Activity で TextWatcher を利用して、
id:edit_text
に入力された文字数をカウントする TextView を作成してください。