I am trying to use the new api's from google, specifically the action bar.
When the build was set at api 10, if I pressed the menu button, I got nice looking menu options, each with a picture and icon. When using api 14, No matter what I try, it always puts the icon in the action bar with NO text. I have tried everything I can think of. I gave it the "with text" property, changed the text to a single character (in case it was a room issue), but nothing.
I have seen this done before, even in the developer guide at android.developer, but I can't seem to find an answer as to HOW to get it to show up.
I suspect that it was a conscious decision by the Android developers to never display a single menu item's text and icon on a narrow action bar. But if you really want to do so, you can use android:actionLayout in your menu.xml file. The Android ActionBar documentation has a slightly better explanation.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menu_foo"
android:title="#string/menu_foo"
android:icon="#drawable/ic_menu_foo"
android:showAsAction="always"
android:actionLayout="#layout/action_button_foo" />
</menu>
Then create your action_button_foo.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="14dp"
android:paddingBottom="14dp"
android:gravity="center"
android:text="#string/menu_foo"
android:drawableLeft="#drawable/ic_menu_foo"
android:background="#drawable/bg_btn_action_bar"
android:clickable="true" />
and use a selector for its background bg_btn_action_bar.xml, so it changes color when you tap it:
<?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/bg_action_bar_pressed" />
<item
android:drawable="#color/transparent" />
</selector>
Now you'll need to make your custom view handle click events. In your Activity, I like to do this, so that I can handle the click in onOptionsItemSelected along with all my other, non-custom items.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my_menu, menu);
final MenuItem item = menu.findItem(R.id.menu_foo);
item.getActionView().setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
onOptionsItemSelected(item);
}
});
return super.onCreateOptionsMenu(menu);
}
This is definitely the same thing I've observed on my Nexus S running 4.0.4. My app uses an action bar with several tabs that are implemented as fragments. My various fragments make adjustments to the menu options displayed on the action bar while the their tab is visible.
This appears to be a bug in ICS, because it performs consistently as follows, both on my Nexus S and in the emulator (both HVGA and WVGA800):
In portrait mode, my logo/up button appears on the top row of the action bar, tabs appear on the second row, and any actions appear as icons only (no text) in the right side of the top row.
But if when I rotate to landscape, the action bar collapses to a single row, and tabs move up to the top bar as a spinner (drop-down list) next to my up button. But notably, then the text appears next to my action icons.
I noticed some other glitches with the tab spinner that lead me to believe that this little corner of ICS is a bit messy/buggy. If I tell the application to split the action bar on narrow displays (by adding android:uiOptions="splitActionBarWhenNarrow" in the manifest, ICS always pushes those items to the bottom bar, even though there's still plenty of room at the top. And even with the extra bar, it still doesn't display the text, just the icon.
On my Xoom running 4.0.4, tabs and action items always appear the way you'd expect them to appear because there's plenty of room.
Workaround: if you really want text on the action bar in portrait mode, you need to give up the icon. Remove the icon from your menu item and the text will appear. This isn't exactly what we're after though.
I've posted a bug report here: https://code.google.com/p/android/issues/detail?id=30180.
If you want your Options Menu to show up in your action bar with Honeycomb, I did this:
In your activity, override this function:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.actionbar_universe, menu);
return true;
}
where R.menu.actionbar_universe define your menu item like this:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/crossholdings" android:showAsAction="always|withText"
android:title="Cross Holdings" android:icon="#drawable/actionbar_cross"/>
</menu>
Note the showAsAction="always|withText" and specify android:title.
If you have that and its not working please copy|paste your menu resource here.
EDIT: This answers the wrong question, but it is the original text.
I use this bit of code to set the title of the action bar, and paint it red with my companies logo. It works well in 3.0.
public ActionBar setActionBarStyle(String title) {
ActionBar actionBar = setActionBarStyle();
actionBar.setTitle(title);
return actionBar;
}
public ActionBar setActionBarStyle() {
ActionBar actionBar = getActionBar();
ShapeDrawable actionBackground = new ShapeDrawable();
actionBackground.getPaint().setColor(Color.RED);
actionBackground.setBounds(0, 0, 5, 5);
actionBar.setBackgroundDrawable(actionBackground);
actionBar.setDisplayUseLogoEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
return actionBar;
}
The "withText" property works with most tablets, but an easy way to get icons and text on smaller devices is to add the text next to the icon as one image (PNG file).
That way, both the text and icon will be seen as one icon and the whole thing will display.
You can use the original icon for tablets by using the withText property.
You have to create an extra menu folder in the res directory titled "menu-w600dp".
The optionmenu.xml in this folder will only apply to screen widths bigger than 600dp
(the ones that will show the icons and text with no problems).
Fixed this issue by reading "If your app is using the Support Library" section under Specify the Actions in XML.
...for compatibility on versions as low as Android 2.1, the showAsAction attribute is not available from the android: namespace. Instead this attribute is provided by the Support Library and you must define your own XML namespace and use that namespace as the attribute prefix. (A custom XML namespace should be based on your app name, but it can be any name you want and is only accessible within the scope of the file in which you declare it.) For example:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
<!-- Search, should appear as action button -->
<item android:id="#+id/action_search"
android:icon="#drawable/ic_action_search"
android:title="#string/action_search"
yourapp:showAsAction="ifRoom" />
...
</menu>
If none of these other things work for you, this may.
I tried many options and I came up with a simple "trick" without any weird line of code, without images. And first solution with custom actionLayout simply did not work for me with API level 10 compatibility.
If you want to display text AND icon on a small action bar it means you know you have the space, right? So you can use 2 menu items:
First with the icon ONLY (ignore warning, if you set a title tablets will show it twice)
Second with the text ONLY
And choose the text action to 'ifRoom' if needed so that if you do need space, the text will go away. It WILL take some more space on the action bar though but was a good compromise for me.
I end up with the following code:
<item
android:id="#+id/menu_save"
android:icon="#drawable/save"
pelmel:showAsAction="always">
</item>
<item
android:id="#+id/menu_save_text"
android:title="#string/profileSave"
pelmel:showAsAction="ifRoom">
</item>
(EDIT Where "pelmel" is your app name END EDIT)
And then your selection handler just has to catch both IDs :
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_save:
case R.id.menu_save_text:
// Your code here
return true;
}
}
Here's another option, based roughly on dgmltn's. The advantages:
More control - e.g. I've swapped the text and image over in my layout.
Easier to use - only requires two extra lines in your activities/fragments.
Only requires two extra files.
Possibly slightly more correct, but it's still a bit of a hack IMO.
I've assumed you're using ActionBarSherlock in this example. First, create the view layout you want. This one is based on ActionBarSherlock's. All I changed was swapping the image/view over, reducing the shared margin/padding to 0 so they are closer, and resolving all the ABS styles.
<com.example.views.ActionMenuTextItemView xmlns:android="http://schemas.android.com/apk/res/android"
style="#android:style/Widget.Holo.ActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:addStatesFromChildren="true"
android:focusable="true"
android:gravity="center"
android:clickable="true"
android:paddingLeft="4dip"
android:paddingRight="4dip" >
<com.actionbarsherlock.internal.widget.CapitalizingButton
android:id="#+id/abs__textButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#null"
android:ellipsize="none"
android:focusable="false"
android:minHeight="48dip"
android:minWidth="48dip"
android:paddingBottom="4dip"
android:paddingLeft="4dip"
android:paddingRight="0dip"
android:paddingTop="4dip"
android:singleLine="true"
android:textAppearance="#android:style/TextAppearance.Holo.Widget.ActionBar.Menu"
android:textColor="#fff3f3f3" />
<ImageButton
android:id="#+id/abs__imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="4dip"
android:layout_marginLeft="0dip"
android:layout_marginRight="4dip"
android:layout_marginTop="4dip"
android:adjustViewBounds="true"
android:background="#null"
android:focusable="false"
android:scaleType="fitCenter"
android:visibility="gone" />
</com.example.views.ActionMenuTextItemView>
Then create the corresponding View class. You may want to copy CapitalizingButton if you are worried about using internal things. Oh, also I never fixed the minimum width stuff. Don't think it really matters though.
package com.example.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.actionbarsherlock.R;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.app.SherlockListActivity;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.internal.widget.CapitalizingButton;
import com.actionbarsherlock.view.MenuItem;
#SuppressLint({ "NewApi" })
public class ActionMenuTextItemView extends LinearLayout implements OnClickListener
{
private ImageButton mImageButton;
private CapitalizingButton mTextButton;
private Object mTarget;
private MenuItem mItem;
// Set up all the data. Object must be a sherlock activity or fragment with an onMenuItemSelected().
public void initialise(MenuItem item, Object target)
{
mItem = item;
mTarget = target;
setIcon(mItem.getIcon());
setTitle(mItem.getTitle());
}
public ActionMenuTextItemView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ActionMenuTextItemView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
public void onFinishInflate()
{
super.onFinishInflate();
mImageButton = (ImageButton) findViewById(R.id.abs__imageButton);
mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton);
mImageButton.setOnClickListener(this);
mTextButton.setOnClickListener(this);
setOnClickListener(this);
}
#Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
mImageButton.setEnabled(enabled);
mTextButton.setEnabled(enabled);
}
public void setIcon(Drawable icon)
{
mImageButton.setImageDrawable(icon);
if (icon != null)
mImageButton.setVisibility(VISIBLE);
else
mImageButton.setVisibility(GONE);
}
public void setTitle(CharSequence title)
{
mTextButton.setTextCompat(title);
setContentDescription(title);
}
#Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event)
{
onPopulateAccessibilityEvent(event);
return true;
}
#Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
super.onPopulateAccessibilityEvent(event);
final CharSequence cdesc = getContentDescription();
if (!TextUtils.isEmpty(cdesc))
event.getText().add(cdesc);
}
#Override
public boolean dispatchHoverEvent(MotionEvent event)
{
// Don't allow children to hover; we want this to be treated as a single component.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
return onHoverEvent(event);
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int minWidth = 0;
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int specSize = MeasureSpec.getSize(widthMeasureSpec);
final int oldMeasuredWidth = getMeasuredWidth();
final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, minWidth) : minWidth;
if (widthMode != MeasureSpec.EXACTLY && minWidth > 0 && oldMeasuredWidth < targetWidth)
{
// Remeasure at exactly the minimum width.
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}
}
#Override
public void onClick(View v)
{
if (mTarget == null)
return;
else if (mTarget instanceof SherlockActivity)
((SherlockActivity)mTarget).onOptionsItemSelected(mItem);
else if (mTarget instanceof SherlockFragmentActivity)
((SherlockFragmentActivity)mTarget).onOptionsItemSelected(mItem);
else if (mTarget instanceof SherlockListActivity)
((SherlockListActivity)mTarget).onOptionsItemSelected(mItem);
else if (mTarget instanceof SherlockListFragment)
((SherlockListFragment)mTarget).onOptionsItemSelected(mItem);
else if (mTarget instanceof SherlockFragment)
((SherlockFragment)mTarget).onOptionsItemSelected(mItem);
else
throw new IllegalArgumentException("Target must be a sherlock activity or fragment.");
}
}
Ok now you're ready to use it. In your menu items that you want to have text, you do the same as what dgmltn said:
<item
android:id="#+id/menu_foo"
android:icon="#drawable/..."
android:showAsAction="always|withText" // Doesn't do anything really.
android:title="Sell"
android:titleCondensed="Sell"
android:actionLayout="#layout/view_action_menu_text_item"/> // Or whatever you called it.
And finally, just add this code to your activity/fragment:
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
getSupportMenuInflater().inflate(R.menu.activity_main, menu);
// The magic lines.
MenuItem it = menu.findItem(R.id.menu_foo);
((ActionMenuTextItemView)it.getActionView()).initialise(it, this);
And that's it!
Related
I designed an action bar that has several variations:
As you can see this action bar always has a header and there may be action buttons.
I understand that I can just create five action bars and use them. But I want to create one component and reuse it everywhere in my application.
For example, in ReactJS I would just create one component ActionBar with some properties and reuse it where I need it, such as:
<ActionBar
isBackButton = true
text = "Screen name"
actionButton = "Add"
/>
or
<ActionBar
isBackButton = false
text = "Another screen name"
actionButton = "None"
/>
How do I achieve this?
Or should I not get steamed up and just create five different action bars?
Or create a universal action bar with text, right and left buttons, and then dynamically customize button icons, text and onClick actions in java code?
I definitely need your advice on what to do.
So the easiest way to do this is with a custom view. You'd create a layout file for it:
toolbar.xml (this is simplifies to just give the basic idea, you need to fill in all the styling data:
<LinearLayout>
<ImageView android:id="#+id/back"/>
<TextView android:id="#+id/text">
<ImageView android:id="#+id/icon>
</LinearLayout>
You'd then write a view
ToolbarView.java:
package com.example
public class ToolbarView extends View {
private ImageView back;
private TextView text;
private ImageView icon;
public ToolbarView(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.toolbar, this)
back = findViewById(R.id.back)
text = findViewById(R.id.text)
//Same for all other views
}
public void setText(String text) {
text.setText(text)
}
public void setBackEnabled(boolean enabled) {
back.setVisibility(enabled ? VISIBLE : GONE)
}
public void setIcon(Drawable icon) {
if(icon == null) {
icon.setVisibility(GONE)
}
else {
icon.setVisibility(VISIBLE)
icon.setDrawable(icon)
}
}
After that, you can just put <com.example.Toolbar/> in your other layouts and it will embed the toolbar. You can get it via FindViewById and then call setText, setBackEnabled, etc.
Currently the action mode toolbar shows above the toolbar, moving the whole layout down, and I want it to show on top of my current toolbar. I tried all solutions in this post and this one:
I tried using windowActionModeOverlay with and without android: prefix, no success
I tried both view.ActionMode and support.v7.view.ActionMode, no success
I tried using startActionMode both on my toolbar and my activity, neither worked.
setContentView is called before super.onCreate, this is not the problem
Right now I just set toolbar visibility manually, which looks awful. Also I set the activity theme before super.onCreate, could it be the problem? If not what is it? How can I make the attribute work?
My activity:
import android.support.v7.view.ActionMode;
import android.support.v7.widget.Toolbar;
// import ...
public class FileExplorerActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.Theme_NoActionBar);
super.onCreate(savedInstanceState);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// ...
}
private void setActionMode(boolean enabled) {
if (enabled) {
actionMode = startSupportActionMode(new ActionMode.Callback() {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.cab, menu);
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// ... change icons color for theme
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// ... handle item clicks
return true;
}
#Override
public void onDestroyActionMode(ActionMode mode) {}
});
} else {
actionMode.finish();
}
}
}
My theme:
<style name="Theme.NoActionBar" parent="Theme.AppCompat.NoActionBar">
<item name="android:actionModeBackground">?attr/colorPrimary</item>
<item name="android:actionModeSplitBackground">#null</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="windowActionModeOverlay">true</item>
</style>
I have tried all the combination and found that you are using values-v21 folder for defining styles for lollipop and above devices and you have forgot to add <item name="windowActionModeOverlay">true</item> in styles.xml in values-v21 folder.
Just to make sure you are following the right path I have just posted a sample project on Github showing how to show ActionBar on the top of Custom toolbar.
You can also download the sample APK and try it.
Answering all the question you have posted in the question
I tried using windowActionModeOverlay with and without android: prefix, no success
You don't have to use android prefix. The windowActionModeOverlay is valid.
<item name="windowActionModeOverlay">true</item>
I tried both view.ActionMode and support.v7.view.ActionMode, no success
This is required as you are trying to use support lib which has a separate Action Bar namespace import android.support.v7.view.ActionMode;
I tried using startActionMode both on my toolbar and my activity, neither worked.
It works in both ways. If you want to look at example you can see it in the github repo.
setContentView is called before super.onCreate, this is not the problem
This isn't a problem. It will work.
Background
many apps (including google plus and facebook) have an action bar item showing the number of in-app-events (or "notifications").
This action item has a number within it and you can click it in order to show the events the app has for the user.
something like that (taken from here) :
The problem
I wish to make it work on old android versions, so i use actionBarSherlock.
Sadly, each solution i use has its disadvantages, and i couldn't find any solution here (on stackOverflow) that handles this with actionBarSherlock (found other solutions, but not with this library).
I've also found a post about it (here) , claiming it's an issue on this library, but it's very old and seems to be closed and marked as fixed, but I can't find out how to use it now.
What I've tried
i've tried the next solutions:
actionLayout . it showed fine, but clicking on it didn't show the clicking effect.
actionViewClass - it didn't even work for some reason.
adding the menu item and its view programmatically.
The question
What's the best way to achieve this ?
EDIT: this is what i've tried using actionLayout :
"action_item_notification.xml" - for now it's the same as "abs__action_menu_item_layout.xml" (here). later i will add a textView to hold the number of notifications.
in the menu xml file, i have this as one of the items:
<item
android:id="#+id/activity_main__menuItem_notifications"
android:actionLayout="#layout/action_item_notification"
android:icon="#drawable/notification_button"
android:showAsAction="always"
android:title="#string/notifications"/>
not only it doesn't show the icon, but long clicking on the item will crash the app, with a NPE on the ActionMenuItemView.java file.
EDIT:ok, so i've found a solution that is almost perfect.
it shows the action item nicely and it even reacts to clicking as the other action items.
I've sadly had one missing feature - long clicking on action item to show the toast of its title. sadly, i couldn't find a way to overcome this so what i did (that works) is handling the long clicking on the view itself, and call a similar code that is used for ActionMenuItemView::onLongClick .
if anyone has a better and nicer solution, please write it down.
i've written this solution in a new answer here.
here's my solution, but it's a bit messy and calls the same code of showing a toast for the action item as the one of actionBarSherlock.
if anyone has a better, cleaner solution, please write it down.
menu file (activity_main.xml) :
...
<item
android:id="#+id/activity_main__menuItem_notifications"
android:showAsAction="always"
android:title="#string/notifications"/>
...
MainActivity.java :
public boolean onCreateOptionsMenu(...){
...
getSupportMenuInflater().inflate(R.menu.activity_main, menu);
//
final MenuItem notificationsMenuItem = menu.findItem(R.id.activity_main__menuItem_notifications);
notificationsMenuItem.setActionView(R.layout.action_item_notification);
setEnableLongClickOnCustomActionItem(notificationsMenuItem,true);
...
public static void setEnableLongClickOnCustomActionItem(final MenuItem menuItem, final boolean enable) {
final View actionView = menuItem.getActionView();
if (actionView == null)
return;
final CharSequence title = menuItem.getTitle();
if (!enable || Strings.isEmpty(title))
actionView.setOnLongClickListener(null);
actionView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(final View v) {
final int[] screenPos = new int[2];
final Rect displayFrame = new Rect();
actionView.getLocationOnScreen(screenPos);
actionView.getWindowVisibleDisplayFrame(displayFrame);
final Context context = actionView.getContext();
final int width = actionView.getWidth();
final int height = actionView.getHeight();
final int midy = screenPos[1] + height / 2;
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
final Toast cheatSheet = Toast.makeText(context, title, Toast.LENGTH_SHORT);
if (midy < displayFrame.height()) {
// Show along the top; follow action buttons
cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT, screenWidth - screenPos[0] - width / 2, height);
} else {
// Show along the bottom center
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
}
cheatSheet.show();
return true;
}
});
layout file of the action item ( action_item_notification.xml) :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:addStatesFromChildren="true"
android:focusable="true" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:background="#null"
android:focusable="false"
android:scaleType="fitCenter"
android:src="#drawable/notification_button" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/imageView1"
android:layout_alignRight="#+id/imageView1"
android:background="#drawable/action_item_notification_counter_background"
android:paddingLeft="1dp"
android:paddingRight="1dp"
android:text="88"
android:textColor="#FFffffff"
tools:ignore="HardcodedText" />
</RelativeLayout>
and a nice drawable for the background of the textView ("action_item_notification_counter_background.xml") :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" >
<solid android:color="#FFff0000" />
</shape>
I found an easier way to solve your problem - which I also had, with my final struggle being also the toast.
So, to solve the toast problem as simply as possible, just do as follow :
final Menu m = menu;
final MenuItem item = m.findItem(R.id.item);
final View actionView = item.getActionView();
actionView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast toast = Toast.makeText(CWalletOffersFragment.this.getContext(), item.getTitle(), Toast.LENGTH_SHORT);
toast.setGravity(Gravity.TOP, v.getRight(),v.getBottom());
toast.show();
return true;
}
});
The position is a bit off, but not by much (you could still tweak it with an offset if you like). I really did not want to write lines upon lines of code for something so simple, so I think this is a worthy tradeoff :) !
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.
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;
}