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的几个问题属于一类,看一张图先。

ActionBar最近学习整理之二:风格自定义_第1张图片

     这张是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。  

    

    
       看到这里,基本上就看出来了Theme.DeviceDefault中actionBarStyle所控制的属性集了,其中这个属性集中的第一项属性
      @android:style/TextAppearance.Holo.Widget.ActionBar.Title 控制着Actionbar上的Title风格,该风格继承自Holo主题的对应风格并重定义了字体大小。
    
    
    ……
       
    ……
       






       为了修改Actionbar中的title风格,我们只要修改默认主题中的actionBarStyle属性中的android:titleTextStyle属性中的android:textSize即可。好吧,有点晕,把上边的内容梳理下,如下图所示:
       ActionBar最近学习整理之二:风格自定义_第2张图片
       我们需要在App主题中使用自己定义的actionBarStyle,在actionBarStyle中使用自己定义的titleTextStyle,后者需继承自TextAppearance.Holo.Widget.ActionBar.Title并且修改字体大小。  
      //定义自己的APP主题 继承自Theme.DeviceDefault
   
   
   
       修改过程如源码注释,在整个自定义过程中需主要:
       a  我们需要采用和源码同样的层次结构来构建自定义风格和属性,如actionBarStyle是Theme.DeviceDefault下的一个属性,那么MyActionBarStyle也就应该是MyTheme下的一个属性,其它同理。
       b  每一个属性或者风格在定义时,需要继承自和源码一样的父类,如MyActionBarStyle父类需要和actionBarStyle的父类一样,继承自Widget.DeviceDefault.ActionBar,我们可以改变的,只是该父类中的item属性值。
       c  对于不同层级的空间属性和风格来说,ThemeDeviceDefault类型的多是以Holo类型为父类,说的比较拗口,参考styles_device_default.xml文件的代码风格为例:
    
    
    
    
    
    
    
   
   
   
   
   

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

      举个例子,我想在Launch界面显示appname和appIcon,在第一个Activity界面显示activityname和activityIcon,需要在AndroidManifest中定义,
         //主界面应用图标
              //在Activity中显示的logo取代了应用icon,logo存在缩放,可使用适当像素图片
                 //表示在Launcher界面显示的应用名
               
               
           
       
   

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

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

ActionBar最近学习整理之二:风格自定义_第5张图片 

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

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

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

ActionBar最近学习整理之二:风格自定义_第6张图片

                                             
       第一屏ActionBar上的图标显示是我们不想要的,如何防止默认图标的闪烁,如果调用API禁止显示图标,那么就第二屏的图标也会消失,不卖关子了,在Q1中定义的MyActionBarStyle加上显示选项的属性
    
        默认的android:displayOptions定义在Styles.xml中的Wiget.ActionBar中
    
       修改默认显示后,为了能够让你想要设置的title或者home正常显示,调用SetDisplayOptions使能对应的标志位即可。
小结
       这篇整理的问题都是较为频繁遇到且和ActionBar选项内容显示相关的,AB是ICS以后才在手机上应用的新组件,通过分析源码可以解决一些比较常见的问题,如本文所提,但是在项目中还有一项比较重要的特性需求:焦点控制,尤其是对带有实体方向导航键的手机来说,如何做好AB上的焦点控制,AB中显示选项内容是如何布局的,菜单项的构建流程又有哪些,下篇关于AB的博文中会整理分享~~
 
      ~~~版权所有,转载请注明~~~~

你可能感兴趣的:(Android整理,ActionBar,ActionItem,标题栏布局,自定义风格)