Custom ListView click issue on items in Android

JavaAndroidAndroid Listview

Java Problem Overview


So I have a custom ListView object. The list items have two textviews stacked on top of each other, plus a horizontal progress bar that I want to remain hidden until I actually do something. To the far right is a checkbox that I only want to display when the user needs to download updates to their database(s). When I disable the checkbox by setting the visibility to Visibility.GONE, I am able to click on the list items. When the checkbox is visible, I am unable to click on anything in the list except the checkboxes. I've done some searching but haven't found anything relevant to my current situation. I found [this question][1] but I'm using an overridden ArrayAdapter since I'm using ArrayLists to contain the list of databases internally. Do I just need to get the LinearLayout view and add an onClickListener like Tom did? I'm not sure.

Here's the listview row layout XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/UpdateNameText"
            android:layout_width="wrap_content"
            android:layout_height="0dip"
            android:layout_weight="1"
	        android:textSize="18sp"
            android:gravity="center_vertical"
    	    />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:id="@+id/UpdateStatusText"
            android:singleLine="true"
            android:ellipsize="marquee"
	        />
    	<ProgressBar android:id="@+id/UpdateProgress" 
    				 android:layout_width="fill_parent" 
    				 android:layout_height="wrap_content"
                     android:indeterminateOnly="false" 
				     android:progressDrawable="@android:drawable/progress_horizontal" 
				     android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal" 
				     android:minHeight="10dip" 
				     android:maxHeight="10dip"    				  
    				 />
	</LinearLayout>
	<CheckBox android:text="" 
			  android:id="@+id/UpdateCheckBox" 
			  android:layout_width="wrap_content" 
			  android:layout_height="wrap_content" 
			  />
</LinearLayout>

And here's the class that extends the ListActivity. Obviously it's still in development so forgive the things that are missing or might be left laying around:

public class UpdateActivity extends ListActivity {

    AccountManager lookupDb;
    boolean allSelected;
    UpdateListAdapter list;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lookupDb = new AccountManager(this);
        lookupDb.loadUpdates();

        setContentView(R.layout.update);
        allSelected = false;
        
        list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
        setListAdapter(list);
        
        Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
        btnEnterRegCode.setVisibility(View.GONE);
        
        Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
        btnSelectAll.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                allSelected = !allSelected;
                
                for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
                    lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
                }
                
                list.notifyDataSetChanged();
                // loop through each UpdateItem and set the selected attribute to the inverse 
                
            } // end onClick
        }); // end setOnClickListener
        
        Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
        btnUpdate.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
            } // end onClick
        }); // end setOnClickListener

        lookupDb.close();
    } // end onCreate


    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        for (UpdateItem item : lookupDb.getUpdateItems()) {
            item.getDatabase().close();        
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        
        UpdateItem item = lookupDb.getUpdateItem(position);
        
        if (item != null) {
            item.setSelected(!item.isSelected());
            list.notifyDataSetChanged();
        }
    }

    private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
        private List<UpdateItem> items;

        public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = null;
            
            if (convertView == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = li.inflate(R.layout.update_row, null);
            } else {
                row = convertView;
            }
            
            UpdateItem item = items.get(position);
            
            if (item != null) {
                TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
                TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
                CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);
            
                upper.setText(item.getName());
                lower.setText(item.getStatusText());
                
                if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
                    cb.setVisibility(View.GONE);
                } else {
                    cb.setVisibility(View.VISIBLE);
                    cb.setChecked(item.isSelected());
                }
                
                ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
                pb.setVisibility(View.GONE);
            }
            return row;
        }
        
    } // end inner class UpdateListAdapter
}

edit: I'm still having this problem. I'm cheating and adding onClick handlers to the textviews but it seems extremely stupid that my onListItemClick() function is not being called at all when I am not clicking on my checkbox. [1]: https://stackoverflow.com/questions/895341/custom-list-clicking-with-checkboxes-in-android

Java Solutions


Solution 1 - Java

The issue is that Android doesn't allow you to select list items that have elements on them that are focusable. I modified the checkbox on the list item to have an attribute like so:

android:focusable="false"

Now my list items that contain checkboxes (works for buttons too) are "selectable" in the traditional sense (they light up, you can click anywhere in the list item and the "onListItemClick" handler will fire, etc).

EDIT: As an update, a commenter mentioned "Just a note, after changing the visibility of the button I had to programmatically disable the focus again."

Solution 2 - Java

In case you have ImageButton inside the list item you should set the descendantFocusability value to 'blocksDescendants' in the root list item element.

android:descendantFocusability="blocksDescendants"

And the focusableInTouchMode flag to true in the ImageButton view.

android:focusableInTouchMode="true"

Solution 3 - Java

I've had a similar issue occur and found that the CheckBox is rather finicky in a ListView. What happens is it imposes it's will on the entire ListItem, and sort of overrides the onListItemClick. You may want to implement a click handler for that, and set the text property for the CheckBox as well, instead of using the TextViews.

I'd say look into this View object as well, it may work better than the CheckBox

[Checked Text View][1]

[1]: http://developer.android.com/reference/android/widget/CheckedTextView.html "Checked Text View"

Solution 4 - Java

use this line in the root view of the list item

android:descendantFocusability="blocksDescendants"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMattCView Question on Stackoverflow
Solution 1 - JavaMattCView Answer on Stackoverflow
Solution 2 - JavamicnoyView Answer on Stackoverflow
Solution 3 - JavaAndrew BurgessView Answer on Stackoverflow
Solution 4 - JavaSuresh kumarView Answer on Stackoverflow