I have a field where the user can type a search query in the action bar of the application. This is declared in the action bar using a menu inflate in the Activity:
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:id="#+id/action_search"
android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"
android:title="#string/search"
></item>
</menu>
I need to customize the appearance of the SearchView (for instance background and text color). So far I could not find a way to do it using XML (using styles or themes).
Is my only option to do it in the code when inflating the menu?
Edit #1: I have tried programmatically but I cannot get a simple way to set the text color. Plus when I do searchView.setBackgroundResource(...) The background is set on the global widget, (also when the SearchView is iconified).
Edit #2: Not much information on the Search Developer Reference either
Seibelj had an answer that is good if you want to change the icons. But you'll need to
do it for every API version. I was using ICS with ActionBarSherlock and it didn't do justice for me but it did push me in the correct direction.
Below I change the text color and hint color. I showed how you might go about changing the
icons too, though I have no interest in that for now (and you probably want to use the default icons anyways to be consistent)
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// Set up the search menu
SearchView searchView = (SearchView)menu.findItem(R.id.action_search).getActionView();
traverseView(searchView, 0);
return true;
}
private void traverseView(View view, int index) {
if (view instanceof SearchView) {
SearchView v = (SearchView) view;
for(int i = 0; i < v.getChildCount(); i++) {
traverseView(v.getChildAt(i), i);
}
} else if (view instanceof LinearLayout) {
LinearLayout ll = (LinearLayout) view;
for(int i = 0; i < ll.getChildCount(); i++) {
traverseView(ll.getChildAt(i), i);
}
} else if (view instanceof EditText) {
((EditText) view).setTextColor(Color.WHITE);
((EditText) view).setHintTextColor(R.color.blue_trans);
} else if (view instanceof TextView) {
((TextView) view).setTextColor(Color.WHITE);
} else if (view instanceof ImageView) {
// TODO dissect images and replace with custom images
} else {
Log.v("View Scout", "Undefined view type here...");
}
}
adding my take on things which is probably a little more efficient and safe across different android versions.
you can actually get a numeric ID value from a string ID name. using android's hierarchyviewer tool, you can actually find the string IDs of the things you are interested in, and then just use findViewById(...) to look them up.
the code below sets the hint and text color for the edit field itself. you could apply the same pattern for other aspects that you wish to style.
private static synchronized int getSearchSrcTextId(View view) {
if (searchSrcTextId == -1) {
searchSrcTextId = getId(view, "android:id/search_src_text");
}
return searchSrcTextId;
}
private static int getId(View view, String name) {
return view.getContext().getResources().getIdentifier(name, null, null);
}
#TargetApi(11)
private void style(View view) {
ImageView iv;
AutoCompleteTextView actv = (AutoCompleteTextView) view.findViewById(getSearchSrcTextId(view));
if (actv != null) {
actv.setHint(getDecoratedHint(actv,
searchView.getContext().getResources().getString(R.string.titleApplicationSearchHint),
R.drawable.ic_ab_search));
actv.setTextColor(view.getContext().getResources().getColor(R.color.ab_text));
actv.setHintTextColor(view.getContext().getResources().getColor(R.color.hint_text));
}
}
You can use the attribute android:actionLayout instead which lets you specify a layout to be inflated. Just have a layout with your SearchView and you won't have to modify anything really.
As to changing text style on the SearchView that is probably not possible as the SearchView is a ViewGroup. You should probably try changing text color via themes instead.
In case anyone wants to modify the views directly, here is how you can change the colors/fonts/images and customize the search box to your pleasure. It is wrapped in a try/catch in case there are differences between versions or distributions, so it won't crash the app if this fails.
// SearchView structure as we currently understand it:
// 0 => linearlayout
// 0 => textview (not sure what this does)
// 1 => image view (the search icon before it's pressed)
// 2 => linearlayout
// 0 => linearlayout
// 0 => ImageView (Search icon on the left of the search box)
// 1 => SearchView$SearchAutoComplete (Object that controls the text, subclass of TextView)
// 2 => ImageView (Cancel icon to the right of the text entry)
// 1 => linearlayout
// 0 => ImageView ('Go' icon to the right of cancel)
// 1 => ImageView (not sure what this does)
try {
LinearLayout ll = (LinearLayout) searchView.getChildAt(0);
LinearLayout ll2 = (LinearLayout) ll.getChildAt(2);
LinearLayout ll3 = (LinearLayout) ll2.getChildAt(0);
LinearLayout ll4 = (LinearLayout) ll2.getChildAt(1);
TextView search_text = (TextView) ll3.getChildAt(1);
search_text.setTextColor(R.color.search_text);
ImageView cancel_icon = (ImageView)ll3.getChildAt(2);
ImageView accept_icon = (ImageView)ll4.getChildAt(0);
cancel_icon.setBackgroundDrawable(d);
accept_icon.setBackgroundDrawable(d);
} catch (Throwable e) {
Log.e("SearchBoxConstructor", "Unable to set the custom look of the search box");
}
This example shows changing the text color and the background colors of the cancel/accept images. searchView is a SearchView object already instantiated with it's background color:
Drawable d = getResources().getDrawable(R.drawable.search_widget_background);
searchView.setBackgroundDrawable(d);
Here is the drawable code:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#color/white" />
</shape>
Obviously, this is hacky, but it will work for now.
From ICS this is doable using themes and styles. I'm using ActionBarSherlock which makes it applicable also for HC and below.
Add a style to define "android:textColorHint":
<style name="Theme.MyHolo.widget" parent="#style/Theme.Holo">
<item name="android:textColorHint">#color/text_hint_corp_dark</item>
</style>
Apply this as "actionBarWidgetTheme" to your theme:
<style name="Theme.MyApp" parent="#style/Theme.Holo.Light.DarkActionBar">
...
<item name="android:actionBarWidgetTheme">#style/Theme.MyHolo.widget</item>
</style>
Presto! Make sure that you use getSupportActionBar().getThemedContext() (or getSupportActionBar() for ActionBarSherlock) if any widgets are initiated where you might have other themes in effect.
How do you inflate the menu xml in your Activity? if you inflate the menu by using getMenuInflator() in your Activity, then the menu and also the searchView get the themed context, that have attached to the activity.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater.inflate(R.menu.search_action_menu, menu);
}
if you check the source code of Activity.getMenuInflator() at API-15, you can see the themed context codes. Here it is.
*/
public MenuInflater getMenuInflater() {
// Make sure that action views can get an appropriate theme.
if (mMenuInflater == null) {
initActionBar();
if (mActionBar != null) {
mMenuInflater = new MenuInflater(mActionBar.getThemedContext());
} else {
mMenuInflater = new MenuInflater(this);
}
}
return mMenuInflater;
}
Related
I have a problem with searchview implementation in android toolbar.
The empty space padding is too big.
I don't want to hide other actions, but these actions are
overlapped by SearchView.
SearchView's underline is not visible
How do i fix issues mentioned above ?
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/action_search"
android:title="#string/car_num"
android:icon="#drawable/ic_search_white_24dp"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
<item
android:id="#+id/action_add_client"
android:icon="#drawable/ic_account_multiple_plus"
android:title="#string/action_add_client"
app:showAsAction="always" />
</menu>
fragment
#Override
public void onCreateOptionsMenu(final Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_fragment_reg_vehicles, menu);
final MenuItem item = menu.findItem(R.id.action_search);
searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setQueryHint("Search");
searchView.setMaxWidth(Integer.MAX_VALUE);
searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(this);
searchView.setOnCloseListener(new SearchView.OnCloseListener() {
#Override
public boolean onClose() {
setItemsVisibility(menu, item, true);
return false;
}
});
searchView.setOnSearchClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setItemsVisibility(menu, item, false);
searchView.requestFocus();
}
});
}
Regarding your posted code, this is the output:
As you can see, there is two left margins: the widget's container and the magnify icon. This is why you have an empty space bigger than an another window with a title. And the menu items are pushed outside the toolbar which, I think, it's the default SearchView ActionView when it's not a CollapseActionView so it fills the parent.
From the source of SearchView widget and its layout abc_search_view.xml, I tried to remove the extra margins and avoid pushing the other items outside the toolbar.
But after many manipulations, my guess is you have to use a custom widget and/or a custom layout. Or to play with setIconifiedByDefault(true) which removes the magnify icon and its extra margin and to use setMaxWidth(MAX_SIZE) where MAX_SIZE is calculated dynamically by Integer.MAX_VALUE - (SIZE_OF_A_MENU_ITEM * NB_OF_MENU_ITEMS)... But it requires a lot of work for nothing. So using a custom layout could be the solution.
However, there is a possible way to keep the appcompat widget, some little workarounds. First, to avoid puhsing out the other items, you can use the CollapseActionView.
<item
...
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always|collapseActionView"/>
And to maintain your requirements, you have to expand it when you initialize it:
final SearchView searchView =
(SearchView) MenuItemCompat.getActionView(item);
MenuItemCompat.expandActionView(item);
Be aware that you have to use setOnActionExpandListener() in order to close the window if you don't want to collapse the item. This suggestion will give you this result:
Still the extra margins, right? Therefore, you have to retrieve the container and the magnify icon by their ids (which you can find in abc_search_view.xml... but let's save some time: they are R.id.search_edit_frame and R.id.search_mag_icon). You can remove their margins by using this method:
private void changeSearchViewElements(View view) {
if (view == null)
return;
if (view.getId() == R.id.search_edit_frame
|| view.getId() == R.id.search_mag_icon) {
LinearLayout.LayoutParams p =
(LinearLayout.LayoutParams) view.getLayoutParams();
p.leftMargin = 0; // set no left margin
view.setLayoutParams(p);
}
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
changeSearchViewElements(viewGroup.getChildAt(i));
}
}
}
By calling it in a thread:
final SearchView searchView =
(SearchView) MenuItemCompat.getActionView(item);
...
searchView.post(new Runnable() {
#Override
public void run() {
changeSearchViewElements(searchView);
}
});
Here's the output:
Finally, to get the line under the field, there is a possible workaround as using a 9-patch drawable and set it as a background. You can easily find how-to on Google. So the condition will be:
private void changeSearchViewElements(View view) {
...
if (view.getId() == R.id.search_edit_frame
|| view.getId() == R.id.search_mag_icon) {
LinearLayout.LayoutParams p =
(LinearLayout.LayoutParams) view.getLayoutParams();
p.leftMargin = 0; // set no left margin
view.setLayoutParams(p);
} else if (view.getId() == R.id.search_src_text) {
AutoCompleteTextView searchEdit = (AutoCompleteTextView) view;
searchEdit.setBackgroundResource(R.drawable.rect_underline_white);
}
...
}
From the OP's comment below, the underline's field can also be done with the following statement:
searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text)
.setBackgroundResource(R.drawable.abc_textfield_search_default_mtrl_alpha);
After these workarounds, as I said, it might be easier to use a custom layout. But if you want to keep the default SearchView widget, this might help.
I want to create my own ActionBar Layout.
Like this (created in Paint for example)
Is it possible to give the second Button the Up-Navigation Properties? So if I press it, it finish this Activity and starts it's parent.
I want to have the burger Icon for the Navigation Drawer, the Up-Icon for Up-Navigation and the Title of the Activity.
Is it possible? Or is there a solution already?
Actually, it's fairly easy(though it's little hacky) to do.
First, create a drawable for back button(preferably - as a selector, to distinguish pressed/normal state:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/back_button_pressed"/>
<item android:drawable="#drawable/back_button"/>
</selector>
Next, set this drawable to the logo of the toolbar toolbar.setLogo(R.drawable.back_button_selector);
Then the only thing left is to set click-listener.
View logoView = getToolbarLogoIcon(toolbar);
logoView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
...
private View getToolbarLogoIcon(Toolbar toolbar){
//check if contentDescription previously was set
boolean hadContentDescription = android.text.TextUtils.isEmpty(toolbar.getLogoDescription());
String contentDescription = String.valueOf(!hadContentDescription ? toolbar.getLogoDescription() : "logoContentDescription");
toolbar.setLogoDescription(contentDescription);
ArrayList<View> potentialViews = new ArrayList<>();
//find the view based on it's content description, set programatically or with android:contentDescription
toolbar.findViewsWithText(potentialViews,contentDescription, View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
//Nav icon is always instantiated at this point because calling setLogoDescription ensures its existence
View logoIcon = null;
if (potentialViews.size() > 0) {
logoIcon = potentialViews.get(0);
}
//Clear content description if not previously present
if (hadContentDescription) {
toolbar.setLogoDescription(null);
}
return logoIcon;
}
(Thanks Nicola's post here). Or if you are not scared of reflection, it can be easily done like this:
try {
Field declaredField = toolbar.getClass().getDeclaredField("mLogoView");
declaredField.setAccessible(true);
View logoView = (View) declaredField.get(toolbar);
logoView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
} catch (Exception ex) {
//error
}
Another possible solution would be to set custom layout to the ActionBar.
Though, I'm advocating to follow the UI/UX guidelines and double-check, if navigation drawer is essential in the secondary activity.
So I have a menu item, that's defined as:
<item
android:id="#+id/action_live"
android:title="#string/action_live"
android:orderInCategory="1"
app:showAsAction="ifRoom|withText" />
It shows as text, as you can see below:
And I want to programmatically change the "LIVE" text color. I've searched for a while and I found a method:
With globally defined:
private Menu mOptionsMenu;
and:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
mOptionsMenu = menu;
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
I do:
MenuItem liveitem = mOptionsMenu.findItem(R.id.action_live);
SpannableString s = new SpannableString(liveitem.getTitle().toString());
s.setSpan(new ForegroundColorSpan(Color.RED), 0, s.length(), 0);
liveitem.setTitle(s);
But nothing happens!
If I do the same for an item of the overflow menu, it works:
Is there some limitation for app:showAsAction="ifRoom|withText" items? Is there any workaround?
Thanks in advance.
Bit late to the party with this one, but I spent a while working on this and found a solution, which may be of use to anyone else trying to do the same thing. Some credit goes to Harish Sridharan for steering me in the right direction.
You can use findViewById(R.id.MY_MENU_ITEM_ID) to locate the menu item (provided that the menu had been created and prepared), and cast it to a TextView instance as suggested by Harish, which can then be styled as required.
public class MyAwesomeActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
super.onCreate(savedInstanceState);
// Force invalidatation of the menu to cause onPrepareOptionMenu to be called
invalidateOptionsMenu();
}
private void styleMenuButton() {
// Find the menu item you want to style
View view = findViewById(R.id.YOUR_MENU_ITEM_ID_HERE);
// Cast to a TextView instance if the menu item was found
if (view != null && view instanceof TextView) {
((TextView) view).setTextColor( Color.BLUE ); // Make text colour blue
((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_SP, 24); // Increase font size
}
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean result = super.onPrepareOptionsMenu(menu);
styleMenuButton();
return result;
}
}
The trick here is to force the menu to be invalidated in the activity's onCreate event (thereby causing the onPrepareMenuOptions to be called sooner than it would normally). Inside this method, we can locate the menu item and style as required.
#RRP give me a clue ,but his solution does not work for me. And #Box give a another, but his answer looks a little not so cleaner. Thanks them. So according to them, I have a total solution.
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);
}
}
});
}
In order to change the colour of menu item you can find that item, extract the title from it, put it in a Spannable String and set the foreground colour to it. Try out this code piece
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
MenuItem mColorFullMenuBtn = menu.findItem(R.id.action_submit); // extract the menu item here
String title = mColorFullMenuBtn.getTitle().toString();
if (title != null) {
SpannableString s = new SpannableString(title);
s.setSpan(new ForegroundColorSpan(Color.parseColor("#FFFFFF")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); // provide whatever color you want here.
mColorFullMenuBtn.setTitle(s);
}
return super.onCreateOptionsMenu(menu);
}
It only becomes a text view after inspection, its real class is ActionMenuItemView, on which we can further set the text color like this:
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));
}
}
}
}
});
}
}
}
}
You could put the change of the color in the onPrepareOptionsMenu:
override fun onPrepareOptionsMenu(menu: Menu?): Boolean
{
val signInMenuItem = menu?.findItem(R.id.menu_main_sign_in)
val title = signInMenuItem?.title.toString()
val spannable = SpannableString(title)
spannable.setSpan(
ForegroundColorSpan(Color.GREEN),
0,
spannable.length,
Spannable.SPAN_INCLUSIVE_INCLUSIVE)
SgnInMenuItem?.title = spannable
return super.onPrepareOptionsMenu(menu)
}
of course you can make it shorter above...
now you can change the color appearance upon other (ie. viewmodel) values...
RG
I spent a lot of hours on this and finally got it into work. There is easy solusion for Android 6 and 7 but it doesn't work on Android 5. This code works on all of them. So, if you are doing it in Kotlin this is my suggestion:
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
setToolbarActionTextColor(menu, R.color.black)
this.menu = menu
return true
}
private fun setToolbarActionTextColor(menu: Menu, color: Int) {
val tb = findViewById<Toolbar>(R.id.toolbar)
tb?.let { toolbar ->
toolbar.post {
val view = findViewById<View>(R.id.my_tag)
if (view is TextView) {
view.setTextColor(ContextCompat.getColor(this, color))
} else {
val mi = menu.findItem(R.id.my_tag)
mi?.let {
val newTitle: Spannable = SpannableString(it.title.toString())
val newColor = ContextCompat.getColor(this, color)
newTitle.setSpan(ForegroundColorSpan(newColor),
0, newTitle.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
it.title = newTitle
}
}
}
}
}
It's complicated, but you can use the app:actionLayout attribute. For example,
my_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/englishList"
android:orderInCategory="1"
app:showAsAction="ifRoom|withText"
app:actionLayout="#layout/custom_menu_item_english_list"
android:title=""/>
</menu>
custom_menu_item_english_list.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/englishListWhiteText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:lineHeight="16dp"
android:textSize="16sp"
android:text="英文"
android:layout_marginRight="8dp"/>
</LinearLayout>
MainActivity.java:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my_menu, menu);
MenuItem item = menu.findItem(R.id.englishList);
item.getActionView().findViewById(R.id.englishListWhiteText)
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
//Handle button click.
}
});
return true;
}
Result:
More Detailed Example=
https://medium.com/#info.anikdey003/custom-menu-item-using-action-layout-7a25118b9d5
if you are using popup menu function to show the menu items in the application and trying to change the design or color of your text items in the menu list, first create a style item in your style.xml file:
<style name="PopupMenuStyle" parent="Widget.AppCompat.PopupMenu">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_gravity">center</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#color/ColorPrimary</item>
<item name="android:textSize">#dimen/textsize</item>
<item name="android:fontFamily">#font/myfonts</item></style>
and use this style in your code as:
val popupWrapper = ContextThemeWrapper(this, R.style.PopupMenuStyle)
val popup = PopupMenu(popupWrapper, your_menu_view)
MenuItem as defined by documentation is an interface. It will definitely be implemented with a view widget before being portrayed as an menu. Most cases these menu items are implemented as TextView. You can use UiAutomatorViewer to see the view hierarchy or even use hierarchyviewer which will be found in [sdk-home]/tools/. Attached one sample uiautomatorviewer screenshot for a MenuItem
So you can always typecast your MenuItem and set the color.
TextView liveitem = (TextView)mOptionsMenu.findItem(R.id.action_live);
liveitem.setTextColor(Color.RED);
EDIT:
Since there was request to see how to use this tool, I'm adding a few more contents.
Make sure you have set environment variable $ANDROID_HOME pointing to your SDK HOME.
In your terminal:
cd $ANDROID_HOME
./tools/uiautomatorviewer
This tool will open up.
The second or third button (refer screenshot) in the menu will capture the screenshot of your attached device or emulator and you can inspect the view and their hierarchy. Clicking on the view will describe the view and their information. It is tool purposely designed for testing and you can inspect any application.
Refer developer site for more info: uiautomatorviewer
I used the following hack to change the homeAsupIndicator programmatically.
int upId = Resources.getSystem().getIdentifier("up", "id", "android");
if (upId > 0) {
ImageView up = (ImageView) findViewById(upId);
up.setImageResource(R.drawable.ic_action_bar_menu);
up.setPadding(0, 0, 20, 0);
}
But this is not working on most new phones (HTC One, Galaxy S3, etc). Is there a way that can be changed uniformly across devices. I need it to be changed only on home screen. Other screens would have the default one. So cannot use the styles.xml
This is what i did to acheive the behavior. I inherited the base theme and created a new theme to use it as a theme for the specific activity.
<style name="CustomActivityTheme" parent="AppTheme">
<item name="android:homeAsUpIndicator">#drawable/custom_home_as_up_icon</item>
</style>
and in the android manifest i made the activity theme as the above.
<activity
android:name="com.example.CustomActivity"
android:theme="#style/CustomActivityTheme" >
</activity>
works great. Will update again when i check on all devices I have. Thanks #faylon for pointing in the right direction
The question was to change dynamically the Up Home Indicator, although this answer was accepting and it is about Themes and Styles. I found a way to do this programmatically, according to Adneal's answer which gives me the clue and specially the right way to do. I used the below snippet code and it works well on (tested) devices with APIs mentioned here.
For lower APIs, I use R.id.up which is not available on higher API. That's why, I retrieve this id by a little workaround which is getting the parent of home button (android.R.id.home) and its first child (android.R.id.up):
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// get the parent view of home (app icon) imageview
ViewGroup home = (ViewGroup) findViewById(android.R.id.home).getParent();
// get the first child (up imageview)
( (ImageView) home.getChildAt(0) )
// change the icon according to your needs
.setImageResource(R.drawable.custom_icon_up));
} else {
// get the up imageview directly with R.id.up
( (ImageView) findViewById(R.id.up) )
.setImageResource(R.drawable.custom_icon_up));
}
Note: If you don't use the SDK condition, you will get some NullPointerException.
API 18 has new methods ActionBar.setHomeAsUpIndicator() - unfortunately these aren't supported in the support library at this moment
http://developer.android.com/reference/android/app/ActionBar.html#setHomeAsUpIndicator(android.graphics.drawable.Drawable)
edit: these are now supported by the support library
http://developer.android.com/reference/android/support/v7/app/ActionBar.html#setHomeAsUpIndicator(android.graphics.drawable.Drawable)
All you need to do is to use this line of code:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
This will change the icon with the up indicator. To disable it later, just call this function again and pass false as the param.
The solution by checking Resources.getSystem() doesn't work on all devices, A better solution to change the homeAsUpIndicator is to set it #null in style and change the logo resource programmatically.
Below is my code from style.xml
<style name="Theme.HomeScreen" parent="AppBaseTheme">
<item name="displayOptions">showHome|useLogo</item>
<item name="homeAsUpIndicator">#null</item>
<item name="android:homeAsUpIndicator">#null</item>
</style>
In code you can change the logo using setLogo() method.
getSupportActionBar().setLogo(R.drawable.abc_ic_ab_back_holo_light); //for ActionBarCompat
getActionBar().setLogo(R.drawable.abc_ic_ab_back_holo_light); //for default actionbar for post 3.0 devices
Also note that the Android API 18 has methods to edit the homeAsUpIndicator programatically, refer documentation.
You can achieve this in an easier way. Try to can change the homeAsUpIndicator attribute of actionBarStyle in your theme.xml and styles.xml.
If you want some padding, just add some white space in your image.
You can try this:
this.getSupportActionBar().setHomeAsUpIndicator( R.drawable.actionbar_indicator ); //for ActionBarCompat
this.getActionBar().setHomeAsUpIndicator( R.drawable.actionbar_indicator ); //for default actionbar for post 3.0 devices
If you need change the position of the icon, you must create a drawable file containing a "layer-list" like this:
actionbar_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="#drawable/indicator"
android:right="5dp"
android:left="10dp" />
</layer-list>
use getActionBar().setCustomView(int yourView); because ActionBar haven't method to change homeUp icon!
Adding to Fllo answer Change the actionbar homeAsUpIndicator Programamtically
I was able to use this hack on Android 4+ but could not understand why the up/home indicator was back to the default one when search widget was expanded. Looking at the view hierarchy, turns out that the up/home indicator + icon section of the action bar has 2 implementations and of course the first on is the one for when the search widget is not expanded. So here is the code I used to work around this and get the up/home indicator changed in both cases.
mSearchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
// https://stackoverflow.com/questions/17585892/change-the-actionbar-homeasupindicator-programamtically
int actionBarId = getResources().getIdentifier("android:id/action_bar", null, null);
View view = getActivity().getWindow().getDecorView().findViewById(actionBarId);
if (view == null
|| !(view instanceof ViewGroup)) {
return true;
}
final ViewGroup actionBarView = (ViewGroup)view;
// The second home view is only inflated after
// setOnActionExpandListener() is first called
actionBarView.post(new Runnable() {
#Override
public void run() {
//The 2 ActionBarView$HomeView views are always children of the same view group
//However, they are not always children of the ActionBarView itself
//(depends on OS version)
int upId = getResources().getIdentifier("android:id/up", null, null);
View upView = actionBarView.findViewById(upId);
ViewParent viewParent = upView.getParent();
if (viewParent == null) {
return;
}
viewParent = viewParent.getParent();
if (viewParent == null
|| !(viewParent instanceof ViewGroup)) {
return;
}
ViewGroup viewGroup = (ViewGroup) viewParent;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = viewGroup.getChildAt(i);
if (childView instanceof ViewGroup) {
ViewGroup homeView = (ViewGroup) childView;
upView = homeView.findViewById(upId);
if (upView != null
&& upView instanceof ImageView) {
Drawable upDrawable = getResources().getDrawable(R.drawable.ic_ab_back_holo_dark_am);
upDrawable.setColorFilter(accentColorInt, PorterDuff.Mode.MULTIPLY);
((ImageView) upView).setImageDrawable(upDrawable);
}
}
}
}
});
If someone uses the library support-v7 appcompat, you can directly call this method:
getSupportActionBar().setHomeAsUpIndicator(int redId)
In other case you can use this solution:
https://stackoverflow.com/a/23522910/944630
If you are using DrawerLayout with ActionBarDrawerToggle, then check out this answer.
this.getSupportActionBar().setDisplayUseLogoEnabled(true);
this.getSupportActionBar().setLogo(R.drawable.about_selected);
Also you can define the logo in manifest in attribute android:logo of and tags and set in theme that you want to use logo instead of app icon in the action bar.
I have an app with ActionBarSherlock using theme Theme.Sherlock.Light.DarkActionBar. Action bar is dark and my menu icons are light. When I run my app on small layouts, 2 or 3 menu items with icons are displayed in the overflow menu.
On Android 3+ the overflow menu items will not display their icons, but on Android 2.3 and earlier I see menu tiles with almost invisible icons, because the tile color is white and icons are close to be white.
As you can see, the light icons are invisible on a white background, but they must have light color to be visible on a dark action bar:
Can I remove icons when menu items are displayed in the overflow menu?
you could use configuration qualifiers.
e.g.
make a drawable folder
/res/drawable-v11/ put all the "light" icons in it.
and for the darker icons use the
/res/drawable/ folder.
be sure to use the same filenames in both folders.
I hope I have understood your problem and this might help you.
However, if you want to change the drawables JUST for the overflow menu, I don't think it's possible. Also because the menu icons are not intended to be used like that. ActionBarSherlock is probably also because of issues like this, not an official library.
I was also facing the same issue:
there are many ways you can achieve this rather than removing image:
1)you can use respective drawable folder to put light and dark image.
2)You can also change the background color by code of your menu by checking your device version.
If you device doen't support overflow menu, the, you can change the background color of your menu as well as you can also change menu text color.
I was also facing the same issue and resolved using following one:
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);
}
3) change background color from styles.xml file
<style name="Theme.MyTheme" parent="Theme.Sherlock.ForceOverflow">
<item name="actionBarStyle">#style/Widget.MyTheme.ActionBar</item>
<item name="android:actionBarStyle">#style/Widget.MyTheme.ActionBar</item>
</style>
<style name="Widget.MyTheme.ActionBar" parent="Widget.Sherlock.ActionBar">
<item name="android:background">#ff000000</item>
<item name="background">#ff000000</item>
</style>
For me, all of the 3 worked fine
Hope, this will work for you as well
Another option is to remove the icons from the non-action items in onPrepareOptionsMenu.
The idea is to use actionbarsherlock's MenuItemImpl.isActionButton to figure out if each item is an action item, and if not to remove the icon. This is made a little bit tricky because onPrepareOptionsMenu is called (at least) twice by ABS - the first time when it is building the action bar, in which case MenuItemImpl.isActionButton has not yet been set and will always return false. If that's the case, you want to leave the icons alone. Once the action bar has been built the isActionButton method will return true for action bar items, false otherwise. So you want to remove the icons for the ones that return false. This is what I came up with:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
boolean buildingOptionsMenu = false;
for (int i=0; i<menu.size(); ++i) {
MenuItemImpl mi = (MenuItemImpl)menu.getItem(i);
if (mi.isActionButton()) {
buildingOptionsMenu = true;
break;
}
}
if (buildingOptionsMenu) {
for (int i=0; i<menu.size(); ++i) {
MenuItemImpl mi = (MenuItemImpl)menu.getItem(i);
if (!mi.isActionButton()) {
mi.setIcon(null);
mi.setIcon(0);
}
}
}
}
return super.onPrepareOptionsMenu(menu);
}
You'll need these two imports:
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
This works in ABS 4.3.0, but since it uses internal library classes it might not work with other versions of the library.
OS 2.x was a mess since the options menu background could be black or white, depending on the device, with no way to know which for sure.
The easy fix was to use grey (#888888) icons for Android 2.x & under and put your modern (ICS/JB) icons in a v11 folder for modern devices:
drawable // old school icons
drawable-v11 // modern icons
Of course that means drawable-mdpi-v11, drawable-hdpi-v11, and so on.
A simple alternative to adding a whole set of duplicate dark icons for 2.x versions can be simply removing the icons from all the items that can go to the overflow menu. For example:
res/menu
<item
android:id="#+id/menu_send_email"
android:showAsAction="ifRoom"
android:title="#string/menu_send_email"/>
res/menu-v11 (or even res/menu-v9, because 2.3 usually has a dark menu)
<item
android:id="#+id/menu_send_email"
android:icon="#drawable/ic_action_send_email"
android:showAsAction="ifRoom"
android:title="#string/menu_send_email"/>
Of course, you need to make the titles short enough to fit into the ActionBar at least on some larger screens, or settle with the fact that they always go into the overflow.