サポートライブラリを備えたAndroid 2.1以降のActionBar。 パート2-ナビゲーション

こんにちは、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-高度な機能



All Articles