Android: Action Bab編程r指南

 

1. Action Bar設計指南

Action Bar位於應用程序頂部的一塊專有空間,它存在於整個app的生命周期裡。它提供如下關鍵功能:

  • 以可預見的方式,使很多功能的實現和顯示更加優秀和易於理解,比如New和Search。
  • 支持應用程序內的導航一致性,支持試圖的切換。
  • 提供一個overflow,使不經常使用的功能隱藏起來,避免雜亂。
  • 為你的應用提供一個專門可以驗證身份的空間。

    如果你是編寫Android應用程序的新手,請註意Action Bar是需要實現的最重要的設計元素之一。在遵循這裡描述的準則,使你的應用程序接口與核心的Android應用程序保持一致。

    1.1通用組成

    Action Bar被分成四個不同的功能區域,適用於大多數應用程序。

    a1

    1. App 圖標

    App圖標確定你的應用程序的身份。如果你願意,可以更換不同的logo或品牌圖標。如果應用程序目前無法顯示主界面,請確認應用程序圖標的左側顯示回退符(up caret)。用戶可以通過回退符回到上層界面。

    a2

    2.視圖控制

    如果您的應用程序在不同的視圖中顯示的數據,試圖控制功能允許用戶切換視圖。視圖切換控制可以通過下拉菜單或tab控件方法實現。如果您的應用程序不支持不同的視圖切換,你也可以使用這部分空間來顯示非交互式的內容,如應用程序標題或較長的品牌信息。

    3.行為按鈕

    行為按鈕部分顯示您的應用程序的最重要的行為。沒有顯示在Action Bar上的其他行為,會自動隱藏到Action Overflow裡。長按按鈕圖標可以查看行為的名稱。

    4. Action Overflow

    很少使用的行為會被移動到Action Overflow裡。(Action Overflow是個術語,你可以理解為菜單)

    1.2 適配屏幕旋轉和屏幕尺寸

    創建一個應用程序時,需要考慮的一個重要UI問題是,如何在不同的屏幕尺寸調整屏幕旋轉。你可以通過使用split action bar機制來處理這個問題。split action bar允許將action bar裡的內容分派到多個子action bar裡。這些子bar顯示在主action bar的下面,或者顯示在屏幕的底部。

    a3

    1.3 Split Action Bars的佈局考慮

    當需要將行為分發到多個action bars裡,佈局可能需要如下考量:

    1. main action bar2. top bar

    3. bottom bar

    如果用戶需要從給定的界面返回到上一層界面,則main action bar裡需要有一個返回符。

    在top bar裡面使用tabs或者spinners,滿足用戶切換不同的view。

    在底部顯示Action Bar的overflow和其他的action buttons。

    a4

    1.4 行為按鈕(Action Buttons)

    在action bar裡的action buttons部分,顯示瞭你的應用最核心的功能行為。考慮好你的應用的哪些行為經常使用,對他們進行排序。更具實際屏幕的顯示空間,系統會顯示排序靠前的action button,而其餘的action button則隱藏在action overflow裡。當某個行為功能在當前上下文無法使用時,則隱藏它,而不是顯示為禁用。

    a5

    對於行為按鈕的排序,建議使用FIT策略。

    F-frequent (頻率) I-important (重要性) T-typical(類型)
    在這個界面上,用戶使用這個行為按鈕,十次中有七次嗎? 你希望每個用戶都能使用這個行為按鈕嗎?它有賣點或者很酷的特性嗎? 在類似的應用裡,同樣的行為按鈕會顯示在action bar上嗎?
    在一行裡,用戶多次使用這個行為按鈕嗎? 這個行為按鈕的不經常使用,會導致一些重要功能的缺失? 考慮上下文,用戶會驚訝這個行為按鈕隱藏到action flow裡嗎
    每次多一步操作,會讓人感覺到厭煩嗎?    

    如果某個行為按鈕滿足F、I、T其中一個策略時,則將其顯示在action bar上;否則將其隱藏在action overflow裡。

    1.5 Action Overflow

    Action Bar裡的action overflow 隱藏瞭應用不常用的行為功能。Action overflow圖標僅在沒有實體菜單鍵的設備上顯示。有實體菜單鍵的設備,通過用戶按下菜單鍵來顯示action overflow。

    主Action Bar裡可以顯示幾個Action button?有如下規則:

    Action button的寬度不能超過主action bar整個寬度的50%。而在底部顯示的action bar裡的action button的寬度可以是整個action bar的寬度。

    以dp為單位的屏幕寬度決定瞭action bar上可以顯示的action button的數量:

    < 360 dp = 2個action button圖標

    360-499 dp = 3個action button圖標

    500-599 dp = 4個action button圖標

    >= 600 dp = 5個action button圖標

    a6

    1.6 共享數據

    當你的應用想分享一些數據,例如圖片或短片,你可要在action bar上使用share action provider(一種action button)。share action provider提供一個選擇共享數據的選擇按鈕,和一個包含其他共享項的微調按鈕。

    a7

    1.7 上下文Action Bars

    上下文Action Bars(簡稱CAB (contextual action bar))是在某段任務特定時間段下的一種臨時action bar,它遮掩瞭應用原有的action bar。CAB經常用於對某段文本段落進行選擇操作的任務場合。

    a8

    當長按某段數據文本時,會觸發選擇模式(selection mode),從而顯示出瞭CAB。

    用戶可以:

    通過CAB選擇額外的數據文本;

    對於選擇的數據文本,通過CAB觸發一個行為,對選中的數據文本進行操作。

    使用back button或者CAB的checkmark button,取消CAB顯示。

    2. Action Bar API 指南

    Action Bar是一個窗口功能,可識別用戶的位置,向用戶提供行為操作和導航模式。使用Action Bar為應用提供熟悉和統一的界面,可以使應用優雅地適應不同的屏幕配置。

    Action Bar的API的第一次加入於Android 3.0(API級別11),但也可以在Android 2.1版本(API7級)及以上使用實現Action Bar的兼容性支持庫。

    註意:確定你從適合的軟件包裡導入ActionBar 類以及相關的APIs。

    如果支持的API級別<11:

    import android.supprt.v7.app.ActionBar

    如果支持的API級別>=11:

    import android.app.ActionBar

    本文檔隻關註API級別>=11以上的ActionBar的使用指南。

    2.1 添加Action Bar

    API級別>=11以上,使用Theme.Holo主題的activity均包含瞭action bar的功能。如果你不想在一個activity裡使用action bar,將該activity的主題設置為Theme.Holo.NoActionBar。

    2.2 移除Action Bar

    在運行時,可以通過調用hide()來隱藏action bar。例如:

    ActionBar actionBar = getSupportActionBar();
      actionBar.hide();

    當action bar隱藏後,系統調整你的應用佈局,填充action bar占據的屏幕空間。通過調研show()來恢復action bar的顯示。

    要註意的是隱藏和刪除Action Bar會導致你的activity重新佈局。如果你的activity經常隱藏和顯示action bar,您可能要啟用overlay mode。Overlay mode在你的activity佈局上面繪制action bar,掩蓋頂部位置。這樣一來,當action bar顯示或隱藏時你的應用佈局仍然保持固定。要啟用overlay mode,為你的activity創建一個自定義主題,設置windowActionBarOverlay為true。

    2.3 使用logo替代icon

    默認情況下,系統在action bar上使用你的應用圖標。應用圖標的icon屬性一般在或元素裡定義。然而,如果你定義瞭logo屬性,你可以使用logo圖片來替代icon。

    一個logo圖片一般寬於一個icon圖標,但是盡可能不要包含多餘的文本。你應當使用用戶認可的,可以代表你的產品品牌的logo。一個很好的例子就是YouTube logo。

    a9

    2.4 添加Action項(Action Items)

    當你的activity啟動後,系統會通過調用你的activity裡的onCreatOptionsMenu()函數來構造行為項。menu resource 定義瞭所有action項,調用這個方法進行構造。

    下面的例子,menu resource定義瞭一些menu項(這裡menu項和Action項表達的含義是一樣的):

    a10

    在你的activity裡的onCreateOptionMenu()方法裡,將menu resource定義的menu項添加到action bar裡:

    @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu items for use in the action bar
          MenuInflater inflater = getMenuInflater();
          inflater.inflate(R.menu.main_activity_actions, menu);
          return super.onCreateOptionsMenu(menu);
      }

    當請求某個menu項作為一個action button顯示到action bar上(默認的menu項都是隱藏都action bar裡的overflow裡),應在該項的標簽裡包含showAsAction=ifRoom。例如:

    
    
    		android:showAsAction=ifRoom /> … 
    

    如果action bar沒有足夠的空間來顯示action button,則action button仍然會隱藏到action overflow裡。

    如果一個menu項既有tible屬性,又有icon屬性。則這個menu項成為action button後,僅在action bar上顯示icon。如果你想顯示文本tible,你需要在showAsAction屬性裡添加withText。例如:

    
    

    你應當在每個menu項裡定義title屬性,原因如下:

    • 隱藏到action overflow裡的menu 項,僅顯示menu項的文本title。
    • 針對視力受損的用戶,應用的實現需要考慮讀取menu項的title屬性。
    • 顯示在action bar上的menu項,通過長按該項圖標,會顯示出該item項的title。

      你也可以在showAsAction屬性裡添加always,使該menu項作為action button,一直顯示在action bar上。然而,這種作法不值得推薦,由於設備的屏幕寬度不同。這種做法會導致佈局問題。最好是使用“ifRoom,系統會判斷如果有足夠的action bar空間,則該menu項作為action button顯示;否則該menu想會自動隱藏到action overflow裡。

      如果一個menu項裡包含瞭action view,並且該menu項是非常重要的功能,必須顯示,你可以考慮使用alwalys值。

      2.5 處理對action項的點擊事件

      當用戶按下一個action button時,系統會調用你的activity裡的onOptionItemSelected()方法。根據這個方法的MenuItem參數,你可以通過調用getItemId()來獲取是哪個action button。getItemId()方法會返回一個menu項的ID,你根據這個ID進行action項的事件響應操作。例如:

      @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle presses on the action bar items
            switch (item.getItemId()) {
                case R.id.action_search:
                    openSearch();
                    return true;
                case R.id.action_compose:
                    composeMessage();
                    return true;
                default:
                    return super.onOptionsItemSelected(item);
            }
        }

      2.6 使用 split action bar

      split action bar在狹窄屏幕(例如手機縱向屏幕)的下方提供瞭一條獨立的bar, activity運行的時候在該bar上顯示所有的action項。使用這種方式可以使狹窄屏幕有更多地空間來顯示action項。icon title和導航的顯示則留在屏幕的頂部。

      a11

      要使能split action bar功能,需要:

      1. 在或者元素裡添加uiOptions=splitActionBarWhenNarrow。這種做法僅在API級別>=14以上有效;

      2. 對於API較老的版本,則在每個元素裡添加添加子元素,在裡添加android.support.UI_OPTIONS。如下示例:

      
            
                
            
        

      使用split action bar時,如果你禁用顯示action bar裡的圖標和文本title,則action bar的頂部空間會被navigation tabs占據。你可以使用setDisplayShowHomeEnabled(false)和setDisplayShowTitleEnabled(false)來禁用action bar的icon和title顯示。

      2.7 使用App Icon向上導航

      使能app icon作為用戶向上導航的按鍵。例如,screen A顯示瞭一些列項,選擇其中一項跳轉到screen B,screen B則應當包含up button,通過該按鍵鍵重新返回到screen A。

       

      說明:up導航鍵與back導航鍵有所區別。back button是根據屏幕的歷史顯示順序,進行回退導航,它的實現與時間順序有關。而up button是根據應用的界面層次結構進行導航。

      通過調研setDislplayHomeAsUpEnabled()方法來使能up導航鍵。如下示例:

      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_details);
      
            ActionBar actionBar = getSupportActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
            ...
        }

      為瞭讓up button工作,你還需要:

      • 在manifest file裡定義parent activity。從Android 4.1 (API level 16)後,你僅需在元素裡的parentActivityName屬性裡聲明parent activity即可。舊的版本,需要定義元素,並添加android.support.PARENT_ACTIVITY。如下示例:
        
              ...
              
              
                  ...
              
              
              
                  
                  
              
          

        這樣,你的activity裡的up button可以正常工作瞭。

        • 或者在你的activity裡覆寫getSupportParentActivityIntent()和onCreateSupportNavigateUpTaskStack()方法。這種方法適合於當前screen的parent activity不確定的情況。如果用戶有多條路徑進入到當前screen,這意味著當前screen有多個不同的parent activity。up button需要返回用戶使用的那條路徑對應的parent Activity。

          當用戶按下activity上的up button在你自己的app task裡進行導航時,系統會調用getSupportParentActivityIntent(),你需要覆寫這個方法,返回一個intent,指明合適的parent activity。

          當用戶按下activity上的up button,而調用activity的task不屬於自己的activity 自己所屬的app時,系統會調用onCreateSupportNavigateUpTaskStack()。你通過這個方法的TaskStackBuilder參數,構建一個合適的back stack。

          即使你覆寫瞭getSupportParentActivityIntent(),你也可以通過在manifest 文件裡定義default parent activity來避免onCreateSupportNavigateUpTaskStack()的覆寫。onCreateSupportNavigateUpTaskStack()的默認實現會根據mainfest文件裡的parent activity聲明,來合成一個合適的back stack。

          說明:如果你的應用層次是通過一系列Fragment來實現的,up button的導航功能,則需要覆寫onSupportNavigateUP(),進行Fragment之間的處理。通常會調用popBackStack()從back stack裡彈出當前的Fragment。

          2.8 添加action view

          action view是一個可以在action bar上顯示替代對應action bar的部件。action view在不改變activities和fragments的情況下,給action項提供快速的訪問。例如,如果你有一個search action,你可要在action bar裡增加一個action view,作為嵌入的searchView來顯示。如下圖所示:

          a13

          要聲明一個action view,要麼使用actionLayout屬性定義一個layout resource,要麼使用actionViewClass屬性定義一個widget class。如下示例添加一個serachView widget:

          
            
          yourapp:actionViewClass=android.support.v7.widget.SearchView />

          註意showAsAction屬性包含collapseActionView值。這是可選的值,用於聲明這個action view應當折疊到一個button裡。如果你想配置action view(例如增加事件監聽器),你可以在onCreateOptionsMenu()回調函數裡實現。通過調用靜態方法MenuItemCompat.getActionView()方法來,傳遞一個MenuItem參數,你就可以獲得一個action view對象。例如,search widget的action view的對象如下獲取:

          @Override
            public boolean onCreateOptionsMenu(Menu menu) {
                getMenuInflater().inflate(R.menu.main_activity_actions, menu);
                MenuItem searchItem = menu.findItem(R.id.action_search);
                SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
                // Configure the search info and add any event listeners
                ...
                return super.onCreateOptionsMenu(menu);}

          對於API級別>=11的Android版本,要獲取一個item對應的action view項,使用getActionView()方法:

          menu.findItem(R.id.action_search).getActionView()

          2.9 處理collapsible action views

          為瞭保持action bar空間,你可以將你的action view折疊到一個action button裡。折疊後,系統會將這個action項置於action overflow裡。但是,如果用戶選中這個action項,這個action項的action view則顯示在action bar上。你可以在showAsAction屬性裡添加”collapseActionView值,來實現action view的折疊功能。

          由於用戶選擇這個action項時,系統會展開它對應的action view,所以你不需要再onOptionItemSelected()回調函數裡響應對應的item項。系統仍然會調用onOptionItemSelected(),但是如果你在該回調函數裡返回true(表明你已經處理瞭這個item項的事件),則item項對應的action view不會展開。

          當用戶按up button或back button時,系統會折疊你的action view。

          如果你需要在你的action view可見的情況下更新你的activity,你可以定義一個onActionExpandListener,將其傳遞給setOnActionExpandListener()。這個Listener會被action view的展開或折疊時的回調函數調用。例如:

          @Override
            public boolean onCreateOptionsMenu(Menu menu) {
                getMenuInflater().inflate(R.menu.options, menu);
                MenuItem menuItem = menu.findItem(R.id.actionItem);
                ...
          
                // When using the support library, the setOnActionExpandListener() method is
                // static and accepts the MenuItem object as an argument
                MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
                    @Override
                    public boolean onMenuItemActionCollapse(MenuItem item) {
                        // Do something when collapsed
                        return true;  // Return true to collapse action view
                    }
          
                    @Override
                    public boolean onMenuItemActionExpand(MenuItem item) {
                        // Do something when expanded
                        return true;  // Return true to expand action view
                    }
                });
            }

          2.10 添加Action Provider

          與action view類似,action provider以定制的佈局來取代一個action button。然而與action view不同的是,action provider控制所有action的行為,當被用戶按下時可顯示一個submenu。

          要聲明一個action provider,在item標簽裡定義actionProviderClass屬性。通過繼承ActionProveder 類,你可以創建自己的action provider。Android系統提供瞭一些預先實現好的action provider。例如ShareActionProvider,可以在action bar上直接共享數據給一些app。

          a14

          由於每個ActionProvider類定義瞭自己的action behavior,你不需要在onOptionItemSelected()方法裡監聽action。如果萬一你需要在onOptionItemSelected()裡監聽action以同步處理其他action,則返回值必須返回false,這樣action provider仍然可以接收到onPerformDefaultAction()回調函數,處理與它相關的action事件。

          然而,如果action provider提供瞭action的submenu,用戶在選擇submenu裡的項時,你的activity接收不到對onOPtionItemSelected()的調用。

          2.11 使用ShareActionProvider

          要使用ShareActionProvider添加 一個share action,需要在標簽裡定義actionProviderClass屬性。例如:

          
            
          yourapp:actionProviderClass=android.support.v7.widget.ShareActionProvider /> …

          現在action provider控制瞭這個action項的顯示和行為。但是你也必須為這個action項提供title屬性,它在overflow裡顯示的時候需要。

          剩下的事就是你定義一個需要共享的Intent。編輯你的onCreateOptionMenu()方法,傳遞MenuItem參數並調用getActionProvider(),獲取action項對應的action provider。然後調用action provider的setShareIntent()方法,向其傳遞一個ACTION_SEND intent。

          你應當在onCreateOptionMenu()裡調用setShareIntent()來初始化share action,但是由於用戶的上下文也許改變,你必須重新調用setShareIntent()來更新你要發送的intent內容。例如:

          private ShareActionProvider mShareActionProvider;
          
            @Override
            public boolean onCreateOptionsMenu(Menu menu) {
                getMenuInflater().inflate(R.menu.main_activity_actions, menu);
          
                // Set up ShareActionProvider's default share intent
                MenuItem shareItem = menu.findItem(R.id.action_share);
                mShareActionProvider = (ShareActionProvider)
                        MenuItemCompat.getActionProvider(shareItem);
                mShareActionProvider.setShareIntent(getDefaultIntent());
          
                return super.onCreateOptionsMenu(menu);
            }
          
            /** Defines a default (dummy) share intent to initialize the action provider.
              * However, as soon as the actual content to be used in the intent
              * is known or changes, you must update the share intent by again calling
              * mShareActionProvider.setShareIntent()
              */
            private Intent getDefaultIntent() {
                Intent intent = new Intent(Intent.ACTION_SEND);
                intent.setType(image/*);
                return intent;
            }

          現在ShareActionProvider處理action項的所有用戶交互,你不需要再onOtionItemSelected()回調函數函數裡進行任何事件處理。

          默認情況下,ShareActionProvider對於每個共享目標根據用戶的使用頻率保持一個次序。用戶經常使用的共享目標列在下拉菜單的最前面。默認情況下,排序信息存儲在一個命名為DEFAULT_SHARE_HISTORY_FILE_NAME的私有文件裡。如果你隻在一個action項裡使用ShareActionProvider,你可以繼續使用這個默認私有文件。相反的,如果你想在多個action項裡使用ShareActionProvider,你需要為每一個action項維護一個存儲信息排序的文件(通過調用setShareHistoryFileName(),並提供一個xml file名,例如custom_share_history.xml)。

          2.12 創建可定制的action provider

          創建自己可定制的action provider,允許你在獨立的模塊裡復用和管理動態的action 項行為,而不是在activity和fragment的代碼裡。可以參考Android實現的ShareActionProvider。

          為一個不同的action項創建action provider,首先你需要繼承ActionProvider類,實現它規定的回調函數。如下:

          • ActionProvider()

            在這個構造函數裡,傳遞你的app的context,後續的後調函數會使用到它。

            • onCreateActionView(MenuItem)

              這裡定義action項的action view。使用context來初始化你的action view的佈局,實現鉤子事件監聽器。如下示例:

              public View onCreateActionView(MenuItem forItem) {
                    // Inflate the action view to be shown on the action bar.
                    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
                    View view = layoutInflater.inflate(R.layout.action_provider, null);
                    ImageButton button = (ImageButton) view.findViewById(R.id.button);
                    button.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            // Do something...
                        }
                    });
                    return view;
                }
              • onPerformDefaultAction()

                當在action overflow裡選中一項menu項時,系統會調用這個函數,action provider會執行一個默認的行為操作。

                然而,如果你的action provider提供瞭一個submenu,通過onPrepareSubMenu()回調函數,submenu會顯示在actionbar裡。所以,如果存在submenu,onPerformDefaultAction()不會被調用。

                對於ActionProvider的擴展示例,請參考ActionBarSettingsActionProviderActivity。

                2.13 添加導航tabs

                action bar提供的tabs使用戶很容易的訪問和切換你的app的不同的view。tabs在適配屏幕寬度方面做到瞭完美。例如,當屏幕的寬度足夠寬時,tabs顯示在action bar上;當屏幕寬度比較窄時,tabs顯示在一個獨立的bar上(稱為stacked action bar)。在一些案例中,android系統會以下拉菜單的方式顯示你的tab項。

                a15

                a16

                首先,你的佈局裡必須包含一個ViewGroup,在其中一個tab關聯一個Fragment。確保ViewGroup有一個resource ID,在你的代碼裡需要使用它切換tab。作為一種選擇,如果tab的內容填充瞭你的activity佈局,則你的activities不需要自己的佈局(你甚至不需要調用setContentView())。相反的,你可以將每個fragment設置在root view下,引用android.R.id.content ID。

                一旦你確定瞭佈局裡的fragment顯示,添加一個tab的基本流程為:

                1. 實現ActionBar.TabListener接口。這個接口為tab events提供瞭回調函數,例如切換tab事件。

                2.對於每一個要添加的tab,實例化一個ActionBar.Tab,通過調用setTabListener()設置ActionBar.TabListener。使用setText()設置tab的title(使用setIcon(),設置tab的icon,可選)。

                3. 調用addTab()增加一個tab。

                註意ActionBar.TabListener回調函數並沒有定義tab與fragment之間的關聯邏輯,僅僅是確定瞭哪個ActionBar.Tab被選中瞭。你必須自己定義每個ActionBar.Tab和對應的Fragment之間的關聯邏輯。有很多方式定義這種邏輯,依賴於你的設計。

                例如,你想為每個tab實現一個ActionBar.TabListener:

                public static class TabListener implements ActionBar.TabListener {
                      private Fragment mFragment;
                      private final Activity mActivity;
                      private final String mTag;
                      private final Class 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 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.
                      }
                  }

                警告:你一定不能再每個上面的callback裡為fragment事務調用commit()—你自己調用,會拋出異常。你也不能將這些fragment事務添加到back stack。

                在這個例子裡,當某個tab被選中,listener簡單的實現瞭attach一個fragment到activity佈局(attach()),添加一個fragment到佈局(add())作為android.R.id.content view group裡的一個子項。當某個tab未被選中時,listener實現瞭detach()。

                剩下的工作就是創建每個ActionBar.Tab,並將它添加到ActionBar。此外,你必須調用setNavigationMode(NAVIGATION_MODE_TABS)來使tabs可見。例如,下面的代碼使用上述的listener添加瞭兩個tabs:

                @Override
                  protected void onCreate(Bundle savedInstanceState) {
                      super.onCreate(savedInstanceState);
                      // Notice that setContentView() is not used, because we use the root
                      // android.R.id.content as the container for each fragment
                
                      // setup action bar for tabs
                      ActionBar actionBar = getSupportActionBar();
                      actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
                      actionBar.setDisplayShowTitleEnabled(false);
                
                      Tab tab = actionBar.newTab()
                                         .setText(R.string.artist)
                                         .setTabListener(new TabListener(
                                                 this, artist, ArtistFragment.class));
                      actionBar.addTab(tab);
                
                      tab = actionBar.newTab()
                                     .setText(R.string.album)
                                     .setTabListener(new TabListener(
                                             this, album, AlbumFragment.class));
                      actionBar.addTab(tab);
                  }

                當你的activity停止,你需要維護當前選中的tab的狀態,這樣當用戶返回時可以回到合適的tab。在保存狀態時,你可要調用getSelectedNavigationIndex()來獲取選中的tab,它返回選中的tab的index position。

                警告:保存每個fragment的狀態很重要,用戶會隨時切換tab到不同的fragment。有一些狀態時默認保存,但是你需要手動保存一些訂制的view。

                說明:上述的ActionBar.TabListener的實現是一種可行的技術之一。其他的實現方法是使用ViewPager來管理fragment,用戶可以使用swipe gesture來切換tab。這種方式下,你可以簡單的告訴ViewPager你的tab的位置(調用onTabSelected() callback)。

                2.14添加drop-down navigation

                Activity導航的另一種模式,是action bar提供的一種下拉式清單(也稱為spinner)。這種下拉式清單可以給activity裡分類的內容提供不同的顯示模式。

                當你的內容需要切換,但切換頻率並不頻繁,則可以使用下拉式清單這種方式實現。如果內容切換頻率頻繁,則建議使用navigation tabs方式實現。

                a17

                完成一個下拉式導航的基本步驟如下:

                1. 創建一個SpinnerAdapter,提供選擇項清單和清單中每項繪制的佈局。

                2. 實現ActionBar.OnNavigationListener,定義用戶選中清單中某項時的操作行為。

                3. 在你的activity的onCreate()方法裡,通過調用setNavigationMode(NAVIGATION_MODE_LIST)使能action bar的下拉式清單功能。

                4. 通過setListNavigationCallbacks()為下拉式清單設置回調函數。例如:

                actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

                這個方法取得你的SpinnerAdapter和ActionBar.OnNavigationListener。

                上述步驟相對簡潔,但是最核心的工作時實現SpinnerAdapter和ActionBar.OnNavigationListener。下面是一個供你起步的示例:

                SpinnerAdapter和OnNavigationListener的實現示例SpinnerAdapter是為spinner widget提供數據的一種adapter,例如action bar上的下拉式清單。SpinnerAdapter是你需要實現的接口,但是Android已經包含瞭一些有用的實現,供你來擴展,例如ArrayAdapter和SimpleCursorAdapter。下面的方式就是通過擴展ArrayAdapter來實現SpinnerAdapter,使用string arrays作為data source。

                 

                createFromResource()方法獲取3個參數:應用的Context,string array的resource ID,每個清單項的佈局。

                一個string array的定義像這樣:

                
                    
                        
                            Mercury
                            Venus
                            Earth
                        
                    

                createFromResource()返回一個ArrayAdapter。

                當用戶從下拉清單裡選擇某項時,在你的activity或fragment需要改變代碼的地方實現ActionBar.OnNavigationListener。這個listener隻有一個方法需要實現:onNavigationItemSelected()。onNavigationItemListener()方法接收清單項在list中的位置和由SpinnerAdapter提供的項ID。

                如下示例顯示瞭onNavigationListener的一個匿名實現,在R.id.fragment_container的佈局容器裡插入瞭一個Fragment:

                mOnNavigationListener = new OnNavigationListener() {
                      // Get the same strings provided for the drop-down's ArrayAdapter
                      String[] strings = getResources().getStringArray(R.array.action_list);
                
                      @Override
                      public boolean onNavigationItemSelected(int position, long itemId) {
                        // Create new fragment from our own Fragment class
                        ListContentFragment newFragment = new ListContentFragment();
                        FragmentTransaction ft = openFragmentTransaction();
                        // Replace whatever is in the fragment container with this fragment
                        //  and give the fragment a tag name equal to the string at the position selected
                        ft.replace(R.id.fragment_container, newFragment, strings[position]);
                        // Apply changes
                        ft.commit();
                        return true;
                      }
                    };

                在這個例子裡,當用戶選擇下拉式清單裡的某項時,在當前佈局裡會添加一個Fragment。增加的Fragment會設置一個標識身份的tag。

                下面的示例顯示瞭ListContentFragment類定義的fragment:

                public class ListContentFragment extends Fragment {
                        private String mText;
                
                        @Override
                        public void onAttach(Activity activity) {
                          // This is the first callback received; here we can set the text for
                          // the fragment as defined by the tag specified during the fragment transaction
                          super.onAttach(activity);
                          mText = getTag();
                        }
                
                        @Override
                        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                            // This is called to define the layout for the fragment;
                            // we just create a TextView and set its text to be the fragment tag
                            TextView text = new TextView(getActivity());
                            text.setText(mText);
                            return text;
                        }
                    }

                 

                2.15設計action bar風格

                如果你想實現表達你自己的應用品牌風格的視覺顯示,action bar允許定制它的外觀,包括action bar的顏色,文本顏色,按鈕風格等。你需要遵循android的style and theme框架來設計你的風格。

                常規外觀

                actionBarStyle

                定義action bar的style resource。默認的style是Widget.AppCompat.ActionBar,作為父style。

                style resource包含的style屬性有:

                • background — drawable resource,定義action bar背景。
                • backgroundStacked —drawable resource,定義stacked action bar(tabs)背景。
                • backgroundSplit — drawable resource,定義split action bar背景。
                • actionButtonStyle — 定義action button的style resource。它的默認值為Widget.AppCompat.ActionButton。用於父style。
                • actionOverflowStyle — 定義overflow action項的style resource。默認值為Widget.AppComat.ActionButton.Overflow。用於父style。
                • displayOptions — 定義action bar顯示項,包含是否用app logo,顯示title,或使能up action等。
                • pider — drawable resource,定義action項之間的分割間隔。
                • titleTextStyle — 為action bar title定義style resource。默認值為TextAppearance.AppCompat.Widget.ActionBar.Title,用於父stytle。

                  windowActonBarOverlay

                  聲明action bar是否使用overlay模式。默認值為false。

                  通常,action bar有自己獨立的空間,而activity layout使用剩餘的空間。當action bar使用overlay模式,activity layout使用全部的空間,系統將action bar畫在layout的頂部。當action bar隱藏或顯示時,如果你想保持你的內容為固定的大小和位置,就可以使用overlay模式。或者你僅僅使用overlay模式作為一種顯示效果,你可以在action bar上使用一個半透明的背景圖,用戶可以透過action bar看到下面的activity layout。

                  當overlay模式使能,你的activity佈局並沒有意識到action bar的位置在它的頂部。因此,頂部這塊背action bar覆蓋的地方,你的layout佈局應當小心謹慎,不要將重要的UI顯示放在這一塊。如果需要,你可以通過actionBarSize屬性來確定action bar的高度。例如:

                  
                  

                  Action Items

                  actionButtonStyle —為action button定義style resource。默認值為Widget.AppCompat.AcitonButton。用於父style。

                  actionBarItemBackground — drawable resource,為每個action項定義背景。這應該是個state-list drawable類型,指示不同的選項狀態。

                  actionBarDivider — drawable resource,定義action項之間的分割間隔。

                  actionMenuTextColor — 定義action項的文本顯示顏色。

                  actionMenuTextAppearance — 定義action項的文本顯示外觀。

                  actionBarWidgetTheme —定義action view的theme resource。

                  Navigation Tabs

                  actionBarTabStyle — 定義action bar的tabs的style resource。默認值為Widget .AppCompat.ActionBar.TabView。用於父style。

                  actionBarTabBarStyle –定義顯示在navigation tabs下的thin bar的style resource。默認值為Widget.AppCompat.ActionBar.TabBar。用於父style。

                  actionBarTabTextStyle —定義navigation tabs的文本 style resource。默認值為Widget.AppCompat.ActionBar.TabText。用於父style。

                  Drop-down lists

                  actionDropDownStyle — 定義drop-down navigation的sytle(例如,background和text style)。默認值為Widget.AppCompat.Spinner.DropDown.ActionBar。用於父style。

                  Theme示例

                  這是一個定制theme的示例,包含瞭對aciton bar的定制:

                  
                    
                        @style/MyActionBar
                            @style/TabTextStyle
                            @color/actionbar_text
                  
                            
                            @style/MyActionBar
                            @style/TabTextStyle
                            @color/actionbar_text
                  
                        @style/TitleTextStyle
                            @drawable/actionbar_background
                            @drawable/actionbar_background
                            @drawable/actionbar_background
                  
                            
                            @style/TitleTextStyle
                            @drawable/actionbar_background
                            @drawable/actionbar_background
                            @drawable/actionbar_background
                  
                        @color/actionbar_text
                  
                        @color/actionbar_text
                    

                  在你的manifest文件裡,你可以對整個app使用你定制的theme:

                  
                  

                  或對某個activity使用你定制的theme:

                  
                  

                  註意:每個theme和style會聲明一個parent theme或parent style。你隻需要覆寫你需要修改的style屬性,其他的屬性由parent提供實現。

                   

                  詳細內容請參考“Acion Bar指南.pdf”。(如需轉發,請標明出處)

                   

發佈留言