57

I have trouble with a ListView. Its items (rows) have an ImageButton. The ImageButton has android:onClick set, so this onClick event is working, but click on row doesn't work.

If I remove the ImageButton from the row item, click on row works (ListView has correct onClick listener). How can I fix it? I need onClick event when the user clicks on the ImageButton, and the standard click event when the user selects the row (not click the ImageButton but click the row).

My ListView:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/restaurants_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="@color/list_devider"
        android:dividerHeight="1dp"
        android:cacheColorHint="@color/list_background" /> 
F43nd1r
  • 7,690
  • 3
  • 24
  • 62
yital9
  • 6,544
  • 15
  • 41
  • 54

7 Answers7

179

Unfortunately,

android:focusable="false"
android:focusableInTouchMode="false"

doesn't work for ImageButton.

I finally found the solution here. In your layout xml for those items, add

android:descendantFocusability="blocksDescendants" 

to the root view.

It works perfectly for a ListView that has ImageButtons. According to official reference, blocksDescendants means that the ViewGroup will block its descendants from receiving focus.

leoly
  • 8,468
  • 6
  • 32
  • 33
  • 9
    For `ToggleButton` the `android:focusable="false"` is enough but for an ImageButton only `android:descendantFocusability="blocksDescendants"` will work. – Mohsen Afshin Aug 19 '13 at 08:05
  • 1
    I added this code to my list item's root view and it works! I can both click on the listview and the ImageButton without any problem. Thanks – Iman Irt Oct 11 '16 at 17:21
  • just to clarify: the `android:descendantFocusability="blocksDescendants"` should be placed in the root viewgroup of the list-item layout – Someone Somewhere Jun 05 '19 at 11:25
7

You can use a custom adapter for your listView (if you haven't already). And there, in the getView(int position, View inView, ViewGroup parent) method of the adapter do something like this:

@Override
public View getView(int position, View inView, ViewGroup parent) {

    View v = inView;
    ViewHolder viewHolder; //Use a viewholder for sufficent use of the listview

    if (v == null) {
        LayoutInflater inflater = (LayoutInflater) adaptersContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inflater.inflate(R.layout.list_item, null);
        viewHolder = new ViewHolder();
        viewHolder.image = (ImageView) v.findViewById(R.id.ImageView);
        v.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) v.getTag();
    }

        .....

    viewHolder.image.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            //Click on imageView
        }i
    });

    v.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            //Click on listView row
        }
    });

        .....

    return (v);
}

See here if you need help creating your custom adapter.

AggelosK
  • 4,313
  • 2
  • 32
  • 37
  • Be aware that this solution prevents the row itself from being selected. In certain cases, you may want a listview where you can select a row AND click on a button within a row. – Johann Jan 02 '13 at 15:45
  • Is it desirable to make a new listener for every requested view? – Maarten Jul 03 '13 at 14:44
  • @Maarten If by desirable you mean efficient,then it is efficient since using the `ViewHolder` technique, the `ListView` will store in memmory only the rows needed and not all the rows. – AggelosK Jul 04 '13 at 06:19
  • And it will create a new object for every row requested -- the reason why Views are recycled in the first place. If you go for performance, you should create a getOnClickListener(View) method and change the recycled listener around in getView(), not create a new one every time. – Maarten Jul 04 '13 at 08:31
  • @Maarten Yes you are right. But i do not think you will gain any significant performance advantage since when the `View` of a row will be destroyed, the Garbage Collector will destroy the `OnClickListener` objects of that `View` since only that particular `View` has a reference to them. – AggelosK Jul 04 '13 at 09:33
  • But say you have a list of 10.000 items and you scroll through it fast. All these click listeners get instantiated and destroyed again (because there's no more reference to them), surely that would give a noticeable performance lag? – Maarten Jul 04 '13 at 09:57
  • @Maarten You can create a sample project to test it. I think you are probably right but i have not tested to give you a definite answer. My code sample was just to show the user how he can make clickable `Views` inside the row of a `ListView` so i was not focusing on performance. – AggelosK Jul 04 '13 at 10:19
6

If a row of listView have any clickable element like Button , Image..etc..then onItemClick will not work. So you need to write the click listener in getView of your list adapter.

For more read this.

Community
  • 1
  • 1
Mohsin Naeem
  • 12,542
  • 3
  • 39
  • 53
2

Set these properties for your button:

      android:focusable="false"
      android:focusableInTouchMode="false"

Or you can set it dynamically in your adapter class:

        yourButton.setFocusable(false);
    yourButton.setFocusableInTouchMode(false);

And make sure that you set the choice mode as single for the listview:

       listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Yogesh Somani
  • 2,624
  • 3
  • 21
  • 34
  • It worked for me, why do you say that it haven't work? I have a row with a checkbox, another stuff and the button. The row can be selected, and the button and the checkbox too. – David Mar 01 '13 at 11:32
  • However, it is really weird that android:focusable="false" doesn't work for ImageButton. – leoly Jul 23 '13 at 06:53
2

In my case, android:descendantFocusability="blocksDescendants" for main layer did not work, neither in the ListView. I also tried android:focusable="false" android:focusableInTouchMode="false" which I heard that it is working for Buttons, but I had ImageButton so it didn't.

But setting the properties of the button in the CS file of the Layout worked.

var imageButton = view.FindViewById<ImageButton>(Resource.Id.imageButton1);
imageButton.Focusable = false;
imageButton.FocusableInTouchMode = false;
imageButton.Clickable = true;
  • I'm using my own ImageButton. non of other answers worked for me, but this works. (I used them in my Custom Adapter) – Sdghasemi May 06 '15 at 14:56
1

If a row has multiple clickable elements, onItemClick() will not work. You will need to set the OnClickListener in the getView() method. Store the listeners the the View's tag so that they can be recycled, add methods to your listeners so they can be specialized for different rows.

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
        View view = super.getView(position, convertView, parent);

        RowClickListeners listeners = (RowClickListeners) view.getTag();
        if (listeners == null) {
            listeners = new RowClickListeners();
        }

        // Row click listener:
        RowClickListener onClickListener = listeners.rowClickListener;
        if (onClickListener == null) {
            onClickListener = new RowClickListener();
            listeners.rowClickListener = onClickListener;
        }
                    onClickListener.setToPosition(pos);
        view.setOnClickListener(onClickListener);

        // Overflow listener:
        View btn = view.findViewById(R.id.ic_row_btn);
        ButtonListener btnListener = listeners.buttonClickListener;
        if (rowListener == null) {
            btnListener = new ButtonListener(activity);
            listeners.rowClickListener = btnListener;
        }
                    btnListener.setToPosition(pos);
        btnListener.setCollection(collectionId);
        btn.setOnClickListener(btnListener);
    }


    public static class RowClickListeners {
        public RowClickListener rowClickListener;
        public ButtonListener buttonClickListener;
    }
Maarten
  • 6,894
  • 7
  • 55
  • 90
0

no single answer above worked for me, but a combination did.

I now set android:descendantFocusability="blocksDescendants" on the ListView and android:focusable="false" android:focusableInTouchMode="false" on the ImageButtons in the XML AND in Java I also set descendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS) on the ListView and focusable(false), focusableInTouchMode(false), clickable(true) on the ImageButtons.

SteelBytes
  • 6,905
  • 1
  • 26
  • 28