Having the android.support.v7.widget.Toolbar and it's background color is changing based on different cases.
The OverflowButton button (the three vertical dots at right of the toolbar) is always having the same background when press on it.
Saw some posts about how to change statically, like quoted below. But really want is to do it dynamically based the toolbar's background color.
Any idea?
To change statically:
You can change the image used for it using the following style declaration.
<style name="MyCustomTheme" parent="style/Theme.Holo">
<item name="android:actionOverflowButtonStyle">#style/MyCustomTheme.OverFlow</item>
</style>
<style name="MyCustomTheme.OverFlow">
<item name="android:src">#drawable/my_overflow_image</item>
</style>
And if you're using ActionBarSherlock, here's how to do it
<style name="MyCustomTheme" parent="style/Theme.Sherlock">
<item name="android:actionOverflowButtonStyle">#style/MyCustomTheme.OverFlow</item>
<item name="actionOverflowButtonStyle">#style/MyCustomTheme.OverFlow</item>
</style>
<style name="MyCustomTheme.OverFlow">
<item name="android:src">#drawable/my_overflow_image</item>
</style>
Edit-- The answer should be:
It could be achieved by just to set partial transparent background in the style of actionOverflowButtonStyle so that whatever the toolbar's color will be blended in.
one solution might be just disable the press state by making it transparent for all toolbars, but it is no desirable.
Another one found, but feel like a hack.
Not sure if anyone have better way to get access to the overflow button so it's background could be set dynamically with proper drawable.
from this bug report,
it could be done as the following setupOverflowMenuButton().
public boolean setupOverflowMenuButton (Menu menu, Toolbar toolbar) {
Toolbar t = toolbar;
for(int i = 0; i < t.getChildCount(); i++) {
if(t.getChildAt(i) instanceof ActionMenuView) {
ActionMenuView v = (ActionMenuView)t.getChildAt(i);
for(int j = 0; j < v.getChildCount(); j++) {
if(v.getChildAt(j) instanceof TintImageView) {
TintImageView v1 = (TintImageView)v.getChildAt(j);
v1.setBackgroundResource(R.drawable.overflow_bt_bg_selector);
}
}
}
}
return super.onPrepareOptionsMenu(menu);
}
Related
I'm using default PopupMenu. I've customized it in my XML style, and now it has a dark style. but I have a problem now: please look at this screenShot I've prepared:
As you can see the arrow is kind of hard to see and I really wish to avoid using popup window now. Is there any way that I could change it to a white arrow?
It's been a long time, but in case anyone else runs into this problem, you can leverage a style to fix this.
it looks like the color of the arrow is controlled by android:textColorSecondary, so if you are programmatically generating a popup menu, you could do something like this (in Kotlin):
val contextThemeWrapper = ContextThemeWrapper(context, R.style.PopupMenuStyle)
val popupMenu = PopupMenu(contextThemeWrapper, view)
val menu = popupMenu.menu
menu.addSubMenu(groupId, itemId, order, groupTitle)
menu.add(groupId, itemId, order, title1)
menu.add(groupId, itemId, order, title2)
etc...
and define your PopupMenuStyle like this in your styles.xml:
<style name="PopupMenuStyle" parent="#style/<some.parent.style>">
<!--this makes an the arrow for a menu's submenu white-->
<item name="android:textColorSecondary">#android:color/white</item>
</style>
custom theme and set listMenuViewStyle
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
<item name="listMenuViewStyle">#style/listMenuViewStyle</item>
</style>
set subMenuArrow
<style name="listMenuViewStyle" parent="Base.Widget.AppCompat.ListMenuView">
<item name="subMenuArrow">#drawable/icon_sub_menu</item>
</style>
apply custom theme to activity
<activity android:name=".material.MyTopAppBarActivity"
android:theme="#style/AppTheme"/>
I've been trying to programatically inflate a linearlayout of buttons using a loop, but it doesn't show up in the ui at all. The array gets populated, however.
My button xml:
<Button xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/BlackKey">
</Button>
My style resource:
<style name="BlackKey">
<item name="android:layout_height">0dp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_weight">2</item>
<item name="android:background">#color/black</item>
<item name="android:layout_margin">3dp</item>
</style>
My initialization code:
container = (FrameLayout) findViewById(R.id.mainframe);
public Button [] BLACKKEYS = new Button[5];
LinearLayout blackkeys = new LinearLayout(this);
blackkeys.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
blackkeys.setOrientation(LinearLayout.HORIZONTAL);
for (int i = 0; i < 8; i++){
Button newbutton = (Button) LayoutInflater.from(this).inflate(R.layout.blackkey, blackkeys, false);
blackkeys.addView(newbutton);
BLACKKEYS[i] = newbutton;
}
container.addView(blackkeys);
So I went ahead and tried the code you provided using Android Studio and I have also received a blank screen. Main point that I saw that caused this is in your BlackKey style, the layout_weight is provided, the layout_height is set to 0dp, and the layout_width is MATCH_PARENT, but the orientation is horizontal -- if ever the button shows up, this will make it only show a single button.
If how I understood on how you want this to show up (five buttons alongside each other in horizontal orientation, with equal widths) something like this:
Then you can just modify the BlackKey styles like this:
<style name="BlackKey">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_weight">2</item>
<item name="android:background">#color/black</item>
<item name="android:layout_margin">3dp</item>
</style>
And also a tip, if ever you're using Android Studio, you can check first if the Button is showing in the Design Tab to see if it shows up properly on the screen. If it doesn't show up there, then try to modify it. Hope this helps you. If you have further questions, or if this is similar to the answer you're looking for but is not complete, just post a comment and I'll try to help you out. :)
I have implemented the new CollapsingToolbarLayout from Chris Banes example code.
However, the images for the backdrop image view have all a white background. The toolbar theme is ThemeOverlay.AppCompat.Dark.ActionBar so the icons are white too, thus I can't see the home button when the CollapsingToolbarLayout is fully expanded.
With app:expandedTitleTextAppearance i can set the color for the title field. Is there also a possibility to set the color of the home buttons and menu icons?
Here is the example how I change my drawer and options icons color when layout is expanded and collapsed:
protected void onCreate(Bundle savedInstanceState) {
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offset)
{
Drawable upArrow = ResourcesCompat.getDrawable(getResources(), R.drawable.drawer_icon, null);
if (offset < -200)
{
upArrow.setColorFilter(Color.parseColor("#000000"), PorterDuff.Mode.SRC_ATOP);
getSupportActionBar().setHomeAsUpIndicator(upArrow);
Drawable drawable = ContextCompat.getDrawable(getApplicationContext(),R.drawable.option_menu_icon);
drawable.setColorFilter(Color.parseColor("#000000"), PorterDuff.Mode.SRC_ATOP);
toolbar.setOverflowIcon(drawable);
}
else
{
upArrow.setColorFilter(Color.parseColor("#ffffff"), PorterDuff.Mode.SRC_ATOP);
getSupportActionBar().setHomeAsUpIndicator(upArrow);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Drawable drawable = ContextCompat.getDrawable(getApplicationContext(),R.drawable.option_menu_icon);
drawable.setColorFilter(Color.parseColor("#ffffff"), PorterDuff.Mode.SRC_ATOP);
toolbar.setOverflowIcon(drawable);
}
}
});
It is better to use 2 different images in Toolbar. Others may cause some unwanted problems:
Trying to set 2 different Toolbar themes will never work because themes are set only on Activity creation, setting another theme has no effect, you need to re-create the Activity.
Using color filter may not result what exactly you want. You may use shadowed arrow and color filter will also paint that shadow which becomes more like outer glow.
So your Toolbar will look like this:
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:contentInsetStart="0dp"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways|snap">
<ImageView
android:id="#+id/imageViewBack"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:scaleType="center"
android:src="#drawable/button_back_white" />
</android.support.v7.widget.Toolbar>
And you will set imageViewBack drawable as your Toolbar collapses:
appBarLayout.addOnOffsetChangedListener((appBarLayout, offset) -> {
final boolean isCollapsed = (offset == (-1 * appBarLayout.getTotalScrollRange()));
imageViewBack.setImageDrawable(ContextCompat.getDrawable(context,
isCollapsed ?
R.drawable.button_back_red :
R.drawable.button_back_white));
});
You can get a nice color transition while scrolling with the following approach using a ColorFilter. Hope you like Kotlin
app_bar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout: AppBarLayout, offset: Int ->
val colorComponent = Math.max(0.3f,offset.toFloat() / -appBarLayout.totalScrollRange)
toolbar.navigationIcon?.colorFilter =
PorterDuffColorFilter(Color.rgb(colorComponent, colorComponent, colorComponent), PorterDuff.Mode.SRC_ATOP)
})
This will give you a dark navigation icon when the CollapsingToolbarLayout is expanded and a white icon in collapsed state.
In my eyes this is only possible if you change the drawables of the home button, the menu icons and the overflow button. Fortunately Google has give us a new API called Tinted Drawables which allows us to set the collor of a drawable or nine-patch image. Here is how it works:
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#android:drawable/ic_menu_camera"
android:tint="#color/menu_icon_color"/>
Now you can use this new defined Drawable like any other in your layout. For the home button and the overflow button you also have to override the style definitions like so:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:homeAsUpIndicator">#drawable/tinted_home_button</item>
<item name="android:actionOverflowButtonStyle">#style/OverFlowButton</item>
</style>
<style name="OverFlowButton" parent="AppTheme">
<item name="android:src">#drawable/tinted_overflow_button</item>
</style>
All these stuff (except the style definitions) is unfortunately only available on API level 21+ and is not included into the support library. If you have to support devices lower then Lollipop I think the best way is to use the Android Assets Studio where you can tint the icons by yourself and download them as a png file.
The home button, overflow button, and some select stock icons from the sdk are affected by colorControlNormal:
<style name="ActionBar" parent="ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="colorControlNormal">#color/accent</item>
</style>
If you have other icons, you would need to loop through and manually filter them:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.sample_actions, menu);
for(int i = 0; i < menu.size(); i++){
Drawable drawable = menu.getItem(i).getIcon();
if(drawable != null) {
drawable.mutate();
drawable.setColorFilter(getResources().getColor(R.color.accent), PorterDuff.Mode.SRC_ATOP);
}
}
return true;
}
I'm moving from a custom MediaRouteButton to one inside the action-bar but it doesn't display properly. The button when custom was white which is what I wanted. However, the button is still white (and barely visible) on the action-bar even though the action-bar is of "Holo.Light" style. The button should be dark.
The button is created as an XML menu item:
<item
android:id="#+id/menu_item_media_route"
android:title="#string/menu_item_media_route"
android:actionViewClass="android.support.v7.app.MediaRouteButton"
android:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
android:showAsAction="always" />
My app is of style "#style/AppTheme":
<style name="AppTheme" parent="android:Theme.Holo.Light">
</style>
My activity of of theme "#style/FullscreenActionbarTheme":
<style name="FullscreenActionbarTheme" parent="android:Theme.Holo.Light">
<item name="android:windowFullscreen">true</item>
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowBackground">#null</item>
<item name="android:actionBarStyle">#style/FullscreenActionbar</item>
</style>
<style name="FullscreenActionbar" parent="#android:style/Widget.Holo.Light.ActionBar.Solid">
</style>
I have no custom "ic_media_route_(on|off).png" drawables -- I used to but removed them.
I've tried changing various styles and though the action-bar will turn dark, the cast button is always white. (As it should be on a dark action bar but not a light one.)
The button is fully functional, just the wrong color. The "chooser" dialog that appears when I press the button is styled "Holo.Light".
So why is my cast button colored white on a "Holo.Light" theme as though it was a "Holo" (dark) theme?
Taken from: Link
Caution: When implementing an activity that provides a media router
interface you must extend either ActionBarActivity or FragmentActivity
from the Android Support Library, even if your android:minSdkVersion
is API 11 or higher.
ActionBarActivity has been superseded by AppCompatActivity, so you should use that instead.
Support-V7 MediaRouteButton depends on this. Look at the super call:
public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(MediaRouterThemeHelper.createThemedContext(context), attrs, defStyleAttr);
....
....
}
MediaRouterThemeHelper.createThemedContext(Context):
public static Context createThemedContext(Context context) {
boolean isLightTheme = isLightTheme(context);
return new ContextThemeWrapper(context, isLightTheme ?
R.style.Theme_MediaRouter_Light : R.style.Theme_MediaRouter);
}
isLightTheme is set by resolving R.attr.isLightTheme <<== This is a support library attribute. It will not be present when your parent theme is provided by the framework, as is the case with android:Theme.Holo.Light.
private static boolean isLightTheme(Context context) {
TypedValue value = new TypedValue();
return context.getTheme().resolveAttribute(R.attr.isLightTheme, value, true)
&& value.data != 0;
}
So, isLightTheme is false & you get the dark-theme version of MediaRouteButton ==> ... always white.
Note that the Caution statement implies that your parent theme must be an AppCompat theme - AppCompatActivity (or ActionBarActivity) can't work with android:Theme.*.
Edit:
A lot of discussion took place here: Link
One can go through the chat-log to read on the approaches tried. In the end, it seems that the media-router support library needs some work to be production-ready. Read more here: MediaRouteActionProvider connection dialog theme.
If all else fails you can change the color programmatically in onCreate():
ImageButton button = ((ImageButton) toolbar.getChildAt( ... )); // The view index of the button
button.setColorFilter(Color.BLACK, PorterDuff.Mode.MULTIPLY);
I've noticed that using AppCompat themes, default toolbar icons get tinted by the attribute colorControlNormal in my style.
<style name="MyTheme" parent="Theme.AppCompat">
<item name="colorControlNormal">#color/yellow</item>
</style>
As you can see above, however, it does not happen with all icons. I provided the "plus" sign, which I got from the official icons, and it does not get tinted (I used the "white" version of the png). From what I have understood from this question, system tints only icons with just an alpha channel. Is this true?
If so: Is there a place where I can find alpha-defined, official material icons? If not - and if Toolbar icons need to be alpha-only to be tinted - how is Google expecting us to use provided icons in a Toolbar?
Somewhere in the SDK I found some icons ending in _alpha.png, and they actually get tinted well. However I need the full set of material icons, and from the official sources I could only find white, grey600 and black ones.
Applying a ColorFilter at runtime would be slightly painful, and my actual Toolbar - with some icons tinted, some others not - looks quite bad.
Another option is to use the new support for vector drawables in the support library.
See res/xml/ic_search.xml in blog post AppCompat — Age of the vectors
Notice the reference to ?attr/colorControlNormal
<vector xmlns:android="..."
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:pathData="..."
android:fillColor="#android:color/white"/>
</vector>
Here is the solution that I use. Call tintAllIcons after onPrepareOptionsMenu or the equivalent location. The reason for mutate() is if you happen to use the icons in more than one location; without the mutate, they will all take on the same tint.
public class MenuTintUtils {
public static void tintAllIcons(Menu menu, final int color) {
for (int i = 0; i < menu.size(); ++i) {
final MenuItem item = menu.getItem(i);
tintMenuItemIcon(color, item);
tintShareIconIfPresent(color, item);
}
}
private static void tintMenuItemIcon(int color, MenuItem item) {
final Drawable drawable = item.getIcon();
if (drawable != null) {
final Drawable wrapped = DrawableCompat.wrap(drawable);
drawable.mutate();
DrawableCompat.setTint(wrapped, color);
item.setIcon(drawable);
}
}
private static void tintShareIconIfPresent(int color, MenuItem item) {
if (item.getActionView() != null) {
final View actionView = item.getActionView();
final View expandActivitiesButton = actionView.findViewById(R.id.expand_activities_button);
if (expandActivitiesButton != null) {
final ImageView image = (ImageView) expandActivitiesButton.findViewById(R.id.image);
if (image != null) {
final Drawable drawable = image.getDrawable();
final Drawable wrapped = DrawableCompat.wrap(drawable);
drawable.mutate();
DrawableCompat.setTint(wrapped, color);
image.setImageDrawable(drawable);
}
}
}
}
}
This won't take care of the overflow, but for that, you can do this:
Layout:
<android.support.v7.widget.Toolbar
...
android:theme="#style/myToolbarTheme" />
Styles:
<style name="myToolbarTheme">
<item name="colorControlNormal">#FF0000</item>
</style>
This works as of appcompat v23.1.0.
I actually was able to do this on API 10 (Gingerbread) and it worked very well.
Edit: It worked on API 22 also...
Here's the final result.
Note: The icon is a drawable resource in the drawable folder(s).
Now here's how its done:
#Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem item = menu.findItem(R.id.action_refresh);
Drawable icon = getResources().getDrawable(R.drawable.ic_refresh_white_24dp);
icon.setColorFilter(getResources().getColor(R.color.colorAccent), PorterDuff.Mode.SRC_IN);
item.setIcon(icon);
}
At this point you can change it to any color you want!
That's the final and true answer
First create style for toolbar like this:
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" >
<item name="iconTint">#color/primaryTextColor</item>
<!--choice your favorite color-->
</style>
Then in your main app or activity theme add this line
<item name="actionBarPopupTheme">#style/AppTheme.PopupOverlay</item>
And finally in you'r layout file add this line to toolbar
android:theme="?attr/actionBarPopupTheme"
And Then you will see your toolbar icons colored in your favorite color
I see this question is getting some views so I'm going to post an answer for those who don't read the comments.
My conjectures in the question were all wrong and it is not a matter of alpha channels, at least not externally. The fact is simply that, quoting #alanv ,
AppCompat only tints its own icons. For now, you will need to manually
tint any icons that you're providing separately from AppCompat.
This might change in the future but also might not. From this answer you can also see the list of icons (they all belong to the internal resource folder of appcompat, so you can't change them) that are automatically tinted and with which color.
Personally I use a colorControlNormal which is black or white (or similar shades), and import the icons with that particular color. Colored icons on a colored background look a little bad. However, another solution I found pleasant is this class on github. You just call MenuColorizer.colorMenu() when you create the menu.
You could just create a custom Toolbar that uses your tint color when inflating the menu.
public class MyToolbar extends Toolbar {
... some constructors, extracting mAccentColor from AttrSet, etc
#Override
public void inflateMenu(#MenuRes int resId) {
super.inflateMenu(resId);
Menu menu = getMenu();
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
Drawable icon = item.getIcon();
if (icon != null) {
item.setIcon(applyTint(icon));
}
}
}
void applyTint(Drawable icon){
icon.setColorFilter(
new PorterDuffColorFilter(mAccentColor, PorterDuff.Mode.SRC_IN)
);
}
}
Just make sure you call in your Activity/Fragment code:
toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);
No reflection, no view lookup, and not so much code, huh?
And don't use onCreateOptionsMenu/onOptionsItemSelected, if you use this approach
For sdk 23 or higher:
<style name="AppThemeToolbar" parent="MyAppTheme">
....
<item name="android:drawableTint">#color/secondaryLightColor</item>
</style>
My toolbar
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:theme="#style/AppThemeToolbar"
android:layout_width="match_parent"
android:layout_height="attr/actionBarSize">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
With androidX you can define your Toolbar like this
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="#style/Toolbar" />
Then, extend an AppCompat theme and set colorControlNormal property as you like:
<style name="Toolbar" parent="Theme.AppCompat.Light">
<item name="colorControlNormal">#color/colorBaseWhite</item>
<item name="android:background">#color/colorPrimary</item>
</style>
This can be done in Kotlin with:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
menu.getItem(0)?.icon?.setTint(Color.WHITE)
}
else {
#Suppress("DEPRECATION")
menu.getItem(0)?.icon?.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
}
It should work on all modern versions of Android, and will fail without a crash if getItem or icon returns null.
try this ... 😊
menu.getItem(0).getIcon().setTint(Color.parseColor("#22CC34"));
#NonNull
public static Drawable setTintDrawable(#NonNull Drawable drawable, #ColorInt int color) {
drawable.clearColorFilter();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
drawable.invalidateSelf();
Drawable wrapDrawable = DrawableCompat.wrap(drawable).mutate();
DrawableCompat.setTint(wrapDrawable, color);
return wrapDrawable;
}
and call in this manner:
MenuItem location = menu.findItem(R.id.action_location);
DrawableUtils.setTintDrawable(location.getIcon(), Color.WHITE);
Basically, when you set menu, the three-dot icon takes up the color of android:textColorSecondary from the AppTheme, which in default is set to Black.
So if you are not using, textColorSecondary anywhere in your project, then you can simply add the following line
<item name="android:textColorSecondary">#color/White</item>
After adding it may look like this.
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Customise your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="android:textColorSecondary">#color/White</item>
</style>