ActionBar几个使用技巧

   源码已上传http://download.csdn.net/detail/klpchan/6830221,格式不太整洁,仅供参考。

      上一篇介绍了ActionBar的选项内容和菜单项,项目中会遇到需要自定义风格的标题栏,CustomView不建议用来定义控件风格,因为没有用到ActionBar的默认组件,如果UIDesigner完全自主设计了一套看起来怎么都不像谷歌建议的AB布局,可以考虑CustomView,大部分情况只是要修改一下例如Title的字体或者标题栏背景。

      按照项目中的需求和网上论坛常见的内容,自定义标题栏风格包括以下几项:

      Q1.1 修改标题栏字体大小、颜色、样式

      Q1.2 自定义ActionBar背景颜色及AB高度

      Q1.3 修改ActionBar上菜单项字体样式

      Q2 在ActonBar上显示与Launch界面不同的Title的icon

      Q3 在含有菜单键的手机上强制显示溢出菜单

      Q4 防止应用启动时ActionBar上默认图标和标题的闪烁

      本文将以上问题打包解答,问题虽多,但是解决思路类路类似。

Q1.x 自定义标题栏选项内容基本风格

     Q1的几个问题属于一类,看一张图先。

     这张是Android系统主题变化流程简图,API 10 及以前的版本,用的是Theme主题,主题属性集定义在Theme.xml中,主题属性值在Style.xml中,API11~13的版本时,默认主题使用的是Theme.Holo,属性集和属性值得定义文件同上,从API14也就是ICS之后,Theme.DeviceDefault作为新的主题,使用了新的文件,如上图所示。本文中以API14为最低版本,默认使用的主题是android/framework/base/core/res/res/values/themes_device_defaults.xml中的Theme.DeviceDefault。  

     <style name="Theme.DeviceDefault" parent="Theme.Holo" >

     Theme.DeviceDefault的父类是Theme.Holo,这里体现了对以前版本的继承关系,Theme.DeviceDefaul中的大部分属性值,都是在Theme.Holo属性值的基础上进行再定义。Theme.DeviceDefault主题定义了对于所有组件的默认定义方式,其中包括Actionbar的属性集。如下所示:     

  1. <!-- Action bar styles -->  
  2. <item name="actionDropDownStyle">@android:style/Widget.DeviceDefault.Spinner.DropDown.ActionBar</item>  
  3. <item name="actionButtonStyle">@android:style/Widget.DeviceDefault.ActionButton</item>  
  4. <item name="actionOverflowButtonStyle">@android:style/Widget.DeviceDefault.ActionButton.Overflow</item>  
  5. <item name="actionBarTabStyle">@style/Widget.DeviceDefault.ActionBar.TabView</item>  
  6. <item name="actionBarTabBarStyle">@style/Widget.DeviceDefault.ActionBar.TabBar</item>  
  7. <item name="actionBarTabTextStyle">@style/Widget.DeviceDefault.ActionBar.TabText</item>  
  8. <item name="actionModeStyle">@style/Widget.DeviceDefault.ActionMode</item>  
  9. <item name="actionModeCloseButtonStyle">@style/Widget.DeviceDefault.ActionButton.CloseMode</item>  
  10. <item name="actionBarStyle">@android:style/Widget.DeviceDefault.ActionBar</item>  
  11. <item name="actionModePopupWindowStyle">@android:style/Widget.DeviceDefault.PopupWindow.ActionMode</item>    
       属性是由“键”和“值”组成,也就是KV,我们通常不会自定义某个控件属性,就是因为不知道该属性的键和值,上述代码段定义了Theme.DeviceDefault中关于Actionbar组件的默认属性键值对,每一个item,左边是键,右边是值。比如第一项<item name="actionDropDownStyle">@android:style/Widget.DeviceDefault.Spinner.DropDown.ActionBar</item>。
       属性键是actionDropDownStyle,属性值是@android:style/Widget.DeviceDefault.Spinner.DropDown.ActionBar,也就是android系统的一个默认属性Widget.DeviceDefault.Spinner.DropDown.ActionBar(这类属性内容是啥,在哪定义,后面会介绍),这个属性就是控制在ActionBar上下拉spinner的显示风格的。
       在这些属性集中,有个item比较重要,<item name="actionBarStyle">@android:style/Widget.DeviceDefault.ActionBar&lt;/item>,属性名是actionBarStyle,属性值是系统默认风格Widget.DeviceDefault.ActionBar,这个风格是个继承自Widget.Holo.Actionbar的子风格,定义在Styles_device_defaults.xml中,源码如下:       
         <style name= "Widget.DeviceDefault.ActionBar" parent= "Widget.Holo.ActionBar" ></style>
  1. <style name="Widget.Holo.ActionBar" parent="Widget.ActionBar">  
  2.     <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>  
  3.     <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item>  
  4.     <item name="android:background">@android:drawable/ab_transparent_dark_holo</item>  
  5.     <item name="android:backgroundStacked">@android:drawable/ab_stacked_transparent_dark_holo</item>  
  6.     <item name="android:backgroundSplit">@android:drawable/ab_bottom_transparent_dark_holo</item>  
  7.     <item name="android:divider">?android:attr/dividerVertical</item>  
  8.     <item name="android:progressBarStyle">@android:style/Widget.Holo.ProgressBar.Horizontal</item>  
  9.     <item name="android:indeterminateProgressStyle">@android:style/Widget.Holo.ProgressBar</item>  
  10.     <item name="android:progressBarPadding">32dip</item>  
  11.     <item name="android:itemPadding">8dip</item>  
  12. </style>  
       看到这里,基本上就看出来了Theme.DeviceDefault中actionBarStyle所控制的属性集了,其中这个属性集中的第一项属性
      <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item> 控制着Actionbar上的Title风格,该风格继承自Holo主题的对应风格并重定义了字体大小。
  1.     <style name="TextAppearance.Holo.Widget.ActionBar.Title"  
  2.            parent="TextAppearance.Holo.Medium">  
  3.         <item name="android:textSize">@android:dimen/action_bar_title_text_size</item>  
  4.     </style><pre class="html" name="code">    <style name="TextAppearance.Holo.Medium" parent="TextAppearance.Medium">  
  5.     </style>  
  6.     ……  
  7.        <style name="TextAppearance.Medium">  
  8.         <item name="android:textSize">18sp</item>  
  9.     </style>  
  10.     ……  
  11.        <style name="TextAppearance">  
  12.         <item name="android:textColor">?textColorPrimary</item>  
  13.         <item name="android:textColorHighlight">?textColorHighlight</item>  
  14.         <item name="android:textColorHint">?textColorHint</item>  
  15.         <item name="android:textColorLink">?textColorLink</item>  
  16.         <item name="android:textSize">16sp</item>  
  17.         <item name="android:textStyle">normal</item>  
  18.     </style></pre>  
  19. <pre></pre>  
  20. <pre></pre>  
  21. <pre></pre>  
  22. <pre></pre>  
  23. <pre></pre>  
  24. <pre></pre>  
       为了修改Actionbar中的title风格,我们只要修改默认主题中的actionBarStyle属性中的android:titleTextStyle属性中的android:textSize即可。好吧,有点晕,把上边的内容梳理下,如下图所示:
      
       我们需要在App主题中使用自己定义的actionBarStyle,在actionBarStyle中使用自己定义的titleTextStyle,后者需继承自TextAppearance.Holo.Widget.ActionBar.Title并且修改字体大小。  
  1. <style name="MyTheme" parent="@android:style/Theme.DeviceDefault"></style>   //定义自己的APP主题 继承自Theme.DeviceDefault  
  2. <style name="MyTheme.WithActionBar" parent="@style/MyTheme">                              
  3.     <item name="android:actionBarStyle">@style/MyActionBarStyle</item>      //在APP主题中使用自定义的actionBarStyle  
  4. </style>  
  5. <style name="MyActionBarStyle" parent="android:style/Widget.DeviceDefault.ActionBar"> //自定义的actionBarStyle,需继承自Widget.DeviceDefault.ActionBar  
  6.     <item name="android:titleTextStyle">@style/MyTitleTextStyle</item>   //在MyActionBarStyle中使用自定义的titleTextStyle属性  
  7. </style>  
  8. <style name="MyTitleTextStyle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title"> //自定义titleTextStyle属性,父类同源码  
  9.     <item name="android:textSize">30sp</item>                                                                                         
  10.     <item name="android:textColor">#FFFF00</item>       //自定义title大小及颜色  
  11. </style>  
       修改过程如源码注释,在整个自定义过程中需主要:
       a  我们需要采用和源码同样的层次结构来构建自定义风格和属性,如actionBarStyle是Theme.DeviceDefault下的一个属性,那么MyActionBarStyle也就应该是MyTheme下的一个属性,其它同理。
       b  每一个属性或者风格在定义时,需要继承自和源码一样的父类,如MyActionBarStyle父类需要和actionBarStyle的父类一样,继承自Widget.DeviceDefault.ActionBar,我们可以改变的,只是该父类中的item属性值。
       c  对于不同层级的空间属性和风格来说,ThemeDeviceDefault类型的多是以Holo类型为父类,说的比较拗口,参考styles_device_default.xml文件的代码风格为例:
  1. </style>  
  2. <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Holo.ActionButton" >  
  3. </style>  
  4. <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Holo.ActionButton.Overflow" >  
  5. </style>  
  6. <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Holo.ActionButton.TextButton" >  
  7. </style>  
  8. <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Holo.ActionMode" >  
  9. </style>  
  10. <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Holo.ActionButton.CloseMode" >  
  11. </style>  
  12. <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Holo.ActionBar" >  
       这段代码表示什么暂且不谈,可以看到整个styles_device_default.xml的代码风格都是<style name="XXX.DeviceDefault.XXX" parent="XXX.Holo.XXX" >,上文已经提到,DeviceDefault类的风格属性是API14及以后的,定义在styles_device_default.xml文件中,该文件中所定义的风格父类大多是把DeviceDefault换成Holo,也就是说是以Holo对应属性为基类再定义的结果,这些Holo类型的风格和属性定义在styles.xml中,也就是API13及以前使用的属性值文件。
       d ActionBar的源码一直在更新,前述内容均是基于ICS、JB、JBP的主题资源设置,针对于4.0以后的系统,按照官网上的提示,和ActionBar自定义相关的默认属性集整理如下图所示,参考http://developer.android.com/guide/topics/ui/actionbar.html#Style

  
       按照上面的分析思路,照猫画虎,可以解决前三个问题 : 修改标题栏字体大小、颜色、样式;自定义ActionBar背景颜色及AB高度 ;修改ActionBar上菜单项字体样式;显示效果如下图:

                                                           

       styles.xml内容如下,在AndroidManifest中注册android:theme="@style/MyTheme.WithActionBar"。

  1.    <style name="MyTheme" parent="@android:style/Theme.DeviceDefault"></style>  
  2.    <style name="MyTheme.WithActionBar" parent="@style/MyTheme">  
  3.        <item name="android:actionMenuTextAppearance">@style/MyABmenuTextAppearance</item> // 使用自定义的菜单文本风格  
  4.        <item name="android:actionMenuTextColor">#FF4500</item> //定义ActionItem字体颜色  
  5.        <item name="android:actionBarStyle">@style/MyActionBarStyle</item>  // 使用自定义的actionBarStyle  
  6.    </style>  
  7.    <style name="MyActionBarStyle" parent="android:style/Widget.DeviceDefault.ActionBar">  
  8.        <item name="android:background">#90EE90</item>  //定义ActionBar背景颜色  
  9.        <item name="android:height">60dip</item>  // 定义ActionBar高度  
  10.        <item name="android:titleTextStyle">@style/MyTitleTextStyle</item>  
  11.        <item name="android:subtitleTextStyle">@style/MySubTitleTextStyle</item>  
  12.    </style>  
  13.    <style name="MyTitleTextStyle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">  
  14.        <item name="android:textSize">30sp</item>  
  15.        <item name="android:textColor">#FFFF00</item> //修改标题字体大小及颜色  
  16.    </style>  
  17.    <style name="MySubTitleTextStyle" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle">  
  18.        <item name="android:textColor">#FFC0CB</item> //修改子标题字体颜色  
  19.    </style>  
  20.    <style name="MyABmenuTextAppearance" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Menu">  
  21.        <item name="android:textSize">13sp</item>                                   
  22.        <item name="android:textStyle">bold</item>  //修改AB菜单项字体风格  
  23.    </style>  

Q2 在ActonBar上显示与Launch界面不同的Title的icon

      举个例子,我想在Launch界面显示appname和appIcon,在第一个Activity界面显示activityname和activityIcon,需要在AndroidManifest中定义,
  1. <application  
  2.     ……  
  3.     android:icon="@drawable/appIcon">      //主界面应用图标  
  4.     <activity  
  5.         android:label="@string/activityname"      //在Activity中显示的标题  
  6.         android:logo="@drawable/activitiyIcon">       //在Activity中显示的logo取代了应用icon,logo存在缩放,可使用适当像素图片  
  7.         <intent-filter  
  8.             android:label="@string/appname">      //表示在Launcher界面显示的应用名  
  9.             <action android:name="android.intent.action.MAIN" />  
  10.             <category android:name="android.intent.category.LAUNCHER" />  
  11.         </intent-filter>  
  12.     </activity>  
  13. </application>  

Q3 在含有菜单键的手机上强制显示溢出菜单

       上一篇博客中提到对于ActionItem来说,如果是带有实体菜单键的终端设备,则不会显示在AB最右端显示溢出按钮,如果想强制显示,怎么办,网上能够找到答案,就是欺骗系统,告诉它该设备是没有菜单键的,在onCreate时添加以下代码,ViewConfiguration是一个参数配置类,用于管理配置和UI相关的常量参数,其中的sHasPermanentMenuKey表示的就是该设备是否含有菜单键,可以利用反射机制强制设置该标志位位false,
  1. try {  
  2.     ViewConfiguration config = ViewConfiguration.get(this);                                
  3.     Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");  
  4.     if(menuKeyField != null) {  
  5.         menuKeyField.setAccessible(true);  
  6.         menuKeyField.setBoolean(config, false);  
  7.     }  
  8. } catch (Exception ex) {  
  9.     // Ignore  
  10. }  
      这样就可以让完成“欺骗”过程,系统误以为没有实体菜单键,目的达到。

 

       知道了How,还想知道Why,源代码里解释了原因,Window在构建菜单时,会调用ActionBarView中的setMenu函数,ActionBar上菜单构建的详细流程我会在以后的博文中整理出来,现在先从setMenu开始分析,  
  1. ActionMenuView menuView;  
  2. final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,  
  3.         LayoutParams.MATCH_PARENT);  
  4. if (!mSplitActionBar) {  
  5.     mActionMenuPresenter.setExpandedActionViewsExclusive(  
  6.             getResources().getBoolean(  
  7.             com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));  
  8.     configPresenters(builder);  
  9.     menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);  
  10.     final ViewGroup oldParent = (ViewGroup) menuView.getParent();  
  11.     if (oldParent != null && oldParent != this) {  
  12.         oldParent.removeView(menuView);  
  13.     }  
  14.     addView(menuView, layoutParams);  
  15. else {  
  16.     mActionMenuPresenter.setExpandedActionViewsExclusive(false);  
  17.     // Allow full screen width in split mode.  
  18.     mActionMenuPresenter.setWidthLimit(  
  19.             getContext().getResources().getDisplayMetrics().widthPixels, true);  
  20.     // No limit to the item count; use whatever will fit.  
  21.     mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);  
  22.     // Span the whole width  
  23.     layoutParams.width = LayoutParams.MATCH_PARENT;  
  24.     configPresenters(builder);  
  25.     menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);  
  26.     if (mSplitView != null) {  
  27.         final ViewGroup oldParent = (ViewGroup) menuView.getParent();  
  28.         if (oldParent != null && oldParent != mSplitView) {  
  29.             oldParent.removeView(menuView);  
  30.         }  
  31.         menuView.setVisibility(getAnimatedVisibility());  
  32.         mSplitView.addView(menuView, layoutParams);  
  33.     } else {  
  34.         // We'll add this later if we missed it this time.  
  35.         menuView.setLayoutParams(layoutParams);  
  36.     }  
  37. }  
      在设置菜单的过程中,无论是否采用菜单项分割模式,都需要配置presenter,这是一个类似于菜单适配器的帮助类,其作用就是把存储于MenuBuilder中的菜单内容显示出来,在没有引入ActionBar之前,使用Menubuilder直接构建普通选项菜单,为了能够在AB中显示选项菜单,ActionMenuPresenter被引入,其中的有类似于列表适配器的getview和bindItemView方法,configPresenters(builder)被用来为MenuBuilder配置Presenter,在configPresenters方法中 :
  1. private void configPresenters(MenuBuilder builder) {  
  2.     if (builder != null) {  
  3.         builder.addMenuPresenter(mActionMenuPresenter);  
  4.         builder.addMenuPresenter(mExpandedMenuPresenter);  
  5.     } else {  
  6.         mActionMenuPresenter.initForMenu(mContext, null);  
  7.         mExpandedMenuPresenter.initForMenu(mContext, null);  
  8.         mActionMenuPresenter.updateMenuView(true);  
  9.         mExpandedMenuPresenter.updateMenuView(true);  
  10.     }  
  11. }  
      Presenter开始初始化过程,用来配置ActionBar上的ActionItem的显示参数,同时调用
  1. final ActionBarPolicy abp = ActionBarPolicy.get(context);  
  2. if (!mReserveOverflowSet) {  
  3.     mReserveOverflow = abp.showsOverflowMenuButton();  
  4. }  
  5. public boolean showsOverflowMenuButton() {  
  6.     return !ViewConfiguration.get(mContext).hasPermanentMenuKey();  
  7. }  
       这就解释了溢出菜单显示的原因,是否有实体菜单键就是判断这样一个特定的标志位,Next。

Q4 防止应用启动时ActionBar上默认图标和标题的闪烁

       对于运行速度不是很快或者setContentView加载内容稍慢的情况下,通常会看到ActionBar上的默认图标和标题先闪烁一下,待界面正常显示时才会变成开发者设置的样式,这个问题非常明显的出现在很多低端型号中,用户体验不是一般的低端,这是框架层写好的,对于Title还可以通过Q2的解决方式禁止其闪烁,貌似默认图标始终会在屏幕内容完全加载完成前出现。不能理解这些话的同学可以找一个配置尴尬的PC主机用eclipse运行模拟器跑一个在主线程里休眠若干ms的应用,下面是这个问题出现的UI Flow:

                                             
       第一屏ActionBar上的图标显示是我们不想要的,如何防止默认图标的闪烁,如果调用API禁止显示图标,那么就第二屏的图标也会消失,不卖关子了,在Q1中定义的MyActionBarStyle加上显示选项的属性
  1.  <style name="MyActionBarStyle" parent="android:style/Widget.DeviceDefault.ActionBar">  
  2.     <item name="android:displayOptions">showCustom</item>    //添加该行,表示默认显示的是CustomView。  
  3.     <item name="android:background">#90EE90</item>  
  4.     <item name="android:height">60dip</item>  
  5.     <item name="android:titleTextStyle">@style/MyTitleTextStyle</item>  
  6.     <item name="android:subtitleTextStyle">@style/MySubTitleTextStyle</item>  
  7. </style>  
        默认的android:displayOptions定义在Styles.xml中的Wiget.ActionBar中
  1. <style name="Widget.ActionBar">  
  2.     <item name="android:background">@android:drawable/action_bar_background</item>  
  3.     <item name="android:displayOptions">useLogo|showHome|showTitle</item>  
  4.     <item name="android:divider">@android:drawable/action_bar_divider</item>  
  5.     <item name="android:height">?android:attr/actionBarSize</item>  
  6.     <item name="android:paddingStart">0dip</item>  
  7.     <item name="android:paddingTop">0dip</item>  
  8.     <item name="android:paddingEnd">0dip</item>  
  9.     <item name="android:paddingBottom">0dip</item>  
  10.     <item name="android:titleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Title</item>  
  11.     <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Subtitle</item>  
  12.     <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item>  
  13.     <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item>  
  14.     <item name="android:homeLayout">@android:layout/action_bar_home</item>  
  15. </style>  
       修改默认显示后,为了能够让你想要设置的title或者home正常显示,调用SetDisplayOptions使能对应的标志位即可。
小结
       这篇整理的问题都是较为频繁遇到且和ActionBar选项内容显示相关的,AB是ICS以后才在手机上应用的新组件,通过分析源码可以解决一些比较常见的问题,如本文所提,但是在项目中还有一项比较重要的特性需求:焦点控制,尤其是对带有实体方向导航键的手机来说,如何做好AB上的焦点控制,AB中显示选项内容是如何布局的,菜单项的构建流程又有哪些,下篇关于AB的博文中会整理分享~~
 
      ~~~版权所有,转载请注明~~~~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值