1

I am trying to achieve the following user-experience: When I have more items in my ListFragment's adapter that can be displayed on the screen, I would like to show the Search action in the ActionBar, but when there are less, then the Add Item action.

I face 2 problems:

  1. I am trying to calculate in onCreateOptionsMenu wether the the number of items are less or more than what can be displayed on the screen, but in that stage the ListView is always empty. Where should I move that calculation, and how can I set the menu actions from another method?

  2. When I use menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) in my code, it does have the effect of not displaying it in the ActionBar, but when I click on it in the MenuOverflow, it doesn't act at all.

I use minSdkVersion 15 without compatibility or support library.

res/menu/list.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.fletech.android.opensesame.key.KeyringActivity" >
    <item android:id="@+id/menu_item_add_item"
        android:orderInCategory="100"
        android:showAsAction="always"
        android:icon="@android:drawable/ic_menu_add"
        android:title="Add key" />
    <item android:id="@+id/menu_item_search"
        android:orderInCategory="200"
        android:showAsAction="ifRoom|collapseActionView"
        android:icon="@android:drawable/ic_menu_search"
        android:title="Search"
        android:actionViewClass="com.fletech.android.opensesame.app.CursorFragment$MySearchView"/>

    <!-- I have more menu items here -->

</menu>

And the code:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.list, menu);

    final MenuItem search = menu.findItem(R.id.menu_item_search);
    mSearchView = (MySearchView)search.getActionView();
    mSearchView.setOnQueryTextListener(this);
    mSearchView.setOnCloseListener(this);
    mSearchView.setIconifiedByDefault(true);

    final ListView lv = getListView();
    if (lv.getFirstVisiblePosition() == 0 && lv.getLastVisiblePosition() == mAdapter.getCount()-1) {
        // all items visible: show add, hide search
        search.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    } else {
        // not all items visible: show search, hide add
        final MenuItem add = menu.findItem(R.id.menu_item_add_item);
        add.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    }
}

update: the second issue was resolved by moving the mSearchView lines to after the setShowAsAction() calls:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.list, menu);

    SendInvitationActivity.onCreateShareOptionsMenu(getActivity(), menu, R.id.menu_item_share);

    final MenuItem search = menu.findItem(R.id.menu_item_search);
    final MenuItem add = menu.findItem(R.id.menu_item_add_item);

    final ListView lv = getListView();
    if (lv.getFirstVisiblePosition() == 0 && lv.getLastVisiblePosition() == mAdapter.getCount()-1) {
        // all items visible: show add, hide search
        search.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    } else {
        // not all items visible: show search, hide add
        add.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    }

    mSearchView = (MySearchView)search.getActionView();
    mSearchView.setOnQueryTextListener(this);
    mSearchView.setOnCloseListener(this);
    mSearchView.setIconifiedByDefault(true);
}
Gavriel
  • 18,880
  • 12
  • 68
  • 105

1 Answers1

1
  1. When the Adapter items changed (added / removed), you need check number of items displayed and then if you want call onCreateOptionsMenu again, just call:

    Activity.invalidateOptionsMenu();
    // Your Adapter
    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
        invalidateOptionsMenu(); // call after super.notifyDataSetChanged();
        // let's delay a second if still problem
        // new Handler().postDelay();
    }
    
  2. Try this:

     final MenuItem add = menu.findItem(R.id.menu_item_add_item);
     final MenuItem search = menu.findItem(R.id.menu_item_search_item);
     int numberItemsVisible = lv.getLastVisiblePosition() - lv.getFirstVisiblePosition() + 1;
     if (numberItemsVisible == mAdapter.getCount()) {
         // all items visible: show add, hide search
         add.setShowAsAction(MenuItem.ALWAYS);
         search.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         // or search.setShowAsAction(MenuItem.NEVER);
     } else {
         // not all items visible: show search, hide add
         add.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
         // or add.setShowAsAction(MenuItem.NEVER);
         search.setShowAsAction(MenuItem.ALWAYS);
     }
    
Khang .NT
  • 1,504
  • 6
  • 24
  • 46
  • 1. you're solving a "future" problem (when number of items change), but this doesn't solve my current problem: to set the menu action for the "1st time" (when I launch the activity) 2. I tried it, it doesn't work. I don't think I need the "ALWAYS" lines, as those are already set in the xml. Somehow when I call search.setShowAsAction it breaks it's behaviour (when I click on it in the overflow it doesn't open the search input in the actionbar). – Gavriel Mar 17 '16 at 07:38
  • can you update : res/menu/list.xml it look not correctly – Khang .NT Mar 17 '16 at 07:42
  • Actually you solved it!!!! 1. I have overridden the adapter's notifyDataSetChanged() and added getActivity().invalidateOptionsMenu(). 2. didn't have to change the setShowAsAction code, just moved the 4 lines of mSearchView AFTER the if – Gavriel Mar 17 '16 at 07:49
  • Oops, it only works partially: lv.getLastVisiblePosition() == -1 always. The list is not populated yet. So either I'll need to do the calculation in another place or if I calculate here I'll need to do it by dividing screen height by item height??? I don't like this aproach :( – Gavriel Mar 17 '16 at 08:00
  • Oh! Not always, i think getLastVisiblePosition() just return -1 when list view haven't finished initialization (when activity creating). – Khang .NT Mar 17 '16 at 08:24
  • maybe, but it still return -1 when I call it from either the a adapter's notifyDataSetChanged() or from LoaderManager's onLoadFinished() (when loading the items from SqLite finished) – Gavriel Mar 17 '16 at 08:27
  • make sure getListView() is correct: http://stackoverflow.com/questions/6275559/how-to-use-getlistview-in-activity – Khang .NT Mar 17 '16 at 08:37
  • that's not my problem, I'm in a ListFragment, and getListView() works, the problem is that the list is still empty when onCreateOptionsMenu is called. I added another question that is hopefully clearer: https://stackoverflow.com/questions/36055179/how-where-in-the-code-to-detect-wether-in-listfragments-listview-all-items-are – Gavriel Mar 17 '16 at 08:41