本文介紹了在Android中將Toolbar做爲ActionBar使用的方法.
而且介紹了在Fragment和嵌套Fragment中使用Toolbar做爲ActionBar使用時須要注意的事項.html
Android的ActionBar每一個版本都會作一些改變, 因此原生的ActionBar在不一樣的系統上看起來可能會不同.
使用support library版本的Toolbar可讓你的應用在多種設備類型上保持一致. support library中老是包含了最新的features.
Android從5.0 (API Level 21)開始提供Material Design, 使用v7版本的Toolbar後, 在任何Android 2.1(API Level 7)以上的機器上均可以看到Material Design風格的Toolbar.java
1.首先項目gradle中添加:android
compile 'com.android.support:appcompat-v7:23.4.0'
2.確保Activity繼承AppCompatActivity
3.在application設置中使用NoActionBar的主題:git
<application android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
4.把Toolbar寫在佈局中github
<android.support.v7.widget.Toolbar android:id="@+id/my_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="@style/ThemeOverlay.AppCompat.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
5.在Activity裏面把Toolbar設置成爲ActionBar
首先把Toolbar find出來, 而後調用setSupportActionBar方法
把Toolbar設置爲本身的ActionBar便可.app
public class ToolbarDemoActivity extends AppCompatActivity { @BindView(R.id.toolbar) Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toolbar_demo); ButterKnife.bind(this); setSupportActionBar(toolbar); } }
而後就能夠隨意使用啦, 用getSupportActionBar能夠獲取ActionBar類型的對象, 從而使用ActionBar的方法.ide
定義menu:佈局
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_android" android:icon="@drawable/ic_android_black_24dp" android:title="@string/action_android" app:showAsAction="always" /> <item android:id="@+id/action_favourite" android:icon="@drawable/ic_favorite_black_24dp" android:title="@string/action_favourite" app:showAsAction="ifRoom" /> <item android:id="@+id/action_settings" android:title="@string/action_settings" app:showAsAction="never" /> </menu>
而後在代碼中inflate和處理它的點擊事件:gradle
@Override public boolean onCreateOptionsMenu(Menu menu) { Log.i(TAG, "onCreateOptionsMenu()"); getMenuInflater().inflate(R.menu.menu_activity_main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_android: Log.i(TAG, "action android selected"); return true; case R.id.action_favourite: Log.i(TAG, "action favourite selected"); return true; case R.id.action_settings: Log.i(TAG, "action settings selected"); return true; default: return super.onOptionsItemSelected(item); } }
添加向上返回parent的action:ui
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toolbar_demo); ButterKnife.bind(this); setSupportActionBar(toolbar); // add a left arrow to back to parent activity, // no need to handle action selected event, this is handled by super getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
而後只須要在manifest中指定parent:
<activity android:name=".toolbar.ToolbarDemoActivity" android:parentActivityName=".MainActivity"></activity>
在Fragment中使用Toolbar的步驟和Activity差很少.
在Fragment佈局中添加一個Toolbar, 而後find它, 而後調用Activity的方法來把它設置成ActionBar:
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
注意此處有一個強轉, 必須是AppCompatActivity纔有這個方法.
可是此時運行到Fragment以後, 發現Toolbar上的文字和按鈕全是Activity傳過來的, 這是由於只有Activity的onCreateOptionsMenu()
被調用了, 可是Fragment的並無被調用.
在Fragment中加上這句:
setHasOptionsMenu(true);
此時Fragment的onCreateOptionsMenu()
回調會被調到了, 可是inflate出的按鈕和Activity中的actions加在一塊兒顯示出來了.
由於Activity的onCreateOptionsMenu()
會在以前調用到.
因而Fragment中的寫成這樣:
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { Log.e(TAG, "onCreateOptionsMenu()"); menu.clear(); inflater.inflate(R.menu.menu_parent_fragment, menu); }
即先clear()一下, 這樣按鈕就只有Fragment中設置的本身的了, 不會有Activity中的按鈕.
前面已經介紹過, Fragment能夠嵌套使用: Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常見錯誤.
那麼在前面的Fragment中再顯示一個子Fragment, 而且又帶有一個不同的Toolbar, 還須要哪些處理呢?
首先, java代碼中仍是須要有:
setHasOptionsMenu(true) ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
而後根據是否須要菜單按鈕, 覆寫onCreateOptionsMenu()方法來inflate本身的menu文件便可.
感受和在普通的Fragment中使用Toolbar做爲ActionBar並無什麼區別.
可是若是你的多個Fragment有不一樣的Toolbar菜單選項, 若是你沒有懂得其中的原理, 可能就會出現一些混亂.
下面來解說一下相關的方法.
一旦調用
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
就會致使ActivityonCreateOptionsMenu()
方法的調用, 而Activity會根據其中Fragment是否設置了setHasOptionsMenu(true)來調用Fragment的
onCreateOptionsMenu()
方法, 調用順序是樹形的, 按層級調用, 中間若是有false則跳過.
假設當前Activity, Parent Fragment和Child Fragment中都設置了本身的Toolbar爲ActionBar.
在打開Child fragment的時候, onCreateOptionsMenu()
的調用順序是.
Activity -> Parent -> Child.
此時parent和child fragment都設置了setHasOptionsMenu(true).
關於這個, 還有如下幾種狀況:
- 若是Parent的`setHasOptionsMenu(false)`, Child爲true, 則Parent的`onCreateOptionsMenu()`不會調用, 打開Child的時候Activity -> Child. - 若是Child的`setHasOptionsMenu(false)`, Parent爲true, 則打開Child的時候仍然會調用Activity和Parent的onCreateOptionsMenu()方法. - 若是Parent和Child都置爲false, 打開Parent和Child Fragment的時候都會調用Activity的onCreateOptionsMenu()方法.
僅僅是child Fragment的show() hide()的切換, activity和parent Fragment的onCreateOptionsMenu()也會從新進入.
這一點我尚未想明白, 是項目中遇到的, 初步推測多是menu的顯隱變化invalidate了menu, 改天有空再試試.
上面的機制經常是致使Toolbar上面的按鈕混淆錯亂的緣由.
舉個例子:
若是咱們如今Activity和Parent Fragment有不一樣的Toolbar按鈕, 可是Child只有文字, 沒有按鈕.
很顯然咱們不須要給child寫menu文件, 也不須要覆寫child裏的onCreateOptionsMenu()
方法.
可是此時無論怎樣, parent的onCreateOptionsMenu()
方法都會被調用, 這樣咱們打開child的時候, toolbar上就神奇地出現了parent裏的按鈕.
這種狀況如何解決呢?
能夠在parent中加一個條件, 當沒有child fragment的時候才作inflate的工做:
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { Log.e(TAG, "onCreateOptionsMenu()"); menu.clear(); if (getChildFragmentManager().getBackStackEntryCount() == 0) { inflater.inflate(R.menu.menu_parent_fragment, menu); } }
另外, 除了setSupportActionBar()
以外, 若是咱們想主動觸發 onCreateOptionsMenu()
方法的調用, 能夠用
invalidateOptionsMenu()
方法.
在Activity和其中的Fragment都有options menu的時候, 須要注意menu item的id不要重複.
覺得點擊事件的分發也是從Activity開始分發下去的, 若是child fragment中有個選項的id和Activity中一個選項的id重複了, 則在Activity中就會將其處理, 不會繼續分發.
以前沒有嵌套Fragment的狀況下, 只要將Fragment加入到Back Stack中, 那麼按下Back鍵的時候pop動做是系統自動作好的.
雖然在添加child fragment的時候將其加入到back stack中, 可是按back鍵的時候仍然是將parent fragment彈出, 只剩下Activity.
這是由於back鍵只檢查第一層Fragment的back stack, 對於child fragment, 須要在其parent中本身處理.
好比這樣處理:
在Activity中
@Override public void onBackPressed() { Fragment fragment = getSupportFragmentManager().findFragmentById(android.R.id.content); if (fragment instanceof ToolbarFragment) { if (((ToolbarFragment) fragment).onBackPressed()) { return; } } super.onBackPressed(); }
其中ToolbarFragment是直接加在Activity中做爲parent fragment的.
在parent fragment中(即ToolbarFragment中):
public boolean onBackPressed() { return getChildFragmentManager().popBackStackImmediate(); }
本文Demo地址: Demo on github
其中的: ToolbarDemoActivity即爲Toolbar Demo.
本文地址: Android Fragment使用(四) Toolbar使用及Fragment中的Toolbar處理
Developer Android:
Training AppBar
v7.widget.Toolbar Reference
v7.app.ActionBar
Guides: action bar menu items and fragments
分類: Android
標籤: Android, ActionBar, Fragment, Nested Fragments, Toolbar