2012-08-31 4 views
7

Dopo aver trascorso alcuni giorni a cercare questo problema, alla fine mi arrendo e posta questa domanda. Qui a domande simili è stata data una risposta (in parte) e sono state proposte delle soluzioni, ma nessuna di esse alla fine mi ha aiutato. La differenza agli argomenti trattati e il mio problema sembra essere: non degli altri sembra usare un SimpleCursorTreeAdapter per alimentare la lista ...Salvare e ripristinare lo stato compresso di ExpandableListActivity con SimpleCursorTreeAdapter

Problema: Quando si cambia l'orientamento del mio dispositivo (Samsung Galaxy S), lo stato di espansione dei gruppi non viene conservato e ripristinato. Tutti i gruppi vengono chiusi e l'elenco scorre verso l'alto.

Il problema è già visibile nelle demo dell'API Android . Se li hai installati, vai a:

Visualizzazioni -> Liste espandibili -> 2. Cursore (Persone): avvia il campione, espandi qualsiasi gruppo, gira il tuo dispositivo e il risultato mostra il gruppo in stato collassato .

Nel seguente codice ho eseguito la seguente operazione: Ho preso il codice dalla demo API (ExpandableList2.java) e l'ho esteso con le implementazioni per onSaveInstantState(), onRestoreInstanceState() e onResume(). Le implementazioni per questi metodi sono state prese da un altro thread di discussione qui (How to preserve scroll position in an 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's 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); 
     } 
    } 
} 
} 

Con debugger ho fatto in modo, tutti questi metodi sono effettivamente chiamati nel giusto ordine quando l'orientamento del dispositivo viene modificato. Tuttavia: il ripristino dello stato non ha alcun effetto.

Ulteriori debugging in esso, ho trovato che il contenuto del restaurato listState in onRestoreInstanceState() contiene un array vuoto, mentre la listState in onSaveInstanceState() contiene i dati nella matrice quando messo in Outstate.

Quindi la mia ipotesi è che il listState Parcelable non viene mantenuto o recuperati correttamente dal sistema ...

Qualcuno può sottolineare cosa non va, o fornire un esempio di lavoro utilizzando il SimpleCursorTreeAdapter con "lavoro" ripristino dello stato di espansione/compressione?

(Nota: per riprodurre questo problema con il codice di attività di cui sopra, è necessario fornire l'autorizzazione android.permission.READ_CONTACTS)

+2

Questa è una domanda ben formata! – giZm0

+0

Dopo aver eseguito il debug di questo problema più volte su diverse versioni di Android, ho rinunciato alla soluzione diretta. Alla fine non sono riuscito a scoprire perché ** listState ** non è stato compilato correttamente in onRestoreInstanceState(). Quindi, ora sto usando la soluzione esplicita descritta in http://stackoverflow.com/questions/4184556/save-and-restore-expanded-collapsed-state-of-an-expandablelistactivity Questo funziona bene per il mio problema . – Michael

risposta

0

questo potrebbe trarre disprezzo da alcuni dei nostri programmatori più scrupolosi, ma hai provato semplicemente facendo i tuoi stati contengono un oggetto static? Ma anche prima, forse potresti trarre beneficio dall'aggiungere uno notifydatasetchanged() da qualche parte dopo aver recuperato il tuo oggetto.