Android onlistclick toggle listview row height causing multiple changes

Situation:

My app contains a ToDo list style list box (each line has a checkbox). The listview lines are structured with 2 text images arranged vertically. The top-most text is the title and the bottom-most is the description, but the description is hidden (View.GONE). Using the ListActivities onListItemClick method, I can set both the height and visibility of the row clicked.

@Override
protected void onListItemClick(ListView list, View view, int position, long id) {
    super.onListItemClick(list, view, position, id);

    view.getLayoutParams().height = 200;
    view.requestLayout();

    TextView desc = (TextView) view.findViewById(R.id.description);
    desc.setVisibility(View.VISIBLE);
}

      

Note. The code is split into the simplest

The above code works fine, except that it expands both the pressed row and the tenth row above or below (next unloaded view?). Expanding the line will also change position when navigating the list.

Background:

List data is fetched from a SQLite database using a managed cursor and set by a custom CursorAdapter. The cursor being driven is sorted by value.

private void updateList() {
    todoCursor = managedQuery(TaskContentProvider.CONTENT_URI, projection, null, null, TaskSQLDatabase.COL_DONE + " ASC");

    startManagingCursor(todoCursor);

    adapter = new TaskListAdapter(this, todoCursor);
    taskList.setAdapter(adapter);
}

      

CursorAdapter consists of basic newView () and bindView ().

Question:

I need some kind of system that keeps track of which rows will be expanded. I tried storing the cursor id in arrays and then checking the adapter if the string should be expanded, but I can't seem to get it to work. Any suggestions would be much appreciated!

+3


source to share


1 answer


ListView

will recycle views when scrolling up and down, when to display a new child, View

it first sees if it doesn't have it (if it finds one, it will use This). If you change the children ListView

as you did (in a method onListItemClick()

) and then loop through the list, ListView

it will eventually reuse that child View

you changed again and you end up with certain views at the position you don't want ( if you keep scrolling ListView

, you will see a random change in position due to recycling View

).

One way to prevent this is to remember the positions the user has clicked and, in the adapter, change the layout of that particular row, but only for the position you want. You can store these ids

in HashMap

(field of your class):

private HashMap<Long, Boolean> status = new HashMap<Long, Boolean>();

      

I have used HashMap

but you can use other containers (the code below will show why I choose HashMap

). Later in the method, onListItemClick()

you will change the selected line, but also keep this line id

so that the adapter knows (and takes steps to prevent you from falling into the wrong reworked views):

@Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            // check and see if this row id isn't already in the status container, 
            // if it is then the row is already set, if it isn't we setup the row and put the id
            // in the status container so the adapter will know what to do
            // with this particular row
            if (status.get(id) == null) {
                status.put(id, true);
                v.getLayoutParams().height = 200;
                v.requestLayout();
                TextView d = (TextView) v.findViewById(R.id.description);
                d.setVisibility(View.VISIBLE);
            } 
    }

      



Then, in the adapter, use a state container with all ids

to set up the strings correctly and prevent rework in our rows:

private class CustomAdapter extends CursorAdapter {

    private LayoutInflater mInflater;

    public CustomAdapter(Context context, Cursor c) {
        super(context, c);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        TextView title = (TextView) view.findViewById(R.id.title);
        title.setText(cursor.getString(cursor.getColumnIndex("name")));
        TextView description = (TextView) view
                .findViewById(R.id.description);
        description.setText(cursor.getString(cursor
                .getColumnIndex("description")));
        // get the id for this row from the cursor
        long rowId = cursor.getLong(cursor.getColumnIndex("_id"));
        // if we don't have this rowId in the status container then we explicitly
        // hide the TextView and setup the row to the default
        if (status.get(rowId) == null) {
            description.setVisibility(View.GONE);
            // this is required because you could have a recycled row that has its 
            // height set to 200 and the description TextView visible
            view.setLayoutParams(new AbsListView.LayoutParams(
                    AbsListView.LayoutParams.MATCH_PARENT,
                    AbsListView.LayoutParams.MATCH_PARENT));
        } else {
            // if we have the id in the status container then the row was clicked and
            // we setup the row with the TextView visible and the height we want
            description.setVisibility(View.VISIBLE);
            view.getLayoutParams().height = 200;
            view.requestLayout();
            // this part is required because you did show the row in the onListItemClick
            // but it will be lost if you scroll the list and then come back 
        }
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View v = mInflater.inflate(R.layout.adapters_showingviews, null);
        return v;
    }

}

      

If you want to toggle the row that was pressed (and something tells me you want to), show TextView

on click / hide TextView

in another clicked row, then just add else clause

in onListItemClick()

and remove the selected row id

from the state container and return the row:

//...
else {
            status.remove(id);
            v.setLayoutParams(new AbsListView.LayoutParams(
                    AbsListView.LayoutParams.MATCH_PARENT,
                    AbsListView.LayoutParams.MATCH_PARENT));
            TextView d = (TextView) v.findViewById(R.id.description);
            d.setVisibility(View.GONE);
        }

      

+2


source







All Articles