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>
相关文章
相关标签/搜索