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!