2

I've been having a lot of trouble with this problem. I have a listview that contains:

ImageView / contactName / TextView / CheckBox

The contactName in the listview is populated by reading the contacts on the phone from a SimpleCursorAdapter. All for elements show when the app runs, but the problem I'm having is connecting the checkboxes to their corresponding item in the list.

Through some research, I found that I must use a getView() to link the checkboxes with the items in the list, but through practice, I can't seem to get it to work right. Furthermore, none of the examples I've tried really explained how to apply getView(). The most full example I've been working from is from here:

http://androidcocktail.blogspot.com/2012/04/adding-checkboxes-to-custom-listview-in.html

The twist is that this reads and populates my listview with my contacts:

private void populateContactList() {
    // Build adapter with contact entries
    Cursor cursor = getContacts();
    String[] fields = new String[] {
            ContactsContract.Data.DISPLAY_NAME       
    };

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.contact_entry, cursor,
            fields, new int[] {R.id.contactEntryText});    
    lv.setAdapter(adapter);        
} // END POPULATECONTACTLIST


private Cursor getContacts()
{ 
    // Run query
    Uri uri = ContactsContract.Contacts.CONTENT_URI;
    String[] projection = new String[] {
            ContactsContract.Contacts._ID,
            ContactsContract.Contacts.DISPLAY_NAME
    };
    String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
            (chkboxAllVisible ? "0" : "1") + "'";
    String[] selectionArgs = null;
    String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";

    return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
} // END GETCONTACTS 

How do I link each checkbox to the a corresponding contact items in my listview?

3 Answers3

2

Ok i have created a test project for you try to understand code if any problem you are having then ask I will try to help you...

HERE IS MY ONCREATE FUNCTION OF ACTIVITY.

super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ArrayList<String> elements = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
    elements.add("elements " + i);
}

CheckBox master_cb = new CheckBox(getApplicationContext());
master_cb.setText("Check All");
//HERE IS THE LIST VIEW WHICH I HAVE CREATED IN MY XML FILE.
ListView lv = (ListView) findViewById(R.id.listView1);
//HERE I AM CREATING CUSTOM ADAPTER OBJECT.
my_custom_adapter adapter = new my_custom_adapter(this, android.R.layout.simple_list_item_1, elements);
lv.addHeaderView(master_cb);
lv.setAdapter(adapter);
master_cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        Intent my_intent = new Intent("master_check_change");
        my_intent.putExtra("check_value", isChecked);
        sendBroadcast(my_intent);
    }
});

}

HERE IS MY CUSTOM ADAPTER.

public class my_custom_adapter extends ArrayAdapter<String> {
    private Context context                     = null;
    ArrayList<String>  elements                 = null;
    private ArrayList<Boolean> itemChecked      = null;

    public my_custom_adapter(Context context, int type, ArrayList<String>  elements)
    {
        super(context, type, elements);
        this.elements =  elements;
        this.context = context;
        itemChecked = new ArrayList<Boolean>();
        BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent.getAction().equals("master_check_change")) {
                    boolean check_value = intent.getBooleanExtra("check_value", false);
                    set_checked(check_value);
                    notifyDataSetChanged();
                }
            }
        };
        context.registerReceiver(receiver, new IntentFilter("master_check_change"));
        set_checked(false);
    }

    // AS EVERY TIME LISTVIEW INFLATE YOUR VIEWS WHEN YOU MOVE THEM SO YOU NEED TO SAVE ALL OF YOUR CHECKBOX STATES IN SOME ARRAYLIST OTHERWISE IT WILL SET ANY DEFAULT VALUE.
    private void set_checked(boolean is_checked)
    {
        for (int i=0; i < elements.size(); i++) {
            itemChecked.add(i, is_checked);
        }
    }

    //THIS IS SIMPLY A CLASS VIEW WILL HOLD DIFFERENT VIEWS OF YOUR ROW.
    static class ViewHolder
    {
        public TextView tv;
        public CheckBox cb;
        public ImageView iv;
    }

    @Override
    public View getView (final int position, View convertView, ViewGroup parent)
    {
        View rowView = convertView;
        ViewHolder holder = null;

        if (rowView == null) {
            LayoutInflater inflater = (LayoutInflater)context.getSystemService(
                                               Context.LAYOUT_INFLATER_SERVICE);
            // HERE I AM INFLATING LISTVIEW LAYOUT.
            rowView = inflater.inflate(R.layout.inflated_layout, null, false);
            holder = new ViewHolder();
            holder.cb = (CheckBox) rowView.findViewById(R.id.checkBox1);
            holder.tv = (TextView) rowView.findViewById(R.id.textView1);
            holder.iv = (ImageView) rowView.findViewById(R.id.imageView1);
            rowView.setTag(holder);

        } else {
            holder = (ViewHolder) rowView.getTag();
        }

        if (holder != null) {
            holder.tv.setText(elements.get(position));

            holder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(CompoundButton buttonView,
                                             boolean isChecked) {
                    itemChecked.set(position, isChecked);
                }
            });

            if(position < itemChecked.size()) {
                holder.cb.setChecked(itemChecked.get(position));
            }
        }
        return rowView;
    }
}

main.xml file is this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >

    </ListView>

</RelativeLayout>

inflated_layout code is :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="17dp" />




    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@+id/checkBox1"
        android:layout_toRightOf="@+id/imageView1"
        android:singleLine="true"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />


    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:src="@drawable/ic_launcher" />

</RelativeLayout>

if you want to use string array instead of arraylist then replace

    String[] elements = new String[10];
    for (int i = 0; i < 10; i++) {
        elements[i] = "elements " + i;
    }

// IN YOUR CUSTOM ADAPTER CUNSTRUCTOR

public my_custom_adapter(Context context, int type, String[]  elements)

and some more changes accordingly

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Bharat Sharma
  • 3,926
  • 2
  • 17
  • 29
  • Ok, I applied your example, but before I test it on mine, do I to set a receiver in my manifest for your custom class? Or could I just add it as a regular activity? Thank you for patience. It's greatly appreciated. :) –  May 11 '12 at 05:40
  • 1
    no you dont need to set your broadcast receiver in manifest file. simply add it you dont need to do anything just create respected files and classes in new project and run it. – Bharat Sharma May 11 '12 at 05:42
  • The master checkbox works! I have a few questions though. How do I set the number of elements to the size of the number of contacts? –  May 11 '12 at 05:54
  • 1
    super(context, type, elements) whatever you will pass in this as "elements" that much number of rows will be created.. so just pass all your contacts as arraylist and it will automatically create rows which will be equal to number of contacts. No of rows will be elements.size() in my example. You have customize your adapter so pass whatever list in your adapter – Bharat Sharma May 11 '12 at 05:57
  • Ok, so from my example of populateContactList() within my original question, I should convert "String fields[]" to an ArrayList? –  May 11 '12 at 06:01
  • 1
    no change custom adapter to string and pass string into super of custom adapter. it can accept both arraylist and string array.. – Bharat Sharma May 11 '12 at 06:21
  • On how you populate my contacts in a textview. My above method lists the names perfectly, but it doesn't cancels out my the functionality of the master checkbox. I tried to add a string to the super, but it tells me to remove it. –  May 11 '12 at 07:31
  • Sorry, my above method cancels the way the master checkbox functionality. Since it is originally in another class, I tried migrating it over to fit in with the holders, but it didnt work either. I'm assuming I need a ImageView for contact pics and a TextView for contacts in the ViewHolder class as well. –  May 11 '12 at 07:43
  • tell me exactly what you want to do. you need imageview, textview and checkbox in each row.... if you will move your master checkbox in custom adapter then it will not work as i have created it in activity not in custom adapter. – Bharat Sharma May 11 '12 at 08:16
  • My master checkbox is already in another activity. I would like to define the contact photo (ImageView) and contact names (TextView) from within the ViewHolder class just like you did for the checkbox and the other textview. I currently have public ImageView photo and public TextView contact setup with the holder. –  May 11 '12 at 08:38
  • but I would need the contacts first to get the proper image. –  May 11 '12 at 08:44
  • I have these: holder.photo = (ImageView) rowView.findViewById(R.id.iv_contactPic); holder.contact = (TextView) rowView.findViewById(R.id.contactEntryText); –  May 11 '12 at 08:46
  • From your update, it has to flexible to the number of contacts a device has. –  May 11 '12 at 08:49
  • yes it will be ... look i have already told you try to understand the code. suppose you are having 15 contacts. these 15 contacts you will pass in arrayadapter. it will call getview method 15 times and inflate your all views including imageview 15 times.. so its simple you need a little practice. It is important to learn custom adapter because listviews are very common and sometimes complex to create... – Bharat Sharma May 11 '12 at 08:54
  • Yes, but the main problem is reading the contacts into a textview other than the way I did it earlier. –  May 11 '12 at 09:02
  • Thank you for your patience though. You are a great help. –  May 11 '12 at 09:02
  • I've just found this post, and have implemented Bharat's code, but at runtime I'm getting this exception: "Activity has leaked IntentReceiver...that was originally registered here. Are you missing a call to unregisterReceiver()?" . Can anyone tell me how/where to do the unregisterReceiver() call in the above code? – Ian M Apr 20 '15 at 15:46
  • @IanM I think best place to unregister receiver is in onDestroy. – Bharat Sharma Apr 22 '15 at 02:44
0
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.contact_entry, cursor,
        fields, new int[] {R.id.contactEntryText});

See in this particular code you are only mapping text source (in field ) with actual textView (R.id.contactEntryText)... So similarly you need to add... another field and corresponding view to map for Checkbox.

or better make a CustomAdapter, you can find tutorials on that and override getView method,you get maximum flexibility.You can do whatever you want to do.

This might help: http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php

-1

don,t go with custom list view you can use default listview having the facility of check boxes but only one with each list item read listview on android developer site for list view property. listview having checkbox you just need to set multiselection list view

Edit 1:

follow the link : click here

public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      setListAdapter(new ArrayAdapter<String>(this,
              android.R.layout.simple_list_item_multiple_choice, GENRES));

      final ListView listView = getListView();

      listView.setItemsCanFocus(false);
      listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
  }
Trikaldarshiii
  • 11,174
  • 16
  • 67
  • 95
  • Reason being is that I need to program the checkbox to work with each individual contact. Also, I have checkbox outside of the listview that serves as a master checkbox. When the master checkbox is checked, it's job is to check all checkboxes within the list. And to test the checkboxes in the list, trying to implement LogCat to display which item is checked. –  May 11 '12 at 03:39