21

I'm pretty new to Android dev and still working out a lot of things.

I've got a main menu showing using the following code, but can't work out how to disable selected items in the menu. Can anybody help me with some sample code?

public class listTest extends ListActivity {

    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        setListAdapter(ArrayAdapter.createFromResource(this, R.array.mainMenu,
                android.R.layout.simple_list_item_1)); 
        //not sure how to disable list items here
    }

    protected void onListItemClick(ListView list, View view, int position, long id) {
        // can disable items when they are clicked on
        view.setEnabled(false);
    }   

}

and I have a string-array in my strings.xml file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="mainMenu">
        <item>Item 1</item>
        <item>Item 2</item>
        <item>Item 3</item>
    </string-array> 
</resources>

Thank you

Martyn
  • 16,432
  • 24
  • 71
  • 104
  • Couple of quick clarifications: if you want a menu, that pops up when the user presses the Menu key, then check out the `onCreateOptionsMenu` method in your `Activity`. Also, you shouldn't need to hold onto the application context like that; an `Activity` already extends `Context` (as you can see in your call to `createFromResource(this, ...)`. – Christopher Orr Feb 02 '10 at 11:44
  • I want a main menu that will show when the application is first opened, so the optionsMenu and contextMenu aren't any good. I've got the list showing and can disable the items when they have been clicked on: protected void onListItemClick(ListView list, View view, int position, long id) { view.setEnabled(false); } but I've no idea of how to set a selected list item to disabled in onCreate. Noted on the application context - thanks. – Martyn Feb 02 '10 at 11:51

4 Answers4

45

In order to disable list items on list creation you have to subclass from ArrayAdapter. You have to override the following methods: isEnabled(int position) and areAllItemsEnabled(). In former you return true or false depending is list item at given position enabled or not. In latter you return false.

If you want to use createFromResource() you will have to implement that method as well, since the ArrayAdapter.createFromResource() still instantiates ArrayAdapter instead of your own adapter.

Finally, the code would look something like the following:

class MenuAdapter extends ArrayAdapter<CharSequence> {

    public MenuAdapter(
            Context context, int textViewResId, CharSequence[] strings) {
        super(context, textViewResId, strings);
    }

    public static MenuAdapter createFromResource(
            Context context, int textArrayResId, int textViewResId) {

        Resources      resources = context.getResources();
        CharSequence[] strings   = resources.getTextArray(textArrayResId);

        return new MenuAdapter(context, textViewResId, strings);
    }

    public boolean areAllItemsEnabled() {
        return false;
    }

    public boolean isEnabled(int position) {
        // return false if position == position you want to disable
    }
}
Viktor Brešan
  • 5,293
  • 5
  • 33
  • 36
  • 15
    Be careful with this solution. The documentation of BaseAdapter states the following "Returns true if the item at the specified position is not a separator." That means if you return false the item is a separator item. Some phones may not draw the android:divider between a normal item and a separator item. – Janusz Jul 27 '11 at 09:50
  • @Janusz yes I faced this issue of divider line, as when I disabled the item at some index the divider line was gone. So i made the view at the index a little gray to look disabled :) – Ali Imran Apr 12 '13 at 12:21
  • @AliImran: can you please elaborate a bit more on how you managed this issue with the divider not being drawn? Thanks – dentex May 19 '15 at 08:49
11

I believe whether a list item is enabled or not is part of that item's state, so I guess you have to manage that in your ListAdapter. When subclassing an adapter, you can override isEnabled(position). For any position you return true here, the ListView will mark this item as disabled.

So what you want to do is something like this:

class MenuAdapter extends ArrayAdapter<String> {

    public boolean isEnabled(int position) {
       // return false if position == positionYouWantToDisable
    }

}

This probably requires e.g. a Map managing the enabled state of each item if you want to be able to enable/disable an item using a setter.

Then set the custom adapter on your ListView.

mxk
  • 43,056
  • 28
  • 105
  • 132
  • Thanks for your reply, this looks good but having issues implementing it. setListAdapter(MenuAdapter.createFromResource(this, R.array.mainMenu,android.R.layout.simple_list_item_1)); MenuAdapter mainMenuList = (MenuAdapter) getListAdapter(); mainMenuList.isEnabled(2); //just set to return false atm Any ideas where I'm going wrong? it currently just crashes – Martyn Feb 03 '10 at 11:31
  • Well, what is the exception you're getting? – mxk Feb 03 '10 at 13:29
  • 02-03 13:51:21.971: ERROR/AndroidRuntime(1365): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.martyn.test/com.martyn.test.test}: java.lang.ClassCastException: android.widget.ArrayAdapter – Martyn Feb 03 '10 at 13:51
  • 1
    on which line? setListAdapter(MenuAdapter.createFromResource(this, R.array.mainMenu,android.R.layout.simple_list_item_1));? That's weird, because createFromResource returns an ArrayAdapter, and an ArrayAdapter is a ListAdapter. Try instantiating it manually instead of using the resource helper then. – mxk Feb 03 '10 at 17:48
  • 1
    shouldn't be 'For any position you return FALSE here'? – superjos Dec 20 '11 at 11:36
6

You can disable a list item (= make it not respond to touches) by calling both

setClickable(false)

and

setFocusable(false)

in your adapter, for example.

By default, this is not automatically reflected graphically, though.

I am currently using that in a list whose list items are not clickable but most of which contain clickable widgets. Works well.

This way, list items are drawn normally including the separator (see Janusz' reply to the accepted answer above).

Raginmari
  • 2,291
  • 23
  • 21
  • Could you be more specific? Where in the adapter do you call this? Your answer appears very promising, but I have no clue how to implement it ... – Pooks Feb 09 '12 at 07:43
  • 1
    I have implemented a subclass of `BaseAdapter` that inflates the list cell layout in its `getView` method. In the layout XML, the root element is (in my case) a `LinearLayout` that is neither focusable nor clickable: `android:focusable="false"` and `android:clickable="false"` – Raginmari Feb 13 '12 at 14:21
  • You can also do this in code in the `getView` method of your adapter by calling `setClickable(false)` and `setFocusable(false)` on the view representing your list item. – Raginmari Feb 13 '12 at 14:23
  • 6
    I tried your solution and it works, but starngely, in reverse! I have this: `public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; if (row == null) { row = inflater.inflate(R.layout.row_style, parent, false); } row.setFocusable(false); row.setClickable(false); return row; }` Which makes all my rows enabled (clickable). But if I use `row.setFocusable(true); row.setClickable(true);` The rows are unclickable. Very strange ... – Pooks Mar 01 '12 at 05:30
  • I just confirmed the same reverse behaviour if I set `android:clickable` and `android:focusable` in my layout's top `FrameLayout`. ie setting to false enables the rows and setting to true disables them. – Pooks Mar 01 '12 at 05:42
  • 5
    To all those thinking that the behavior works in reverse, it doesn't. When you make a list item clickable(true), subsequent clicks are delivered to the list item itself, so the event will not be propagated to the parent (listview). If the list item doesn't react to clicks, nothing happens. – Jamol Nov 13 '13 at 09:57
  • So it's an hack-ish way to disable the click events on an item. Have you found the proper way? I'm struggling to. I think it's too much of a popular problem, can't have just this poor solutions. – doplumi Jan 12 '14 at 22:08
  • @jamolkhon Wow, that actually makes sense! Although it should really be clarified in the description for getView() because it is very unintuitive. – Pooks Jan 21 '14 at 00:32
-3

Or in simple way to un-register and register OnItemClickListener can be a better idea.

To register:

lstDevice.setOnItemClickListener(context);

To de-register:

lstDevice.setOnItemClickListener(null);

CoDe
  • 11,056
  • 14
  • 90
  • 197