Save and restore ExpandableListActivity minimized state with SimpleCursorTreeAdapter

After spending several days researching this issue, I finally refuse and post this question. Answers to such questions were answered (in part), and solutions were proposed, but they did not help me. The difference between the topics discussed and my problem is as follows: Non from the others, it seems, uses SimpleCursorTreeAdapter to list ...

Problem: when I change the orientation of my device (Samsung Galaxy S), the state of group expansion is not saved and is not restored. All groups are shown closed and the list scrolls up.

The problem is already visible in demos of Android . If you installed them, go to the next step:

Views β†’ Extensible Lists β†’ 2. Cursor (People): run the sample, expand any group, rotate the device, and the result will display the group when folded.

In the code below, I did the following: I took the code from the API demo (ExpandableList2.java) and expanded it with implementations for onSaveInstantState (), onRestoreInstanceState () and onResume (). Implementations of these methods were taken from another topic of discussion here ( How to save scroll position in ExpandableListView ).

import android.app.ExpandableListActivity; import android.content.AsyncQueryHandler; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; import android.view.View; import android.widget.CursorTreeAdapter; import android.widget.ExpandableListView; import android.widget.SimpleCursorTreeAdapter; public class MainActivity extends ExpandableListActivity { private static final String LIST_STATE_KEY = "levelSelectListState"; private static final String LIST_POSITION_KEY = "levelSelectListPosition"; private static final String ITEM_POSITION_KEY = "levelSelectItemPosition"; private static final String[] CONTACTS_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME }; private static final int GROUP_ID_COLUMN_INDEX = 0; private static final String[] PHONE_NUMBER_PROJECTION = new String[] { Phone._ID, Phone.NUMBER }; private static final int TOKEN_GROUP = 0; private static final int TOKEN_CHILD = 1; private static final class QueryHandler extends AsyncQueryHandler { private CursorTreeAdapter mAdapter; public QueryHandler(Context context, CursorTreeAdapter adapter) { super(context.getContentResolver()); this.mAdapter = adapter; } @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { case TOKEN_GROUP: mAdapter.setGroupCursor(cursor); break; case TOKEN_CHILD: int groupPosition = (Integer) cookie; mAdapter.setChildrenCursor(groupPosition, cursor); break; } } } public class MyExpandableListAdapter extends SimpleCursorTreeAdapter { // Note that the constructor does not take a Cursor. This is done to // avoid querying the // database on the main thread. public MyExpandableListAdapter(Context context, int groupLayout, int childLayout, String[] groupFrom, int[] groupTo, String[] childrenFrom, int[] childrenTo) { super(context, null, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo); } @Override protected Cursor getChildrenCursor(Cursor groupCursor) { // Given the group, we return a cursor for all the children within // that group // Return a cursor that points to this contact phone numbers Uri.Builder builder = Contacts.CONTENT_URI.buildUpon(); ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX)); builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY); Uri phoneNumbersUri = builder.build(); mQueryHandler.startQuery(TOKEN_CHILD, groupCursor.getPosition(), phoneNumbersUri, PHONE_NUMBER_PROJECTION, Phone.MIMETYPE + "=?", new String[] { Phone.CONTENT_ITEM_TYPE }, null); return null; } } private QueryHandler mQueryHandler; private CursorTreeAdapter mAdapter; private Parcelable listState; private int listPosition; private int itemPosition; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Set up our adapter mAdapter = new MyExpandableListAdapter( this, android.R.layout.simple_expandable_list_item_1, android.R.layout.simple_expandable_list_item_1, new String[] { Contacts.DISPLAY_NAME }, // Name for group // layouts new int[] { android.R.id.text1 }, new String[] { Phone.NUMBER }, // Number for child layouts new int[] { android.R.id.text1 }); setListAdapter(mAdapter); mQueryHandler = new QueryHandler(this, mAdapter); // Query for people mQueryHandler.startQuery(TOKEN_GROUP, null, Contacts.CONTENT_URI, CONTACTS_PROJECTION, Contacts.HAS_PHONE_NUMBER + "=1", null, null); } @Override protected void onDestroy() { super.onDestroy(); // Null out the group cursor. This will cause the group cursor and all // of the child cursors // to be closed. mAdapter.changeCursor(null); mAdapter = null; } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); ExpandableListView listView = this.getExpandableListView(); listState = listView.onSaveInstanceState(); outState.putParcelable(LIST_STATE_KEY, listState); listPosition = listView.getFirstVisiblePosition(); outState.putInt(LIST_POSITION_KEY, listPosition); View itemView = listView.getChildAt(0); itemPosition = itemView == null ? 0 : itemView.getTop(); outState.putInt(ITEM_POSITION_KEY, itemPosition); } @Override protected void onRestoreInstanceState(Bundle state) { super.onRestoreInstanceState(state); listState = state.getParcelable(LIST_STATE_KEY); listPosition = state.getInt(LIST_POSITION_KEY); itemPosition = state.getInt(ITEM_POSITION_KEY); } @Override protected void onResume() { super.onResume(); ExpandableListView listView = this.getExpandableListView(); if (listView != null) { if (listState != null) { // yes, this code is reached listView.onRestoreInstanceState(listState); listView.setSelectionFromTop(listPosition, itemPosition); } } } } 

With the debugger, I made sure that all of these methods are actually called in the correct order when the orientation of the device changes. However: restoration of state without effect.

Further debugging in it, I found that the contents of the restored listState in onRestoreInstanceState () contains an empty array, and the listState in onSaveInstanceState () contains data in its array when it is placed in outState.

So my assumption is that listState Parcelable is not saved or not restored by the system ...

Can someone point out what is going wrong, or provide a working example using the SimpleCursorTreeAdapter with a β€œworking” restore state of expand / collapsed?

(Note: To reproduce this issue with the activity code above, you need to grant android.permission.READ_CONTACTS permission)

+7
source share
1 answer

This may scorn some of our more meticulous programmers, but have you tried making state containers a static object? But even before that, you might notifydatasetchanged() it useful to add notifydatasetchanged() after receiving your object.

0
source

All Articles