AppCompat v21 PopupMenu style - android

I'm implementing a custom PopupMenu in my app and faced a difficulty styling it.
PopupMenu is created as:
public DropDownMenu(Context context, Button button, int menuId,
OnMenuItemClickListener listener) {
mButton = button;
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
mPopupMenu.setOnMenuItemClickListener(listener);
mButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
}
});
}
public DropDownMenu addDropDownMenu(Button button, int menuId) {
DropDownMenu menu = new DropDownMenu(mContext, button, menuId, this);
mMenus.add(menu);
return menu;
}
in styles.xml I set:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="popupMenuStyle">#style/PopupMenu</item>
<item name="textAppearanceLargePopupMenu">#style/PopupMenu.MenuItemText.Large</item>
<item name="textAppearanceSmallPopupMenu">#style/PopupMenu.MenuItemText.Small</item>
</style>
<style name="PopupMenu" parent="Widget.AppCompat.PopupMenu">
<item name="android:popupBackground">#color/anybill_blue</item>
</style>
I tried applying various styles and attributes, but no matter what I do I always see this:
How to get rid of those ridiculous incorrect layout padding? I guess it's all about I'm missing some important style attribute.
Not sure if it matters, but style worked fine when I used it without AppCompat lib in API 19 code.
UPDATE: if I use android.support.v7.widget.PopupMenu, then style is as above. If I use android.widget.PopupMenu, then no styles applied at all - it's always white background and black text, but displayed correctly

You can try to add this to your popupstyle:
<item name="android:popupElevation">0dp</item>

Related

Change background color of android action bar at runtime

I'm trying to change the background color of android action bar at runtime, according to an image VibrantColor.
I found some references, but neither worked for me.
Change action bar color in android
How do I change the background color of the ActionBar of an ActionBarActivity using XML?
I have this code to get the color from the image, and it works.
public int getImageColor(Bitmap foto) {
Palette palette = Palette.generate(foto);
List<Palette.Swatch> swatchList = palette.getSwatches();
int maiorPopulação = 0;
int color = 0;
for (Palette.Swatch sw : swatchList){
if (sw.getPopulation() > maiorPopulação) {
maiorPopulação = sw.getPopulation();
color = sw.getRgb();
}
}
return palette.getVibrantColor(color);
}
Then I try to set the returned color to the ActionBar:
public static void alteraCor(Fragment fragmento, int color) {
ActionBarActivity atividade = (ActionBarActivity)fragmento.getActivity();
android.support.v7.app.ActionBar actionBar = atividade.getSupportActionBar();
if ( fragmento instanceof Detalhes )
actionBar.setBackgroundDrawable(new ColorDrawable(Color.rgb(
Color.red(color),
Color.green(color),
Color.blue(color)
)));
else
actionBar.setBackgroundDrawable(new ColorDrawable(fragmento.getResources().getColor(R.color.fundoEscuro)));
}
I called alteraCor from onCreate and from onAttach on Fragment, but neither of them worked.
EDIT:
I use the folowing Style:
<style name="VendasFacil" parent="#style/Theme.AppCompat">
<item name="android:textColor">#color/Fonte</item>
<item name="android:background">#color/fundoPrimeiroPlano</item>
<item name="android:windowBackground">#color/fundoPrimeiroPlano</item>
<item name="actionBarTheme">#style/VendasFacil.ActionBar</item>
<item name="android:actionBarTheme" tools:ignore="NewApi">#style/VendasFacil.ActionBar</item>
</style>
<style name="VendasFacil.ActionBar" parent="#style/Widget.AppCompat.ActionBar">
<item name="android:background">#color/fundoEscuro</item>
<item name="android:displayOptions">showHome|homeAsUp|showTitle</item>
<item name="displayOptions">showHome|homeAsUp|showTitle</item>
<item name="android:homeAsUpIndicator">#drawable/ic_launcher</item>
<item name="homeAsUpIndicator">#drawable/ic_drawer</item>
</style>
I do not have the color I want to use as background previously to put it in a style file. It is as if the color was defined by the user at runtime.
You will need to create a style for that. You can then change the style at runtime to have the actionbar reflect it.
This might be useful: http://developer.android.com/guide/topics/ui/actionbar.html#AdvancedStyles
Here is an example of a style changing the actionbar color:
<style name="MyActionBar" parent="#android:style/Widget.Holo.Light.ActionBar">
<item name="android:background">ANY_HEX_COLOR_CODE</item>
</style>
You can use Android Action Bar Style Generator . It's generate XML files. Copy to your project. It's easy and nice.
Hey there appears to be a small bug in your code:
actionBar.setBackgroundDrawable(new ColorDrawable(Color.rgb(
Color.red(color),
Color.green(color),
Color.blue(color)
)));
Your are setting the r the g and the b value to the hex value of the color. What you need to do instead is simply:
actionBar.setBackgroundDrawable(new ColorDrawable(color));

Display ActionMode over Toolbar

I am trying to use the android.view.ActionMode with the new android.support.v7.widget.Toolbar, in addition to the traditional android.app.ActionBar. I am able to display it with:
toolbar.startActionMode(callback);
The problem is that the ActionMode is displayed over the ActionBar, and not over the Toolbar. Is there a way to change that?
I tryied to set the following in my theme, but it does not seem to change anything:
<item name="windowActionModeOverlay">true</item>
Since you are using the Toolbar, I also assume you are using the
AppCompatActivity and have replaced the built in ActionBar with
your custom Toolbar using setSupportActionBar(toolbar);
First of all ensure you are importing the correct namespace:
import androidx.appcompat.view.ActionMode;
// Or
import android.support.v7.view.ActionMode;
and NOT
import android.view.ActionMode;
then use
_actionMode = startSupportActionMode(this);
and NOT
_actionMode = startActionMode(this);
Do not start it on your activity, but on your toolbar. In you activity:
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.startActionMode(mActionModeCallback)
and you have to use
<item name="windowActionModeOverlay">true</item>
in your theme as stated by Andre.
Try this in your theme:
<item name="windowActionModeOverlay">true</item>
I think the one thing people are not making clear is that the line
<item name="windowActionModeOverlay">true</item>
is placed in the Base theme i.e AppTheme and not the AppTheme.NoActionBar
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="transparent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
find your AndroidManifest.xml ,next add below code in your application or Activity theme
<item name="windowActionModeOverlay">true</item>
so like:
<style name="WorkTimeListTheme" parent="AppTheme.NoActionBar">
<item name="windowActionModeOverlay">true</item>
<item name="actionModeBackground">#color/theme_primary</item>
</style>
So, after days going thru this thread, I finally got it working.
I'd like to summarize what I did.
Note: This is the solution using a Toolbar to replace the default ActionBar in AppCompatActivity.
I added this line to my AppTheme: It tells android that you want your action mode to overlay the toolbar
<item name="windowActionModeOverlay">true</item>
Use the right imports:
You have to use these imports in order to work with AppCompatActivity:
import androidx.appcompat.view.ActionMode;
// or
import android.support.v7.view.ActionMode;
Start the ActionMode on your Activity like so:
actionMode = startSupportActionMode(callback);
And not like so:
actionMode = startActionMode(callback);
You Activity creates the ActionMode on the toolbar automatically, because it's set as the supportActionToolbar.
The style handles the dsiplaying as overlay.
Thanks to #Kuffs and #Lefty.
This is the solution I made.
In my onCreateActionMode method of ActionMode.Callback, I add this:
StandaloneActionMode standaloneActionMode = (StandaloneActionMode) actionMode;
Field mContextView;
try {
mContextView = StandaloneActionMode.class.getDeclaredField("mContextView");
mContextView.setAccessible(true);
View contextView = (View) mContextView.get(standaloneActionMode);
MarginLayoutParams params = (MarginLayoutParams) contextView.getLayoutParams();
params.topMargin = mToolbar.getTop();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
It works for me.
I have tried all the methods above, but it still doesn`t work. And then, I tried the below method:
private class ActionModeCallback implements ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
actionMode.getMenuInflater().inflate(R.menu.note_find_action, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
((AppCompatActivity) getActivity()).getSupportActionBar().hide();
return false;
}
#Override
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode actionMode) {
((AppCompatActivity) getActivity()).getSupportActionBar().show();
}
}
Here, I used action mode and startSupportActionMode method of support library. At the same time I have also tried to modify the theme of given activity. Surely, it doesn`t work. So, if you really have no better choice you may try this one.
Just recently, I have found that I used the Colorful frame to enable multiple theme of my app, this will change the theme in code. When I tried to modify the style in this framework, it works.
Hope it works.
You can leave all the rubbish that only works in certain scenarios/phones/blue moons and simply hack it (in a very clean way):
Create 2 menu groups:
<menu>
<group android:id="#+id/group_normal">
<item id="#+id/action_edit"/>
</group>
<group android:id="#+id/group_action_mode"
android:visible="false">
<item id="#+id/action_mode_1"/>
<item id="#+id/action_mode_2"/>
..
</group>
</menu>
In your Fragment (or Activity):
class MyFragment: Fragment() {
private var actionMode = false
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
toolbar.apply {
setNavigationOnClickListener {
if(actionMode) endActionMode(this)
else findNavController().navigateUp()
}
setOnMenuItemClickListener {
when(it.itemId) {
R.id.action_edit -> {
// start action mode
startActionMode(this)
}
R.id.action_mode_1 -> {
// todo
}
R.id.action_mode_2 -> {
// todo
endActionMode(this)
}
...
else -> return#setOnMenuItemClickListener false
}
true
}
}
}
private fun startActionMode(toolbar: Toolbar) {
with(toolbar.menu) {
setGroupVisible(R.id.group_normal, false)
setGroupVisible(R.id.group_action_mode, true)
}
toolbar.title = 0 // todo format number of selected items
actionMode = true
}
private fun endActionMode(toolbar: Toolbar) {
with(toolbar.menu) {
setGroupVisible(R.id.group_normal, true)
setGroupVisible(R.id.group_action_mode, false)
}
toolbar.setTitle(R.string.original_title)
actionMode = false
}
}
Works every time as intended. Add extra functionality as needed.
If you see the view tree,You will can write below code:
ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
traverseView(decorView);
/**
* traverse find actionmodebar
* #param root view
*/
public void traverseView(View root) {
if (root==null){
return;
}
if (root instanceof ActionBarContextView){
root.setVisibility(View.GONE);
return;
}
if ((root instanceof ViewGroup)) { // If view is ViewGroup, apply this method on it's child views
ViewGroup viewGroup = (ViewGroup) root;
for (int i = 0; i < viewGroup.getChildCount(); ++i) {
traverseView(viewGroup.getChildAt(i));
}
}
}
Make sure to put this line:
<item name="windowActionModeOverlay">true</item>
in the style of the theme that you're using in your app (it's written in your manifest file).
After hours of debugging this problem, I realized that I was making changes to a style that I DIDN'T EVEN USE!

MediaRouteActionProvider connection dialog theme

I've tried to change theme of the MediaRouteActionProvider connection dialog. I using in my application a Light theme with Dark Actionbar, so the dialog have dark gray content, but the background is dark..
When the app is connected to a device, the other dialogs are ok, they have white background with the correct theme. (For exmaple in VideoMediaRouteControllerDialog and on the disconnect dialog.)
Have you any idea, how can I change the connection dialog's theme?
Thank you very much!
//Screenshot 1: Connection dialog (with the theme issue)
//Screenshot 2: Controller dialog (with the right, needed theme)
Unfortunately that dialog doesn't follow the standard theme (Dialogs in Android are all pretty unfriendly in general but that one is among the hardest to work with). Since that dialog is provided by media router, you can only provide a customized theme if you replace that completely with your own dialog.
You can try subclassing MediaRouteDialogFactory and override onCreateChooserDialogFragment() method and pass your implementation to the ActionProvide:
mediaRouteActionProvider.setDialogFactory(yourDialogFactoryImlementation)
You can take a look at the CCL where I do a similar thing not for the chooser dialog but for the controller.
Right now theming these Dialogs have issue - wrong theme applied to Dialog
You can override themes used in MediaRouterThemeHelper
<style name="Theme.MediaRouter.Light.DarkControlPanel">
<item name="mediaRoutePlayDrawable">#drawable/mr_ic_play_dark</item>
<item name="mediaRoutePauseDrawable">#drawable/mr_ic_pause_dark</item>
<item name="mediaRouteCastDrawable">#drawable/mr_ic_cast_dark</item>
<item name="mediaRouteAudioTrackDrawable">#drawable/ic_audiotrack</item>
<item name="mediaRouteControllerPrimaryTextStyle">#style/Widget.MediaRouter.ControllerText.Primary.Dark</item>
<item name="mediaRouteControllerSecondaryTextStyle">#style/Widget.MediaRouter.ControllerText.Secondary.Dark</item>
</style>
<style name="Theme.MediaRouter.LightControlPanel">
<item name="mediaRoutePlayDrawable">#drawable/mr_ic_play_light</item>
<item name="mediaRoutePauseDrawable">#drawable/mr_ic_pause_light</item>
<item name="mediaRouteCastDrawable">#drawable/mr_ic_cast_light</item>
<item name="mediaRouteAudioTrackDrawable">#drawable/mr_ic_audiotrack_light</item>
<item name="mediaRouteControllerPrimaryTextStyle">#style/Widget.MediaRouter.ControllerText.Primary.Light</item>
<item name="mediaRouteControllerSecondaryTextStyle">#style/Widget.MediaRouter.ControllerText.Secondary.Light</item>
</style>
What I did was pulling the mediarouter appcompat library source from GitHub, then I fixing the theming and rebuilding the whole thing into my own custom mediarouter library.
What you're looking for in the code is MediaRouteChooserDialog, and even there, the constructor that only takes a Context as a parameter, as that's the one being called by onCreateChooserDialog() in MediaRouteChooserDialogFragment.
I was lazy so I simply put android.R.style.Theme_Holo_Light_Dialog instead of the 0 in the constructor, and it worked just fine. But of course you can always look for a more sophisticated solution.
I made it work similar as #Naddaf described it. You need to extend MediaRouteDialogFactory (you can set this to the MediaRouteActionProvider or MediaRouteButton with setDialogFactory() ) and override the method:
#Override
public MediaRouteChooserDialogFragment onCreateChooserDialogFragment(){
return new CustomMediaRouteChooserDialogFragment();
}
Then in your CustomMediaRouteChooserDialogFragment override:
#Override
public CustomMediaRouteChooserDialog onCreateChooserDialog(Context context, Bundle savedInstanceState)
{
return new CustomMediaRouteChooserDialog(context);
}
And in the CustomMediaRouteChooserDialog create a constructor, where you set your holo light theme.
public CustomMediaRouteChooserDialog(Context context)
{
super(context, android.R.style.Theme_Holo_Light_Dialog);
}
Hope this helps!
Based on the other answers, this worked for me:
set a custom action provider in the menu item
<item
android:id="#+id/media_route_menu_item"
android:title="#string/cast_menu_title"
app:actionProviderClass="MediaRouteActionProviderThemeLight"
app:showAsAction="always"/>
this is the custom action provider using a light theme
public class MediaRouteActionProviderThemeLight extends MediaRouteActionProvider {
private static final int THEME_DIALOG = android.support.v7.mediarouter.R.style.Theme_MediaRouter_Light;
/**
* Creates the action provider.
*
* #param context The context.
*/
public MediaRouteActionProviderThemeLight(Context context) {
super(context);
setDialogFactory(new MediaRouteDialogFactoryThemeLight());
}
private static class MediaRouteDialogFactoryThemeLight extends MediaRouteDialogFactory {
#NonNull
#Override
public MediaRouteChooserDialogFragment onCreateChooserDialogFragment() {
return new MediaRouteChooserDialogFragmentThemeLight();
}
#NonNull
#Override
public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
return new MediaRouteControllerDialogFragmentThemeLight();
}
}
public static class MediaRouteChooserDialogFragmentThemeLight extends MediaRouteChooserDialogFragment {
#Override
public MediaRouteChooserDialog onCreateChooserDialog(Context context, Bundle savedInstanceState) {
return new MediaRouteChooserDialog(context, THEME_DIALOG);
}
}
public static class MediaRouteControllerDialogFragmentThemeLight extends MediaRouteControllerDialogFragment {
#Override
public MediaRouteControllerDialog onCreateControllerDialog(Context context, Bundle savedInstanceState) {
return new MediaRouteControllerDialog(context, THEME_DIALOG);
}
}
}
take into account the dialog with play/pause buttons and volume control use the material colors from your main theme, colorPrimary as background and textColorPrimary for the title/subtitle. In case your app use dark theme you should overwrite the background using the theme below, and change the THEME_DIALOG constant in the class MediaRouteActionProviderThemeLight:
<style name="CastAppThemeMediaRouter" parent="Theme.MediaRouter.Light">
<item name="colorPrimaryDark">#color/primary_dark_material_light</item>
<item name="colorPrimary">#color/primary_material_light</item>
<item name="colorAccent">#color/accent_material_light</item>
</style>
To use a light theme with dark controls use the following theme. Be sure to set as primaryColor a dark color, the volume bar is set to light/dark automatically based in the primaryColor.
<style name="CastThemeMediaRouter" parent="Theme.MediaRouter.Light.DarkControlPanel">
<item name="colorPrimary">#color/black</item>
</style>

Android Custom Icon ShareActionProvider?

i'm using a ShareActionProvider, but i want to custom the icon (i want to change the color, because currently, it's white).
I'm using this code :
mShareActionProvider = (ShareActionProvider) item.getActionProvider();
Intent myIntent = new Intent(Intent.ACTION_SEND);
myIntent.setType("text/plain");
myIntent.putExtra(Intent.EXTRA_TEXT, str_share);
mShareActionProvider.setShareIntent(myIntent);
The XML :
<item
android:id="#+id/menu_item_share"
android:showAsAction="ifRoom"
android:title="#string/titlePartager"
android:actionProviderClass="android.widget.ShareActionProvider"
android:icon="#drawable/ic_share"/>
How can i change the icon (or color) ?
thx,
Edit / Short answer: if using AppCompat's ShareActionProvider, just provide a new actionModeShareDrawable in your theme definition.
<style name="MyTheme" parent="Theme.AppCompat">
<item name="actionModeShareDrawable">#drawable/my_share_drawable</item>
</style>
If not using AppCompat, then this resource is defined for Lollipor or newer, but not for previous versions.
Below is answer for the native ShareActionProvider (which was the original scope of this question).
To change this image, you should change the value of actionModeShareDrawable for your app's theme. Take a look at the ShareActionProvider's onCreateActionView() method:
public View onCreateActionView() {
// Create the view and set its data model.
...
// Lookup and set the expand action icon.
TypedValue outTypedValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId);
...
Unfortunately this attribute is not public in the Android framework (though it is if using compatibility libraries, such as AppCompat or ActionBarSherlock). In that case, it's just a matter of overriding that value for the theme.
If you are using neither of these libraries, the only solution (that I know of) is to create a subclass of ShareActionProvider and reimplement the onCreateActionView() method. You can then use whatever drawable you want instead.
EDIT However this is further complicated by the fact that the implementation of onCreateActionView() uses other classes that are not public either. To avoid duplicating a lot of code, you can just change the icon via reflection, like this:
public class MyShareActionProvider extends ShareActionProvider
{
private final Context mContext;
public MyShareActionProvider(Context context)
{
super(context);
mContext = context;
}
#Override
public View onCreateActionView()
{
View view = super.onCreateActionView();
if (view != null)
{
try
{
Drawable icon = ... // the drawable you want (you can use mContext to get it from resources)
Method method = view.getClass().getMethod("setExpandActivityOverflowButtonDrawable", Drawable.class);
method.invoke(view, icon);
}
catch (Exception e)
{
Log.e("MyShareActionProvider", "onCreateActionView", e);
}
}
return view;
}
}
As with any solutions that involve reflection, this may be brittle if the internal implementation of ShareActionProvider changes in the future.
To change the icon for the ShareActionProvider you need to extend the AppCompat theme and set your custom icon to "actionModeShareDrawable":
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="actionModeShareDrawable">#drawable/ic_share</item>
</style>
You can change background color by defining custom style, as:
<resources>
<style name="MyTheme" parent="#android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">#style/MyActionBar</item>
</style>
<style name="MyActionBar" parent="#android:style/Widget.Holo.Light.ActionBar">
<item name="android:background">ANY_HEX_COLOR_CODE</item>
</style>
</resources>
Now you need to set "MyTheme" as theme for application / activity.

Change ActionMode Overflow icon

Is there a way to change the ActionMode Overflow icon without changing the icon for the "normal" ActionBar?
I still need to figure out how to only change the Overflow-Icon inside of the ActionMode-Actionbar as I changed my Overflow-Icon in the default-Actionbar which is not visible in the ActionMode-Actionbar (and no, I don't want to change the background of my ActionMode-Actionbar!)
Okay.
Let's start with defining some styles. I will try and explain why we are defining them in this fashion:
// This is just your base theme. It will probably include a lot more stuff.
// We are going to define the style 'OverflowActionBar' next.
<style name="BaseTheme" parent="android:Theme.Holo.Light">
....
....
....
<item name="android:actionOverflowButtonStyle">#style/OverflowActionBar</item>
</style>
// Assigning a parent to this style is important - we will inherit two attributes -
// the background (state-selector) and the content description
<style name="OverflowActionBar" parent="#android:style/Widget.Holo.ActionButton.Overflow">
<item name="android:src">#drawable/overflow_menu_light</item>
</style>
// Next up is an extension to our 'BaseTheme'. Notice the parent here.
<style name="ChangeOverflowToDark" parent="#style/BaseTheme">
<item name="android:actionOverflowButtonStyle">#style/OverflowActionMode</item>
</style>
// One last thing is to define 'OverflowActionMode'. Again, we inherit useful
// attributes by assigning 'Widget.Holo.ActionButton.Overflow' as the parent.
<style name="OverflowActionMode" parent="#android:style/Widget.Holo.ActionButton.Overflow">
<item name="android:src">#drawable/overflow_menu_dark</item>
</style>
All our work with styles.xml is done. The very last bit happens at runtime. I suppose you already have an implementation of ActionMode.Callback.
In your activity, define a method - changeOverflowIcon():
public void changeOverflowIcon() {
getTheme().applyStyle(R.style.ChangeOverflowToDark, true);
}
You will be calling this method from onCreateActionMode(...) of your ActionMode.Callback implementation:
public class CustomActionModeCallback implements ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
changeOverflowIcon()
// other initialization
return true;
}
#Override
public boolean onPrepareActionMode(final ActionMode mode, Menu menu) {
return true;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {}
}
A bit of explanation:
The assignment in 'BaseTheme' is for the ActionBar. It will pick the drawable overflow_menu_light since we are assigning it in the base theme of your app.
getTheme().applyStyle(R.style.ChangeOverflowToDark, true)
The second argument true forces the current theme to override the old attributes with the new ones. Since we only define one attribute in ChangeOverflowToDark, its value is overwritten. The ActionBar is not affected because it has already used the old attribute. But, the action mode is yet to be created (it will be created when we return true from onCreateActionMode(...)). When the action mode checks for this attributes value, it gets the new one.
There's more...
The answer given by Manish is quite awesome. I could have never thought of using the content description to find the exact ImageButton. But what if you could find the ImageButton using a straightforward findViewById()?
Here's how you can:
First, we will need unique ids. If your project doesn't currently have a res/values/ids.xml file, create one. Add a new id to it:
<item type="id" name="my_custom_id" />
The setup I discussed above will remain the same. The only difference will be in OverflowActionMode style:
<style name="OverflowActionMode" parent="#android:style/Widget.Holo.ActionButton.Overflow">
<item name="android:src">#drawable/overflow_menu_dark</item>
<item name="android:id">#id/my_custom_id</item>
</style>
The id we defined above will be assigned to the ImageButton when we call getTheme().applyStyle(R.style.ChangeOverflowToDark, true);
I'll borrow the code snippet from Manish's answer here:
private ActionMode.Callback mCallback = new ActionMode.Callback()
{
#Override
public boolean onPrepareActionMode( ActionMode mode, Menu menu )
{
mDecorView.postDelayed(new Runnable() {
#Override
public void run() {
ImageButton btn = (ImageButton) mDecorView.findViewById(R.id.my_custom_id);
// Update the image here.
btn.setImageResource(R.drawable.custom);
}
}, 500); // 500 ms is quite generous // I would say that 50 will work just fine
return true;
}
}
Best of both worlds?
Let's say we need R.drawable.overflow_menu_light for ActionBar and R.drawable.overflow_menu_dark for ActionMode.
Styles:
<style name="BaseTheme" parent="android:Theme.Holo.Light">
....
....
....
<item name="android:actionOverflowButtonStyle">#style/OverflowActionMode</item>
</style>
<style name="OverflowActionMode" parent="#android:style/Widget.Holo.ActionButton.Overflow">
<item name="android:src">#drawable/overflow_menu_dark</item>
<item name="android:id">#id/my_custom_id</item>
</style>
As defined in our style, the ActionBar will pick R.drawable.overflow_menu_dark - but don't we need the light version for the ActionBar? Yes - we will assign that in the activity's onPrepareOptionsMenu(Menu) callback:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ImageButton ib = (ImageButton)
getWindow().getDecorView()
.findViewById(R.id.my_custom_id);
if (ib != null)
ib.setImageResource(R.drawable.overflow_menu_light);
}
}, 50L);
return super.onPrepareOptionsMenu(menu);
}
We are doing this here because before onPrepareOptionsMenu(Menu), the ImageButton would not have been created.
Now, we don't need to deal with ActionMode - because it will pick the dark drawable from the theme.
My apologies for this gigantic post. I really hope it helps.
ImageButton is the widget used to display the menu overflow. actionOverflowButtonStyle is used for styling the ImageButton. This styling is applied in ActionMenuPresenter.
private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
public OverflowMenuButton(Context context) {
super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
...
}
}
ActionMenuPresenter class is used for building action menus both in action bar and action modes. Hence by overriding the theme files will apply same style in both modes. The only way to accomplish is it programatically as it is done here for the action bar.
Here is the code of how it can be done for action mode overflow icon. You can assign the drawable to the ImageButton in ActionMode.Callback.onPrepareActionMode method.
public class MainActivity extends Activity {
ViewGroup mDecorView;
public void onCreate(Bundle savedInstanceState) {
// Assign mDecorView to later use in action mode callback
mDecorView = (ViewGroup) getWindow().getDecorView();
}
private ActionMode.Callback mCallback = new ActionMode.Callback()
{
#Override
public boolean onPrepareActionMode( ActionMode mode, Menu menu )
{
// We have to update the icon after it is displayed,
// hence this postDelayed variant.
// This is what I don't like, but it is the only way to move forward.
mDecorView.postDelayed(new Runnable() {
#Override
public void run() {
ArrayList<View> outViews = new ArrayList<View>();
// The content description of overflow button is "More options".
// If you want, you can override the style and assign custom content
// description and use it here.
mDecorView.findViewsWithText(outViews, "More Options", View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if(!outViews.isEmpty()) {
View v = outViews.get(0);
if(v instanceof ImageButton) {
ImageButton btn = (ImageButton) v;
// Update the image here.
btn.setImageResource(R.drawable.custom);
}
}
}
}, 500);
return true;
}
}
}
You should be able to do that using styles:
ActionBarSherlock:
<style name="MyTheme" parent="Theme.Sherlock.Light">
<item name="actionOverflowButtonStyle">#style/MyTheme.OverFlow</item>
</style>
<style name="MyTheme.OverFlow" parent="Widget.Sherlock.ActionButton.Overflow">
<item name="android:src">#drawable/YOUR_ICON_GOES_HERE</item>
</style>
ActioBar:
<style name="MyTheme" parent="#android:style/Theme.Holo">
<item name="android:actionOverflowButtonStyle">#style/MyTheme.OverFlow</item>
</style>
<style name="MyTheme.OverFlow" parent="#android:style/Widget.Holo.ActionButton.Overflow">
<item name="android:src">#drawable/YOUR_ICON_GOES_HERE</item>
</style>
Make sure to set MyTheme in the manifest.
Is there a way to change the ActionMode Overflow icon without changing the icon for the "normal" ActionBar?
Regards how to change the overflow icon, I think there are many answers as above.
If you just want to change the color of the overflow icon, you can use a simple way.
<style name="BaseAppTheme" parent="Theme.xxxx.Light.NoActionBar.xxx">
...
<item name="actionOverflowButtonStyle">#style/ActionMode.OverFlow</item>
</style>
<style name="ActionMode.OverFlow" parent="#style/Widget.AppCompat.ActionButton.Overflow">
<item name="android:tint">#color/black</item> #or any color you want.#
</style>
It works for me. I investigated a bit, just check this screenshot http://prntscr.com/vqx1ov you will know the reason.
And I don't suggest to set the colour of colorControlNormal, it will change the color of "back arrow" and "overflow icon" on ActionBar.
In my case, I just want a different color of the three dots icon, and to achieve it, I set <item name="actionBarTheme">#style/Widget.ActionMode.ActionBar</item> in my theme, and Widget.ActionMode.ActionBar looks like below:
<style name="Widget.ActionMode.ActionBar" parent="#style/ThemeOverlay.AppCompat.Light">
<item name="colorControlNormal">the color I want</item>
</style>

Categories

Resources