一、ActionBar介紹
在Android 3.0中除了我們重點講解的Fragment外,Action Bar也是一個非常重要的交互元素,Action Bar取代了傳統的tittle bar和menu,在程序運行中一直置於頂部,對於Android平板設備來說屏幕更大它的標題使用Action Bar來設計可以展示更多豐富的內容,方便操控。
二、ActionBar的功能
用圖的方式來講解它的功能
<1> ActionBar的圖標,可顯示軟件圖標,也可用其他圖標代替。當軟件不在最高級頁面時,圖標左側會顯示一個左箭頭,用戶可以通過這個箭頭向上導航;
<2> 如果你的應用要在不同的View中顯示數據,這部分允許用戶來切換視圖。一般的作法是用一個下拉菜單或者是Tab選項卡。如果只有一個界面,那這里可以顯示應用程序的標題或者是更長一點的商標信息;
<3> 兩個action按鈕,這里放重要的按鈕功能,為用戶進行某項操作提供直接的訪問;
<4> overflow按鈕,放不下的按鈕會被置於“更多...”菜單項中,“更多...”菜單項是以下拉形式實現的。
三、ActionBar 奧義·詳解
1、添加ActionBar
ActionBar的添加非常簡單,只需要在AndroidManifest.xml中指定Application或Activity的theme是Theme.Holo或其子類就可以了,在Android 3.0及更高的版本中,Activity中都默認包含有ActionBar組件。
2、取消ActionBar
如果需要隱藏Action Bar可以在你的Activity的屬性中設置主題風格為NoTitleBar在你的manifest文件中
<activity android:theme="@android:style/Theme.NoTitleBar">
還有一種做法,在運行時調用hide()方法也可以隱藏ActionBar,調用show()方法來顯示ActionBar()。
ActionBar actionBar = getActionBar();
actionBar.hide();
當你隱藏ActionBar時,系統會將Activity的整個內容充滿整個空間。
注意:如果使用一個主題(theme)來移除Activity上得ActionBar,那么窗口將不再會有ActionBar,因此在運行時也就沒有辦法來添加ActionBar——調用getActionBar()方法會返回null值。
3.修改Action Bar的圖標和標題
默認情況下,系統會使用<application>或者<activity>中icon屬性指定的圖片來作為ActionBar的圖標,但是我們也可以改變這一默認行為。如果我們想要使用另外一張圖片來作為ActionBar的圖標,可以在<application>或者<activity>中通過logo屬性來進行指定,而標題中的內容使用label屬性來指定。比如項目的res/drawable目錄下有一張cnblog_icon.png圖片,就可以在AndroidManifest.xml中這樣指定:
<activity android:name=".MainActivity" android:label="召喚ActionBar吧" android:logo="@drawable/cnblog_icon" >
效果圖如下:
4.添加Action按鈕
ActionBar還可以根據應用程序當前的功能來提供與其相關的Action按鈕,這些按鈕都會以圖標或文字的形式直接顯示在ActionBar上。當然,如果按鈕過多,ActionBar上顯示不完,多出的一些按鈕可以隱藏在overflow里面(最右邊的三個點就是overflow按鈕),點擊一下overflow按鈕就可以看到全部的Action按鈕了。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/user_p" android:icon="@drawable/icon_user_p" android:showAsAction="always" android:title="用戶"/> <item android:id="@+id/write_p" android:icon="@drawable/icon_write_p" android:showAsAction="always" android:title="發布"/> <item android:id="@+id/favo_p" android:icon="@drawable/icon_favo_p" android:showAsAction="never" android:title="收藏"/> </menu>
可以看到,這里我們通過兩個<item>標簽定義了三個Action按鈕。<item>標簽中又有一些屬性,其中id是該Action按鈕的唯一標識符,icon用於指定該按鈕的圖標,title用於指定該按鈕可能顯示的文字(在圖標能顯示的情況下,通常不會顯示文字),actionViewClass用來指定一個構建視窗所使用的布局資源,showAsAction則指定了該按鈕顯示的位置,主要有以下幾種值可選:
fRoom | 會顯示在Item中,但是如果已經有4個或者4個以上的Item時會隱藏在溢出列表中。當然個 |
數並不僅僅局限於4個,依據屏幕的寬窄而定 | |
never | 永遠不會顯示。只會在溢出列表中顯示,而且只顯示標題,所以在定義item的時候,最好 |
把標題都帶上。 | |
always | 無論是否溢出,總會顯示。 |
withText | withText值示意Action bar要顯示文本標題。Action bar會盡可能的顯示這個 |
標題,但是,如果圖標有效並且受到Action bar空間的限制,文本標題有可 | |
能顯示不全。 | |
collapseActionView | 聲明了這個操作視窗應該被折疊到一個按鈕中,當用戶選擇這個按鈕時,這個操作視窗展開。否則, |
這個操作視窗在默認的情況下是可見的,並且即便在用於不適用的時候,也要占據操作欄的有效空間。 | |
一般要配合ifRoom一起使用才會有效果。 |
接着,重寫Activity的onCreateOptionsMenu()方法,代碼如下所示:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_main, menu);
return super.onCreateOptionsMenu(menu); }
這部分代碼很簡單,僅僅是調用了MenuInflater的inflate()方法來加載menu資源就可以了。現在重新運行一下程序,結果如下圖所示:
可以看到,menu_search和menu_setting這兩個按鈕已經在ActionBar中顯示出來了,而menu_delete這個按鈕由於showAsAction屬性設置成了never,所以被隱藏到了overflow當中,只要點擊一下overflow按鈕就可以看到它了。
- 當ActionBar中的剩余空間不足的時候,如果Action按鈕指定的showAsAction屬性是ifRoom的話,該Action按鈕就會出現在overflow當中,此時就只有title能夠顯示了。
- 如果Action按鈕在ActionBar中顯示,用戶可能通過長按該Action按鈕的方式來查看到title的內容。
5.響應Action按鈕的點擊事件
當用戶點擊Action按鈕的時候,系統會調用Activity的onOptionsItemSelected()方法,通過方法傳入的MenuItem參數,我們可以調用它的getItemId()方法和menu資源中的id進行比較,從而辨別出用戶點擊的是哪一個Action按鈕,比如:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.user_p: Toast.makeText(this, "你點擊了“用戶”按鍵!", Toast.LENGTH_SHORT).show(); return true; case R.id.write_p: Toast.makeText(this, "你點擊了“發布”按鍵!", Toast.LENGTH_SHORT).show(); return true; case R.id.favo_p: Toast.makeText(this, "你點擊了“收藏”按鍵!", Toast.LENGTH_SHORT).show(); return true; default: return super.onOptionsItemSelected(item); } }
可以看到,我們讓每個Action按鈕被點擊的時候都彈出一個Toast,現在重新運行一下代碼,結果如下圖所示:
5.通過Action Bar圖標進行導航
啟用ActionBar圖標導航的功能,可以允許用戶根據當前應用的位置來在不同界面之間切換。比如,A界面展示了一個列表,點擊某一項之后進入了B界面,這時B界面就應該啟用ActionBar圖標導航功能,這樣就可以回到A界面。
setTitle("Yanis"); setContentView(R.layout.activity_main); ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true);
現在重新運行一下程序,結果如下圖所示:
可以看到,在ActionBar圖標的左側出現了一個向左的箭頭,通常情況下這都表示返回的意思,因此最簡單的實現就是在它的點擊事件里面加入finish()方法就可以了,如下所示:
switch (item.getItemId()) { case android.R.id.home: finish(); return true; ... }

第二步需要在AndroidManifest.xml中配置父Activity,如下所示:
<activity android:name="com.yanis.actionbar.TabActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.yanis.actionbar.MainActivity" /> </activity>
可以看到,這里通過meta-data標簽指定了MainActivity的父Activity是LaunchActivity,在Android 4.1版本之后,也可以直接使用android:parentActivityName這個屬性來進行指定,如下所示:
<activity android:name="com.yanis.actionbar.TabActivity" android:parentActivityName="com.yanis.actionbar.MainActivity" > </activity>
第三步則需要對android.R.id.home這個事件進行一些特殊處理,如下所示:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); if (NavUtils.shouldUpRecreateTask(this, upIntent)) { TaskStackBuilder.create(this) .addNextIntentWithParentStack(upIntent) .startActivities(); } else { upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, upIntent); } return true; ...... } }
其中,調用NavUtils.getParentActivityIntent()方法可以獲取到跳轉至父Activity的Intent,然后如果父Activity和當前Activity是在同一個Task中的,則直接調用navigateUpTo()方法進行跳轉,如果不是在同一個Task中的,則需要借助TaskStackBuilder來創建一個新的Task。
6.添加Action View
ActionView是一種可以在ActionBar中替換Action按鈕的控件,它可以允許用戶在不切換界面的情況下通過ActionBar完成一些較為豐富的操作。比如說,你需要完成一個搜索功能,就可以將SeachView這個控件添加到ActionBar中。
<item android:id="@+id/action_search" android:actionViewClass="android.widget.SearchView" android:showAsAction="always" android:title="搜索"/>
如果你還希望在代碼中對SearchView的屬性進行配置(比如添加監聽事件等),完全沒有問題,只需要在onCreateOptionsMenu()方法中獲取該ActionView的實例就可以了,代碼如下所示:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); MenuItem searchItem = menu.findItem(R.id.action_search); SearchView searchView = (SearchView) searchItem.getActionView(); // 配置SearchView的屬性 ...... return super.onCreateOptionsMenu(menu); }
在得到了SearchView的實例之后,就可以任意地配置它的各種屬性了。關於SearchView的更多詳細用法,可以參考官方文檔 http://developer.android.com/guide/topics/search/search-dialog.html
除此之外,有些程序可能還希望在ActionView展開和合並的時候顯示不同的界面,其實我們只需要去注冊一個ActionView的監聽器就能實現這樣的功能了,代碼如下所示:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); MenuItem searchItem = menu.findItem(R.id.action_search); searchItem.setOnActionExpandListener(new OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { Log.d("TAG", "on expand"); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { Log.d("TAG", "on collapse"); return true; } }); return super.onCreateOptionsMenu(menu); }
可以看到,調用MenuItem的setOnActionExpandListener()方法就可以注冊一個監聽器了,當SearchView展開的時候就會回調onMenuItemActionExpand()方法,當SearchView合並的時候就會調用onMenuItemActionCollapse()方法,我們在這兩個方法中進行相應的UI操作就可以了。
7.Overflow按鈕不顯示的情況
雖然現在我們已經掌握了不少ActionBar的用法,但是當你真正去使用它的時候還是可能會遇到各種各樣的問題,比如很多人都會碰到overflow按鈕不顯示的情況。明明是同樣的一份代碼,overflow按鈕在有些手機上會顯示,而在有些手機上偏偏就不顯示,如下圖:
可以看到,ActionBar最右邊的overflow按鈕不見,按一下Menu鍵,隱藏在overflow中的Action按鈕就會從底部出來。
有人總結了一下,overflow按鈕的顯示情況和手機的硬件情況是有關系的,如果手機沒有物理Menu鍵的話,overflow按鈕就可以顯示,如果有物理Menu鍵的話,overflow按鈕就不會顯示出來。比如我們啟動一個有Menu鍵的模擬器,然后將代碼運行到該模擬器上
實際上,在ViewConfiguration這個類中有一個叫做sHasPermanentMenuKey的靜態變量,系統就是根據這個變量的值來判斷手機有沒有物理Menu鍵的。當然這是一個內部變量,我們無法直接訪問它,但是可以通過反射的方式修改它的值,讓它永遠為false就可以了,代碼如下所示:
@Override protected void onCreate(Bundle savedInstanceState) { ...... setOverflowShowingAlways(); } private void setOverflowShowingAlways() { try { ViewConfiguration config = ViewConfiguration.get(this); Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); menuKeyField.setAccessible(true); menuKeyField.setBoolean(config, false); } catch (Exception e) { e.printStackTrace(); } }
這里我們在onCreate()方法的最后調用了setOverflowShowingAlways()方法,而這個方法的內部就是使用反射的方式將sHasPermanentMenuKey的值設置成false,現在重新運行一下代碼,結果如下圖所示:
可以看到,即使是在有Menu鍵的手機上,也能讓overflow按鈕顯示出來了,這樣就可以大大增加我們軟件界面和操作的統一性。
8.讓Overflow中的選項顯示圖標
如果你點擊一下overflow按鈕去查看隱藏的Action按鈕,你會發現這部分Action按鈕都是只顯示文字不顯示圖標的,如下圖所示:
這是官方的默認效果,Google認為隱藏在overflow中的Action按鈕都應該只顯示文字。當然,如果你認為這樣不夠美觀,希望在overflow中的Action按鈕也可以顯示圖標,我們仍然可以想辦法來改變這一默認行為。
@Override public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR && menu != null) { if (menu.getClass().getSimpleName().equals("MenuBuilder")) { try { Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); m.setAccessible(true); m.invoke(menu, true); } catch (Exception e) { } } } return super.onMenuOpened(featureId, menu); }
可以看到,這里我們重寫了一個onMenuOpened()方法,當overflow被展開的時候就會回調這個方法,接着在這個方法的內部通過返回反射的方法將MenuBuilder的setOptionalIconsVisible變量設置為true就可以了。

和Action View有點類似,Action Provider也可以將一個Action按鈕替換成一個自定義的布局。但不同的是,Action Provider能夠完全控制事件的所有行為,並且還可以在點擊的時候顯示子菜單。
為了添加一個Action Provider,我們需要在<item>標簽中指定一個actionViewClass屬性,在里面填入Action Provider的完整類名。我們可以通過繼承ActionProvider類的方式來創建一個自己的Action Provider,同時,Android也提供好了幾個內置的Action Provider,比如說ShareActionProvider。
由於每個Action Provider都可以自由地控制事件響應,所以它們不需要在onOptionsItemSelected()方法中再去監聽點擊事件,而是應該在onPerformDefaultAction()方法中去執行相應的邏輯。
那么我們就先來看一下ShareActionProvider的簡單用法吧,編輯menu資源文件,在里面加入ShareActionProvider的聲明,如下所示:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_share" android:actionProviderClass="android.widget.ShareActionProvider" android:showAsAction="ifRoom" android:title="分享" /> ... </menu>
注意,ShareActionProvider會自己處理它的顯示和事件,但我們仍然要記得給它添加一個title,以防止它會在overflow當中出現。
接着剩下的事情就是通過Intent來定義出你想分享哪些東西了,我們只需要在onCreateOptionsMenu()中調用MenuItem的getActionProvider()方法來得到該ShareActionProvider對象,再通過setShareIntent()方法去選擇構建出什么樣的一個Intent就可以了。代碼如下所示:
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_main, menu); MenuItem shareItem = menu.findItem(R.id.action_share); ShareActionProvider provider = (ShareActionProvider) shareItem .getActionProvider(); provider.setShareIntent(getDefaultIntent()); return super.onCreateOptionsMenu(menu); } private Intent getDefaultIntent() { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("image/*"); return intent; }
可以看到,這里我們通過getDefaultIntent()方法來構建了一個Intent,該Intent表示會將所有可以共享圖片的程度都列出來。重新運行一下程序,效果如下圖所示:
細心的你一定觀察到了,這個ShareActionProvider點擊之后是可以展開的,有點類似於overflow的效果,這就是Action Provider的子菜單。
10.添加導航Tabs
Tabs的應用可以算是非常廣泛了,它可以使得用戶非常輕松地在你的應用程序中切換不同的視圖。而Android官方更加推薦使用ActionBar中提供的Tabs功能,因為它更加的智能,可以自動適配各種屏幕的大小。比如說,在平板上屏幕的空間非常充足,Tabs會和Action按鈕在同一行顯示,如下圖所示:
而如果是在手機上,屏幕的空間不夠大的話,Tabs和Action按鈕則會分為兩行顯示,如下圖所示:
下面我們就來看一下如何使用ActionBar提供的Tab功能,大致可以分為以下幾步:
1. 實現ActionBar.TabListener接口,這個接口提供了Tab事件的各種回調,比如當用戶點擊了一個Tab時,你就可以進行切換Tab的操作。
2.為每一個你想添加的Tab創建一個ActionBar.Tab的實例,並且調用setTabListener()方法來設置ActionBar.TabListener。除此之外,還需要調用setText()方法來給當前Tab設置標題。
3.最后調用ActionBar的addTab()方法將創建好的Tab添加到ActionBar中。
看起來並不復雜,總共就只有三步,那么我們現在就來嘗試一下吧。首先第一步需要創建一個實現ActionBar.TabListener接口的類,代碼如下所示:
package com.yanis.yc_ui_actionbar_tab; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.Activity; import android.app.Fragment; import android.app.FragmentTransaction; public class TabListener<T extends Fragment> implements ActionBar.TabListener { private Fragment mFragment; private final Activity mActivity; private final String mTag; private final Class<T> mClass; /** Constructor used each time a new tab is created. * @param activity The host Activity, used to instantiate the fragment * @param tag The identifier tag for the fragment * @param clz The fragment's Class, used to instantiate the fragment */ public TabListener(Activity activity, String tag, Class<T> clz) { mActivity = activity; mTag = tag; mClass = clz; } /* The following are each of the ActionBar.TabListener callbacks */ public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = Fragment.instantiate(mActivity, mClass.getName()); ft.add(android.R.id.content, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { if (mFragment != null) { // Detach the fragment, because another one is being attached ft.detach(mFragment); } } public void onTabReselected(Tab tab, FragmentTransaction ft) { // User selected the already selected tab. Usually do nothing. } }
這段代碼並不長,我們簡單分析一下。當Tab被選中的時候會調用onTabSelected()方法,在這里我們先判斷mFragment是否為空,如果為空的話就創建Fragment的實例並調用FragmentTransaction的add()方法,如果不會空的話就調用FragmentTransaction的attach()方法。
而當Tab沒有被選中的時候,則調用FragmentTransaction的detach()方法,將UI資源釋放掉。
當Tab被重新選中的時候會調用onTabReselected()方法,如果沒有特殊需求的話,通常是不需要進行處理的。
接下來第二步要給每一個Tab創建一個ActionBar.Tab的實例,在此之前要先准備好每個Tab頁對應的Fragment。比如說這里我們想創建三個Tab頁,准備好這三個Tab頁對應的Fragment和對應的布局文件。
package com.yanis.yc_ui_actionbar_tab; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class Fragment1 extends android.app.Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment1, container, false); } }
沒有什么實質性的代碼,只是顯示了指定的布局文件。
Fragment都准備好了之后,接下來就可以開始創建Tab實例了,創建好了之后則再調用addTab()方法添加到ActionBar當中,這兩步通常都是在Activity的onCreate()方法中執行的,代碼如下:
package com.yanis.actionbar; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.app.TaskStackBuilder; import android.view.MenuItem; public class TabActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab); initView(); } private void initView() { // 提示getActionBar方法一定在setContentView后面 ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // 添加Tab選項 Tab tab = actionBar .newTab() .setText("澳門風雲2") .setTabListener( new TabListener<Fragment1>(this, "film1", Fragment1.class)); actionBar.addTab(tab); tab = actionBar .newTab() .setText("五十度灰") .setTabListener( new TabListener<Fragment2>(this, "film2", Fragment2.class)); actionBar.addTab(tab); tab = actionBar .newTab() .setText("爸爸去哪兒2") .setTabListener( new TabListener<Fragment3>(this, "film3", Fragment3.class)); actionBar.addTab(tab); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); if (NavUtils.shouldUpRecreateTask(this, upIntent)) { TaskStackBuilder.create(this) .addNextIntentWithParentStack(upIntent) .startActivities(); } else { upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, upIntent); } return true; default: return super.onOptionsItemSelected(item); } } }
可以看到,這里是使用連綴的寫法來創建Tab的。首先調用ActionBar的newTab()方法來創建一個Tab實例,接着調用了setText()方法來設置標題,然后再調用setTabListener()方法來設置事件監聽器,最后再調用ActionBar的addTab()方法將Tab添加到ActionBar中。
好了,這樣的話代碼就編寫完了,效果如下圖所示:
11.添加下拉列表導航
1.1 簡單介紹
作為Activity內部的另一種導航(或過濾)模式,操作欄提供了內置的下拉列表。下拉列表能夠提供Activity中內容
的不同排序模式。
啟用下拉式導航的基本過程如下:
<1> 創建一個給下拉提供可選項目的列表,以及描畫列表項目時所使用的布局;
<2> 實現ActionBar.OnNavigationListener回調,在這個回調中定義當用戶選擇列表中一個項目時所發生的行為;
<3> 用setNavigationMode()方法該操作欄啟用導航模式;
<4> 用setListNavigationCallbacks()方法給下拉列表設置回調方法。
1.2 效果圖如下:
1.3 代碼實現
①准備列表數據( strings.xml)
<string-array name="action_list"> <item>Fragment1</item> <item>Fragment2</item> <item>Fragment3</item> </string-array>
②然后就是主界面代碼了
package com.yanis.actionbar; import android.app.ActionBar; import android.app.ActionBar.OnNavigationListener; import android.app.Activity; import android.app.Fragment; import android.app.TaskStackBuilder; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.view.MenuItem; import android.widget.ArrayAdapter; import android.widget.SpinnerAdapter; public class ListActivity extends Activity { private OnNavigationListener mOnNavigationListener; private String[] arry_list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_list); initView(); } /** * 初始化組件 */ private void initView() { ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); // //導航模式必須設為NAVIGATION_MODE_LIST actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); // 定義一個下拉列表數據適配器 SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this, R.array.action_list, android.R.layout.simple_spinner_dropdown_item); arry_list = getResources().getStringArray(R.array.action_list); mOnNavigationListener = new OnNavigationListener() { @Override public boolean onNavigationItemSelected(int position, long itemId) { Fragment newFragment = null; switch (position) { case 0: newFragment = new Fragment1(); break; case 1: newFragment = new Fragment2(); break; case 2: newFragment = new Fragment3(); break; default: break; } getFragmentManager() .beginTransaction() .replace(R.id.container, newFragment, arry_list[position]).commit(); return true; } }; actionBar.setListNavigationCallbacks(mSpinnerAdapter, mOnNavigationListener); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); if (NavUtils.shouldUpRecreateTask(this, upIntent)) { TaskStackBuilder.create(this) .addNextIntentWithParentStack(upIntent) .startActivities(); } else { upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, upIntent); } return true; default: return super.onOptionsItemSelected(item); } } }
12.自定義ActionBar樣式
雖說ActionBar給用戶提供了一種全局統一的界面風格和操作方式,但這並不意味着所有應用程序的ActionBar都必須要長得一模一樣。如果你需要修改ActionBar的樣式來更加好地適配你的應用,可以非常簡單地通過Android樣式和主題來實現。
其實Android內置的幾個Activity主題中就已經包含了"dark"或"light"這樣的ActionBar樣式了,同時你也可以繼承這些主題,然后進行更深一步的定制。
1. 使用主題
- Theme.Holo,這是一個深色系的主題。
- Theme.Holo.Light,這是一個淺色系的主題。


<application android:theme="@android:style/Theme.Holo.Light" ... />
如果你只想讓ActionBar使用深色系的主題,而Activity的內容部分仍然使用淺色系的主題,可以通過聲明Theme.Holo.Light.DarkActionBar這個主題來實現,效果如下圖所示:
2. 自定義背景
<resources> <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> <item name="android:actionBarStyle">@style/MyActionBar</item> </style> <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> <item name="android:background">#f4842d</item> </style> </resources>
可以看到,這里我們定義了一個CustomActionBarTheme主題,並讓它繼承自Theme.Holo.Light。然后在其內部重寫了actionBarStyle這個屬性,然后將這個屬性指向了MyActionBar這個樣式,我們在這個樣式中又重寫了background屬性,並給它指定了一個背景色。

<resources> <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> <item name="android:actionBarStyle">@style/MyActionBar</item> </style> <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> <item name="android:background">#f4842d</item> <item name="android:backgroundStacked">#d27026</item> </style> </resources>
可以看到,這里又重寫了backgroundStacked屬性,這個屬性就是用於指定Tabs背景色的。那么再次重新運行程序,效果如下圖所示:
3. 自定義文字顏色
現在整個ActionBar的顏色是屬於偏暗系的,而ActionBar中文字的顏色又偏偏是黑色的,所以看起來並不舒服,那么接下來我們就學習一下如果自定義文字顏色,將文字顏色改成白色。
<resources> ...... <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> ...... <item name="android:titleTextStyle">@style/MyActionBarTitleText</item> </style> <style name="MyActionBarTitleText" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title"> <item name="android:textColor">#fff</item> </style> </resources>
可以看到,這里在MyActionBar樣式里面重寫了titleTextStyle屬性,並將它指向了另一個自定義樣式MyActionBarTitleText,接着我們在這個樣式中指定textColor的顏色是#fff,也就是白色。

<resources> <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> <item name="android:actionBarStyle">@style/MyActionBar</item> <item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item> </style> <style name="MyActionBarTabText" parent="@android:style/Widget.Holo.ActionBar.TabText"> <item name="android:textColor">#fff</item> </style> </resources>
這里我們在CustomActionBarTheme主題中重寫actionBarTabTextStyle屬性,並將它指向一個新建的MyActionBarTabText樣式,然后在這個樣式中重寫textColor屬性,將顏色指定為白色即可。

4. 自定義Tab Indicator




<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected" /> <item android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/tab_selected" /> <item android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed" /> <item android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed" /> </selector>
四種狀態分別引用了四張圖片,這樣就把state-list drawable文件寫好了。接着修改style.xml文件,代碼如下所示:
<resources> <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> ...... <item name="android:actionBarTabStyle">@style/MyActionBarTabs</item> </style> <style name="MyActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView"> <item name="android:background">@drawable/actionbar_tab_indicator</item> </style> </resources>
這里先是重寫了actionBarTabStyle這個屬性,並將它指向了另一個自定義樣式MyActionBarTabs,接着在這個樣式中重寫background屬性,然后指向我們剛才創建的actionbar_tab_indicator即可。
