Related
I'm adding the new Bottom Navigation View from the material design library to a project, and I would like to have no pre selected item by default.
For now first item is selected by default.
I have used
mBottomNavigation.getMenu().getItem(0).setChecked(false);
but when doing it in for loop for all the menu item last item is selected again by default.
Is there a way we can achieve this?
Not sure about the proper way to achieve this but a work around will help-
setCheckable(false) for first item
navigation.getMenu().getItem(0).setCheckable(false);
item.setCheckable(true) inside onNavigationItemSelected()
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
item.setCheckable(true);
mTextMessage.setText(R.string.title_home);
return true;
}
return false;
}
I came up with another solution
Just add one more item to your menu.xml file for example
This is my bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/navigation_home"
android:icon="#drawable/ic_home_black_24dp"
android:title="#string/home" />
<item
android:id="#+id/navigation_cart"
android:icon="#drawable/ic_shopping_cart_black_24dp"
android:title="#string/cart" />
<item
android:id="#+id/navigation_wishlist"
android:icon="#drawable/ic_favorite_border_black_24dp"
android:title="#string/wish_list" />
<item
android:id="#+id/navigation_account"
android:icon="#drawable/ic_person_black_24dp"
android:title="#string/account" />
<!-- Our invisible item -->
<item
android:id="#+id/invisible"
android:visible="false"
android:icon="#drawable/ic_person_black_24dp"
android:title="#string/account" />
</menu>
Notice that I have added that item at last position and given it an id invisible and also set it's visibility to false.
Now, In the activity just set selected item id to this id like this
bottomNavMenu.setSelectedItemId(R.id.invisible);
Thanks
XML Code
<group android:checkableBehavior="single">
<item android:id="#+id/nav_item1" />
<item android:id="#+id/nav_item2" />
<item android:id="#+id/nav_item3" />
<item android:id="#+id/nav_item4" />
<item
android:id="#+id/nav_item5"
android:icon="#drawable/icon_item5"
android:title="Home"
android:visible="false"/>
</group>
JAVA Code
bottomNavigationView.getMenu().getItem(4).setChecked(true);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.nav_item1:
return true;
case R.id.nav_item2:
return true;
case R.id.nav_item3:
return true;
case R.id.nav_item4:
return true;
}
// Default operation you want to perform
return false;
}
});
If anyone interested in a nice Kotlin solution, here's mine:
//disable the preselected first item
//<navigation> is the bottom navigation view
navigation.menu.getItem(0).isCheckable=false
Then in the selection listener, make sure that you'll show the user what he selected
BottomNavigationView.OnNavigationItemSelectedListener { item: MenuItem ->
when (item.itemId) {
R.id.option1 -> {
item.isCheckable=true //here is the magic
//notify the listener
return#OnNavigationItemSelectedListener true
}
R.id.option2 ->{
item.isCheckable=true
//notify the listener
return#OnNavigationItemSelectedListener true
}
R.id.option3 ->{
//go to forgot user fragment
item.isCheckable=true
//notify the listener
return#OnNavigationItemSelectedListener true
}
else -> false
}
}
Finally , make a selector color so you can change easily in color
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true"
android:color="#color/colorAccent" />
<item android:color="#color/colorPrimary" />
And add the selector to the navigation view
app:itemIconTint="#color/navigation_colors"
app:itemTextColor="#color/navigation_colors"
Now, if you need to change the colours, just change the selector.
Add this line in your onCreate method
mBottomNavigation.setSelectedItemId("ID OF YOUR MENU ITEM");
Use setCheckable instead of setChecked(false) in line:
mBottomNavigation.getMenu().getItem(0).setCheckable(false);
It works for me.
I've created a sample project and by default there is no checked item. Just make sure you don't have any navigationView.setCheckedItem(R.id.id) on your code.
My solution was to select a different tab and immediately after select the tab I initially wanted.
bottomNavigationView.selectedItemId = R.id.dummy_tab
bottomNavigationView.selectedItemId = R.id.tab_to_select
The setOnItemReselectedListener disables reselect tab selections, but the navigationView has a default tab selected and don't render after creation because is invoking setOnItemReselectedListener
Tried the invisible solution but didn't work with programmatically bottomMenu creation.
Old question, but if you still facing issue - just use this:
bottomNavigationView.getMenu().getItem(0).setCheckable(false);
And it:
bottomNavigationView.setOnItemReselectedListener(new NavigationBarView.OnItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
if (item.getItemId() == R.id.navigation_home) {
item.setCheckable(true);
}
}
});
I combined the solution mentioned by #Ashish Kumar and resolved my query and
private void customizeBottomBar() {
mBottomNavigation.getMenu().getItem(0)
.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_reserve_normal));
changeMenuItemCheckedStateColor(mBottomNavigation, getUnCheckedColor(), getUnCheckedColor());
}
/**
* Method to change the color state of bottom bar view
* #param bottomNavigationView - BottomNavigation view instance
* #param checkedColorHex int value of checked color code
* #param uncheckedColorHex int value of unchecked color code
*/
void changeMenuItemCheckedStateColor(BottomNavigationView bottomNavigationView,
int checkedColorHex, int uncheckedColorHex) {
int[][] states = new int[][]{
new int[]{-android.R.attr.state_checked}, // unchecked
new int[]{android.R.attr.state_checked}, // checked
};
int[] colors = new int[]{
uncheckedColorHex,
checkedColorHex
};
ColorStateList colorStateList = new ColorStateList(states, colors);
bottomNavigationView.setItemTextColor(colorStateList);
bottomNavigationView.setItemIconTintList(colorStateList);
}
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>
I want to have similar menu item functionality as in the chrome browser for mobile as it is in the picture. I want to have back, forward, refresh menu items in a single row. How can I implement a similar menu item? is there any reference or is there is any hack to bring this functionality?
My app is aimed only for tablets. Here is my current Action bar menu item:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/favorite"
android:showAsAction="always"
android:title="Happy"/>
<item
android:id="#+id/share_button"
android:icon="#drawable/share"
android:showAsAction="always"
android:title="Share"/>
<item
android:id="#+id/hola_button"
android:showAsAction="always"
android:title="Hola"/>
<item
android:icon="#drawable/more_actions"
android:showAsAction="always">
<menu>
<item
android:id="#+id/back_button"
android:icon="#drawable/back"
android:title="back"/>
<item
android:id="#+id/forward_button"
android:icon="#drawable/forward"
android:title="forward"/>
<item
android:id="#+id/refresh_button"
android:icon="#drawable/refresh"
android:title="refresh"/>
</menu>
</item>
</menu>
This looks like a customized Dialog with a listview and a custom listheader
OR
a listview below a simple layout with 3 buttons on top within a dialog.
You could show the same on the actionbar menu item click.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getSupportMenuInflater().inflate(R.menu.menu_items, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.show_dlg:
// Show your custom dialog
break;
}
return super.onOptionsItemSelected(item);
}
Also this tutorial would help as a reference for inflating custom menus
http://www.codeproject.com/Articles/173121/Android-Menus-My-Way
EDIT:
This examples is as an OverflowMenu (since ABS ditched the overflow-theme). you can inflate any kind of layout-combinations. This class extends from PopUpWindow and doesn't use the typical onCreateOptions. It uses the ABS-CustomView and a PopUpWindow to create menu's.
From android reference: A popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current activity.
The layout looks similar to your requested layout. This view is anchored to the ActionBar but you can display it anywhere you want. Popup window supports many show functions out of the box.
Customizable OverflowMenu
public class OverflowMenu extends PopupWindow {
private View mContentView;
public OverflowMenu(Context context, int resourceId) {
super(context);
mContentView = LayoutInflater.from(context).inflate(resourceId, null);
setContentView(mContentView);
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
// Handle touchevents
setOutsideTouchable(true);
setFocusable(true);
setBackgroundDrawable(new BitmapDrawable());
}
/**
* Attach the OverflowMenu View to the ActionBar's Right corner
* #param actionBarView
*/
public void show(View actionBarView) {
int x = actionBarView.getMeasuredWidth() - mContentView.getMeasuredWidth();
showAsDropDown(actionBarView, x, 0);
}
/**
* Return mContentView,
* used for mContentView.findViewById();
* #return
*/
public View getView(){
return mContentView;
}
}
MainActivity
public class MainActivity extends SherlockActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mai n);
final ActionBar actionBar = getSupportActionBar();
actionBar.setCustomView(R.layout.actionbar);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
final OverflowMenu menu = new OverflowMenu(this,R.layout.menu_overflow);
final ImageButton overflowBtn = (ImageButton) actionBar.getCustomView().findViewById(R.id.actionbar_overflow);
overflowBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
menu.show(actionBar.getCustomView());
}
});
}
}
You could add the menu item to the page with something like this :
OptionCommand command = new OptionCommand();
command.setActionView(view);
command.setIcon(canvas.getContext().getResources().getDrawable(android.R.drawable.ic_menu_search));
command.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
canvas.getActivity().getOptionCommands().add(command);
I'm having a bit of trouble customizing the search icon in the SearchView. On my point of view, the icon can be changed in the Item attributes, right? Just check the code bellow..
Can someone tell me what I'm doing wrong?
This is the menu I'm using, with my custom search icon icn_lupa. But when I run the app, I always get the default search icon...
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_search"
android:title="#string/menu_search"
android:icon="#drawable/icn_lupa"
android:showAsAction="always"
android:actionViewClass="android.widget.SearchView" />
</menu>
I've found another way to change the search icon which goes in the same line as Diego Pino's answer but straight in onPrepareOptionsMenu.
In your menu.xml (same as before)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/action_search"
android:icon="#drawable/ic_action_fav"
android:title="#string/action_websearch"
android:showAsAction="always|never"
android:actionViewClass="android.widget.SearchView" />
</menu>
In your activity:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem searchViewMenuItem = menu.findItem(R.id.action_search);
mSearchView = (SearchView) searchViewMenuItem.getActionView();
int searchImgId = getResources().getIdentifier("android:id/search_button", null, null);
ImageView v = (ImageView) mSearchView.findViewById(searchImgId);
v.setImageResource(R.drawable.your_new_icon);
mSearchView.setOnQueryTextListener(this);
return super.onPrepareOptionsMenu(menu);
}
I followed the example for changing the edittext in this example.
You should be able to do this for all icons/backgrounds in your SearchView, to find the right ID you can check here.
UPDATE November 2017:
Since this answer android has been updated with the possibility of changing the search icon through the XML.
If you target anything below android v21 you can use:
<android.support.v7.widget.SearchView
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:searchIcon="#drawable/ic_search_white_24dp"
app:closeIcon="#drawable/ic_clear_white_24dp" />
Or v21 and later:
<SearchView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:searchIcon="#drawable/ic_search_white_24dp"
android:closeIcon="#drawable/ic_clear_white_24dp" />
And there are even more options:
closeIcon
commitIcon
goIcon
searchHintIcon
searchIcon
voiceIcon
Nice answer from #just_user
For my case, since I am using the appcompat v7 library for the SearchView + ActionBar, i modified his solution a bit to make it compatible to my project, it should work so as long as you did not modify anything when you added appcompat v7 as library
XML:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:metrodeal="http://schemas.android.com/apk/res-auto" >
<item
android:id="#+id/main_menu_action_search"
android:orderInCategory="100"
android:title="#string/search"
metrodeal:showAsAction="always"
metrodeal:actionViewClass="android.support.v7.widget.SearchView"
android:icon="#drawable/search_btn"/>
</menu>
Java code:
#Override
public void onPrepareOptionsMenu(Menu menu) {
MenuItem searchViewMenuItem = menu.findItem(R.id.main_menu_action_search);
SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchViewMenuItem);
int searchImgId = android.support.v7.appcompat.R.id.search_button; // I used the explicit layout ID of searchview's ImageView
ImageView v = (ImageView) mSearchView.findViewById(searchImgId);
v.setImageResource(R.drawable.search_btn);
super.onPrepareOptionsMenu(menu);
}
Excuse for the very big icon (I have not resized the icon just yet), but it should work as it is.
I was struggling with this too but then I accidentaly used 'collapseActionView' and that fixed it!
My menu.xml looks like this now:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_search"
android:title="#string/menu_search"
android:showAsAction="always|withText|collapseActionView"
android:actionViewClass="android.widget.SearchView"
android:icon="#android:drawable/ic_menu_search" />
</menu>
The downside of this is that on tablets the SearchView will appear on the left side of the ActionBar instead of where the searchicon is, but I don't mind that.
I defined a style to do it .
here is my xml:
<android.support.v7.widget.SearchView
android:id="#+id/sv_search"
android:icon="#android:drawable/ic_menu_search"
android:layout_width="fill_parent"
**style="#style/CitySearchView"**
android:layout_height="wrap_content"/>
and this is my style:
<style name="CitySearchView" parent="Base.Widget.AppCompat.SearchView">
<item name="searchIcon">#drawable/ic_more_search</item>
</style>
That it!
After finish that,just take a look at Base.Widget.AppCompat.SearchView.
<style name="Base.Widget.AppCompat.SearchView" parent="android:Widget">
<item name="layout">#layout/abc_search_view</item>
<item name="queryBackground">#drawable/abc_textfield_search_material</item>
<item name="submitBackground">#drawable/abc_textfield_search_material</item>
<item name="closeIcon">#drawable/abc_ic_clear_mtrl_alpha</item>
<item name="searchIcon">#drawable/abc_ic_search_api_mtrl_alpha</item>
<item name="goIcon">#drawable/abc_ic_go_search_api_mtrl_alpha</item>
<item name="voiceIcon">#drawable/abc_ic_voice_search_api_mtrl_alpha</item>
<item name="commitIcon">#drawable/abc_ic_commit_search_api_mtrl_alpha</item>
<item name="suggestionRowLayout">#layout/abc_search_dropdown_item_icons_2line</item>
</style>
every item can be override by define a new style .
Hope it helps!
There's a way to do this. The trick is to recover the ImageView using its identifier and setting a new image with setImageResource(). This solution is inspired on Changing the background drawable of the searchview widget.
private SearchView searchbox;
private void customizeSearchbox() {
setSearchHintIcon(R.drawable.new_search_icon);
}
private void setSearchHintIcon(int resourceId) {
ImageView searchHintIcon = (ImageView) findViewById(searchbox,
"android:id/search_mag_icon");
searchHintIcon.setImageResource(resourceId);
}
private View findViewById(View v, String id) {
return v.findViewById(v.getContext().getResources().
getIdentifier(id, null, null));
}
SearchView searchView = (SearchView) findViewById(R.id.address_search);
try {
Field searchField = SearchView.class
.getDeclaredField("mSearchButton");
searchField.setAccessible(true);
ImageView searchBtn = (ImageView) searchField.get(searchView);
searchBtn.setImageResource(R.drawable.search_glass);
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
}
After some research I found the solution here. The trick is that the icon is not in an ImageView but in the Spannable object.
// Accessing the SearchAutoComplete
int queryTextViewId = getResources().getIdentifier("android:id/search_src_text", null, null);
View autoComplete = searchView.findViewById(queryTextViewId);
Class<?> clazz = Class.forName("android.widget.SearchView$SearchAutoComplete");
SpannableStringBuilder stopHint = new SpannableStringBuilder(" ");
stopHint.append(getString(R.string.your_new_text));
// Add the icon as an spannable
Drawable searchIcon = getResources().getDrawable(R.drawable.ic_action_search);
Method textSizeMethod = clazz.getMethod("getTextSize");
Float rawTextSize = (Float)textSizeMethod.invoke(autoComplete);
int textSize = (int) (rawTextSize * 1.25);
searchIcon.setBounds(0, 0, textSize, textSize);
stopHint.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Set the new hint text
Method setHintMethod = clazz.getMethod("setHint", CharSequence.class);
setHintMethod.invoke(autoComplete, stopHint);
In menu xml:
<item
android:id="#+id/menu_filter"
android:actionLayout="#layout/menu_filter"
android:icon="#drawable/ic_menu_filter"
android:orderInCategory="10"
android:showAsAction="always"
android:title="#string/menu_filter"/>
and create the layout/menu_filter:
<?xml version="1.0" encoding="utf-8"?>
<SearchView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:searchIcon="#drawable/ic_menu_filter"/>
then in activity's onCreateOptionsMenu or onPrepareOptionsMenu:
SearchView searchView = (SearchView) menu.findItem(R.id.menu_filter).getActionView();
searchView.setOnQueryTextListener(this);
It looks like the actionViewClass overides the icon and it doesn't look like you can change it from this class.
You got two solutions:
Live with it and I think it's the best option in terms of user experience and platform conformity.
Define your own actionViewClass
<SearchView
android:searchIcon="#drawable/ic_action_search"
..../>
use the searchIcon xml tag
This works with Material Design (MaterialComponents theme) and BottomAppBar.
If you are using androidx library, for example:
<item
android:id="#+id/sv"
android:title="#string/search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always" />
You can create a method and invoke it from wherever you want:
/**
* Set SearchView Icon
* #param i Drawable icon
*/
private void setSVIcon(int i) {
ImageView iv = searchView.findViewById(androidx.appcompat.R.id.search_button);
iv.setImageDrawable(ResourcesCompat.getDrawable(getResources(), i, null));
}
Usage example:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(m, menu);
MenuItem mn = menu.findItem(R.id.sv);
if (mn != null) {
searchview = (SearchView) mn.getActionView();
setSVIcon(R.drawable.ic_sr);
}
}
Update hint of AutocompleteTextView for updating search icon in the expanded mode, copied from android source,
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
mSearchMenuItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) mSearchMenuItem.getActionView();
int searchImgId = getResources().getIdentifier("android:id/search_button", null, null);
ImageView v = (ImageView) searchView.findViewById(searchImgId);
v.setImageResource(R.drawable.search_or);
int searchTextViewId = searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
AutoCompleteTextView searchTextView = (AutoCompleteTextView) searchView.findViewById(searchTextViewId);
searchTextView.setHintTextColor(getResources().getColor(R.color.hint_color_white));
searchTextView.setTextColor(getResources().getColor(android.R.color.white));
searchTextView.setTextSize(18.0f);
SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon
ssb.append(hintText);
Drawable searchIcon = getResources().getDrawable(R.drawable.search_or);
int textSize = (int) (searchTextView.getTextSize() * 1.25);
searchIcon.setBounds(0, 0, textSize, textSize);
ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
searchTextView.setHint(ssb);
return super.onPrepareOptionsMenu(menu);
}
From API 21 you can change it in xml:
android:searchIcon="#drawable/loupe"
android:closeIcon="#drawable/x_white"
for api level < 21, i did this:
int searchImgId = getResources().getIdentifier("android:id/search_mag_icon", null, null);
ImageView ivIcon = (ImageView) searchView.findViewById(searchImgId);
if(ivIcon!=null)
ivIcon.setImageResource(R.drawable.ic_search);
from this
to this
There are three magnifying glass icons. two of them are shown when IconizedByDefault is true(one which is shown before pressing and one is shown in the "hint") and one is shown all the time when IconizedByDefault is false. all the fields are private so the way to get them is by their xml id. (most of the code is mentioned separately in other answers in this post already)
when IconizedByDefault is true change the icon in the hint (which is seen only after pressing the icon) by :
mSearchSrcTextView = (SearchAutoComplete)findViewById(R.id.search_src_text);
then do the same as in the android source code:
final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
newSearchIconDrawable.setBounds(0, 0, textSize, textSize);
final SpannableStringBuilder ssb = new SpannableStringBuilder(" ");
ssb.setSpan(new ImageSpan(newSearchIconDrawable), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(hintText);
mSearchHintIcon was replaced with newSearchIconDrawable which is your new search icon.
Then set the hint with
mSearchSrcTextView.setHint(ssb);
The other 2 icons are in an ImageView, which can be found by their Id.
for the icon when searchview is closed (when iconizedByDefault is true) do:
mSearchButton = (ImageView) findViewById(R.id.search_button);
and for the one that always appears (if iconizedByDefault is false)
mCollapsedIcon = (ImageView) findViewById(R.id.search_mag_icon);
Desperate solution using Kotlin
val s = (searchView.getAllChildren().firstOrNull() as? LinearLayout)?.getAllChildren()?.filter { it is AppCompatImageView }?.firstOrNull() as? AppCompatImageView
s?.setImageResource(R.drawable.search)
getAllChildren:
fun ViewGroup.getAllChildren() : ArrayList<View> {
val views = ArrayList<View>()
for (i in 0..(childCount-1)) {
views.add(getChildAt(i))
}
return views
}
Hope it helps someone.
My solution:
Use two menu xml files. In one of the xmls the menu item has an actionView and in the other one no. Initially inflate the collapsed menu and when the menu item is clicked, invalidate the menu and inflate the expanded menu xml and make sure you call setIconified(false);
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
if(!mShowSearchView)
{
inflater.inflate(R.menu.menu_collapsed, menu);
}
else
{
inflater.inflate(R.menu.menu_expanded, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setIconified(false);
searchView.setOnCloseListener(new OnCloseListener()
{
#Override
public boolean onClose()
{
mShowSearchView = false;
ActivityCompat.invalidateOptionsMenu(getActivity());
return false;
}
});
}
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == R.id.action_filter)
{
menu.showMenu();
}
else if (item.getItemId() == R.id.action_search)
{
mShowSearchView = true;
ActivityCompat.invalidateOptionsMenu(getActivity());
}
return super.onOptionsItemSelected(item);
}
Just name your icon the same name as the icon that is used by the search view. When it compiles it takes the resource in the project over the icon in the library.
I use the AppCompat library. Yes, specifying android:icon="#drawable/search_icon_png" doesnt work.
So i looked into the source code of #style/Theme.AppCompat and found the icon that android uses.
<item name="searchViewSearchIcon">#drawable/abc_ic_search</item>
So if you rename your search icon inside your drawables to abc_ic_search.png, this icon is rendered as its found in your app drawable first, rather than the appcompat drawable folder.
Works for me :)
Using this approach you can customize the close and clear icons for the search widget as well.
Can I change the background color of a Menu item in Android?
Please let me know if anyone have any solution to this. The last option will be obviously to customize it but is there any way for changing the text color without customizing it.
One simple line in your theme :)
<item name="android:actionMenuTextColor">#color/your_color</item>
It seems that an
<item name="android:itemTextAppearance">#style/myCustomMenuTextAppearance</item>
in my theme and
<style name="myCustomMenuTextAppearance" parent="#android:style/TextAppearance.Widget.IconMenu.Item">
<item name="android:textColor">#android:color/primary_text_dark</item>
</style>
in styles.xml change the style of list-items but not menu items.
You can change the color of the MenuItem text easily by using SpannableString instead of String.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.your_menu, menu);
int positionOfMenuItem = 0; // or whatever...
MenuItem item = menu.getItem(positionOfMenuItem);
SpannableString s = new SpannableString("My red MenuItem");
s.setSpan(new ForegroundColorSpan(Color.RED), 0, s.length(), 0);
item.setTitle(s);
}
If you are using the new Toolbar, with the theme Theme.AppCompat.Light.NoActionBar, you can style it in the following way.
<style name="ToolbarTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColorPrimary">#color/my_color1</item>
<item name="android:textColorSecondary">#color/my_color2</item>
<item name="android:textColor">#color/my_color3</item>
</style>`
According to the results I got,
android:textColorPrimary is the text color displaying the name of your activity, which is the primary text of the toolbar.
android:textColorSecondary is the text color for subtitle and more options (3 dot) button. (Yes, it changed its color according to this property!)
android:textColor is the color for all other text including the menu.
Finally set the theme to the Toolbar
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:theme="#style/ToolbarTheme"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"/>
I went about it programmatically like this:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.changeip_card_menu, menu);
for(int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
SpannableString spanString = new SpannableString(menu.getItem(i).getTitle().toString());
spanString.setSpan(new ForegroundColorSpan(Color.BLACK), 0, spanString.length(), 0); //fix the color to white
item.setTitle(spanString);
}
return true;
}
If you are using menu as <android.support.design.widget.NavigationView /> then just add below line in NavigationView :
app:itemTextColor="your color"
Also available colorTint for icon, it will override color for your icon as well. For that you have to add below line:
app:itemIconTint="your color"
Example:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:itemTextColor="#color/color_white"
app:itemIconTint="#color/color_white"
android:background="#color/colorPrimary"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer"/>
Hope it will help you.
in Kotlin I wrote these extensions:
fun MenuItem.setTitleColor(color: Int) {
val hexColor = Integer.toHexString(color).toUpperCase().substring(2)
val html = "<font color='#$hexColor'>$title</font>"
this.title = html.parseAsHtml()
}
#Suppress("DEPRECATION")
fun String.parseAsHtml(): Spanned {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY)
} else {
Html.fromHtml(this)
}
}
and used like this:
menu.findItem(R.id.main_settings).setTitleColor(Color.RED)
as you can see in this question you should:
<item name="android:textColorPrimary">yourColor</item>
Above code changes the text color of the menu action items for API >= v21.
<item name="actionMenuTextColor">#android:color/holo_green_light</item>
Above is the code for API < v21
I used the html tag to change a single item's text colour when the menu item is inflated. Hope it would be helpful.
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
menu.findItem(R.id.main_settings).setTitle(Html.fromHtml("<font color='#ff3824'>Settings</font>"));
return true;
}
SIMPLEST way to make custom menu color for single toolbar, not for AppTheme
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay.MenuBlue">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</android.support.design.widget.AppBarLayout>
usual toolbar on styles.xml
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
our custom toolbar style
<style name="AppTheme.AppBarOverlay.MenuBlue">
<item name="actionMenuTextColor">#color/blue</item>
</style>
I was using Material design and when the toolbar was on a small screen clicking the more options would show a blank white drop down box. To fix this I think added this to the main AppTheme:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:itemTextAppearance">#style/menuItem</item>
</style>
And then created a style where you set the textColor for the menu items to your desired colour.
<style name="menuItem" parent="Widget.AppCompat.TextView.SpinnerItem">
<item name="android:textColor">#color/black</item>
</style>
The parent name Widget.AppCompat.TextView.SpinnerItem I don't think that matters too much, it should still work.
to change menu item text color use below code
<style name="AppToolbar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:itemTextAppearance">#style/menu_item_color</item>
</style>
where
<style name="menu_item_color">
<item name="android:textColor">#color/app_font_color</item>
</style>
The short answer is YES. lucky you!
To do so, you need to override some styles of the Android default styles :
First, look at the definition of the themes in Android :
<style name="Theme.IconMenu">
<!-- Menu/item attributes -->
<item name="android:itemTextAppearance">#android:style/TextAppearance.Widget.IconMenu.Item</item>
<item name="android:itemBackground">#android:drawable/menu_selector</item>
<item name="android:itemIconDisabledAlpha">?android:attr/disabledAlpha</item>
<item name="android:horizontalDivider">#android:drawable/divider_horizontal_bright</item>
<item name="android:verticalDivider">#android:drawable/divider_vertical_bright</item>
<item name="android:windowAnimationStyle">#android:style/Animation.OptionsPanel</item>
<item name="android:moreIcon">#android:drawable/ic_menu_more</item>
<item name="android:background">#null</item>
</style>
So, the appearance of the text in the menu is in #android:style/TextAppearance.Widget.IconMenu.Item
Now, in the definition of the styles :
<style name="TextAppearance.Widget.IconMenu.Item" parent="TextAppearance.Small">
<item name="android:textColor">?textColorPrimaryInverse</item>
</style>
So now we have the name of the color in question, if you look in the color folder of the resources of the system :
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#android:color/bright_foreground_light_disabled" />
<item android:state_window_focused="false" android:color="#android:color/bright_foreground_light" />
<item android:state_pressed="true" android:color="#android:color/bright_foreground_light" />
<item android:state_selected="true" android:color="#android:color/bright_foreground_light" />
<item android:color="#android:color/bright_foreground_light" />
<!-- not selected -->
</selector>
Finally, here is what you need to do :
Override "TextAppearance.Widget.IconMenu.Item" and create your own style. Then link it to your own selector to make it the way you want.
Hope this helps you.
Good luck!
Options menu in android can be customized to set the background or change the text appearance. The background and text color in the menu couldn’t be changed using themes and styles. The android source code (data\res\layout\icon_menu_item_layout.xml)uses a custom item of class “com.android.internal.view.menu.IconMenuItem”View for the menu layout. We can make changes in the above class to customize the menu. To achieve the same, use LayoutInflater factory class and set the background and text color for the view.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);
getLayoutInflater().setFactory(new Factory() {
#Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (name .equalsIgnoreCase(“com.android.internal.view.menu.IconMenuItemView”)) {
try{
LayoutInflater f = getLayoutInflater();
final View view = f.createView(name, null, attrs);
new Handler().post(new Runnable() {
public void run() {
// set the background drawable
view .setBackgroundResource(R.drawable.my_ac_menu_background);
// set the text color
((TextView) view).setTextColor(Color.WHITE);
}
});
return view;
} catch (InflateException e) {
} catch (ClassNotFoundException e) {}
}
return null;
}
});
return super.onCreateOptionsMenu(menu);
}
Thanks for the code example.
I had to modify it go get it to work with a context menu.
This is my solution.
static final Class<?>[] constructorSignature = new Class[] {Context.class, AttributeSet.class};
class MenuColorFix implements LayoutInflater.Factory {
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (name.equalsIgnoreCase("com.android.internal.view.menu.ListMenuItemView")) {
try {
Class<? extends ViewGroup> clazz = context.getClassLoader().loadClass(name).asSubclass(ViewGroup.class);
Constructor<? extends ViewGroup> constructor = clazz.getConstructor(constructorSignature);
final ViewGroup view = constructor.newInstance(new Object[]{context,attrs});
new Handler().post(new Runnable() {
public void run() {
try {
view.setBackgroundColor(Color.BLACK);
List<View> children = getAllChildren(view);
for(int i = 0; i< children.size(); i++) {
View child = children.get(i);
if ( child instanceof TextView ) {
((TextView)child).setTextColor(Color.WHITE);
}
}
}
catch (Exception e) {
Log.i(TAG, "Caught Exception!",e);
}
}
});
return view;
}
catch (Exception e) {
Log.i(TAG, "Caught Exception!",e);
}
}
return null;
}
}
public List<View> getAllChildren(ViewGroup vg) {
ArrayList<View> result = new ArrayList<View>();
for ( int i = 0; i < vg.getChildCount(); i++ ) {
View child = vg.getChildAt(i);
if ( child instanceof ViewGroup) {
result.addAll(getAllChildren((ViewGroup)child));
}
else {
result.add(child);
}
}
return result;
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
LayoutInflater lInflater = getLayoutInflater();
if ( lInflater.getFactory() == null ) {
lInflater.setFactory(new MenuColorFix());
}
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.myMenu, menu);
}
For me this works with Android 1.6, 2.03 and 4.03.
i found it Eureka !!
in your app theme:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:actionBarStyle">#style/ActionBarTheme</item>
<!-- backward compatibility -->
<item name="actionBarStyle">#style/ActionBarTheme</item>
</style>
here is your action bar theme:
<style name="ActionBarTheme" parent="#style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
<item name="android:background">#color/actionbar_bg_color</item>
<item name="popupTheme">#style/ActionBarPopupTheme</item
<!-- backward compatibility -->
<item name="background">#color/actionbar_bg_color</item>
</style>
and here is your popup theme:
<style name="ActionBarPopupTheme">
<item name="android:textColor">#color/menu_text_color</item>
<item name="android:background">#color/menu_bg_color</item>
</style>
Cheers ;)
Simply add this to your theme
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:itemTextAppearance">#style/AppTheme.ItemTextStyle</item>
</style>
<style name="AppTheme.ItemTextStyle" parent="#android:style/TextAppearance.Widget.IconMenu.Item">
<item name="android:textColor">#color/orange_500</item>
</style>
Tested on API 21
Thanks to max.musterman, this is the solution I got to work in level 22:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
MenuItem searchMenuItem = menu.findItem(R.id.search);
SearchView searchView = (SearchView) searchMenuItem.getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setSubmitButtonEnabled(true);
searchView.setOnQueryTextListener(this);
setMenuTextColor(menu, R.id.displaySummary, R.string.show_summary);
setMenuTextColor(menu, R.id.about, R.string.text_about);
setMenuTextColor(menu, R.id.importExport, R.string.import_export);
setMenuTextColor(menu, R.id.preferences, R.string.settings);
return true;
}
private void setMenuTextColor(Menu menu, int menuResource, int menuTextResource) {
MenuItem item = menu.findItem(menuResource);
SpannableString s = new SpannableString(getString(menuTextResource));
s.setSpan(new ForegroundColorSpan(Color.BLACK), 0, s.length(), 0);
item.setTitle(s);
}
The hardcoded Color.BLACK could become an additional parameter to the setMenuTextColor method. Also, I only used this for menu items which were android:showAsAction="never".
Adding this into my styles.xml worked for me
<item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
You can set color programmatically.
private static void setMenuTextColor(final Context context, final Toolbar toolbar, final int menuResId, final int colorRes) {
toolbar.post(new Runnable() {
#Override
public void run() {
View settingsMenuItem = toolbar.findViewById(menuResId);
if (settingsMenuItem instanceof TextView) {
if (DEBUG) {
Log.i(TAG, "setMenuTextColor textview");
}
TextView tv = (TextView) settingsMenuItem;
tv.setTextColor(ContextCompat.getColor(context, colorRes));
} else { // you can ignore this branch, because usually there is not the situation
Menu menu = toolbar.getMenu();
MenuItem item = menu.findItem(menuResId);
SpannableString s = new SpannableString(item.getTitle());
s.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, colorRes)), 0, s.length(), 0);
item.setTitle(s);
}
}
});
}
If you want to set color for an individual menu item, customizing a toolbar theme is not the right solution. To achieve this, you can make use of android:actionLayout and an action view for the menu item.
First create an XML layout file for the action view. In this example we use a button as an action view:
menu_button.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/menuButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Done"
android:textColor="?android:attr/colorAccent"
style="?android:attr/buttonBarButtonStyle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
In the code snippet above, we use android:textColor="?android:attr/colorAccent" to customize button text color.
Then in your XML layout file for the menu, include app:actionLayout="#layout/menu_button" as shown below:
main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menuItem"
android:title=""
app:actionLayout="#layout/menu_button"
app:showAsAction="always"/>
</menu>
Last override the onCreateOptionsMenu() method in your activity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
MenuItem item = menu.findItem(R.id.menuItem);
Button saveButton = item.getActionView().findViewById(R.id.menuButton);
saveButton.setOnClickListener(view -> {
// Do something
});
return true;
}
...or fragment:
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater){
inflater.inflate(R.menu.main_menu, menu);
MenuItem item = menu.findItem(R.id.menuItem);
Button saveButton = item.getActionView().findViewById(R.id.menuButton);
button.setOnClickListener(view -> {
// Do something
});
}
For more details on action views, see the Android developer guide.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.search, menu);
MenuItem myActionMenuItem = menu.findItem( R.id.action_search);
SearchView searchView = (SearchView) myActionMenuItem.getActionView();
EditText searchEditText = (EditText) searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
searchEditText.setTextColor(Color.WHITE); //You color here
My situation was settings text color in the options menu (main app menu showed on menu button press).
Tested in API 16 with appcompat-v7-27.0.2 library, AppCompatActivity for MainActivity and AppCompat theme for the application in AndroidManifest.xml.
styles.xml:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="actionBarPopupTheme">#style/PopupTheme</item>
</style>
<style name="PopupTheme" parent="#style/ThemeOverlay.AppCompat.Light">
<item name="android:textColorSecondary">#f00</item>
</style>
Don't know if that textColorSecondary affects other elements but it controls the menu text color.
I searched some examples on the topic but all ready-to-use snippets didn't work.
So I wanted to investigate it with the source code for the appcompat-v7 library (specifically with the res folder of the .aar package).
Though in my case I used Eclipse with exploded .aar dependencies. So I could change the default styles and check the results. Don't know how to explode the libraries to use with Gradle or Android Studio directly. It deserves another thread of investigation.
So my purpose was so find which color in the res/values/values.xml file is used for the menu text (I was almost sure the color was there).
I opened that file, then duplicated all colors, put them below the default ones to override them and assigned #f00 value to all of them.
Start the app.
Many elements had red background or text color. And the menu items too. That was what I needed.
Removing my added colors by blocks of 5-10 lines I ended with the secondary_text_default_material_light color item.
Searching that name in the files within the res folder (or better within res/colors) I found only one occurrence in the color/abc_secondary_text_material_light.xml file (I used Sublime Text for these operations so it's easier to find thing I need).
Back to the values.xml 8 usages were found for the #color/abc_secondary_text_material_light.
It was a Light theme so 4 left in 2 themes: Base.ThemeOverlay.AppCompat.Light and Platform.AppCompat.Light.
The first theme was a child of the second one so there were only 2 attributes with that color resource: android:textColorSecondary and android:textColorTertiaryin the Base.ThemeOverlay.AppCompat.Light.
Changing their values directly in the values.xml and running the app I found that the final correct attribute was android:textColorSecondary.
Next I needed a theme or another attribute so I could change it in my app's style.xml (because my theme had as the parent the Theme.AppCompat.Light and not the ThemeOverlay.AppCompat.Light).
I searched in the same file for the Base.ThemeOverlay.AppCompat.Light. It had a child ThemeOverlay.AppCompat.Light.
Searching for the ThemeOverlay.AppCompat.Light I found its usage in the Base.Theme.AppCompat.Light.DarkActionBar theme as the actionBarPopupTheme attribute value.
My app's theme Theme.AppCompat.Light.DarkActionBar was a child of the found Base.Theme.AppCompat.Light.DarkActionBar so I could use that attribute in my styles.xml without problems.
As it's seen in the example code above I created a child theme from the mentioned ThemeOverlay.AppCompat.Light and changed the android:textColorSecondary attribute.
Sephy's solution doesn't work. It's possible to override the options menu item text appearance using the method described above, but not the item or menu. To do that there are essentially 3 ways:
How to change the background color of the options menu?
Write your own view to display and override onCreateOptionsMenu and onPrepareOptionsMenu to get the results you want. I state this generally because you can generally do whatever you want in these methods, but you probably won't want to call into super().
Copy code from the open-source SDK and customize for your behavior. The default menu implementation used by Activity will no longer apply.
See Issue 4441: Custom Options Menu Theme for more clues.
try this code....
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);
getLayoutInflater().setFactory(new Factory() {
#Override
public View onCreateView(String name, Context context,
AttributeSet attrs) {
if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) {
try {
LayoutInflater f = getLayoutInflater();
final View view = f.createView(name, null, attrs);
new Handler().post(new Runnable() {
public void run() {
// set the background drawable
view.setBackgroundResource(R.drawable.my_ac_menu_background);
// set the text color
((TextView) view).setTextColor(Color.WHITE);
}
});
return view;
} catch (InflateException e) {
} catch (ClassNotFoundException e) {
}
}
return null;
}
});
return super.onCreateOptionsMenu(menu);
}
Add textColor as below
<style name="MyTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light">
<item name="android:textColor">#color/radio_color_gray</item>
</style>
and use it in Toolbar in xml file
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/MyTheme.PopupOverlay" />
This is how you can color a specific menu item with color, works for all API levels:
public static void setToolbarMenuItemTextColor(final Toolbar toolbar,
final #ColorRes int color,
#IdRes final int resId) {
if (toolbar != null) {
for (int i = 0; i < toolbar.getChildCount(); i++) {
final View view = toolbar.getChildAt(i);
if (view instanceof ActionMenuView) {
final ActionMenuView actionMenuView = (ActionMenuView) view;
// view children are accessible only after layout-ing
actionMenuView.post(new Runnable() {
#Override
public void run() {
for (int j = 0; j < actionMenuView.getChildCount(); j++) {
final View innerView = actionMenuView.getChildAt(j);
if (innerView instanceof ActionMenuItemView) {
final ActionMenuItemView itemView = (ActionMenuItemView) innerView;
if (resId == itemView.getId()) {
itemView.setTextColor(ContextCompat.getColor(toolbar.getContext(), color));
}
}
}
}
});
}
}
}
}
By doing that you loose the background selector effect, so here is the code to apply a custom background selector to all of the menu item children.
public static void setToolbarMenuItemsBackgroundSelector(final Context context,
final Toolbar toolbar) {
if (toolbar != null) {
for (int i = 0; i < toolbar.getChildCount(); i++) {
final View view = toolbar.getChildAt(i);
if (view instanceof ImageButton) {
// left toolbar icon (navigation, hamburger, ...)
UiHelper.setViewBackgroundSelector(context, view);
} else if (view instanceof ActionMenuView) {
final ActionMenuView actionMenuView = (ActionMenuView) view;
// view children are accessible only after layout-ing
actionMenuView.post(new Runnable() {
#Override
public void run() {
for (int j = 0; j < actionMenuView.getChildCount(); j++) {
final View innerView = actionMenuView.getChildAt(j);
if (innerView instanceof ActionMenuItemView) {
// text item views
final ActionMenuItemView itemView = (ActionMenuItemView) innerView;
UiHelper.setViewBackgroundSelector(context, itemView);
// icon item views
for (int k = 0; k < itemView.getCompoundDrawables().length; k++) {
if (itemView.getCompoundDrawables()[k] != null) {
UiHelper.setViewBackgroundSelector(context, itemView);
}
}
}
}
}
});
}
}
}
}
Here is the helper function also:
public static void setViewBackgroundSelector(#NonNull Context context, #NonNull View itemView) {
int[] attrs = new int[]{R.attr.selectableItemBackgroundBorderless};
TypedArray ta = context.obtainStyledAttributes(attrs);
Drawable drawable = ta.getDrawable(0);
ta.recycle();
ViewCompat.setBackground(itemView, drawable);
}
For changing the text color, you can just set a custom view for the MenuItem, and then you can define the color for the text.
Sample Code : MenuItem.setActionView()
Simply just go to
Values - styles and inside styles and type
your color