Search code examples
androidlistviewsimplecursoradapter

simplecursoradapter updates views randomly


I have a ListView in the activity. Data for ListView is populated from Contacts using SimpleCursorAdapter. The layout for ListView's row is two TextViews representing person name and number, and an ImageView for which visibility is set to invisible.

I have custom ViewBinder for the adapter, which checks if ImageView should be visible or not. The problem is that image is visible randomly. I guess the problem is in ViewHolder pattern that SimleCursorAdapter's implementation use with newView and bindView.

Can I solve the problem without writing custom cursor adapter or not?

Source below:

list_row.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <TextView
        android:id="@+id/contact_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="10dp"
        android:drawableLeft="@drawable/directory_pushed"
        android:drawablePadding="10dp"
        android:text="contact name" />

    <TextView
        android:id="@+id/contact_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/contact_name"
        android:text="093797888" />

    <ImageView
        android:id="@+id/selected"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/icon_phone"
        android:visibility="gone" />

</RelativeLayout>

Contacts.java

public class Contacts extends Activity implements
        AdapterView.OnItemClickListener, ViewBinder {

    private ListView mContactList;
    private SimpleCursorAdapter mAdapter;
    private List<String> contacts;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.contacts);

        Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
        String[] projection = new String[] { Phone._ID, Phone.DISPLAY_NAME,
                Phone.NUMBER };
        String selection = Phone.TYPE + "=?";
        String[] selectionArgs = new String[] { String
                .valueOf(Phone.TYPE_MOBILE) };
        String sortOrder = Phone.DISPLAY_NAME + " ASC";

        contacts = LoginInfo.getContacts(this);

        Cursor managedCursor = getContentResolver().query(uri, projection,
                selection, selectionArgs, sortOrder);

        mContactList = (ListView) findViewById(R.id.contacts_list);

        mAdapter = new SimpleCursorAdapter(this, R.layout.contacts_row,
                managedCursor, new String[] { Phone.DISPLAY_NAME, Phone.NUMBER,
                        Phone.NUMBER }, new int[] { R.id.contact_name,
                        R.id.contact_number, R.id.selected }, 0);

        mAdapter.setViewBinder(this);

        mContactList.setOnItemClickListener(this);

        mContactList.setAdapter(mAdapter);
    }

    @Override
    protected void onPause() {
        super.onPause();

        LoginInfo info = LoginInfo.getLoginInfo(this);
        info.contacts = null;
        info.contacts = contacts;

        LoginInfo.updateCache(this, info);
    }

    @Override
    public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
        if (view.getId() == R.id.selected) {
            String number = cursor.getString(columnIndex);

            if (isSelected(number)) {
                view.setVisibility(View.VISIBLE);
            }

            return true;
        }

        return false;
    }

    private boolean isSelected(String number) {
        int index = contacts.indexOf(number);
        return !(index == -1);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {

        Cursor c = (Cursor) mAdapter.getItem(position);

        String number = c.getString(c.getColumnIndex(Phone.NUMBER));

        contacts.add(number);
    }

}

Solution

  • In my opinion creating custom adapter is simpler but If You wish to keep it that way try this

    @Override
    public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
        if (view.getId() == R.id.selected) {
            String number = cursor.getString(columnIndex);
    
            if (isSelected(number)) {
                view.setVisibility(View.VISIBLE);
            }
            else {
                view.setVisibility(View.GONE);
            }
    
            return true;
        }
    
        return false;
    }