Action Bar

          Action bar是一個標識應用程序和用戶位置的窗口功能,並且給用戶提供操作和導航模式。在大多數的情況下,當你需要突出展現用戶行爲或全局導航的activity中使用action bar,因爲action bar能夠使應用程序給用戶提供一致的界面,並且系統能夠很好根據不同的屏幕配置來適應操作欄的外觀。你能夠用ActionBar的對象的API來控制操作欄的行爲和可見性,這些API被添加在Android3.0(API 級別 11)中。

      Action bar的主要目的是:

        1.  提供一個用於識別應用程序的標示和用戶的位置的專用空間。

         這個空間的左邊是應用的圖標或logo,以及Activity的標題。但是,如果是像當前選擇的標籤這樣的標識當前View對象的導航標籤,你可以選擇刪除Activity的標題。

        2.  在不同的應用程序之間提供一致的導航和視覺體驗。

        Action bar提供了用於Fragment間切換的內置導航標籤。它還提供了一個用於替換導航模式或優化當前視覺效果(如按照不同條件排序的列表)的下拉列表。

        3.  突出Activity的關鍵操作(如“搜索”、“創建”、“共享”等),並且在可預見的方法內給用戶提供快捷的訪問。

       對於關鍵的用戶操作,你能夠通過把選項菜單項作爲操作項直接放到操作欄中,從而提供快捷的訪問。操作項目還能提供一個操作窗口,這個窗口給更直接的操作行爲提供一個嵌入的窗口部件。沒有改進成操作項的菜單項在溢出菜單中還是有效的,用戶既可以使用設備上的菜單按鈕(設備上有按鈕的時候),也可以使用操作欄中的溢出菜單按鈕(當設備上不包含菜單按鈕時)來顯示這些操作項目。

       上面的總結一下:Action bar就是替換3.0以前的tittle bar和menu。

             

        圖1. Honeycomb Gallery應用中的操作欄,從左邊開始,依次是logo、導航選項標籤和操作項(在右邊插入的一個懸浮菜單按鈕)。

         Note: If you're looking for information about the contextual action bar for displaying contextual action items, see the Menu guide.

        Action Bar Design   For design guidelines, read Android Design's Action Bar guide.


添加Action Bar

       從Android3.0(API級別 11)開始,Action bar被包含在所有的使用Theme.Hole主題的Activity(或者是這些Activity的子類)中,當targetSdkVersion或minSdkVersion屬性被設置爲“11”或更大的數值是,這個主題是默認的主題一。如:

  1. <manifest ... >  
  2.     <uses-sdk android:minSdkVersion="4"  
  3.               android:targetSdkVersion="11" />  
  4.     ...  
  5. </manifest>  

       在這個例子中,應用程序要求最小的API版本級別是4(Android 1.6),但是它還要求了目標API版本級別是11(Android 3.0)。這樣,當應用程序運行在Android3.0或更高的版本上時,系統就會給每個Activity應用holographic  主題,這樣,每個Activity就會包含Action bar。

        如果你想使用ActionBar API來進行添加導航模式和修改操作欄樣式的操作,你應該把minSdkVersion屬性設置爲“11”或更大的值。有一些方法可以使你的應用支持更舊的Android版本,同時在API等級爲11或更高的API等級的機器的使你的應用支持一些Action bar apis。爲了保持後向兼容,請參考邊框內的內容(邊框內容如下)。

Remaining backward-compatible

If you want to provide an action bar in your application and remain compatible with versions of Android older than 3.0, you need to create the action bar in your activity's layout (because theActionBar class is not available on older versions).

To help you, the Action Bar Compatibility sample app provides an API layer and action bar layout that allows your app to use some of theActionBar APIs and also support older versions of Android by replacing the traditional title bar with a custom action bar layout.

刪除Action bar

      如果你不想要Action bar,把Activity的主題設置爲Theme.Holo.NoActionBar就可以了,如:

  1. <activity android:theme="@android:style/Theme.Holo.NoActionBar">  
         或者使用Action bar的 hide()方法,如下:

  1. ActionBar actionBar = getActionBar();  
  2. actionBar.hide();  

       當Action bar隱藏時,系統會調整你的Activity來填充當前有效的屏幕空間。你能夠使用show()方法來再次顯示操作欄。

      在隱藏和刪除Action bar時,要當心爲了適應被Action bar佔用的空間而導致的Activity的重新佈局。如果你的Activity有規律的隱藏和顯示Action bar,你可能想要使用覆蓋模式。覆蓋模式在Activity的頂部描畫操作欄,而不是在它們所擁有的屏幕的區域。這樣,在Action bar隱藏和重新顯示時,你的佈局保持不變。要使用覆蓋模式,就要給Activity創建一個主題,並且把android:windowActionBarOverlay屬性設置爲true。

       提示:如果你有一個刪除了Action bar的定製化的Activity主題,它把android:windowActionBar樣式屬性設置爲false。但是,如果你使用了刪除Action bar的一個主題,那麼,創建窗口將不允許Action bar再顯示,因此,你不能在以後給這個Activity添加Action bar---因爲getActionBar()方法將返回null。


添加操作項

       有些時候,你可能想要讓用戶直接訪問選項菜單中的一個項目,因此你要把應該在Action bar中顯示的菜單項作爲一個操作項來聲明。操作項能夠能夠包含一個圖標或文本標題。如果一個菜單項不作爲一個操作項顯示,那麼系統就會把它放到懸浮菜單中。懸浮菜單既可以通過設備的Menu按鈕來顯示,也可以在Action bar中一個額外的按鈕來顯示。

       當Activity首次啓動時,系統會調用onCreateOptionsMenu()方法給你的Activity組裝Action bar和懸浮菜單。在這個回調方法中應該加載在XML文件中定義的菜單項資源,如:

  1. @Override  
  2. public boolean onCreateOptionsMenu(Menu menu) {  
  3.     MenuInflater inflater = getMenuInflater();  
  4.     inflater.inflate(R.menu.main_activity, menu);  
  5.     return true;  
  6. }  

             

         圖2. 帶有圖標和文本標題的兩個操作項,和懸浮菜單按鈕。

       在XML文件中,你能夠通過給<item>元素聲明android:showAsAction=”ifRoom”屬性,請求把一個菜單項作爲一個操作項來顯示。用這種方式,只在有有效的空間時,菜單項才能顯示在Action bar中。如果沒有足夠的空間,這個菜單項會顯示在懸浮菜單中。

        如果你的菜單項支持標題和圖標---帶有android:title和android:icon屬性---那麼默認情況下,操作項僅顯示圖標。如果你要顯示文本標題,就要給android:showAsAction屬性添加withText設置,如:

  

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <menu xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <item android:id="@+id/menu_save"  
  4.           android:icon="@drawable/ic_menu_save"  
  5.           android:title="@string/menu_save"  
  6.           android:showAsAction="ifRoom|withText" />  
  7. </menu>  

提示:withText值示意Action bar要顯示文本標題。Action bar會儘可能的顯示這個標題,但是,如果圖標有效並且受到Action bar空間的限制,文本標題有可能顯示不全。

當用戶選擇了一個操作項時,Activity會接收一個onOptionsItemSelected()的回調,要把android:id屬性支持的ID傳遞給這個方法。

給每個菜單項定義android:title屬性是至關重要的,即使你沒有給操作項聲明標題。原因如下:

1.  如果Action bar中沒有足夠的空間來顯示操作項,那麼菜單項就會顯示在懸浮菜單中,並僅顯示標題;

2.  屏幕閱讀器要給視障用戶朗讀菜單項標題;

3.  如果僅用圖標來顯示操作項,那麼,用戶能夠長按這個項目,用操作項的標題來顯示提示信息。

注意:如果你添加源於Fragment對象的菜單項,那麼通過Fragment類的onCreateOptionsMenu onCreateOptionsMenu回調方法,當用戶選擇其中一個Fragment菜單項時,系統會對用那個Fragment對象對應的onOptionsItemSelected()方法。但是,Activity有機會首先處理這個事件,因爲系統在調用對應的Fragment對象的onOptionsItemSelected()方法之前會調用Activity的相同的回調方法。

       你也能夠聲明一個菜單項,讓它始終作爲操作項來顯示,而不是在空間不足時就放到懸浮菜單中。大多數情況下,你不應該使用always屬性值來強制一個菜單項始終顯示在操作欄中,但是,當提供了一個不給懸浮菜單提供默認操作的操作窗口時,你就需要始終顯示一個菜單項。但是要警惕,太多的操作項會創建一個混亂的UI,並且會導致窄屏設備上的佈局問題。最好的方法是使用ifRoom屬性值,它允許系統在操作欄空間不足的時候,把菜單項移到懸浮菜單中。

選擇操作項:

       通過評估一些關鍵的特性,你應該仔細的選擇選項菜單中的那些菜單項應該作爲操作項來顯示,通常,每個操作項應該至少滿足下列特性之一:

        1.  經常使用:用戶百分之七十以上的訪問都需要使用的操作,或者是要連續的多次使用的操作。

        2.  重要:它是一個用戶能夠很容易找到的操作,即使它不是經常性的操作,也需要用戶在需要的時候能夠輕易的找到它,並執行。

             如,Wi-Fi設置中的添加網絡等。

        3.  典型:它是一些類似應用程序的操作欄中提供的典型操作,因此,用戶都期望在操作欄中能夠找到它。

            如,電子郵件或社交應用程序中的“刷新”操作。

        如果你想要把四個以上的菜單項調整爲操作項,那麼你就應該認真考慮一下他們相對的重要性級別,並且嘗試不要超過四個以上的操作項設置(並且還有使用“ifRoom”屬性值的設置,允許系統在遇到空間受限的比較小的屏幕的時候,能夠把靠後的操作項放到懸浮菜單中)。即使在一些寬屏設備上,空間充足,你也不應該創建很多操作項,這樣會擾亂UI的佈局,而且更像一個桌面工具欄,因此要保持最小數量的操作項。

       另外,以下操作應該永遠不要作爲操作項來顯示:設置、幫助、意見反饋、或類似的操作。要把它們始終保留在懸浮菜單中。

       注意:不是所有的設備都給檢索提供了專有的硬件按鈕,因此,如果是你應用程序中的一個重要功能,它應該始終作爲一個操作項來顯示(而且通常要把放到第一項的位置,尤其是操作窗口中提供這個操作的時候)。

使用分離式操作欄

        當你的應用程序正在Android4.0(API 級別 14)或以上的版本上運行,那麼還有一種叫做“分隔操作欄”的額外模式對action bar有效。當你啓用分隔操作欄模式時,在屏幕的底部會顯示一個獨立的橫條,用於顯示Activity在窄屏設備(如豎屏手機)上運行時的所有操作項。

       把action bar分隔成獨立的操作項,確保在窄屏設備上有合適的空間來顯示所有的操作項,同時把導航條和標題元素留在頂部。

       要啓用分離式操作欄,只需簡單的在<application>或<activity>元素中添加uiOptions=”splitActionBarWhenNarrow”屬性設置就可以了。

       要注意,Android會基於當前屏幕的尺寸用各種方式來調整操作欄的外觀。使用分離式操作欄只是你能夠啓用的允許操作欄針對不同屏幕尺寸來進一步優化用戶體驗的選項之一。你還可以允許操作欄把導航選項標籤摺疊到主操作欄中,如果你在操作欄中使用導航選項標籤,那麼一旦操作項在窄屏設備上被分離,這些導航選項標籤就可能填充到主操作欄中,而不是被分離到堆疊起來的操作欄。尤其是如果你禁用了操作欄的圖標和標題(用setDisplayShowHomeEnabled(false)setDisplayShowTitleEnabled(false)方法),那麼導航選項標籤就會摺疊到主操作欄中,如下圖3中第二個設備的顯示:

                       

圖3. 左側是帶有導航選項標籤的分離式操作欄,右側是禁用了應用圖標和標題的分離式操作欄。

注意:儘管android:uiOptions屬性在Android4.0(API 級別 14)中才被添加,但是爲了保持跟Android的低版本的兼容性,即使你的minSdkVersion屬性值小於14,那麼你的應用程序也可以安全的包含android:uiOptions屬性。在舊版本上運行時,因爲系統不能理解這個屬性,所以只是簡單的忽略了這個XML屬性。在你的清單文件中包含這個屬性的唯一條件是,你必須在支持API級別14或更高以上版本的平臺上編譯你的應用程序。爲了保持兼容性,你不能在你的應用程序代碼中使用由minSdkVersion屬性聲明的版本所不支持的API,只有XML屬性才能被舊的平臺版本安全的忽略。

導航欄使用應用圖標

       默認情況下,應用程序圖標顯示在操作欄的左邊。你能夠把這個圖標當做操作項來使用。應用程序應該在這個圖標上響應以下兩個操作之一:

       1.  返回應用程序的“主”Activity;

       2.  嚮應用程序上級頁面導航。

       當用戶觸摸這個圖標時,系統會調用Activity帶有android.R.id.home ID的onOptionsItemSelected()方法。在這個響應中,你既可以啓動主Activity,也可以返回你的應用程序結構化層次中用戶上一步操作的界面。

       如果你要通過應用程序圖標的響應來返回主Activity,那麼就應該在Itent對象中包括FLAG_ACTIVITY_CLEAR_TOP標識。用這個標記,如果你要啓動的Activity在當前任務中已經存在,那麼,堆棧中這個Activity之上的所有的Activity都有被銷燬,並且把這個Activity顯示給用戶。添加這個標識往往是重要的,因爲返回主Activity相當與一個回退的動作,因此通常不應該再創建一個新的主Activity的實例,否則,最終可能會在當前任務中產生一個很長的擁有多個主Activity的堆棧。

例如,下例的onOptionsItemSelected()方法實現了返回應用程序的主Activity的操作:

  1. @Override  
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3.     switch (item.getItemId()) {  
  4.         case android.R.id.home:  
  5.             // app icon in action bar clicked; go home  
  6.             Intent intent = new Intent(this, HomeActivity.class);  
  7.             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
  8.             startActivity(intent);  
  9.             return true;  
  10.         default:  
  11.             return super.onOptionsItemSelected(item);  
  12.     }  
  13. }  
       在用戶從另一個應用程序進入當前Activity的情況下,你可能還想要添加FLAG_ACTIVITY_NEW_TASK標識。這個標識確保在用戶返回主頁或上級頁面時,新的Activity不會被添加到當前的任務中,而是在屬於你自己的應用程序的任務中啓動。例如,如果用戶通過被另一個應用程序調用的Intent對象啓動了你的應用程序中的一個Activity,那麼選擇操作欄圖標來返回主頁或上級頁面時,FLAG_ACTIVITY_CLEAR_TOP標識會在屬於你的應用程序的任務中啓動這個Activity(不是當前任務)。系統既可以用這個新的Activity做根Activity來啓動一個新的任務,也可以把存在後臺的擁有這個Activity實例的一個既存任務帶到前臺來,並且目標Activity會接受onNewIntent()回調。因此,如果你的Activity要接收另一個應用程序的Intent對象,那麼通常應該給這個Intent對象添加FLAG_ACTIVITY_NEW_TASK標識,如:
  1. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);  

注意:如果你要使用應用圖標來返回主頁,要注意從Android4.0(API 級別 14)開始,必須通過調用setHomeButtonEnabled(true)方法確保這個圖標能夠作爲一個操作項(在以前的版本,默認情況下,這個圖標就能夠作爲一個操作項)。

嚮應用程序上級頁面導航

       作爲傳統的回退導航(把用戶帶回任務歷史中的前一個窗口)的補充,你能夠讓action bar圖標提供向上級頁面導航的功能,它應用把用戶帶回到你的應用程序的上級頁面。例如,當前頁面時你的應用程序層次比較深的一個頁面,觸摸應用程序圖標應該返回返回上一級頁面(當前頁面的父頁面)。
          
         圖4. Email應用程序的標準圖標(左)和向上導航圖標(右)。系統會自動添加向上指示。
       例如,圖5演示了當用戶從一個應用程序導航到一個屬於不同應用程序的Activity時,“回退”按鈕的行爲。

            

       但是,如果在編輯完郵件之後,想要停留在Email應用程序中,那麼向上導航就允許你把用戶導航到Email應用程序中編輯郵件頁面的上級頁面,而不是返回到前一個Activity。圖6演示了這種場景,在這個場景中,用戶進入到Email應用程序後,不是按回退按鈕,而是按操作欄圖標來向上導航。

       

         圖6. 從People應用進入Email應用後,向上導航的行爲。

         要是應用程序圖標能夠向上導航,就要在你的ActionBar中調用SetDisplayHomeAsUpEnabledtrue(true)方法。

  1. protected void onCreate(Bundle savedInstanceState) {  
  2.     super.onCreate(savedInstanceState);  
  3.   
  4.     setContentView(R.layout.main);  
  5.     ActionBar actionBar = getActionBar();  
  6.     actionBar.setDisplayHomeAsUpEnabled(true);  
  7.     ...  
  8. }  

       當用戶觸摸這個圖標時,系統會調用帶有android.R.id.home ID的onOptionsItemSelected()方法。

       請記住要在Intent對象中使用FLAG_ACTIVITY_CLEAR_TOP標識,以便你不會這個父Activity存在的情況下,再創建一個新的實例。例如,如果你不使用FLAG_ACTIVITY_CLEAR_TOP標識,那麼向上導航後,再按回退按鈕,實際上會把用戶帶到應用程序的下級頁面,這是很奇怪的。

注意:如果有很多用戶能夠到達應用程序中當前Activity的路徑,那麼,向上圖標應該沿着當前Activity的實際啓動路徑逐步的向會導航。

添加操作視窗

       操作視窗是作爲操作項目按鈕的替代品顯示在操作欄中的一個可視構件。例如,如果你有一個用於搜索的可選菜單項,你可以用SearchView類來替代操作欄上的搜索按鈕,如圖7所示:

             

       圖7. 摺疊(上)和展開(下)的搜索視窗的操作欄

       要個菜單資源中的一個項目聲明一個操作視窗,你既可以使用android:actionLayout屬性也android:actionViewClass屬性來分別指定一個佈局資源或要使用的可視構件類。例如:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <menu xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <item android:id="@+id/menu_search"  
  4.           android:title="@string/menu_search"  
  5.           android:icon="@drawable/ic_menu_search"  
  6.           android:showAsAction="ifRoom|collapseActionView"  
  7.           android:actionViewClass="android.widget.SearchView" />  
  8. </menu>  

        android:showAsAction屬性也可包含“collapseActionView”屬性值,這個值是可選的,並且聲明瞭這個操作視窗應該被摺疊到一個按鈕中,當用戶選擇這個按鈕時,這個操作視窗展開。否則,這個操作視窗在默認的情況下是可見的,並且即便在用於不適用的時候,也要佔據操作欄的有效空間。

       如果需要給操作視窗添加一些事件,那麼就需要在onCreateOptionsMenu()回調執行期間做這件事。你能夠通過調用帶有菜單項ID的findItem()方法來獲取菜單項,然後再調用getActionView()方操作視窗中的元素。例如,使用以下方法獲取上例中的搜索視窗構件。

  1. @Override  
  2. public boolean onCreateOptionsMenu(Menu menu) {  
  3.     getMenuInflater().inflate(R.menu.options, menu);  
  4.     SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();  
  5.     // Configure the search info and add any event listeners  
  6.     ...  
  7.     return super.onCreateOptionsMenu(menu);  
  8. }  

處理可摺疊的操作視窗

        操作視窗讓你在不改變Activity或Fragment的情況下,就可以給用戶提供快捷的訪問和豐富的操作。但是,默認情況下讓操作視窗可見可能不太合適。要保證操作欄的空間(尤其是在小屏幕設備上運行時),你能夠把操作視窗摺疊進一個操作項按鈕中。當用戶選擇這個按鈕時,操作視窗就在操作欄中顯示。被摺疊的時候,如果你定義了android:showAsAction=”ifRoom”屬性,那麼系統可能會把這個項目放到溢出菜單中,但是當用戶選項了這個菜單項,它依然會顯示在操作欄中。通過給android:showAsAction屬性添加“collapseActionView”屬性值,你能夠讓操作視窗可以摺疊起來。

        因爲在用戶選擇這個項目時,系統會展開這個操作視窗,所以你不必要在onOptionsItemSelected()回調方法中響應這個菜單項。在用戶選擇這個菜單項時,系統會依然調用onOptionsItemSelected()方法,但是除非你在方法中返回了true(指示你已經替代系統處理了這個事件),否則系統會始終展開這個操作視窗。

       當用戶選擇了操作欄中的“向上”圖標或按下了回退按鈕時,系統也會把操作視窗摺疊起來。

如果需要,你能夠在代碼中通過在expandActionView()和collapseActionView()方法來展開或摺疊操作視窗。

       注意:儘管把操作視窗摺疊起來是可選的,但是,如果包含了SearchView對象,我們推薦你始終把這個視窗摺疊起來,只有在需要的時候,由用戶選擇後才把它給展開。在提供了專用的“搜索”按鈕的設備上也要小心了,如果用戶按下了“搜索”按鈕,那麼也應該把這個搜索視窗給展開,簡單的重寫Activity的onKeyUp()回調方法,監聽KEYCODE_SEARCH類型的按鍵事件,然後調用expandActionView()方法,就可以把操作視窗給展開。

如果你需要根據操作視窗的可見性來更新你的Activity,那麼你可以定義一個OnActionExpandListener事件,並且用setOnActionExpandListener()方法來註冊這個事件,然後就能夠在操作視窗展開和摺疊時接受這個回調方法了,如:

  1. @Override  
  2. public boolean onCreateOptionsMenu(Menu menu) {  
  3.     getMenuInflater().inflate(R.menu.options, menu);  
  4.     MenuItem menuItem = menu.findItem(R.id.actionItem);  
  5.     ...  
  6.   
  7.     menuItem.setOnActionExpandListener(new OnActionExpandListener() {  
  8.         @Override  
  9.         public boolean onMenuItemActionCollapse(MenuItem item) {  
  10.             // Do something when collapsed  
  11.             return true;  // Return true to collapse action view  
  12.         }  
  13.   
  14.         @Override  
  15.         public boolean onMenuItemActionExpand(MenuItem item) {  
  16.             // Do something when expanded  
  17.             return true;  // Return true to expand action view  
  18.         }  
  19.     });  
  20. }  

添加一個操作提供器

       與操作視窗類似,操作提供器(由ActionProvider類定義的)用一個定製的佈局代替一個操作項目,它還需要對所有這些項目行爲的控制。當你在操作欄中給一個菜單項聲明一個操作項目時,它不僅要一個定製的佈局來控制這個菜單項的外觀,而且當它在顯示在溢出菜單中時,還要處理它的默認事件。無論是在操作欄中還是在溢出菜單中,它都能夠提供一個子菜單。

       例如,ActionProvider的擴展類ShareActionProvider,它通過在操作欄中顯示一個有效的共享目標列表來方便共享操作。與使用傳統的調用ACTION_SEND類型Intent對象的操作項不同,你能夠聲明一個ShareActionProvider對象來處理一個操作項。這種操作提供器會保留一個帶有處理ACTION_SEND類型Intent對象的應用程序的下拉列表,即使這個菜單項顯示在溢出菜單中。因此,當你使用像這樣的操作提供器時,你不必處理有關這個菜單項的用戶事件。

要給一個操作項聲明一個操作提供器,就要在菜單資源中對應的<item>元素中定義android:actionProviderClass屬性,提供器要使用完整的類名。例如:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <menu xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <item android:id="@+id/menu_share"  
  4.           android:title="@string/share"  
  5.           android:showAsAction="ifRoom"  
  6.           android:actionProviderClass="android.widget.ShareActionProvider" />  
  7.     ...  
  8. </menu>  

       在這個例子中,用ShareActionProvider類作爲操作提供器,在這裏,操作提供器需要菜單項的控制,並處理它們在操作欄中的外觀和行爲以及在溢出菜單中的行爲。你必須依然給這個菜單項提供一個用於溢出菜單的文本標題。

儘管操作提供器提供了它在溢出菜單中顯示時所能執行的默認操作,但是Activity(或Fragment)也能夠通過處理來自onOptionsItemSelected()回調方法的點擊事件來重寫這個默認操作。如果你不在這個回調方法中處理點擊事件,那麼操作提供器會接收onPerformDefaultAction()回調來處理事件。但是,如果操作提供器提供了一個子菜單,那麼Activity將不會接收onOptionsItemSelected()回調,因爲子菜單的顯示替代了選擇時調用的默認菜單行爲。

使用ShareActionProvider類

       如果你想要在操作欄中提供一個“共享”操作,以充分利用安裝在設備上的其他應用程序(如,把一張圖片共享給消息或社交應用程序使用),那麼使用ShareActionProvider類是一個有效的方法,而不是添加一個調用ACTION_SEND類型Intent對象的操作項。當你給一個操作項使用ShareActionProvider類時,它會呈現一個帶有能夠處理ACTION_SEND類型Intent對象的應用程序的下拉列表(如圖8所示)。
                

        圖8. Gallery 應用截屏,用ShareActionProvider對象展開顯示共享目標。

        創建子菜單的所有邏輯,包括共享目標的封裝、點擊事件的處理(包在溢出菜單中的項目顯示)等,都在ShareActionProvider類中實現了---你需要編寫的唯一的代碼是給對應的菜單項聲明操作提供器,並指定共享的Intent對象。

        默認情況,ShareActionProvider對象會基於用戶的使用頻率來保留共享目標的排列順序。使用頻率高的目標應用程序會顯示在下來列表的上面,並且最常用的目標會作爲默認共享目標直接顯示在操作欄。默認情況下,排序信息被保存在由DEFAULT_SHARE_HISTORY_FILE_NAME指定名稱的私有文件中。如果你只使用一種操作類型ShareActionProvider類或它的一個子類,那麼你應該繼續使用這個默認的歷史文件,而不需要做任何事情。但是,如果你使用了不同類型的多個操作的ShareActionProvider類或它的一個子類,那麼爲了保持它們自己的歷史,每種ShareActionProvider類都應該指定它們自己的歷史文件。給每種ShareActionProvider類指定不同的歷史文件,就要調用setShareHistoryFileName()方法,並且提供一個XML文件的名字(如,custom_share_history.xml)

        注意:儘管ShareActionProvider類是基於使用頻率來排列共享目標的,但是這種行爲是可擴展的,並且ShareActionProvider類的擴展能夠基於歷史文件執行不同的行爲和排序。

        要添加ShareActionProvider對象,只需簡單的給android.actionProviderClass屬性設定android.widget.ShareActionProvider屬性值就可以了。唯一要做的事情是定義你要用於共享的Intent對象,你必須先調用getActionProvider()方法來獲取跟菜單項匹配的ShareActionProvider對象,然後調用setShareIntent()方法。

      如果對於共享的Intent對象的格式依賴與被選擇的菜單項,或其他的在Activity生存週期內改變的變量,那麼你應該把ShareActionProvider對象保存在一個成員屬性裏,並在需要的時候調用setShareIntent()方法來更新它。如:

  1. private ShareActionProvider mShareActionProvider;  
  2. ...  
  3.   
  4. @Override  
  5. public boolean onCreateOptionsMenu(Menu menu) {  
  6.     mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share).getActionProvider();  
  7.   
  8.     // If you use more than one ShareActionProvider, each for a different action,  
  9.     // use the following line to specify a unique history file for each one.  
  10.     // mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");  
  11.   
  12.     // Set the default share intent  
  13.     mShareActionProvider.setShareIntent(getDefaultShareIntent());  
  14.   
  15.     return true;  
  16. }  
  17. // When you need to update the share intent somewhere else in the app, call  
  18. // mShareActionProvider.setShareIntent()  
       上例中ShareActionProvider對象處理所有的跟這個菜單項有關的用戶交互,並且不需要處理來自onOptionsItemSelected()回調方法的點擊事件。

創建一個自定義的操作提供器

       當你想要創建一個有動態行爲和在懸浮菜單中有默認圖標的操作視窗時,繼承ActionProvider類來定義這些行爲是一個比好的的方案。創建自己的操作提供器,提供一個有組織的可重用的組件,而不是在Fragment或Activity的代碼中處理各種操作項的變換和行爲。

       要創建自己的操作提供器,只需簡單的繼承ActionProvider類,並且實現合適的回調方法。你應該實現以下重要的回調方法:

ActionProvider()

        這個構造器把應用程序的Context對象傳遞個操作提供器,你應該把它保存在一個成員變量中,以便其他的回調方法使用。

OnCreateActionView()

        這是你給菜單項定義操作視窗的地方。使用從構造器中接收的Context對象,獲取一個LayoutInflater對象的實例,並且用XML資源來填充操作視窗,然後註冊事件監聽器。如:

  1. public View onCreateActionView() {  
  2.     // Inflate the action view to be shown on the action bar.  
  3.     LayoutInflater layoutInflater = LayoutInflater.from(mContext);  
  4.     View view = layoutInflater.inflate(R.layout.action_provider, null);  
  5.     ImageButton button = (ImageButton) view.findViewById(R.id.button);  
  6.     button.setOnClickListener(new View.OnClickListener() {  
  7.         @Override  
  8.         public void onClick(View v) {  
  9.             // Do something...  
  10.         }  
  11.     });  
  12.     return view;  
  13. }  

onPerformDefaultAction()
         
在選中懸浮菜單中的菜單時,系統會調用這個方法,並且操作提供器應該這對這個選中的菜單項執行默認的操作。

       但是,如果你的操作提供器提供了一個子菜單,即使是懸浮菜單中一個菜單項的子菜單,那麼也要通過onPrepareSubMenu()回調方法來顯示子菜單。這樣onPerformDefaultAction()在子菜單顯示時就不會被調用。

       注意:實現了onOptionsItemSelected()回調方法的Activity或Frament對象能夠通過處理item-selected事件(並且返回true)來覆蓋操作提供器的默認行爲,這種情況下,系統不會調用onPerformDefaultAction()回調方法。

添加導航選項標籤

       當你想要在一個Activity中提供導航選擇標籤時,使用操作欄的選項標籤是一個非常好的選擇(而不是使用TabWidget類),因爲系統會調整操作欄選項標籤來適應不同尺寸的屏幕的需要---在屏幕足夠寬的時候,導航選項標籤會被放到主操作欄中;當屏幕太窄的時候,選項標籤會被放到一個分離的橫條中,如圖9和圖10所示。
      
          圖9. Honeycomb Gallery應用程序中的操作欄選項標籤的截圖
      
          圖10. 在窄屏設備上被堆放在操作欄中的選項標籤的截屏 

       要使用選項標籤在Fragmengt之間切換,你必須在每次選擇一個選項標籤時執行一個Fragment事務。如果你不熟悉如何使用FragmentTransaction對象來改變Fragment,請閱讀Fragment開發指南。

       首先,你的佈局必須包含一個用於放置跟每個Fragment對象關聯的選項標籤的ViewGroup對象。並且要確保這個ViewGroup對象有一個資源ID,以便你能夠在選項標籤的切換代碼中能夠引用它。另外,如果選項標籤的內容填充在Activity的佈局中(不包括操作欄),那麼Activity不需要任何佈局(你甚至不需要調用setContentView()方法)。相反,你能夠把每個Fragment對象放到默認的根ViewGroup對象中,你能夠用android.R.id.content ID來引用這個ViewGroup對象(在Fragment執行事務期間,你能夠在下面的示例代碼中看到如何使用這個ID的。

        決定了Fragment對象在佈局中的顯示位置後,添加選項標籤的基本過程如下:

          1.  實現ActionBar.TabListener接口。這個接口中回調方法會響應選項標籤上的用戶事件,以便你能夠切換Fragment對象;

           2.  對於每個要添加的選項標籤,都要實例化一個ActionBar.Tab對象,並且調用setTabListener()方法設置ActionBar.Tab對象的事件監聽器。還可以用setText()或setIcon()方法來設置選項標籤的標題或圖標。

         3.  通過調用addTab()方法,把每個選項標籤添加到操作欄。

        在查看ActionBar.TabListener接口時,注意到回調方法只提供了被選擇的ActionBar.Tab對象和執行Fragment對象事務的FragmentTransaction對象---沒有說明任何有關Fragment切換的事。因此。你必須定義自己的每個ActionBar.Tab之間的關聯,以及ActionBar.Tab所代表的適合的Fragment對象(爲了執行合適的Fragment事務)。依賴你的設計,會有幾種不同的方法來定義這種關聯。在下面的例子中,ActionBar.TabListener接口的實現提供了一個構造器,這樣每個新的選項標籤都會使用它自己的監聽器實例。每個監聽器實例都定義了幾個在對應Fragment對象上執行事務時必須的幾個成員變量。

       例如,以下示例是ActionBar.TabListener接口的一種實現,在這個實現中,每個選項標籤都使用了它自己的監聽器實例:

  1. public static class TabListener<T extends Fragment> implements ActionBar.TabListener {  
  2.     private Fragment mFragment;  
  3.     private final Activity mActivity;  
  4.     private final String mTag;  
  5.     private final Class<T> mClass;  
  6.   
  7.     /** Constructor used each time a new tab is created. 
  8.       * @param activity  The host Activity, used to instantiate the fragment 
  9.       * @param tag  The identifier tag for the fragment 
  10.       * @param clz  The fragment's Class, used to instantiate the fragment 
  11.       */  
  12.     public TabListener(Activity activity, String tag, Class<T> clz) {  
  13.         mActivity = activity;  
  14.         mTag = tag;  
  15.         mClass = clz;  
  16.     }  
  17.   
  18.     /* The following are each of the ActionBar.TabListener callbacks */  
  19.   
  20.     public void onTabSelected(Tab tab, FragmentTransaction ft) {  
  21.         // Check if the fragment is already initialized  
  22.         if (mFragment == null) {  
  23.             // If not, instantiate and add it to the activity  
  24.             mFragment = Fragment.instantiate(mActivity, mClass.getName());  
  25.             ft.add(android.R.id.content, mFragment, mTag);  
  26.         } else {  
  27.             // If it exists, simply attach it in order to show it  
  28.             ft.attach(mFragment);  
  29.         }  
  30.     }  
  31.   
  32.     public void onTabUnselected(Tab tab, FragmentTransaction ft) {  
  33.         if (mFragment != null) {  
  34.             // Detach the fragment, because another one is being attached  
  35.             ft.detach(mFragment);  
  36.         }  
  37.     }  
  38.   
  39.     public void onTabReselected(Tab tab, FragmentTransaction ft) {  
  40.         // User selected the already selected tab. Usually do nothing.  
  41.     }  
  42. }  

       警告:針對每個回調中的Fragment事務,你都不必調用commit()方法---系統會調用這個方法,並且如果你自己調用了這個方法,有可能會拋出一個異常。你也不能把這些Fragment事務添加到回退堆棧中。

在這個例子中,當對應的選項標籤被選擇時,監聽器只是簡單的把一個Fragment對象附加(attach()方法)到Activity佈局上---或者,如果沒有實例化,就會創建這個Fragment對象,並且把它添加(add()方法)到佈局中(android.R.id.content ViewGroup的一個子類),當這個選項標籤解除選擇時,對應的Fragment對象也會被解除與佈局的依附關係。

       ActionBar.TabListener的實現做了大量的工作,剩下的事情就是創建每個ActionBar.Tab對象並把它添加到ActionBar對象中,另外,你必須調用setNavigationMode(NAVIGATION_MODE_TABS)方法來讓選項標籤可見。如果選項標籤的標題實際指示了當前的View對象,你也可以通過調用setDisplayShowTitleEnabled(false)方法來禁用Activity的標題。

       例如,下面的代碼使用上面定義的監聽器在操作欄中添加了兩個選項標籤。

  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     // Notice that setContentView() is not used, because we use the root  
  5.     // android.R.id.content as the container for each fragment  
  6.   
  7.     // setup action bar for tabs  
  8.     ActionBar actionBar = getActionBar();  
  9.     actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);  
  10.     actionBar.setDisplayShowTitleEnabled(false);  
  11.   
  12.     Tab tab = actionBar.newTab()  
  13.             .setText(R.string.artist)  
  14.             .setTabListener(new TabListener<ArtistFragment>(  
  15.                     this"artist", ArtistFragment.class));  
  16.     actionBar.addTab(tab);  
  17.   
  18.     tab = actionBar.newTab()  
  19.         .setText(R.string.album)  
  20.         .setTabListener(new TabListener<AlbumFragment>(  
  21.                 this"album", AlbumFragment.class));  
  22.     actionBar.addTab(tab);  
  23. }  

        注意:以上有關ActionBar.TabListener的實現,只是幾種可能的技術之一。在API Demos應用中你能夠看到更多的這種樣式。

        如果Activity終止了,那麼你應該保存當前選擇的選項標籤的狀態,以便當用戶再次返回時,你能夠打開合適的選項標籤。在保存狀態的時刻,你能夠用getSelectedNavigationIndex()方法查詢當前的被選擇的選項標籤。這個方法返回被選擇的選項標籤的索引位置。

       警告:保存每個Fragment所必須的狀態是至關重要的,因爲當用戶用選項標籤在Fragment對象間切換時,它會查看Fragment在離開時樣子。

       注意:在某些情況下,Android系統會把操作欄選項標籤作爲一個下拉列表來顯示,以便確保操作欄的最優化顯示。

添加下拉式導航

       作爲Activity內部的另一種導航(或過濾)模式,操作欄提供了內置的下拉列表。下拉列表能夠提供Activity中內容的不同排序模式。

       啓用下拉式導航的基本過程如下:

         1.  創建一個給下拉提供可選項目的列表,以及描畫列表項目時所使用的佈局;

         2.  實現ActionBar.OnNavigationListener回調,在這個回調中定義當用戶選擇列表中一個項目時所發生的行爲;

         3.  用setNavigationMode()方法該操作欄啓用導航模式,如:

  1. ActionBar actionBar = getActionBar();  
  2. actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);  

        4.  用setListNavigationCallbacks()方法給下拉列表設置回調方法,如:

  1. actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);  

這個方法需要SpinnerAdapter和ActionBar.OnNavigationListener對象。下面是 SpinnerAdapter and ActionBar.OnNavigationListener 的例子。

 Example SpinnerAdapter and OnNavigationListener

設置操作欄的樣式

       如果你對應用程序中的可視構件進行了定製化的設計,那麼你可能也會要對操作欄做一些重新設計,以便跟應用程序的設計匹配。要這樣做的話,需要使用Android的樣式與主題框架中的一些特殊的樣式屬性來重新設置操作欄的樣式。

       注意:改變外觀的背景圖片依賴與當前按鈕的狀態(選擇、按下、解除選擇),因此你使用的可描畫的資源必須是一個可描畫的狀態列表。

       警告:對於你提供的所有可描畫的背景,要確保使用NinePatch類型可描畫資源,以便允許圖片的拉伸。NinePatch類型的圖片應該比40像素高30像素寬的圖片要小。


普通的外觀

android:windowActionBarOverlay

      這個屬性聲明瞭操作欄是否應該覆蓋Activity佈局,而不是相對Activity的佈局位置的偏移。這個屬性的默認值是false。

      通常,在屏幕上,操作欄需要它自己的空間,並且把剩下的空間用來填充Activity的佈局。當操作欄四覆蓋模式時,Activity會使用所有的有效空間,系統會在Activity的上面描畫操作欄。如果你想要在操作欄隱藏和顯示時,佈局中的內容保持固定的尺寸好位置,那麼這種覆蓋模式是有用的。你也可能只是爲了顯示效果來使用它,因爲你可以給操作欄設置半透明的背景,以便用戶依然能夠看到操作欄背後的Activity佈局。

       注意:默認情況下,Holo主題會用半透明背景來描畫操作欄。但是,你能夠用自己的樣式來修改它,並且默認的情況下,DeviceDefault主題在不同的設備上可能使用不透明的背景。

       覆蓋模式被啓用時,Activity佈局不會感知到操作欄覆蓋在它的上面,因此,在操作欄覆蓋的區域,最好不要放置一些重要的信息或UI組件。如果適合,你能夠引用平臺的actionBarSize值來決定操作欄的高度,例如,在XML佈局文件中引用這個值。

  1. <SomeView  
  2.     ...  
  3.     android:layout_marginTop="?android:attr/actionBarSize" />  

       你還能夠用getHeight()方法在運行時獲取操作欄的高度。如果在Activity生存週期的早期調用這個方法,那麼在調用時所反映的操作欄的高度可能不包括被堆放的操作欄(因爲導航選項標籤)。要看如何在運行時判斷操作欄總的高度(包括被堆放的操作欄),請看Honeycomb Gallery示例應用中的TitlesFragment類。

操作項元素

android:actionButtonStyle
給操作項按鈕定義樣式資源。
android:actionBarItemBackground
 給每個操作項的背景定義可描畫資源(被添加在API 級別 14中)。
android:itemBackground
 給每個懸浮菜單項的背景定義可描畫資源。
android:actionBarDivider
給操作項之間的分隔線定義可描畫資源(被添加在API 級別 14中)
android:actionMenuTextColor
給顯示在操作項中文本定義顏色。
android:actionMenuTextAppearance
 給顯示在操作項中文本定義樣式資源。
android:actionBarWidgetTheme
給作爲操作視窗被填充到操作欄中的可視構件定義主題資源(被添加在API級別14中)。導航選項標籤

android:actionBarTabStyle
 給操作欄中的選項標籤定義樣式資源。
android:actionBarTabBarStyle
給顯示在導航選項標籤下方的細條定義樣式資源。
android:actionBarTabTextStyle
給導航選項標籤中的文本定義樣式資源。下拉列表

android:actionDropDownStyle
 給下拉導航列表定義樣式(如背景和文本樣式)。如,下例XML文件中給操作欄定義了一些定製的樣式:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <!-- the theme applied to the application or activity -->  
  4.     <style name="CustomActivityTheme" parent="@android:style/Theme.Holo">  
  5.         <item name="android:actionBarTabTextStyle">@style/CustomTabTextStyle</item>  
  6.         <item name="android:actionBarDivider">@drawable/ab_divider</item>  
  7.         <item name="android:actionBarItemBackground">@drawable/ab_item_background</item>  
  8.     </style>  
  9.   
  10.     <!-- style for the action bar tab text -->  
  11.     <style name="CustomTabTextStyle" parent="@android:style/TextAppearance.Holo">  
  12.         <item name="android:textColor">#2456c2</item>  
  13.     </style>  
  14. </resources>  

       注意:一定要在<style>標籤中聲明一個父主題,這樣定製的主題可以繼承所有沒有明確聲明的樣式。在修改操作欄樣式時,使用父主題是至關重要的,它會讓你能夠簡單的覆寫你想要改變的操作欄樣式,而不影響你不想修改的樣式(如文本的外觀或操作項的邊緣)。

      你能夠在清單文件中把定製的主題應用到整個應用程序或一個單獨的Activity對象,如:

  1. <application android:theme="@style/CustomActivityTheme" ... />  

高級樣式

      如果需要比上述屬性更高級的樣式,可以在Activity的主題中包含android:actionBarStyle和android:actionBarSplitStyle屬性。這兩個屬性的每一個都指定了另一種能夠給操作欄定義各種屬性的樣式,包括帶有android:background、android:backgroundSplit、android:backgroundStacked屬性的不同背景。如果要覆蓋這些操作欄樣式,就要確保定義一個像Widget.Holo.ActionBar這樣的父操作欄樣式。

例如,如果要改變操作欄背景,你可以使用下列樣式:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <!-- the theme applied to the application or activity -->  
  4.     <style name="CustomActivityTheme" parent="@android:style/Theme.Holo">  
  5.         <item name="android:actionBarStyle">@style/MyActionBar</item>  
  6.         <!-- other activity and action bar styles here -->  
  7.     </style>  
  8.   
  9.     <!-- style for the action bar backgrounds -->  
  10.     <style name="MyActionBar" parent="@android:style/Widget.Holo.ActionBar">  
  11.         <item name="android:background">@drawable/ab_background</item>  
  12.         <item name="android:backgroundStacked">@drawable/ab_background</item>  
  13.         <item name="android:backgroundSplit">@drawable/ab_split_background</item>  
  14.     </style>  
  15. </resources>  


原文地址:http://developer.android.com/guide/topics/ui/actionbar.html#ActionView



/**
* @author 張興業
*  我的新浪微博:@張興業TBOW
發佈了7 篇原創文章 · 獲贊 5 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章