I'm using native ActionBar and ShareActionProvider.
There is a code in my activity that hides ActionBar in four seconds:
Handler hideUIHandler = new Handler();
hideUIHandler.postDelayed(new Runnable() {
public void run() {
getActionBar().hide();
}
}, 4000);
Unfortunately hideUIHandler hides ActionBar even when I clicked the "Share" item to see menu providing by ShareActionProvider.
I don't want to hide ActionBar in that case. What should I do?
You can override Activity.onMenuOpened and Activity.onPanelClosed to determine when the overflow menu or other sub-menus are showing, such as the ShareActionProvider. Here's an example:
/** True if an options menu has been opened, false otherwise */
private boolean mMenuOpened;
#Override
public boolean onMenuOpened(int featureId, Menu menu) {
mMenuOpened = true;
return super.onMenuOpened(featureId, menu);
}
#Override
public void onPanelClosed(int featureId, Menu menu) {
super.onPanelClosed(featureId, menu);
mMenuOpened = false;
}
Alternatively
ActivityChooserView has a way to check ActivityChooserView.isShowingPopup, but you'll have to subclass ShareActionProvider and invoke it via reflection because the ActivityChooserView class is hidden.
Here's an example:
AccessibleShareActionProvider
/**
* A subclass of {#link ShareActionProvider} that uses reflection to invoke
* <p>
* <code>android.widget.ActivityChooserView.isShowingPopup</code>
* <p>
*/
public class AccessibleShareActionProvider extends ShareActionProvider {
/** The action view created for {#link ShareActionProvider} */
private View mActivityChooserView;
/**
* Constructor for <code>AccessibleShareActionProvider</code>
*
* #param context The {#link Context} to use
*/
public AccessibleShareActionProvider(Context context) {
super(context);
}
/**
* {#inheritDoc}
*/
#Override
public View onCreateActionView(MenuItem forItem) {
mActivityChooserView = super.onCreateActionView(forItem);
return mActivityChooserView;
}
/**
* #return True if showing, false if already dismissed
*/
public boolean isShowingPopup() {
try {
final Class<?> acv = Class.forName("android.widget.ActivityChooserView");
final Method isShowingPopup = acv.getMethod("isShowingPopup", new Class[] {});
return (boolean) isShowingPopup.invoke(mActivityChooserView, new Object[] {});
} catch (final Exception ignored) {
// Nothing to do
}
return false;
}
}
Your MenuItem
<item
android:id="#+id/menu_item_share"
android:actionProviderClass="your_path_to.AccessibleShareActionProvider"
android:showAsAction="ifRoom"
android:title="#string/share"/>
In your Activity
private Handler mHandler = new Handler();
private AccessibleShareActionProvider mShareProvider;
#Override
public boolean onCreateOptionsMenu(Menu menu) {
...
final MenuItem item = menu.findItem(R.id.menu_item_share);
mShareProvider = (AccessibleShareActionProvider) item.getActionProvider();
...
return super.onCreateOptionsMenu(menu);
}
private final Runnable mHideActionBar = new Runnable() {
#Override
public void run() {
// If the ShareActionProvider is showing, delay for 4 seconds
if (mShareProvider.isShowingPopup()) {
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(mHideActionBar, 4000);
return;
}
getActionBar().hide();
}
};
Related
I have an activity with NavigationDrawerFragment, shown here:
NavigationDrawerFragment.java
/**
* Fragment used for managing interactions for and presentation of a navigation drawer.
* See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction">
* design guidelines</a> for a complete explanation of the behaviors implemented here.
*/
public class NavigationDrawerFragment extends Fragment {
/**
* Remember the position of the selected item.
*/
private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
/**
* Per the design guidelines, you should show the drawer on launch until the user manually
* expands it. This shared preference tracks this.
*/
private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
/**
* A pointer to the current callbacks instance (the Activity).
*/
private NavigationDrawerCallbacks mCallbacks;
/**
* Helper component that ties the action bar to the navigation drawer.
*/
private ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout mDrawerLayout;
private ListView mDrawerListView;
private View mFragmentContainerView;
private int mCurrentSelectedPosition = 0;
private boolean mFromSavedInstanceState;
private boolean mUserLearnedDrawer;
private EventActivity mainActivity = null;
public NavigationDrawerFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null) {
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
}
// Select either the default item (0) or the last selected item.
selectItem(mCurrentSelectedPosition);
}
#Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Indicate that this fragment would like to influence the set of actions in the action bar.
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mainActivity = (EventActivity) getActivity();
mFragmentContainerView = inflater.inflate(R.layout.fragment_drawer_attendee, container, false);
... NOT IMPORTANT ...
mDrawerListView.setAdapter(new MenuEventAdapter(getActivity(), menuAL));
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
mDrawerListView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
... NOT IMPORTANT ...
}
});
return mFragmentContainerView;
}
public boolean isDrawerOpen() {
return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
}
/**
* Users of this fragment must call this method to set up the navigation drawer interactions.
*
* #param fragmentId The android:id of this fragment in its activity's layout.
* #param drawerLayout The DrawerLayout containing this fragment's UI.
*/
public void setUp(int fragmentId, DrawerLayout drawerLayout) {
mFragmentContainerView = getActivity().findViewById(fragmentId);
mDrawerLayout = drawerLayout;
// set a custom shadow that overlays the main content when the drawer opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the navigation drawer and the action bar app icon.
mDrawerToggle = new ActionBarDrawerToggle(
getActivity(), /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
R.string.navigation_drawer_open, /* "open drawer" description for accessibility */
R.string.navigation_drawer_close /* "close drawer" description for accessibility */
) {
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if (!isAdded()) {
return;
}
getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
}
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
if (!isAdded()) {
return;
}
if (!mUserLearnedDrawer) {
// The user manually opened the drawer; store this flag to prevent auto-showing
// the navigation drawer automatically in the future.
mUserLearnedDrawer = true;
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(getActivity());
sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
}
getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
}
};
// If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer,
// per the navigation drawer design guidelines.
if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
mDrawerLayout.openDrawer(mFragmentContainerView);
}
// Defer code dependent on restoration of previous instance state.
mDrawerLayout.post(new Runnable() {
#Override
public void run() {
mDrawerToggle.syncState();
}
});
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
private void selectItem(int position) {
mCurrentSelectedPosition = position;
if (mDrawerListView != null) {
mDrawerListView.setItemChecked(position, true);
}
if (mDrawerLayout != null) {
mDrawerLayout.closeDrawer(mFragmentContainerView);
}
if (mCallbacks != null) {
mCallbacks.onNavigationDrawerItemSelected(position);
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mCallbacks = (NavigationDrawerCallbacks) activity;
} catch (ClassCastException e) {
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
}
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Forward the new configuration the drawer toggle component.
mDrawerToggle.onConfigurationChanged(newConfig);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// If the drawer is open, show the global app actions in the action bar. See also
// showGlobalContextActionBar, which controls the top-left area of the action bar.
if (mDrawerLayout != null && isDrawerOpen()) {
inflater.inflate(R.menu.global, menu);
showGlobalContextActionBar();
}
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
/*if (item.getItemId() == R.id.action_example) {
Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show();
return true;
}
*/
return super.onOptionsItemSelected(item);
}
/**
* Per the navigation drawer design guidelines, updates the action bar to show the global app
* 'context', rather than just what's in the current screen.
*/
private void showGlobalContextActionBar() {
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
private ActionBar getActionBar() {
return ((ActionBarActivity) getActivity()).getSupportActionBar();
}
/**
* Callbacks interface that all activities using this fragment must implement.
*/
public static interface NavigationDrawerCallbacks {
/**
* Called when an item in the navigation drawer is selected.
*/
void onNavigationDrawerItemSelected(int position);
}
}
When I select an item from the drawer, it will add a new Fragment to the activity container.
Each fragment have it's own menu, as shown in the following example:
EventInformationFragment.java
public class EventInformationFragment extends Fragment
{
private View rootView = null;
private EventActivity mainActivity = null;
private GoogleMap mMap;
private boolean isStaff = false;
private LayoutInflater inflater = null;
private ViewGroup container = null;
private TextView forCopy = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
if(EventActivityHelper.getHelper().inSchedule)
{
return null;
}
mainActivity = (EventActivity) GlobalContents.getNowActivity();
mainActivity.getActionBar().setTitle(R.string.Event);
this.inflater = inflater;
this.container = container;
GlobalContents.dismissProgressBar();
if(!GlobalContents.getGlobalContents().getAuthenticatedUser().isStaff())
{
return elaborateAttendeeView();
}
isStaff = true;
return elaborateStaffView();
}
private View elaborateAttendeeView()
{
... NOT IMPORTANT ...
}
private View elaborateStaffView()
{
... NOT IMPORTANT ...
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
if(menu.hasVisibleItems())
menu.clear();
if(isStaff)
inflater.inflate(R.menu.event, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public void onPrepareOptionsMenu(Menu menu)
{
if(menu.hasVisibleItems())
menu.clear();
MenuInflater inflater = getActivity().getMenuInflater();
if(isStaff)
inflater.inflate(R.menu.event, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.action_edit:
mainActivity.getSupportFragmentManager()
.beginTransaction()
.remove(mainActivity.currentFragment)
.remove(getFragmentManager().findFragmentById(R.id.map))
.commit();
mainActivity.currentFragment = new EventEditInformationFragment();
mainActivity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, mainActivity.currentFragment).commit();
break;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
... NOT IMPORTANT ...
}
#Override
public boolean onContextItemSelected(MenuItem item)
{
... NOT IMPORTANT ...
}
}
Here is the exact part of EventInformationFragment that I inflates the menu:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
if(menu.hasVisibleItems())
menu.clear();
if(isStaff)
inflater.inflate(R.menu.event, menu);
super.onCreateOptionsMenu(menu, inflater);
}
The problem is: when I open the Fragment through the NavigationDrawerFragment, the menu doesn't inflate!
In the EventInformationFragment, I intent to another activity, and, onResume this activity, the menu inflates!
What am I doing wrong?
Here is the activity that has the NavigationDrawerFragment and, at some point, EventInformationFragment
EventActivity.java
public class EventActivity extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks
{
private static final String TAG = "EventActivity";
private NavigationDrawerFragment mNavigationDrawerFragment;
public Event currentEvent = null;
private PagerSlidingTabStrip tabs;
private ViewPager pager;
/* FOR TABBING */
private MyPagerAdapter adapter;
public Fragment currentFragment = null;
public Map<String, Fragment> fragments = null;
private Bundle instance;
public Vector<List<Lecture>> currentLectures = null;
private Calendar[] realScheduleDays;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_with_fragment_and_drawer);
currentEvent = GlobalContents.getGlobalContents().getCurrentEvent();
GlobalContents.setNowActivity(this);
if(GlobalContents.getGlobalContents().getAuthenticatedUser() == null)
{
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
finish();
}
else if(currentEvent == null)
{
Intent intent = new Intent(this, ProfileEventActivity.class);
startActivity(intent);
finish();
}
else
{
GlobalContents.getGlobalContents().setProgressBar();
/* DRAWER */
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
setTitle(getString(R.string.Event));
mNavigationDrawerFragment.setUp(R.id.navigation_drawer, mDrawerLayout);
if(!EventActivityHelper.getHelper().inSchedule)
dismissTabsForSchedule();
else
loadTabsForSchedule();
/* CONTENT */
if (savedInstanceState == null)
{
EventActivityHelper.getHelper();
if(!EventActivityHelper.getHelper().inSchedule)
{
currentFragment = new EventInformationFragment();
fragments = new HashMap<String, Fragment>();
fragments.put(currentFragment.getClass().getName(), currentFragment);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, currentFragment).commit();
}
}
}
}
public void loadTabsForSchedule()
{
... NOT IMPORTANT ...
}
public void dismissTabsForSchedule()
{
... NOT IMPORTANT ...
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
/*fragmentManager
.beginTransaction()
.replace(R.id.container,
PlaceholderFragment.newInstance(position + 1)).commit();*/
}
public void restoreActionBar() {
ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
//getMenuInflater().inflate(R.menu.event, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
#Override
protected void onResume()
{
super.onResume();
GlobalContents.setNowActivity(this);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
GlobalContents.setNowActivity(this);
switch(requestCode)
{
case Constants.FOR_REFRESH:
if(resultCode == Constants.FOR_REFRESH_OK)
{
currentLectures = null;
loadTabsForSchedule();
}
break;
}
}
... NOT IMPORTANT FROM NOW ON...
}
After a lot of search and tries, I've managed to make it work.
Before I commit the Fragment, I've placed an invalidateOptionsMenu().
It works now.
How to check that the action overflow PopupMenu is now showing?
It is need for me when I trying to auto-hide ActionBar, and don't need to hide when ActionBar's overflow popup is showing.
You can override Activity.onMenuOpened and Activity.onPanelClosed to determine when the overflow menu or other sub-menus are showing, such as the ShareActionProvider. Here's an example:
/** True if an options menu has been opened, false otherwise */
private boolean mMenuOpened;
#Override
public boolean onMenuOpened(int featureId, Menu menu) {
mMenuOpened = true;
return super.onMenuOpened(featureId, menu);
}
#Override
public void onPanelClosed(int featureId, Menu menu) {
super.onPanelClosed(featureId, menu);
mMenuOpened = false;
}
Alternatively
ActionBarView subclasses AbsActionBarView which contains AbsActionBarView.isOverflowMenuShowing. Since these class are internal and hidden, you'll need to access it via reflection.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int actionBarViewId = getResources().getIdentifier("action_bar", "id", "android");
final View actionBarView = findViewById(actionBarViewId);
actionBarView.postDelayed(new Runnable() {
#Override
public void run() {
System.out.println(isOverflowMenuShowing(actionBarView));
}
}, 2500);
}
private static boolean isOverflowMenuShowing(View actionBarView) {
try {
final Class<?> abv = Class.forName("com.android.internal.widget.ActionBarView");
final Method isOverflowShowing = abv.getMethod("isOverflowMenuShowing", new Class[] {});
return (boolean) isOverflowShowing.invoke(actionBarView, new Object[] {});
} catch (final Exception ignored) {
// Nothing to do
}
return false;
}
My menu xml code menu.xml:
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Single menu item
Set id, icon and Title for each menu item
-->
<item android:id="#+id/back"
android:icon="#drawable/back1"
android:showAsAction="never"
android:title="Back" />
<item android:id="#+id/My_Profile"
android:icon="#drawable/myprofile"
android:showAsAction="never"
android:title="My Profile" />
<item android:id="#+id/Job_Alert"
android:icon="#drawable/jobalert4"
android:showAsAction="never"
android:title="Job Alert !" />
<item android:id="#+id/saved_job"
android:icon="#drawable/jobapplied"
android:title="Saved Jobs"
/>
<item android:id="#+id/Logout"
android:icon="#drawable/logout"
android:title="Logout" />
</menu>
I am calling menu xml like this
PopupMenu popup = new PopupMenu(getBaseContext(), v);
popup.getMenuInflater().inflate(R.menu.menu, popup.getMenu());
popup.show();
But it does not show the icon.
How can I set the icon on the popup menu?
You can create popup menu with icon using the MenuBuilder and MenuPopupHelper.
MenuBuilder menuBuilder =new MenuBuilder(this);
MenuInflater inflater = new MenuInflater(this);
inflater.inflate(R.menu.menu, menuBuilder);
MenuPopupHelper optionsMenu = new MenuPopupHelper(this, menuBuilder, view);
optionsMenu.setForceShowIcon(true);
// Set Item Click Listener
menuBuilder.setCallback(new MenuBuilder.Callback() {
#Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
switch (item.getItemId()) {
case R.id.opt1: // Handle option1 Click
return true;
case R.id.opt2: // Handle option2 Click
return true;
default:
return false;
}
}
#Override
public void onMenuModeChange(MenuBuilder menu) {}
});
optionsMenu.show();
menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/opt1"
android:icon="#mipmap/ic_launcher"
android:title="option 1" />
<item
android:id="#+id/opt2"
android:icon="#mipmap/ic_launcher"
android:title="option 2" />
</menu>
You can enable icons for popup menu by using Java reflection to call a hidden method as below:
public static void setForceShowIcon(PopupMenu popupMenu) {
try {
Field[] fields = popupMenu.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
It's because when you use the showAsAction="never" attribute, the default overflow does not return your icon. You could create your own overflow like this:
<item android:title=""
android:id="#+id/overflow"
android:showAsAction="always"
android:icon="#drawable/overflow_icon">
<menu >
<item android:id="#+id/back"
android:icon="#drawable/back1"
android:title="Back" />
<item android:id="#+id/My_Profile"
android:icon="#drawable/myprofile"
android:title="My Profile" />
<item android:id="#+id/Job_Alert"
android:icon="#drawable/jobalert4"
android:title="Job Alert !" />
<item android:id="#+id/saved_job"
android:icon="#drawable/jobapplied"
android:title="Saved Job"/>
<item android:id="#+id/Logout"
android:icon="#drawable/logout"
android:title="Logout" />
</menu>
</item>
You could use reflection as described here:
https://stackoverflow.com/a/18431605/4521603
Or if you use Xamarin / C#:
add:
using Java.Lang.Reflect;
Then use this in your code:
PopupMenu puMenu = new PopupMenu(Activity, v)
Field field = puMenu.Class.GetDeclaredField("mPopup");
field.Accessible = true;
Java.Lang.Object menuPopupHelper = field.Get(puMenu);
Method setForceIcons = menuPopupHelper.Class.GetDeclaredMethod("setForceShowIcon", Java.Lang.Boolean.Type);
setForceIcons.Invoke(menuPopupHelper, true);
puMenu.Inflate (Resource.Menu.your_actions);
puMenu.Show ();
Use this:
/**
* Copied from android.support.v7.widget.PopupMenu.
* "mPopup.setForceShowIcon(true);" in the constructor does the trick :)
*
* #author maikvlcek
* #since 5:00 PM - 1/27/14
*/
public class IconizedMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
private Context mContext;
private MenuBuilder mMenu;
private View mAnchor;
private MenuPopupHelper mPopup;
private OnMenuItemClickListener mMenuItemClickListener;
private OnDismissListener mDismissListener;
/**
* Callback interface used to notify the application that the menu has closed.
*/
public interface OnDismissListener {
/**
* Called when the associated menu has been dismissed.
*
* #param menu The PopupMenu that was dismissed.
*/
public void onDismiss(IconizedMenu menu);
}
/**
* Construct a new PopupMenu.
*
* #param context Context for the PopupMenu.
* #param anchor Anchor view for this popup. The popup will appear below the anchor if there
* is room, or above it if there is not.
*/
public IconizedMenu(Context context, View anchor) {
mContext = context;
mMenu = new MenuBuilder(context);
mMenu.setCallback(this);
mAnchor = anchor;
mPopup = new MenuPopupHelper(context, mMenu, anchor);
mPopup.setCallback(this);
mPopup.setForceShowIcon(true);
}
/**
* #return the {#link android.view.Menu} associated with this popup. Populate the returned Menu with
* items before calling {#link #show()}.
*
* #see #show()
* #see #getMenuInflater()
*/
public Menu getMenu() {
return mMenu;
}
/**
* #return a {#link android.view.MenuInflater} that can be used to inflate menu items from XML into the
* menu returned by {#link #getMenu()}.
*
* #see #getMenu()
*/
public MenuInflater getMenuInflater() {
return new SupportMenuInflater(mContext);
}
/**
* Inflate a menu resource into this PopupMenu. This is equivalent to calling
* popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
* #param menuRes Menu resource to inflate
*/
public void inflate(int menuRes) {
getMenuInflater().inflate(menuRes, mMenu);
}
/**
* Show the menu popup anchored to the view specified during construction.
* #see #dismiss()
*/
public void show() {
mPopup.show();
}
/**
* Dismiss the menu popup.
* #see #show()
*/
public void dismiss() {
mPopup.dismiss();
}
/**
* Set a listener that will be notified when the user selects an item from the menu.
*
* #param listener Listener to notify
*/
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mMenuItemClickListener = listener;
}
/**
* Set a listener that will be notified when this menu is dismissed.
*
* #param listener Listener to notify
*/
public void setOnDismissListener(OnDismissListener listener) {
mDismissListener = listener;
}
/**
* #hide
*/
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
if (mMenuItemClickListener != null) {
return mMenuItemClickListener.onMenuItemClick(item);
}
return false;
}
/**
* #hide
*/
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (mDismissListener != null) {
mDismissListener.onDismiss(this);
}
}
/**
* #hide
*/
public boolean onOpenSubMenu(MenuBuilder subMenu) {
if (subMenu == null) return false;
if (!subMenu.hasVisibleItems()) {
return true;
}
// Current menu will be dismissed by the normal helper, submenu will be shown in its place.
new MenuPopupHelper(mContext, subMenu, mAnchor).show();
return true;
}
/**
* #hide
*/
public void onCloseSubMenu(SubMenuBuilder menu) {
}
/**
* #hide
*/
public void onMenuModeChange(MenuBuilder menu) {
}
/**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
*/
public interface OnMenuItemClickListener {
/**
* This method will be invoked when a menu item is clicked if the item itself did
* not already handle the event.
*
* #param item {#link MenuItem} that was clicked
* #return <code>true</code> if the event was handled, <code>false</code> otherwise.
*/
public boolean onMenuItemClick(MenuItem item);
}
}
Source: https://gist.github.com/mediavrog/9345938
The setForceShowIcon function is now public and not restricted anymore so this works!
private fun showPopupMenu(view: View) {
PopupMenu(this, view).apply {
menuInflater.inflate(R.menu.my_menu, menu)
setForceShowIcon(true)
}.show()
}
if trying this code in any activity then replace getbBaseContext() with this i.e activity context
PopupMenu popup = new PopupMenu(this, v);
before popupMenu.show(); use
try {
Field mFieldPopup=popupMenu.getClass().getDeclaredField("mPopup");
mFieldPopup.setAccessible(true);
MenuPopupHelper mPopup = (MenuPopupHelper) mFieldPopup.get(popupMenu);
mPopup.setForceShowIcon(true);
} catch (Exception e) {
}
You can implement this By the use of Reflection if u don`t familiar with it with the help of this awesome java advanced feature u can modify the runtime behavior of applications running in the JVM you can look at the object and perform its methods on runtime and in our case we need to modify popupMenu behavior at runtime instead of extend the core class and modify it ;) hope that help Try my method working like a charm
private void showPopupMenu(View view) {
// inflate menu
PopupMenu popup = new PopupMenu(mcontext, view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.main, popup.getMenu());
Object menuHelper;
Class[] argTypes;
try {
Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
fMenuHelper.setAccessible(true);
menuHelper = fMenuHelper.get(popup);
argTypes = new Class[]{boolean.class};
menuHelper.getClass().getDeclaredMethod("setForceShowIcon", argTypes).invoke(menuHelper, true);
} catch (Exception e) {
}
popup.show();
}
As a topic:
With the current state of my work, I can show items on settings tab and action bar.
But the methods included don't work like expected. I'm not sure what's the problem.
Can you take a look at the following code to help me understand my errors?
public class Automat extends Activity {
DataBase dataBase;
ArrayList<Resource> res;
ViewPager myPager;
boolean move;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_automat);
// Show the Up button in the action bar.
setupActionBar();
move=true;
dataBase = new DataBase(this);
dataBase.open();
res = dataBase.getList();
ViewPagerAdapter2 adapter = new ViewPagerAdapter2(this, res, dataBase);
System.out.println("ADAPTER" + adapter);
myPager = (ViewPager) findViewById(R.id.myPager2);
//myPager.setOffscreenPageLimit(0);
myPager.setAdapter(adapter);
adapter.notifyDataSetChanged();
adapter.startUpdate(myPager);
dataBase.close();
class MyTimerTask extends TimerTask {
public void run() {
runOnUiThread(new Runnable() {
public void run() {
if (move){
myPager.setCurrentItem(myPager.getCurrentItem() + 1);
}else {}
}
});
System.out.println("");
}
}
MyTimerTask myTask = new MyTimerTask();
Timer myTimer = new Timer();
myTimer.schedule(myTask, 15000, 15000);
}
/**
* Set up the {#link android.app.ActionBar}, if the API is available.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.automat, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
switchPause();
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
private void switchPause(){
if(move){
System.out.println("MOVE = FALSE");
move=false;
}else {
System.out.println("MOVE = true");
move=true;
}
}
}
In method onOptionsItemSelected use code like following
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case android.R.id.home:
newGame();
return true;
case R.id.help:
showHelp();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
I am using the the SearchView in the ActionBar of the ListView. The magnifying glass can be touched, the SearchView shows its edit box, and the user can enter the text for filtering the content of the list. It almost works. However, when the user presses the Up button, the SearchView collapses back to the icon, the text inside the widget is cleared, and the filtering is reset. The effect (in my case) is that the list can be filtered only when the SearchView is not iconified. The wanted behaviour is to keep the filter text also after the SearchView was collapsed.
Attention: The behaviour probably changed in Android 4.3. With 4.2.2 it worked as wanted. See the observations below.
Details: To be more specific, the menu contains the following item:
<item android:id="#+id/menu_search_customers"
android:title="#string/menu_search_text"
android:icon="#android:drawable/ic_menu_search"
android:showAsAction="ifRoom|collapseActionView"
android:actionViewClass="android.widget.SearchView" />
Notice the icon and the android:showAsAction. I belive the Up button appears by default when the SearchView is expanded (by Up I mean the < plus the icon -- see the right image with the blue book from the official Navigation with Back and Up). It seems that the default handler implementation just collapses the expanded SearchView (returns back to the icon state).
When debugging, I have found that the onQueryTextChange() is fired with the empty text when the Up is used. (I believe this was not the case with Android 4.2.2, because it worked as wanted before the OS update.) This is the reason why the filtering of the list items is also reset -- see my onQueryTextChange() below. I want the SearchView collapsed, and the filter text displayed as subtitle in the action bar.
So far, my code related to the SearchView looks like this:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// MenuInflater adds the magnifying glass icon for the SearchView
// to the ActionBar as the always visible menu item.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.customers_menu, menu);
// Get the related SearchView widget.
SearchView sv = (SearchView) menu.findItem(R.id.menu_search_customers)
.getActionView();
// Get the changes immediately.
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
// I am not sure whether the onQueryTextSubmit() is important
// for the purpose.
#Override
public boolean onQueryTextSubmit(String query) {
getActionBar().setSubtitle(mCurFilter);
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
// The newText is stored into a member variable that
// is used when the new CursorLoader is created.
mCurFilter = newText;
getActionBar().setSubtitle(mCurFilter);
getLoaderManager().restartLoader(0, null,
CustomersOverviewActivity.this);
return true;
}
});
return true;
}
The restarted loader calls the onCreateLoader. Notice the mCurFilter is used for building the SQL query:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { CustomerTable._ID,
CustomerTable.CODE,
CustomerTable.NAME,
CustomerTable.STREET,
CustomerTable.TOWN };
String selection = null; // init
String[] selectionArgs = null; // init
if ( ! mCurFilter.isEmpty()) {
selection = CustomerTable.NAME + " like ?";
selectionArgs = new String[]{ "%" + mCurFilter +"%" };
}
CursorLoader cursorLoader = new CursorLoader(this,
DemoContentProvider.CUSTOMERS_CONTENT_URI, projection,
selection, selectionArgs,
orderInfo);
return cursorLoader;
}
I would like to detect the situation when the Up is pressed before the onQueryTextChange() is called. This way (say) I could set a flag and block the mCurFilter assignment by the emptied SearchView content. Also, when the search icon is expanded again, I would like to initialize the text in the expanded SearchView from the mCurFilter before it is shown (i.e. the expanded view is preset with the filter text). How that can be done?
Update: The earlier implementation of the SearchView had...
#Override
public void onActionViewCollapsed() {
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
Now, it contains...
#Override
public void onActionViewCollapsed() {
setQuery("", false);
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
Do you know what could be the reason for setting the query to the empty string? Should I override the new implementation by the old code? Or is there a better way?
I have written a StatefulSearchView which retains the text:
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
public class StatefulSearchView extends SearchView implements android.view.View.OnLayoutChangeListener, OnQueryTextListener,android.widget.SearchView.OnCloseListener{
private boolean mSaveText=true;
private OnQueryTextListener mQueryListener;
private String mQuery;
private OnCloseListener mCloseListener;
private boolean fromIconify = true;
public StatefulSearchView(Context context, AttributeSet attrs) {
super(context, attrs);
addOnLayoutChangeListener(this);
super.setOnCloseListener(this);
}
public StatefulSearchView(Context context) {
super(context);
// TODO Auto-generated constructor stub
addOnLayoutChangeListener(this);
super.setOnCloseListener(this);
}
public void setSaveSearchTextState(boolean save){
this.mSaveText = save;
this.setSaveEnabled(mSaveText);
}
public void setOnStatefulQueryTextListener(OnQueryTextListener listener) {
mQueryListener = listener;
super.setOnQueryTextListener(this);
}
#Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if(super.isIconfiedByDefault() || !super.isIconified() && !TextUtils.isEmpty(mQuery) && mSaveText){
setSavedText(mQuery);
}
Log.i("onLayoutChanged()",""+mQuery);
}
#Override
public void setIconified(boolean iconify) {
mQuery = getQuery().toString();
Log.i("setIconified()",""+mQuery);
super.setOnCloseListener(null);
super.setIconified(iconify);
super.setIconified(iconify);
super.setOnCloseListener(this);
fromIconify = true;
}
#Override
public void setOnCloseListener(OnCloseListener listener) {
mCloseListener = listener;
super.setOnCloseListener(this);
}
#Override
protected Parcelable onSaveInstanceState() {
Parcelable state = super.onSaveInstanceState();
return new SearchQueryState(state, mQuery, mSaveText);
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
SearchQueryState sqs = (SearchQueryState)state;
super.onRestoreInstanceState(sqs.getSuperState());
mQuery = sqs.getSavedQuery();
mSaveText = sqs.getSaveText();
}
#Override
public boolean onQueryTextChange(String arg0) {
mQuery = arg0;
return mQueryListener.onQueryTextChange(mQuery);
}
#Override
public boolean onQueryTextSubmit(String arg0) {
// TODO Auto-generated method stub
return mQueryListener.onQueryTextSubmit(arg0);
}
private TextView getTextView(){
int searchTextViewId = getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
return (TextView) this.findViewById(searchTextViewId);
}
private void setSavedText(String s){
super.setOnQueryTextListener(null);
Log.i("setSavedText()",""+s);
TextView t = getTextView();
t.setText(s);
if(!TextUtils.isEmpty(s))
((EditText)t).setSelection(s.length());
super.setOnQueryTextListener(mQueryListener);
}
private class SearchQueryState extends BaseSavedState{
private boolean mSaveText;
private String mQueryText;
public SearchQueryState(Parcel arg0) {
super(arg0);
this.mQueryText = arg0.readString();
this.mSaveText = arg0.readInt() == 1;
}
public SearchQueryState(Parcelable superState, String queryText, boolean saveText) {
super(superState);
this.mQueryText = queryText;
this.mSaveText = saveText;
}
public boolean getSaveText(){
return this.mSaveText;
}
public String getSavedQuery(){
return mQueryText;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
super.writeToParcel(dest, flags);
dest.writeString(mQueryText);
dest.writeInt(mSaveText? 1: 0);
}
}
#Override
public boolean onClose() {
Log.i("onClose()", "Is from setIconified(): "+fromIconify);
if(!fromIconify){
mQuery = null;
fromIconify = false;
}
return mCloseListener == null ? false : mCloseListener.onClose();
}
}
In demonstration activity:
public class MainActivity extends Activity{
private StatefulSearchView mSearchView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getActionBar().setHomeButtonEnabled(true);
}
#Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
if(item.getItemId()==android.R.id.home) {
mSearchView.setIconified(true);
return true;
}
return super.onMenuItemSelected(featureId, item);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
MenuItem item = menu.findItem(R.id.action_search);
mSearchView =(StatefulSearchView)item.getActionView();
mSearchView.setSaveSearchTextState(true);
mSearchView.setOnStatefulQueryTextListener(new OnQueryTextListener(){
#Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return false;
}});
return true;
}
In menu xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/action_search"
android:orderInCategory="100"
android:showAsAction="always"
android:actionViewClass="com.nikola.despotoski.saveablesearchview.StatefulSearchView"
android:title="#string/action_settings"/>
</menu>
In the source of the SearchView, it clearly says that they change the text to "":
#Override
public void onActionViewCollapsed() {
setQuery("", false);
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
/**
* {#inheritDoc}
*/
#Override
public void onActionViewExpanded() {
if (mExpandedInActionView) return;
mExpandedInActionView = true;
mCollapsedImeOptions = mQueryTextView.getImeOptions();
mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
mQueryTextView.setText("");
setIconified(false);
}
Let me know if you have issues.
I'm not sure I understand your problem but you can just detect when up is clicked like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
doSOmething();
return true;
}
return super.onOptionsItemSelected(item);
}
If you intercept the up click, you can presumably do anything you want here. Returning true will consume the event and that should prevent any default action from taking place. This way you can do whatever you want the up button to do while at the same time consuming the up event to prevent clearing of your filters.
I struggle with this a little until I found the solution.
Declare your menuItem like this, check the showAsAction attribute, type only ifRoom, if you set collapseActionView the widget will collapse and show the back button on the actionbar
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="#+id/search"
android:actionViewClass="android.widget.SearchView"
android:icon="#drawable/ic_2_action_search"
android:showAsAction="ifRoom"
android:title="#null"/>
</menu>
Set up your SearchView as usual, remember to Add setIconifiedByDefault this will make the icon to start up as an icon
SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
searchView = (SearchView) menu.findItem(R.id.search).getActionView();
searchView.setIconifiedByDefault(true);
searchView.setOnQueryTextListener(new SearchViewOnQueryListener());
searchView.setOnCloseListener(new SearchViewOnCloseListener());
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
On your QueryListener is where you handle the end of your search like so, here is where you use onActionViewCollapse() which collapse back the ViewSearch
#Override
public boolean onQueryTextSubmit(String query) {
makeSearchRequest(SEARCH_TYPE_KEYWORD, query);
searchView.setQuery("", false);
searchView.clearFocus();
searchView.onActionViewCollapsed();
buttonClearSearchResults.setVisibility(View.VISIBLE);
return false;
}