こんにちは、Habr!
前の記事で、プロジェクトへのサポートライブラリの追加について
説明し、 SupportActionBarの簡単な例を示しました。 しかし、ActionBarはメニューの代わりとしてだけでなく、アプリケーションをナビゲートする方法としても頻繁に使用されます。 カットの下に、それを実装する方法が書かれています。
ナビゲーション方法
ActionBarには3つのナビゲーションメソッドがあります。
NAVIGATION_MODE_STANDART-本質的にナビゲーションではなく、要素を含むActionBarのみ。
NAVIGATION_MODE_LIST-ヘッダーではなくドロップダウンリスト。
NAVIGATION_MODE_TABS -ActionBarの下のタブ。
ドロップダウンリスト
何も作成しないで、
前の記事のプロジェクトを取り上げましょう。 新しいクラス
-ScreenFragmentを作成しましょう。これは、アプリケーションのさまざまな画面の類似物になります。
import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class ScreenFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView tv = new TextView(getActivity()); tv.setText("Screen " + getArguments().getInt(MainActivity.key_screen_number)); tv.setTextSize(30); return tv; } }
別のxmlマークアップファイルを作成しませんでした。ここでは特に必要ありません。 引数から画面番号を取得し、プログラムで作成されたTextViewに貼り付けて表示します。
onCreate()メソッドのコードを変更し、別のコードを
MainActivityに追加し
ます 。
public static final String key_screen_number = "key_screen_number"; ActionBar ab; FragmentTransaction ft; ScreenFragment screen_fragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ab = getSupportActionBar(); ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); String[] screens = new String[] {"Screen 1", "Screen 2", "Screen 3"}; ArrayAdapter<String> sp_adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, screens); sp_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); ab.setListNavigationCallbacks(sp_adapter, this); selected_list_item_position = -1; ab.setSelectedNavigationItem(0); } public boolean onNavigationItemSelected(int position, long id) { ft = getSupportFragmentManager().beginTransaction(); screen_fragment = new ScreenFragment(); Bundle args = new Bundle(); args.putInt(key_screen_number, position + 1); screen_fragment.setArguments(args); ft.replace(android.support.v7.appcompat.R.id.action_bar_activity_content, screen_fragment); ft.commit(); return true; }
onCreateでは、ActionBarにナビゲーションメソッド(リスト)を使用し、そのためのアダプターを準備し、イベントハンドラーを割り当てることを伝えます。 メソッドは1つだけです
-onNavigationItemSelected(int position、long id) 。 ユーザーがドロップダウンリストで項目を選択すると呼び出されます。 ここでは、新しい
ScreenFragmentを作成し、画面番号を付けて表示できるようにします。 次に、
FragmentTransactionを開始し、このフラグメントをid = android.support.v7.appcompat.R.id.action_bar_activity_contentでビューに追加します。 これはFrameLayoutで、setContentView()からのレイアウトが追加されます。 アプリケーションを起動し、さまざまな画面を選択します。



ドロップダウンリストの要素のマークアップとして、システムレイアウトを使用しますが、見た目はあまり良くありません。 したがって、独自のものを使用することをお勧めします。
Adapter.setDropDownViewResource()メソッドは
、それを
追加する責任が
あります。タブ
タブのナビゲート方法を変更するには、
MainActivityを微調整し
ます 。
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ab = getSupportActionBar(); ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); Tab tab = ab.newTab(); tab.setText("Screen 1"); tab.setTabListener(this); ab.addTab(tab, 0, true); tab = ab.newTab(); tab.setText("Screen 2"); tab.setTabListener(this); ab.addTab(tab, 1, false); tab = ab.newTab(); tab.setText("Screen 3"); tab.setTabListener(this); ab.addTab(tab, 2, false); }
MainActivity ... implements ... TabListenerも実行する必要があり
ます 。 これはタブクリックハンドラーです。 彼には3つの方法があります。
onTabUnselected([タブ]タブ、FragmentTransaction ft) -現在のタブが閉じるときに呼び出されます。
onTabSelected(タブタブ、FragmentTransaction ft) -新しいタブが開かれたときに呼び出され
ます(前のタブの直後に機能します)。
onTabReselected([タブ]タブ、FragmentTransaction ft) -ユーザーが既に開いているタブをクリックすると:
public void onTabUnselected(Tab tab, FragmentTransaction ft) { } public void onTabSelected(Tab tab, FragmentTransaction ft) { screen_fragment = new ScreenFragment(); Bundle args = new Bundle(); args.putInt(key_screen_number, tab.getPosition() + 1); screen_fragment.setArguments(args); ft.replace(android.support.v7.appcompat.R.id.action_bar_activity_content, screen_fragment); } public void onTabReselected(Tab tab, FragmentTransaction ft) { }
ここでは、FragmentTransactionを作成する必要がなくなりました。最初に提供されます(フラグメントを使用することを前提としています)。 ただし、このFragmentTransactionの場合、addToBackStack()およびcommit()メソッドを呼び出すことはできません。 また、必要なもの(テキスト、アイコン、位置など)をすべて引き出すことができる押したタブもあります。
システムが
適切でない場合は、ビューに
タブを割り当てることができ
ます-setCustomView(int layoutResId)アプリケーションを起動し、タブをクリックします。


ちなみに、タブが多数ある場合、ヘッダーは(Google Playのように)水平方向にスクロールできますが、スワイプはヘッダーの下では機能しません。
「ドロップダウンリスト」への追加
ほとんどの場合、既に選択されているナビゲーション項目をクリックすると、画面上で何も変更する必要はありません。 さて、タブを使用すると、すべてが明確になります
-onTabReselected()メソッドに触れないでください。 しかし、リストはどうですか? すべてが非常に簡単です:
MainActivityに変数を追加します
private int selected_list_item_position;
そして
onNavigationItemSelected(int position、long id)のコードを変更します:
public boolean onNavigationItemSelected(int position, long id) { if (position != selected_list_item_position) { ft = getSupportFragmentManager().beginTransaction(); screen_fragment = new ScreenFragment(); Bundle args = new Bundle(); args.putInt(key_screen_number, position + 1); screen_fragment.setArguments(args); ft.replace(android.support.v7.appcompat.R.id.action_bar_activity_content, screen_fragment); ft.commit(); selected_list_item_position = position; return true; } return false; }
これで、開いているナビゲーション項目を選択した場合にのみ、新しい画面が開きます。
メニュー
通常、異なるタブには異なるコンテンツが含まれ、そのメニューは異なる必要があります。 Googleのスタッフがこの機会を作りました。 次に、タブの例ですべてを表示します。
ScreenFragmentに次のコードを追加します。
public static final String key_menu_resource = "key_menu_resource"; @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(getArguments().getInt(key_menu_resource), menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); Log.d("MENU", "Cliced MenuItem is " + item.getTitle() + " (from ScreenFragment)"); return true; }
res / menu /フォルダーに3つのファイルを作成します。
screen_1.xml :
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/item1" android:title="Item 1" android:icon="@android:drawable/ic_menu_add"/> <item android:id="@+id/settings" android:title="Settings" android:icon="@android:drawable/ic_menu_edit"/> </menu>
screen_2.xml :
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/item2" android:title="Item 2" android:icon="@android:drawable/ic_menu_camera"/> <item android:id="@+id/settings" android:title="Settings" android:icon="@android:drawable/ic_menu_edit"/> </menu>
screen_3.xml :
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/item3" android:title="Item 3" android:icon="@android:drawable/ic_menu_call" /> </menu>
onTabSelected()の変更:
private int[] menu_resources = new int[] {R.menu.screen_1, R.menu.screen_2, R.menu.screen_3}; public void onTabSelected(Tab tab, FragmentTransaction ft) { screen_fragment = new ScreenFragment(); Bundle args = new Bundle(); args.putInt(key_screen_number, tab.getPosition() + 1); args.putInt(ScreenFragment.key_menu_resource, menu_resources[tab.getPosition()]); screen_fragment.setArguments(args); screen_fragment.setHasOptionsMenu(true); ft.replace(android.support.v7.appcompat.R.id.action_bar_activity_content, screen_fragment); }
ここで、
onCreateOptionsMenuメソッドを削除(またはコメントアウト)する必要があり
ます -これは今や私たちを妨害するだけです。 また、
MainActivityのonOptionsItemSelected()も調整されています。
@Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() != R.id.settings) { return false; } else { Log.d("MENU", "Cliced MenuItem is " + item.getTitle() + " (from MainActivity)"); return true; } }
ここで私がしたことを説明します。 実際、フラグメント内にメニューを作成することもできます。 表示するには、Fragment.setHasOptionsMenu(true)メソッドを呼び出す必要があります。 Activityではなくフラグメントでメニューを作成すると、
onOptionsItemSelected()メソッドが
MainActivityで最初に
呼び出され、Activityで
falseが返された場合のみ
ScreenFragmentで
呼び出されます。 ここでは、各
ケースの終わりに
switch / case が必要な
場合ではなく、
trueを返します。 これは、クリックが既に処理されており、フラグメントでonOptionsItemSelectedを呼び出す必要がないことを意味します。 たとえば、各タブには「設定」というメニュー項目があります。 各フラグメントにコードを入力しないために、このアイテムをクリックすると
trueを返し
ます 。 次に、onOptionsItemSelected()はActivityでのみ呼び出されます。たとえば、新しいSettingsActivityを開くことができます。 プログラムを実行し、異なるタブでデバイスの「メニュー」ボタンを押すと、異なる要素が表示されます。
ログ内のメニュー項目をクリックすると、その名前だけでなく、どのクラスでクリックが処理されたかがわかります。 または、この設定要素を使用して
res / menu /フォルダーに個別のxmlファイルを作成し、
onCreateOptionsMenu()メソッドの
MainActivityでこのファイルからメニューを作成できます。 その後、2つのメニューは結合されているかのようになり、両方のポイントが表示されます。
状態保存
タブを切り替えるときに、タブ上のコンテンツの状態を保持する必要があることがよくあります。 このため、フラグメントには特別なメソッド
-setRetainInstance(boolean retain)があります。 パラメータで
trueを渡すと、フラグメントは再作成されません。 これを確認するために、
MainActivityのonTabSelected()メソッドを書き換え
ます 。
private int[] menu_resources = new int[] {R.menu.screen_1, R.menu.screen_2, R.menu.screen_3}; private ScreenFragment[] screens = new ScreenFragment[] {new ScreenFragment(), new ScreenFragment(), new ScreenFragment()}; public void onTabSelected(Tab tab, FragmentTransaction ft) { screen_fragment = screens[tab.getPosition()]; Bundle args = new Bundle(); args.putInt(key_screen_number, tab.getPosition() + 1); args.putInt(ScreenFragment.key_menu_resource, menu_resources[tab.getPosition()]); screen_fragment.setArguments(args); screen_fragment.setHasOptionsMenu(true); screen_fragment.setRetainInstance(true); ft.replace(android.support.v7.appcompat.R.id.action_bar_activity_content, screen_fragment); }
あとがき
基本的に私が言いたかったのはそれだけです。 この記事は大規模であることが判明しましたが、うまくいけば便利です)
パート1-プロジェクトへのサポートライブラリの追加、簡単な例、検索
パート3-高度な機能