Android修改ActionBar二级菜单的外形,以及上下文菜单样式
原因:老板不喜欢android菜单那种方方正正的弹出框,所以要求修改弹出框的样式为圆角的。
我也是第一次去修改,就在网上找了类似的文章研究。发现基本上都是修改个底色就没了,并没有修改到形状。根据我上一次修改Alertdialog的外观一个逻辑,就是去找theme主要中的属性,看看有没有可以修改到的二级菜单和上下文菜单的属性,对应添加就行。
对于这种修改,是需要翻android源码的。因为现在本人在做这方面的工作,所以对安卓修改的自由度更大。如果小伙伴只是单纯的开发app,但是想要自己的UI更加个性、高度的自定义。我强烈建议去拉一个AOSP源码,否则对于一些theme参数里包含的style,app开发者是看不见的。也就是黑盒去各种试,很折磨人
<style name="templateTheme" parent="@android:style/Theme.Material.Light">
<item name="内部属性">你自定义的值</item>
</style>
向这里的Theme.Material.Light,定义在frameworks/base/core/res/res/values/themes_material.xml(PS:这个是AOSP源码的东西,如果不想下载源码的话,只能在google上找themes_material.xml源码内容,google有提供Android Source)
<style name="Theme.Material.Light" parent="Theme.Light">
<item name="colorForeground">@color/foreground_material_light</item>
<item name="colorForegroundInverse">@color/foreground_material_dark</item>
<item name="colorBackground">@color/background_material_light</item>
<item name="colorBackgroundFloating">@color/background_floating_material_light</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
<item name="disabledAlpha">@dimen/disabled_alpha_material_light</item>
<item name="primaryContentAlpha">@dimen/primary_content_alpha_material_light</item>
<!-- Text styles -->
.....
<!-- Button styles -->
.....
<!-- List attributes -->
.....
</style>
可以看到这里面包含很多可以自定义的地方。我们要修改上下文菜单和actionBar的弹出菜单,所以我们去找对应的地方,上下文菜单即context menu,通过查找,可以找到这个属性
<item name="contextPopupMenuStyle">@style/Widget.Material.ContextPopupMenu</item>
之后,我们查找这个属性Widget.Material.ContextPopupMenu,位于frameworks/base/core/res/res/values/styles_material.xml中
<style name="Widget.Material.ContextPopupMenu" parent="Widget.Material.ListPopupWindow">
<item name="overlapAnchor">true</item>
</style>
发现Widget.Material.ContextPopupMenu这个继承Widget.Material.ListPopupWindow,去找Widget.Material.ListPopupWindow
<style name="Widget.Material.ListPopupWindow">
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
<item name="popupAnimationStyle">@empty</item>
<item name="popupEnterTransition">@transition/popup_window_enter</item>
<item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
</style>
这里就是我们能够修改可以其作用的地方了,可以修改的都有类型定义。按照自己所需的自定义就好。我这里是修改了popupBackground,可以看到是一个drawable属性,所以就可以如下修改
<style name="templateTheme" parent="@android:style/Theme.Material.Light">
<item name="@android:popupBackground">@drawable/你自己定义drawable文件名</item>
</style>
这样就可以修改上下文菜单的样式了。
那么,actionbar的二级菜单也是这样修改的,只不过并不想这样的简单。要想修改二级菜单,我们就得翻源码了。当然,我也是试错过。先介绍错误实验吧。
<!--第一个属性修改之后,并没有什么用-->
<item name="popupMenuStyle">@style/Widget.Material.Light.PopupMenu</item>
<!--第二个属性,可以看到为空,说明我们可以高度的自定义,我这里是直接配置的-->
<item name="popupTheme">@null</item>
第二个属性配置如下:
<style name="CustomerPopupMenuWindow">
<!--这里的drawable,是一个圆角边框,蓝色填充-->
<item name="android:background">@drawable/context_menu_style</item>
<item name="android:textColor">@color/item_color_changed_by_mode</item>
</style>
配置之后,会发现二级菜单出现了嵌套,最外层是白色的window,内部是蓝色圆角边框的二级菜单。说明这个和alertdialog一样是在window类上有加了一个view的形式。可以参考我的alertdialog修改内容
链接
正确的翻代码
- 翻找ActionBar源码
public abstract class ActionBar //是一个抽象类,找实现 public class ToolbarActionBar extends ActionBar //这个是我们要找的toolbar类,这个其实就是我刚刚说的那个view public class WindowDecorActionBar extends ActionBar implements ActionBarOverlayLayout.ActionBarVisibilityCallback //这一个其实我上面说的window
- 在ToolbarActionBar.class中,可以找到
之后,在这个ActionMenuPresenter.class中,我找到了这个代码private DecorToolbar mDecorToolbar; //这哥也是抽象类,顺藤摸瓜,我们就找到了我们最后需要的 public class ActionBarView extends AbsActionBarView implements DecorToolbar //在这个类里,我们可以找到ActionMenuPresenter.class,看这个类的名字,就知道这是用来干啥的。就不必再解释了吧。这个实际上是定义在AbsActionBarView的。
com.android.internal.R.attr.actionOverflowMenuStyle这个就是我们要找的,之后我们themes文件找这个,会发现private OverflowPopup mOverflowPopup; private class OverflowPopup extends MenuPopupHelper { public OverflowPopup(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly) { //注意这里com.android.internal.R.attr.actionOverflowMenuStyle,这个就是我们要找到二级菜单的样式属性 super(context, menu, anchorView, overflowOnly, com.android.internal.R.attr.actionOverflowMenuStyle); setGravity(Gravity.END); setPresenterCallback(mPopupPresenterCallback); } @Override protected void onDismiss() { if (mMenu != null) { mMenu.close(); } mOverflowPopup = null; super.onDismiss(); } }
<item name="actionOverflowMenuStyle">@style/Widget.Material.Light.PopupMenu.Overflow</item> <!--styles_material.xml--> <style name="Widget.Material.Light.PopupMenu.Overflow" parent="Widget.Material.PopupMenu.Overflow"/> <!--注意对应关系和继承关系就可以看明白了--> <style name="Widget.Material.ListPopupWindow"> <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item> <item name="popupBackground">@drawable/popup_background_material</item> <item name="popupElevation">@dimen/floating_window_z</item> <item name="popupAnimationStyle">@empty</item> <item name="popupEnterTransition">@transition/popup_window_enter</item> <item name="popupExitTransition">@transition/popup_window_exit</item> <item name="dropDownVerticalOffset">0dip</item> <item name="dropDownHorizontalOffset">0dip</item> <item name="dropDownWidth">wrap_content</item> </style> <style name="Widget.Material.PopupMenu" parent="Widget.Material.ListPopupWindow"/> <style name="Widget.Material.PopupMenu.Overflow"> <item name="overlapAnchor">true</item> <item name="dropDownHorizontalOffset">-4dip</item> </style>
最后,在自己的style文件中,配置actionOverflowMenuStyle这个属性就可以了。
总结
整个过程,还是比较曲折的。我也是试了几种思路才改出来了的。希望大家提出修改意见。