Android ActionBar詳解

關於ActionBar,相信你們並不陌生,可是真正可以熟練使用的也不是不少,這篇文章主要爲你們詳細介紹ActionBar的相關知識,ActionBar是在Android3.0中引入的概念,因此在2.x系統中使用ActionBar咱們須要依賴ActionBarSherklock或者androi-support-v7庫,ActionBarSherklock是anroid中很是有名的一個開源項目,android-support-v7是Google後來推出的一個庫,有了v7庫後AndroidBarSherklock這個開源項目基本能夠退出歷史舞臺了,在使用android-support-v7的過程當中,必定不能僅僅使用它的jar包,由於它的jar包不包含一些重要的資源文件,咱們必須導入android-support-v7工程,而後讓咱們的工程依賴它。css

ActionBar的做用html

一、幫助用戶知道你如今處於哪一個頁面前端

二、爲用戶提供統一的導航界面java

關於更多的ActionBar導航功能請參見 http://developer.android.com/guide/topics/ui/actionbar.htmlandroid

ActionBar在界面中的展示形式 以下圖:git

從圖中能夠看到一個ActionBar包括 APP icon,ActionItems,Action Overflow,其實ActionBar基本就代替了之前版本中的菜單的概念,在不支持ActionBar的App中,若是你建立了菜單,當你點擊菜單鍵時,下方會出來菜單,而在支持Actionbar的菜單中這些菜單向就出如今了ActionItem和Action Overflow裏面了(詳細請見後面),有些細心同窗在平時可能會發現同一款App,在有些手機上出現了Overflow,而有些手機卻沒有,其實這個是有規律的,在有菜單物理鍵的手機上是沒有Overflow的,在沒有物理菜單的手機上纔有,其實Google一直主張Android設備去掉物理菜單鍵,因此相信之後大部分手機都沒有菜單物理鍵的,因此菜單的概念也會慢慢的淡化。github

下面咱們開始學習ActionBar吧 ,因爲3.0以後的系統和2.x系統仍是有稍微的區別,因此我今天打算先講講2.x系統中ActionBar的使用,而後講解3.0以後系統ActionBar的使用。windows

這裏咱們就使用android-support-v7吧,畢竟它是Google推出來的。微信

在2.x上使用ActionBar的步驟app

一、導入android-support-v7庫,這個庫其實在你的sdk裏面就有(前提是你已經下載下來了),如個人路徑:D:\android-sdk-windows\extras\android\support\v7\appcompat

二、建立項目,讓咱們的庫依賴andorid-support-v7,並讓須要使用ActionBar的Activity依賴ActionBarActivity,並進行以下配置:

<activity android:theme="@style/Theme.AppCompat.Light" ... >

就這麼簡單,你的項目就已經引入了ActionBar了,若是你想隱藏ActionBar,以下操做便可

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

這裏要提醒一下:因爲ActionBar在隱藏的時候會重現繪製Activity的界面,從而填充ActionBar的空白,因此當你頻繁的隱藏和顯示ActionBar時,會致使Activity的界面頻繁重繪,爲了不這種狀況發生,你能夠再actionBarStyle中將 windowActionBarOverlay這個屬性設置爲true,也就是說ActionBar會在Activity的上面,隱藏和顯示不會影響Activity

我上面引入的ActionBar沒有引入任何內容,默認裏面就是一個設置項,如今咱們就像裏面加入本身的item吧

建立res/menu/main_activity_actions.xml文件並加入以下內容,

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
   <item
        android:id="@+id/action_copy"
        android:icon="@drawable/copy"
        android:title="複製"/>
     <item
        android:id="@+id/action_cut"
        android:icon="@drawable/cut"
        android:title="剪切"/>
      <item
        android:id="@+id/action_discard"
        android:icon="@drawable/discard"
        android:title="刪除"/>
       <item
        android:id="@+id/action_edit"
        android:icon="@drawable/edit"
        android:title="編輯"/>
       
        <item
        android:id="@+id/action_email"
        android:icon="@drawable/email"
        android:title="郵箱"/>
        
</menu>

而後重寫下面的方法:

@Override
  public boolean onCreateOptionsMenu(Menu menu)
  {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
	//返回true纔會顯示overflow按鈕
    return true;
  }

經過以上步驟,咱們就已經向ActionBar中加入了5個Item,運行一下看看效果吧,以下圖

ActionBar是出來了,可是和咱們上面說的ActionBar貌似有些區別,這些item都進入了overflow了,另外就是我明明設置了icon屬性,卻沒有看見icon

對於這兩個問題,咱們一一解決吧

item默認都是進入Overflow的,若是想有些重要的Item 進入ActionItem 裏面,那麼須要配置一個屬性showAsAction

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item 
          android:id="@+id/action_copy"
		  android:icon="@drawable/copy"
		  android:title="複製"
          yourapp:showAsAction="always"  />
    ...
</menu>

注意這裏的showAsActoin命名空間是自定義的,不是"android",由於這個屬性在2.x系統中沒有,是andorid-support-v7定義的,你運行一下,會發現複製已經到了ActionItem的位置了

第二個問題就是設置了icon爲何沒有顯示?其實在Actionbar中,處於ActionItem位置的item默認是顯示icon而不顯示title的,咱們能夠經過在showAsActoin中配置"always|withText",讓Tiele顯示出來(可是也不必定會顯示,只是儘可能顯示),而處於Overflow的item 默認是顯示title而不顯示icon的,這個能夠經過反射機制來改變,方案以下:

public void setOverflowIconVisiable(Menu menu)
  {
    try
    {
      Class clazz=Class.forName("com.android.internal.view.menu.MenuBuilder");
      Field field=clazz.getDeclaredField("mOptionalIconsVisible");
      if(field!=null)
      {
        field.setAccessible(true);
        field.set(menu , true);
      }
    } catch (ClassNotFoundException e)
    {
      e.printStackTrace();
    } catch (NoSuchFieldException e)
    {
      e.printStackTrace();
    } catch (IllegalArgumentException e)
    {
      e.printStackTrace();
    } catch (IllegalAccessException e)
    {
      e.printStackTrace();
    }
    
  }

而後再onMenuOpened方法中調用

@Override
public boolean onMenuOpened(int featureId, Menu menu) {

    if(featureId == Window.FEATURE_ACTION_BAR && menu != null)
    {
      setOverflowIconVisiable(menu);
    }
    
    return super.onMenuOpened(featureId, menu);
}

運行效果以下圖,能夠看到icon已經顯示了

目前這些ActionItem和Overflowsitems都是沒有響應事件的,如今爲他們添加吧,若是之前使用過菜單的同窗會以爲和菜單是同樣的

 @Override
  public boolean onOptionsItemSelected(MenuItem item)
  {
    switch(item.getItemId())
    {
      case R.id.action_copy:
        Toast.makeText(this, item.getTitle(), 1000).show();
        return true;
      case R.id.action_cut:
        Toast.makeText(this, item.getTitle(), 1000).show();
        return true;
      case R.id.action_delete:
        Toast.makeText(this, item.getTitle(), 1000).show();
        return true;
      case R.id.action_edit:
        Toast.makeText(this, item.getTitle(), 1000).show();
        return true;
      case R.id.action_email:
        Toast.makeText(this, item.getTitle(), 1000).show();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Fragment中加入ActionItem

接下來學習一下在Fragment裏面使用ActionItem吧

public class MyFragment extends Fragment
{
  private static final String TAG = "MyFragment";
  
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
	//必定要調用,不然沒法將菜單加入ActionItem
    setHasOptionsMenu(true);
  }
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
  {
    
    TextView tv=new TextView(this.getActivity());
    tv.setText("Hello Gavin");
    return tv;
  }
  
  @Override
  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
  {
    inflater.inflate(R.menu.fragment_men, menu);
    super.onCreateOptionsMenu(menu, inflater);
  }
  
  @Override
  public boolean onOptionsItemSelected(MenuItem item)
  {
    switch(item.getItemId())
    {
      case R.id.action_share:
        Toast.makeText(this.getActivity(), item.getTitle(), 1000).show();
        return true;
      
    }
    return super.onOptionsItemSelected(item);
  }
}

使用上和Activity差很少,有一點須要注意,對於菜單的響應事件,Activity優於Fragment響應,因此在Activity的onOptionsItemSelected方法中對於沒有處理的事件須要調用super.onOptionsItemSelected(item)(也能夠是false,可是推薦前者),若是返回true,那麼Fragment是不會執行onOptionsItemSelectedd的。

將ActionBar置於屏幕下方

若是須要將ActionBar置於屏幕下方,那麼僅僅須要以下配置便可

<manifest ...>
    <activity  ... >
        <meta-data android:name="android.support.UI_OPTIONS"
                   android:value="splitActionBarWhenNarrow" />
    </activity>
</manifest>


ActionBar的導航功能

ActionBar最重要的功能就是其導航功能,使用其導航功能時,須要進行以下配置

一、顯示導航按鈕

ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);

配置完了後,運行以下圖,你會發現Actionbar多了一個相似返回的icon

二、爲導航添加事件

在onOptionsItemSelected方法中加入以下分支判斷

case android.R.id.home:
    finish();
    return true;

這個是最簡單的處理,就是finish掉自身,更多強大的導航功能請參見後面

看到上面的導航功能估計不少同窗會以爲它的功能和物理返回鍵的功能不是同樣嗎?若是按照我上面的實現,確實差很少,可是ActionBar真正的意圖不是這樣的,咱們知道物理返回鍵時根據Activity棧回退到前一個Activity,是不能直接回退到前前一個Activity的,可是ActionBar是能夠的,下面就來實現如下吧

實現這個導航功能有兩種實現方式

a、爲當前Activity指定父Activity

b、重寫 getSupportParentActivityIntent() 和onCreateSupportNavigateUpTaskStack()

其中a比較適合須要返回的父Activity比較 固定,不會由於上下文環境而改變,實現也簡單,只須要進行以下配置

<activity
    android:name="com.action.demo.SecondActivity">
             
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.action.demo.MainActivity" />
            
</activity>

並重寫SecondActivity的  onOptionsItemSelected方法

@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;  
    }
    return super.onOptionsItemSelected(item);
  }

b比較適合根據上下文環境選擇導航到某一個Activity,例如,能夠分別從MainActivity,ThreeActivity進入到SecondActivity,那麼未來源標記爲1和2(經過Intent 傳遞),重寫getSupportParentActivityIntent

 @Override
  public Intent getSupportParentActivityIntent()
  {
    if(from==1)
    {
      return new Intent().setClassName(this.getPackageName(), "com.action.demo.ThreeActivity");
    }else if(from==2)
    {
      return new Intent().setClassName(this.getPackageName(), "com.action.demo.ThreeActivity");
    }
  
    return super.getSupportParentActivityIntent();
  }

這樣即可以動態的實現導航


添加ActionView

一個ActionView就是一個Widget,用來替換ActionBar中的一些按鈕,能夠在不用切換Activity和 Fragment的狀況下實現一些豐富的功能,下面使用SearchView來做爲例子講解ActionView的使用

在main.xml中加入item

 <item android:id="@+id/action_search"
          android:title="搜索"
          android:icon="@drawable/search"
          myapp:showAsAction="ifRoom|collapseActionView"
          myapp:actionViewClass="android.support.v7.widget.SearchView" />

而後在onCreateOptionsMenu中加入以下代碼

 MenuItem searchItem=menu.findItem(R.id.action_search);
    final SearchView searchView=(SearchView) MenuItemCompat.getActionView(searchItem);
    searchView.setOnQueryTextListener(new OnQueryTextListener()
    {
      
      @Override
      public boolean onQueryTextSubmit(String arg0)
      {
        Toast.makeText(MainActivity.this, arg0, 1000).show();
        searchView.onActionViewCollapsed();
        return true;
      }
      
      @Override
      public boolean onQueryTextChange(String arg0)
      {
        return false;
      }
    });

添加Action Provider

Action Provider和Action View 有些相似,和Action View不一樣的是當Action Provider按下的時候能夠顯示子菜單。下面使用ShareActionProvider爲例講解ActionProvider的使用

<item android:id="@+id/action_share1"
          android:title="分享"
          myapp:showAsAction="ifRoom"
          myapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
          />

在onCreateOptionsMenu中加入以下代碼

ShareActionProvider mShareActionProvider = (ShareActionProvider)
            MenuItemCompat.getActionProvider(menu.findItem(R.id.action_share1));
mShareActionProvider.setShareIntent(getDefaultIntent());

運行效果以下圖:

ShareActionProvider是Android系統自帶的一個Provider,下面咱們試試自定義的Provider,咱們來模仿微信中的右側的「+」號功能

public class CustomActionProvider extends ActionProvider
{
  private static final String TAG = "CustomActionProvider";
  public CustomActionProvider(Context context)
  {
    super(context);
  }

  

  @Override
  public View onCreateActionView()
  {
    return null;//LayoutInflater.from(getContext()).inflate(R.layout.provider_layout, null);
  }
  //這個方法是點擊「+」號會默認執行的地方,注意這個方法能夠被onOptionsItemSelected方法攔截掉
  @Override
  public boolean onPerformDefaultAction()
  {
    Toast.makeText(this.getContext(), "onPerformDefaultAction", 1000).show();
    return super.onPerformDefaultAction();
  }
  
  @Override
  public void onPrepareSubMenu(SubMenu subMenu)
  {
    super.onPrepareSubMenu(subMenu);
    subMenu.clear();
    subMenu.add("發起羣聊").setIcon(R.drawable.alluser).setOnMenuItemClickListener(new OnMenuItemClickListener()
    {
      
      @Override
      public boolean onMenuItemClick(MenuItem item)
      {
        return true;
      }
    });
  }
  //必定要重寫該方法,並返回true,不然不會出現子菜單
  @Override
  public boolean hasSubMenu()
  {
    return true;
  }
  
}

在ActionBar中添加Tab

使用ActionBar中的Tab功能,使用ActionBar中的Tab功能很是簡單

ActionBar bar=this.getSupportActionBar();
    bar.setDisplayHomeAsUpEnabled(true);
    bar.setDisplayShowTitleEnabled(false);
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    bar.addTab(bar.newTab().setText("電影").setTabListener(this));
    bar.addTab(bar.newTab().setText("電視劇").setTabListener(this));
    bar.addTab(bar.newTab().setText("直播").setTabListener(this));

效果以下圖

ActionBar的Style和Theme

在學習ActionBar的style和Theme以前,咱們先來了解一下Style和Theme是啥吧,Style即便一些屬性的集合,相似於css文件(前端開發的同窗都很是熟悉),Theme其實就是Style,只不過Theme是用於Activity的Style,在Android系統中自帶了不少了Theme,這些Theme中有的有ActionBar,有的沒有ActionBar,對於沒有Actionbar的Theme對於的Activity,當調用getSupportActoin時返回null,

雖說ActionBar是爲用戶提供的統一的導航功能,可是這個並不意味着全部的ActionBar都長一個樣,咱們仍是能夠根據需求進行定製的,下面咱們就來學習定製本身的ActionBar

在學習自定義ActionBar以前給你們推薦一個網址:http://jgilfelt.github.io/android-actionbarstylegenerator/  能夠幫助咱們進行定製ActionBar

這裏我先給出豎屏和橫屏的效果圖,而後給出style文件,ActionBar的style屬性很是多,咱們只須要知道一些比較經常使用的,而後知道怎麼查詢這個屬性就好了,我能夠到http://developer.android.com/guide/topics/ui/actionbar.html  ActionBar更多屬性的使用

效果圖以下:

豎屏圖:


橫屏圖:

style.xml

<pre code_snippet_id="471209" snippet_file_name="blog_20140918_20_6003929" name="code" class="html"><style name="MyActionBarTheme" parent="@style/Theme.AppCompat">  
          
        <item name="android:actionBarStyle">@style/ActionBarStyle</item>  
        <item name="android:actionMenuTextColor">@color/menu_color</item>  
        <item name="android:windowActionBarOverlay">false</item>  
        <item name="android:itemBackground">@drawable/action_item_bg_selector</item>   
        <item name="android:actionOverflowButtonStyle">@style/ActionBarOverflowStyle</item>  
        <item name="android:actionBarTabTextStyle">@style/TabTextStyle</item>  
        <item name="android:actionBarTabStyle">@style/ActionBarTab</item>  
         
        <!--Support Library-->  
        <item name="actionBarStyle">@style/ActionBarStyle</item>  
        <item name="actionMenuTextColor">@color/menu_color</item>  
        <item name="windowActionBarOverlay">false</item>  
        <item name="actionOverflowButtonStyle">@style/ActionBarOverflowStyle</item>  
        <item name="actionBarTabTextStyle">@style/TabTextStyle</item>  
        <item name="actionOverflowButtonStyle">@style/ActionBarOverflowStyle</item>  
        <item name="actionBarTabStyle">@style/ActionBarTab</item>  
    </style>  
      
    <style name="ActionBarStyle" parent="@style/Widget.AppCompat.ActionBar">  
       <item name="android:background">@drawable/action_back</item>  
       <item name="android:titleTextStyle">@style/TitleTextStyle</item>  
       <item name="background">@drawable/action_back</item>  
       <item name="titleTextStyle">@style/TitleTextStyle</item>  
       <item name="backgroundStacked">@drawable/action_back_stack</item>  
       <item name="backgroundSplit">@drawable/action_back_stack</item>  
         
    </style>  
      
    <style name="TitleTextStyle"  
           parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">  
        <item name="android:textColor">@color/actionbar_text</item>  
    </style>  
  
     
    <style name="TabTextStyle"  
           parent="@style/Widget.AppCompat.ActionBar.TabText">  
        <item name="android:textColor">@color/actionbar_text</item>  
        <item name="android:textSize">15sp</item>  
        <item name="android:textStyle">normal</item>  
        <item name="android:maxLines">1</item>  
    </style>  
      
    <style name="ActionBarOverflowStyle" parent="Widget.AppCompat.ActionButton.Overflow">  
        <item name="android:src">@drawable/more</item>  
          
    </style>  
      
    <style name="ActionBarTab" parent="Widget.AppCompat.ActionBar.TabView">  
         <item name="android:background">@drawable/tab_indicator</item>  
    </style></pre><br><br>

細心的同窗會發現這裏每一個屬性聲明瞭兩遍(個別只有一個),這是由於咱們使用的是support-library-v7庫,帶前綴「android:」的是對應Android系統的,沒有這個前綴的是support-librayr的。

最後給出tab_indicator的定義,這個是tab指示器的背景圖片,這個並非我手動寫的,我是利用上面給出的網址生成的。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Non focused states -->
    <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@android:color/transparent" />
    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/tab_selected" />

    <!-- Focused states -->
    <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected_focused" />
    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/tab_selected_focused" />

    <!-- Pressed -->
    <!--    Non focused states -->
    <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed" />
    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed" />

    <!--    Focused states -->
    <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed" />
    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed" />
</selector>
相關文章
相關標籤/搜索