Android导航栏ActionBar的具体分析

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/39378825

关于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-v7project,然后让我们的project依赖它。


ActionBar的作用
1、帮助用户知道你如今处于哪个页面
2、为用户提供统一的导航界面

关于许多其它的ActionBar导航功能请參见 http://developer.android.com/guide/topics/ui/actionbar.html 我的这篇文章基本也是參考这里的

ActionBar在界面中的展现形式 例如以下图:



从图中能够看到一个ActionBar包括 APP icon,ActionItems,Action Overflow,事实上ActionBar基本就取代了曾经版本号中的菜单的概念,在不支持ActionBar的App中,假设你创建了菜单,当你点击菜单键时,下方会出来菜单,而在支持Actionbar的菜单中这些菜单向就出如今了ActionItem和Action Overflow里面了(具体请见后面),有些细心同学在平时可能会发现同一款App,在有些手机上出现了Overflow,而有些手机却没有,事实上这个是有规律的,在有菜单物理键的手机上是没有Overflow的,在没有物理菜单的手机上才有,事实上Google一直主张Android设备去掉物理菜单键,所以相信以后大部分手机都没有菜单物理键的,所以菜单的概念也会慢慢的淡化。

以下我们開始学习ActionBar吧 ,由于3.0之后的系统和2.x系统还是有略微的差别,所以我今天打算先讲讲2.x系统中ActionBar的使用,然后解说3.0之后系统ActionBar的使用。
这里我们就使用android-support-v7吧,毕竟它是Google推出来的。


在2.x上使用ActionBar的步骤

1、导入android-support-v7库,这个库事实上在你的sdk里面就有(前提是你已经下载下来了),如我的路径:D:android-sdk-windowsextrasandroidsupportv7appcompat
2、创建项目,让我们的库依赖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才会显示overflowbutton
    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最重要的功能就是其导航功能,使用其导航功能时,须要进行例如以下配置
1、显示导航button
ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
配置完了后,执行例如以下图,你会发现Actionbar多了一个相似返回的icon

2、为导航增加事件
在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中的一些button,能够在不用切换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功能很easy
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

<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>


细心的同学会发现这里每一个属性声明了两遍(个别仅仅有一个),这是由于我们使用的是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>

先写到这里吧,大家有哪里没有看明确的地方欢迎留言....

原文地址:https://www.cnblogs.com/blfshiye/p/4263084.html