Junmar Calimbas Jose
Junmar Calimbas Jose

Reputation: 143

ListViewAdapter extends CursorAdapter order messes up when scrolled

I am so confused. I've found some suggestions on the net but I can't implement it on this code. Here's my problem. Everytime I scroll, list view's order messes up. I have no idea what to do. I really need some help. I will really appreciate your kindness. Here's my code:

public class ListViewAdapterMeasurement extends CursorAdapter {
    TextView lblContent, lblDate;
    DBHelper dbHelper;
    Button btnSelect;

    public ListViewAdapterMeasurement(Context context, Cursor c) {
        super(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = LayoutInflater.from(context).inflate(R.layout.details_feed, parent, false);

        lblContent = (TextView) view.findViewById(R.id.lblContent);
        lblDate = (TextView) view.findViewById(R.id.lblDate);

        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        View convertView = view;
        if (convertView == null)
        {
            LayoutInflater vi = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = vi.inflate(R.layout.details_feed, null);
        }

        dbHelper = new DBHelper(getApplicationContext());

        int intContentIndex = cursor.getColumnIndex((Tables.FeedTable.COLUMN_CONTENT));
        String strContentIndex = cursor.getString(intContentIndex);
        int intDateIndex = cursor.getColumnIndex((Tables.FeedTable.COLUMN_DATE));
        String strDateIndex = cursor.getString(intDateIndex);

        lblContent.setText(strContentIndex);
        lblDate.setText(strDateIndex);


  }
}

Upvotes: 3

Views: 94

Answers (2)

Vasiliy
Vasiliy

Reputation: 16238

The main issue with your code is that lblContent and lblDate are members of the adapter and get overriden any time newView() is called. Instead, you should store pair of these variables for each View which gets instantiated in newView(), and use them in bindView().

In order to achieve this, you can use the so-called ViewHolder pattern. A general idea behind this pattern can be seen in the below code (which is based on your original code, but I didn't test it):

public class ListViewAdapterMeasurement extends CursorAdapter {

    /**
     * One instance of this class will be attached to each View in order to prevent 
     * repeated calls to findViewById()
     */
    private static class ViewHolder {
        private TextView lblContent;
        private TextView lblDate;

        public ViewHolder(TextView content, TextView date) {
            lblContent = content;
            lblDate = date;
        }

    }


    DBHelper dbHelper;

    public ListViewAdapterMeasurement(Context context, Cursor c) {
        super(context, c, FLAG_REGISTER_CONTENT_OBSERVER);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = LayoutInflater.from(context).inflate(R.layout.details_feed, parent, false);

        TextView lblContent = (TextView) view.findViewById(R.id.lblContent);
        TextView lblDate = (TextView) view.findViewById(R.id.lblDate);

        ViewHolder viewHolder = new ViewHolder(lblContent, lblDate); // put references to sub-Views into ViewHolder

        view.setTag(viewHolder); // attach ViewHolder to each newly created View

        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        dbHelper = new DBHelper(getApplicationContext());

        int intContentIndex = cursor.getColumnIndex((Tables.FeedTable.COLUMN_CONTENT));
        String strContentIndex = cursor.getString(intContentIndex);
        int intDateIndex = cursor.getColumnIndex((Tables.FeedTable.COLUMN_DATE));
        String strDateIndex = cursor.getString(intDateIndex);

        ViewHolder viewHolder = (ViewHolder) view.getTag(); // retrieve View's ViewHolder

        viewHolder.lblContent.setText(strContentIndex);
        viewHolder.lblDate.setText(strDateIndex);
  }

}

Upvotes: 2

Henning Luther
Henning Luther

Reputation: 2075

In Android the views can be used several times, which means an already instantiated view from "newView" can be used in "bindView" more than once.To be clear: "newView" is called not so often(<=) than "bindView". Saving states in "newView" therefor is not a thing you can do. In "newView" you can only manipulate properties which counts for all views ever instantiated from this adapter which are not manipulated in "bindView". All dynamic values for each single row( or item) has do be set in "bindView" since there a reused view can(and will) appear. Saving single inner views of the row (or item) in your adapter leads to unexpected behavior and can not be done. This is your problem. "newView" is called when there is no already instantiated and free(not shown/ recyclable) view in the "view-object-pool". Also you must consider to reset certain subviews in "bindView" in the case an already filled view comes along here and a property stays unset for certain row/item in special circumstances. At the end: You can not know if in "bindView" the given view is a newly constructed or recycled one. Hope that clear things. Happy coding

Upvotes: 2

Related Questions