Question

Ok, so this has been somewhat addressed alot on this site, however I do not believe the exact problem with what my code uses. I am filling a listView with CheckedTextViews which works completely. However when I click on an item it gets checked but when I scroll up and down random rows are also checked. I realize it must have something to do with how the ListView keeps track of the items. I am running into some errors at the moment. I attempted to fill a hashmap with the list of the rows so I can keep track which one is set to true and which are false. However I am not positive where to implement the map and try to fill it.

Here is my OnCreate

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.viewmenu);

    //Get table name of menu clicked. 
    Bundle extras = getIntent().getExtras();
    tableName = extras.getString("table");

    // map each contact's name to a TextView in the ListView layout
    String[] from = new String[] { "name" };
    int[] to = new int[] { R.id.toppingCheckedTextView };

    for(int i=0; i< from.length; i++){
        map.put(i, false);
    }

    contactAdapter = new SimpleCursorAdapter(
            ViewToppingListing.this, R.layout.toppings_list_item, null, from, to);
    setListAdapter(contactAdapter); // set contactView's adapter
 } 

I attempt to place the map in the onCreate to fill it however it complains about a nullpointer.

Here is where I tried using the OnListItemClick method

 @Override
protected void onListItemClick(ListView arg0, View arg1, int arg2, long arg3){      
    final int index = arg2 - arg0.getFirstVisiblePosition();
    View v = arg0.getChildAt(index);
    CheckedTextView ctv = (CheckedTextView) v.findViewById(R.id.toppingCheckedTextView);

    if((Boolean)map.get(index) == true){
        ctv.setChecked(true);
        ctv.setVisibility(View.VISIBLE);

    } else{
        ctv.setVisibility(View.GONE);
    }   

} 

I have read alot on this, and it seems that alot of solutions involves using getView(), however I don't know if that applies to my situation. Any help would be greatly appreciated!

Was it helpful?

Solution

First of all do you need a SimpleCursorAdapter? You set the adapter with a null cursor:

contactAdapter = new SimpleCursorAdapter(
            ViewToppingListing.this, R.layout.toppings_list_item, null, from, to); // the third parameter is the cursor and you set it to null!

The behavior you see it's because of the ListView is recycling views and yes you'll have to implement your own adapter and override bindView(). The code bellow is based on another answer to a similar question maybe you'll want to look at it( Getting the selected View from ListView ). Here is an example:

public class TestCursorAdapter extends ListActivity {

    MySimpleAdapter adapter;
    private HashMap<Long, Boolean> positionHide = new HashMap<Long, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] columns = new String[] { "_id", "name" };
        MatrixCursor mc = new MatrixCursor(columns); // cursor for testing
        for (int i = 1; i < 35; i++) {
            long id = i;
            mc.addRow(new Object[] { id, "Name" + i });
        }
        String[] from = new String[] { "name" };
        int[] to = new int[] { R.id.checked_text };
        adapter = new MySimpleAdapter(this,
                R.layout.adapter_mysimpleadapter_row, mc, from, to);
        setListAdapter(adapter);
    }

    private class MySimpleAdapter extends SimpleCursorAdapter {

        public MySimpleAdapter(Context context, int layout, Cursor c,
                String[] from, int[] to) {
            super(context, layout, c, from, to);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            super.bindView(view, context, cursor);
            CheckedTextView ctv = (CheckedTextView) view
                    .findViewById(R.id.checked_text);
            long pos = cursor.getLong(0); // the id from the cursor
            if (positionHide.get(pos) == null) {
                ctv.setChecked(false);
                // we don't have this id in the hashmap so the value is by
                // default false, the TextView is GONE
            } else {
                // we have the value in the Hashmap so see what it is and set
                // the textview visibility from this value
                Boolean tmp = positionHide.get(pos);
                if (tmp.booleanValue()) {
                    ctv.setChecked(true);
                } else {
                    ctv.setChecked(false);
                }
            }

        }

    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Boolean tmp = positionHide.get(id);
        if (tmp == null) {
            // if null we don't have this key in the hashmap so
            // we add it with the value true
            positionHide.put(id, true);
        } else {
            positionHide.put(id, !tmp.booleanValue());
            // if the value exists in the map then inverse it's value
        }
        adapter.notifyDataSetChanged(); // notify the adapter that something has
                                        // changed
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top