How to use ActionMenuView? - android

Since SplitActionBar is no longer supported in Android 5.0, I am trying to use an ActionMenuView to achieve a SplitActionBar effect. But I could not find much information on how to use ActionMenuView.
I know I can add a ActionMenuView in the layout file, but I don't know how to add menu items. It doesn't seem like I could inflate them like I do with SplitActionBar.
Could you give some sample code on how to use ActonMenuView? Thanks!

Getting ActionMenuView to display a whole screen's width of icons is a chore. Here is an example to do what you want. Make sure your ActionMenuView XML item is wrap_content for height and width, then gravity to the right. Surround it in a LinearLayout which takes the whole width and provides background color.
Use this code to initialize the ActionMenuView (obviously you will need to change the button callbacks)
ActionMenuView actionMenuView = (ActionMenuView) findViewById(R.id.editBar);
final Context context = this;
MenuBuilder menuBuilder = new MenuBuilder(context);
menuBuilder.setCallback(new MenuBuilder.Callback() {
#Override
public boolean onMenuItemSelected(MenuBuilder menuBuilder, MenuItem menuItem) {
return onOptionsItemSelected(menuItem);
}
#Override
public void onMenuModeChange(MenuBuilder menuBuilder) {
}
});
// setup a actionMenuPresenter which will use up as much space as it can, even with width=wrap_content
ActionMenuPresenter presenter = new ActionMenuPresenter(context);
presenter.setReserveOverflow(true);
presenter.setWidthLimit(getResources().getDisplayMetrics().widthPixels, true);
presenter.setItemLimit(Integer.MAX_VALUE);
// open a menu xml into the menubuilder
getMenuInflater().inflate(R.menu.editbar, menuBuilder);
// runs presenter.initformenu(mMenu) too, setting up presenter's mmenu ref... this must be before setmenuview
menuBuilder.addMenuPresenter(presenter, this);
// runs menuview.initialize too, so menuview.mmenu = mpresenter.mmenu
actionMenuView.setPresenter(presenter);
presenter.updateMenuView(true);
For what it's worth, I had to read the support library source code for 8 hours to get this to work. The documentation is garbage.

It seems the API has changed in the meantime. Currently, the following code works:
ActionMenuView actions = new ActionMenuView(activity);
MenuBuilder menuBuilder = (MenuBuilder) actions.getMenu();
menuBuilder.setCallback(new MenuBuilder.Callback() {
#Override
public boolean onMenuItemSelected(MenuBuilder menuBuilder, MenuItem menuItem) {
return onOptionsItemSelected(menuItem);
}
#Override
public void onMenuModeChange(MenuBuilder menuBuilder) {
}
});
inflater.inflate(R.menu.my_menu, menuBuilder);

If you are using the v7 appCompat library make sure your activity extends from ActionBarActivity and that you use the support version of the ActionMenuView.
Likewise if you are not using the support library be sure to use the ActionMenuView outside the support library.
From there you can get the ActionMenuView from your layout and populate its menu using the following method:
getMenuInflater().inflate(R.menu.your_menu_here, actionMenuView.getMenu())
If you aren't in an activity where getMenuInflater() is accessible create your own MenuInflater or SupportMenuInflater.

In appcompat-v7:27.0.2, the ActionMenuView requires a minimum width of 56dp. Do not use android:layout_width="wrap_content".
If your popup theme is being ignored, make sure you call setPopupTheme(int) before any call to getMenu() on ActionMenuView.

Inflate
you can use this code in your activity :
menuInflater.inflate(R.menu.{your_menu_res_id}, {your_ActionMenuView_instance}.menu)
like this :
menuInflater.inflate(R.menu.settings_menu, settings_menu.menu)
Item Click Listener
then you can add item click listenter :
{your_ActionMenuView_instance}.setOnMenuItemClickListener()
like this :
settings_menu.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.menu_settings_save -> {
// your code
return#setOnMenuItemClickListener true
}
else -> return#setOnMenuItemClickListener false
}
}

Related

How to get the "up" button used on the Toolbar?

This is a short question:
I'm trying to force the action bar (used by a Toolbar) to use LTR alignment. I've succeeded making the layout itself use LTR, but not the "up" button (as I've done here, before Toolbar was introduced) .
It seems this view doesn't have an ID, and I think using getChildAt() is too risky.
Can anyone help?
The answer
Here's one way I've found to solve this, based on this answer .
I made it so that it is guarranteed to find only the "up" button, and whatever it does, it will revert back to the previous state it was before.
Here's the code:
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#Override
public boolean onCreateOptionsMenu(final Menu menu)
{
// <= do the normal stuff of action bar menu preparetions
if(VERSION.SDK_INT>=VERSION_CODES.JELLY_BEAN_MR1&&getResources().getConfiguration().getLayoutDirection()==View.LAYOUT_DIRECTION_RTL)
{
final ArrayList<View> outViews=new ArrayList<>();
final CharSequence previousDesc=_toolbar.getNavigationContentDescription();
for(int id=0;;++id)
{
final String uniqueContentDescription=Integer.toString(id);
_toolbar.findViewsWithText(outViews,uniqueContentDescription,View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if(!outViews.isEmpty())
continue;
_toolbar.setNavigationContentDescription(uniqueContentDescription);
_toolbar.findViewsWithText(outViews,uniqueContentDescription,View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if (outViews.isEmpty())
if (BuildConfig.DEBUG)
throw new RuntimeException(
"You should call this function only when the toolbar already has views");
else
break;
outViews.get(0).setRotation(180f);
break;
}
_toolbar.setNavigationContentDescription(previousDesc);
}
//
return super.onCreateOptionsMenu(menu);
}
It seems this view doesn't have an ID
You're right, the navigation view is created programmatically and never sets an id. But you can still find it by using View.findViewsWithText.
View.findViewsWithText comes with two flags:
View.FIND_VIEWS_WITH_TEXT
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION
The navigation view's default content description is "Navigate up" or the resource id is abc_action_bar_up_description for AppCompat and action_bar_up_description for the framework's, but you can easily apply your own using Toolbar.setNavigationContentDescription.
Here's an example implementation:
final Toolbar toolbar = ...;
toolbar.setNavigationContentDescription("up");
setActionBar(toolbar);
final ArrayList<View> outViews = Lists.newArrayList();
toolbar.findViewsWithText(outViews, "up", View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
outViews.get(0).setRotation(180f);
Results

How to remove icons from an ActionBarSherlock's overflow menu on Android 2.3?

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.

How to implement Action Bar with Fragment? [duplicate]

I would like to dynamically change the "home" icon in the ActionBar. This is easily done in v14 with ActionBar.setIcon(...), but I can't find anyway to accomplish this in previous versions.
If your actionbar works like Sherlock and is based on menu items, this is my solution:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem switchButton = menu.findItem(R.id.SwitchSearchOption);
if(searchScriptDisplayed){
switchButton.setIcon(R.drawable.menu_precedent);
}else{
switchButton.setIcon(R.drawable.icon_search);
}
return super.onPrepareOptionsMenu(menu);
}
If you are using the ActionbarCompat code provided by google, you can access the home icon via the ActionBarHelperBase.java class for API v4 onwards.
//code snippet from ActionBarHelperBase.java
...
private void setupActionBar() {
final ViewGroup actionBarCompat = getActionBarCompat();
if (actionBarCompat == null) {
return;
}
LinearLayout.LayoutParams springLayoutParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT);
springLayoutParams.weight = 1;
// Add Home button
SimpleMenu tempMenu = new SimpleMenu(mActivity);
SimpleMenuItem homeItem = new SimpleMenuItem(tempMenu,
android.R.id.home, 0, mActivity.getString(R.string.app_name));
homeItem.setIcon(R.drawable.ic_home_ftn);
addActionItemCompatFromMenuItem(homeItem);
// Add title text
TextView titleText = new TextView(mActivity, null,
R.attr.actionbarCompatTitleStyle);
titleText.setLayoutParams(springLayoutParams);
titleText.setText(mActivity.getTitle());
actionBarCompat.addView(titleText);
}
...
You should be able to modify the code to the home button accessible to the activities that extend ActionBarActivity and change it that way.
Honeycomb seems a little harder and it doesn't seem to give such easy access. At a guess, its id should also be android.R.id.home so you may be able to pull that from the view in ActionBarHelperHoneycomb.java
I would say you do something like this :
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_drawer);
see the link How to change the icon actionBarCompat
The ActionBar will use the android:logo attribute of your manifest, if one is provided. That lets you use separate drawable resources for the icon (Launcher) and the logo (ActionBar, among other things).

Why android:actionBarDivider doesn't work?

Having
<item name="android:actionBarDivider">#drawable/action_bar_divider</item>
in my Activity style has zero effect. Doesn't matter if I'm using native ActionBar or ActionBarSherlock. Why?
I worked around it by programatically inserting dividers into the view hierarchy. It occured to me later that dividers may be only visible in the bottom action bar (I needed them at the top bar). But I didn't verify that.
public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main_activity, menu);
new Handler().post(new Runnable() {
#Override
public void run() {
for (int id : new int[] { R.id.action_1, R.id.action_2 }) {
View actionView = findViewById(id);
insertDividerBefore(actionView);
}
}
});
return true;
}
android:actionBarDivider
Defines a drawable resource for the divider between action items. (Added in API level 14.)
Make sure whether you are using API level 14 or later.

Honeycomb - customize SearchView inside the action bar

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;
}

Categories

Resources