Mantieni / Salva / Ripristina posizione di scorrimento quando si ritorna a ListView


397

Ho molto tempo ListViewche l'utente può scorrere prima di tornare alla schermata precedente. Quando l'utente riapre questo ListView, voglio che l'elenco venga fatto scorrere sullo stesso punto che era in precedenza. Qualche idea su come raggiungere questo obiettivo?


22
Penso che le soluzioni menzionate da Eugene Mymrin / Giorgio Barchiesi siano migliori della risposta accettata
Mira Weller,

Risposte:


631

Prova questo:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.setSelectionFromTop(index, top);

Spiegazione:

ListView.getFirstVisiblePosition()restituisce la voce di elenco visibile in alto. Ma questo elemento può essere parzialmente scartato e se vuoi ripristinare l'esatta posizione di scorrimento dell'elenco devi ottenere questo offset. Quindi ListView.getChildAt(0)restituisce l' Viewelemento per l'elenco in alto e quindi View.getTop() - mList.getPaddingTop()restituisce il relativo offset dalla parte superiore di ListView. Quindi, per ripristinare la ListViewposizione di scorrimento, chiamiamo ListView.setSelectionFromTop()con l'indice dell'articolo che vogliamo e un offset per posizionare il suo bordo superiore dalla parte superiore di ListView.


2
Non capisco davvero come funziona. Sto usando listView.getFirstVisiblePosition () solo e lo capisco, ma non sono sicuro di quello che sta succedendo qui. Qualche possibilità di una breve spiegazione? :)
HXCaine,

56
Quindi ListView.getFirstVisiblePosition () restituisce la voce di elenco visibile in alto. Ma questo elemento può essere parzialmente scartato e se vuoi ripristinare l'esatta posizione di scorrimento dell'elenco devi ottenere questo offset. Quindi ListView.getChildAt (0) restituisce la vista per la voce di elenco superiore e quindi View.getTop () restituisce il relativo offset dalla parte superiore di ListView. Quindi, per ripristinare la posizione di scorrimento di ListView, chiamiamo ListView.setSelectionFromTop () con l'indice dell'elemento desiderato e un offset per posizionare il bordo superiore dalla parte superiore di ListView. Tutto chiaro?
Ian

15
Questo può essere reso ancora più semplice prevede di utilizzare una linea di salvare: int index = mList.getFirstVisiblePosition();e solo una riga per ripristinare: mList.setSelectionFromTop(index, 0);. Ottima risposta però (+1)! Ho cercato una soluzione elegante a questo problema.
Phil

17
@Phil la tua soluzione semplicistica non funziona per ripristinare l'esatta posizione di scorrimento.
Ixx,

2
come ha detto @nbarraille, il codice dovrebbe essere più simile a "v.getTop () - mList.getPaddingTop ()." Altrimenti passerai un'ora come ho fatto cercando di capire perché ripristina sempre solo un
taglio di

544
Parcelable state;

@Override
public void onPause() {    
    // Save ListView state @ onPause
    Log.d(TAG, "saving listview state");
    state = listView.onSaveInstanceState();
    super.onPause();
}
...

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    // Set new items
    listView.setAdapter(adapter);
    ...
    // Restore previous state (including selected item index and scroll position)
    if(state != null) {
        Log.d(TAG, "trying to restore listview state");
        listView.onRestoreInstanceState(state);
    }
}

106
Penso che questa sia la risposta migliore in quanto questo metodo ripristina l'esatta posizione di scorrimento e non solo il primo elemento visibile.
Mira Weller,

9
Ottima risposta ma non funziona quando si carica dinamicamente il contenuto e si desidera salvare lo stato nel metodo onPause ().
Gio

13
ottima soluzione. Solo un problema. Se gli elementi sono grandi e si scorre, ma il primo elemento è ancora visibile l'elenco torna in alto. se scorri fino al secondo elemento o in qualsiasi altro luogo, tutto funziona
passsy

4
@passsy Hai mai trovato una soluzione l'elenco che salta in cima?
Papajohn000,

2
Come ha detto aaronvargas, questo non poteva funzionare quando ListView.getFirstVisiblePosition () è 0.
VinceStyling

54

Ho adottato la soluzione suggerita da @ (Kirk Woll) e funziona per me. Ho anche visto nel codice sorgente Android per l'app "Contatti" che usano una tecnica simile. Vorrei aggiungere altri dettagli: In cima alla mia classe derivata da ListActivity:

private static final String LIST_STATE = "listState";
private Parcelable mListState = null;

Quindi, alcuni metodi hanno la precedenza:

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListState = state.getParcelable(LIST_STATE);
}

@Override
protected void onResume() {
    super.onResume();
    loadData();
    if (mListState != null)
        getListView().onRestoreInstanceState(mListState);
    mListState = null;
}

@Override
protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListState = getListView().onSaveInstanceState();
    state.putParcelable(LIST_STATE, mListState);
}

Naturalmente "loadData" è la mia funzione per recuperare i dati dal DB e inserirli nell'elenco.

Sul mio dispositivo Froyo, questo funziona sia quando cambi l'orientamento del telefono, sia quando modifichi un elemento e torni all'elenco.


1
Questa soluzione ha funzionato per me. Gli altri no. Come si comporta il salvataggio / ripristino dello stato dell'istanza di ListView per quanto riguarda l'eliminazione e l'inserimento di elementi in ListView?
Bryan,

2
Cordiali saluti, se lo usi in un frammento (sto usando un frammento di elenco) e vai ad altri frammenti, questo si arresterà in modo anomalo perché la vista del contenuto non viene creata quando si chiama mListState = getListView().onSaveInstanceState().Principalmente sulla rotazione del dispositivo.
Mgamerz,

2
onRestoreInstanceStatenon viene mai chiamato :(
dVaffection il

1
@GiorgioBarchiesi Chi è @ (Kirk Wool) la cui soluzione funziona per te? Apparentemente l'utente ha cambiato il suo nome.
Piovezan,

1
Sì, questa è la religione ufficiale. Ma nel mio caso i dati vengono caricati localmente, ha una lunghezza molto limitata e si carica in una frazione di secondo, quindi sono diventato eretico :-)
Giorgio Barchiesi

26

Un modo molto semplice:

/** Save the position **/
int currentPosition = listView.getFirstVisiblePosition();

//Here u should save the currentPosition anywhere

/** Restore the previus saved position **/
listView.setSelection(savedPosition);

Il metodo setSelection reimposterà l'elenco sull'elemento fornito. Se non in modalità touch, l'elemento verrà effettivamente selezionato se in modalità touch l'elemento verrà posizionato solo sullo schermo.

Un approccio più complicato:

listView.setOnScrollListener(this);

//Implements the interface:
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
    mCurrentX = view.getScrollX();
    mCurrentY = view.getScrollY();
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

//Save anywere the x and the y

/** Restore: **/
listView.scrollTo(savedX, savedY);

2
si è verificato un errore nella risposta. Il metodo si chiama setSelection
Janusz

L'idea funziona bene, ma c'è uno pseudo problema. La prima posizione visibile potrebbe essere mostrata solo un po ', (forse solo una piccola parte della riga), e setSelection () imposta questa riga in modo che sia completamente visibile quando il ripristino è fatto.
rantravee,

Dai un'occhiata alla mia seconda opzione. L'ho appena aggiunto alla mia prima risposta
Francesco Laurita,

27
view.getScrollX () e view.getScrollY () restituiscono sempre 0!
rantravee,

3
Dai un'occhiata alla mia soluzione (accettata) sopra usando View.getChildAt () e View.getTop (). Non è possibile utilizzare View.getScrollY () su ListView perché ListView mantiene lo scorrimento interno.
Ian

17

Ho trovato qualcosa di interessante al riguardo.

Ho provato setSelection e scrolltoXY ma non ha funzionato affatto, l'elenco è rimasto nella stessa posizione, dopo alcuni tentativi ed errori ho ottenuto il seguente codice che funziona

final ListView list = (ListView) findViewById(R.id.list);
list.post(new Runnable() {            
    @Override
    public void run() {
        list.setSelection(0);
    }
});

Se invece di pubblicare Runnable provi runOnUiThread non funziona neanche (almeno su alcuni dispositivi)

Questa è una soluzione molto strana per qualcosa che dovrebbe essere semplice.


1
Ho riscontrato lo stesso problema! E setSelectionFromTop()non funziona.
ofavre

+1. listnew.post (), non funziona su alcuni dispositivi e su alcuni altri dispositivi non funziona in alcuni casi, ma runOnUIThread funziona bene.
Omar Rehman,

@Omar, puoi dare alcuni esempi di dispositivi e sistemi operativi su cui questo non funziona? Ho provato un HTC G2 (2.3.4) e un Galaxy Nexus (4.1.2) e ha funzionato su entrambi. Nessuna delle altre risposte in questo thread ha funzionato per nessuno dei miei dispositivi.
Dan J

2
listview.post funziona bene sul mio Galaxy Note con 4.0.4, ma non funziona sul mio Nexus One con 2.3.6. Tuttavia, onOnUIThread (azione) funziona bene su entrambi i dispositivi.
Omar Rehman,

11

ATTENZIONE!! C'è un bug in AbsListView che non consente a onSaveState () di funzionare correttamente se ListView.getFirstVisiblePosition () è 0.

Quindi, se hai immagini di grandi dimensioni che occupano la maggior parte dello schermo e scorri fino alla seconda immagine, ma viene mostrata una piccola parte della prima, la posizione di scorrimento non verrà salvata ...

da AbsListView.java:1650 (commenti miei)

// this will be false when the firstPosition IS 0
if (haveChildren && mFirstPosition > 0) {
    ...
} else {
    ss.viewTop = 0;
    ss.firstId = INVALID_POSITION;
    ss.position = 0;
}

Ma in questa situazione, il "top" nel codice seguente sarà un numero negativo che causa altri problemi che impediscono il corretto ripristino dello stato. Quindi, quando il "top" è negativo, prendi il bambino successivo

// save index and top position
int index = getFirstVisiblePosition();
View v = getChildAt(0);
int top = (v == null) ? 0 : v.getTop();

if (top < 0 && getChildAt(1) != null) {
    index++;
    v = getChildAt(1);
    top = v.getTop();
}
// parcel the index and top

// when restoring, unparcel index and top
listView.setSelectionFromTop(index, top);

Funziona come un fascino ma non lo capisco del tutto. Se il primo elemento è ancora visibile, che senso ha ottenere il bambino successivo? SetSelectionFromTop con il nuovo indice non dovrebbe causare la visualizzazione dell'elenco a partire dal secondo figlio? Come sto ancora vedendo il primo?
Tafi,

@tafi, il primo elemento potrebbe essere "parzialmente" visibile. Quindi la parte superiore di quell'elemento sarebbe sopra lo schermo e quindi sarebbe un numero negativo. (Ciò causa altri problemi che non ricordo ...) Quindi usiamo la parte superiore del secondo elemento per il posizionamento, che dovrebbe essere sempre (?) Disponibile, altrimenti non ci sarebbe alcuno scorrimento in primo luogo !
aaronvargas,

6
private Parcelable state;
@Override
public void onPause() {
    state = mAlbumListView.onSaveInstanceState();
    super.onPause();
}

@Override
public void onResume() {
    super.onResume();

    if (getAdapter() != null) {
        mAlbumListView.setAdapter(getAdapter());
        if (state != null){
            mAlbumListView.requestFocus();
            mAlbumListView.onRestoreInstanceState(state);
        }
    }
}

È abbastanza


Ciao, il tuo codice qui sopra mi aiuterebbe con un elenco di RecyclerView in cui instancestate non viene salvato tra le attività? Ho pubblicato la seguente domanda qui: stackoverflow.com/questions/35413495/… ?
AJW,

6

Per alcuni alla ricerca di una soluzione a questo problema, la radice del problema potrebbe essere la posizione in cui si sta impostando l'adattatore delle visualizzazioni elenco. Dopo aver impostato l'adattatore nella visualizzazione elenco, reimposta la posizione di scorrimento. Solo qualcosa da considerare. Ho spostato l'impostazione dell'adattatore nel mio onCreateView dopo aver acquisito il riferimento a listview e ho risolto il problema per me. =)


1

Sto pubblicando questo perché sono sorpreso che nessuno l'abbia menzionato.

Dopo che l'utente ha fatto clic sul pulsante Indietro, tornerà alla visualizzazione elenco nello stesso stato in cui è uscito.

Questo codice sovrascriverà il pulsante "su" per comportarsi allo stesso modo del pulsante indietro, quindi nel caso di Listview -> Dettagli -> Torna a Listview (e senza altre opzioni) questo è il codice più semplice per mantenere la posizione di scorrimento e il contenuto nella lista.

 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
         case android.R.id.home:
             onBackPressed();
             return(true);
     }
     return(super.onOptionsItemSelected(item)); }

Attenzione: se puoi passare a un'altra attività dall'attività dei dettagli, il pulsante su ti riporterà a quell'attività, quindi dovrai manipolare la cronologia dei pulsanti di back affinché questa funzioni.



1

LA MIGLIORE SOLUZIONE È:

// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());

// ...

// restore index and position
mList.post(new Runnable() {
    @Override
    public void run() {
      mList.setSelectionFromTop(index, top);
   }
});

DEVI CHIAMARE IN POSIZIONE E IN FILO!


1

È possibile mantenere lo stato di scorrimento dopo un nuovo caricamento se si salva lo stato prima di ricaricare e ripristinarlo dopo. Nel mio caso ho fatto una richiesta di rete asincrona e ricaricato l'elenco in un callback dopo averlo completato. Qui è dove ripristino lo stato. L'esempio di codice è Kotlin.

val state = myList.layoutManager.onSaveInstanceState()

getNewThings() { newThings: List<Thing> ->

    myList.adapter.things = newThings
    myList.layoutManager.onRestoreInstanceState(state)
}

0

Se stai usando frammenti ospitati su un'attività puoi fare qualcosa del genere:

public abstract class BaseFragment extends Fragment {
     private boolean mSaveView = false;
     private SoftReference<View> mViewReference;

     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          if (mSaveView) {
               if (mViewReference != null) {
                    final View savedView = mViewReference.get();
                    if (savedView != null) {
                         if (savedView.getParent() != null) {
                              ((ViewGroup) savedView.getParent()).removeView(savedView);
                              return savedView;
                         }
                    }
               }
          }

          final View view = inflater.inflate(getFragmentResource(), container, false);
          mViewReference = new SoftReference<View>(view);
          return view;
     }

     protected void setSaveView(boolean value) {
           mSaveView = value;
     }
}

public class MyFragment extends BaseFragment {
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          setSaveView(true);
          final View view = super.onCreateView(inflater, container, savedInstanceState);
          ListView placesList = (ListView) view.findViewById(R.id.places_list);
          if (placesList.getAdapter() == null) {
               placesList.setAdapter(createAdapter());
          }
     }
}

0

Se stai salvando / ripristinando la posizione di scorrimento di ListViewte stesso, essenzialmente stai duplicando la funzionalità già implementata nel framework Android. La ListViewposizione di scorrimento bene ripristini solo bene da solo, tranne un avvertimento: come @aaronvargas menzionati c'è un bug in AbsListViewche non ti consente di ripristinare posizione di scorrimento bene per la prima voce dell'elenco. Tuttavia, il modo migliore per ripristinare la posizione di scorrimento non è ripristinarlo. Il framework Android lo farà meglio per te. Assicurati di aver soddisfatto le seguenti condizioni:

  • assicurati di non aver chiamato setSaveEnabled(false)metodo e di non aver impostato l' android:saveEnabled="false"attributo per l'elenco nel file di layout xml
  • per il metodo di ExpandableListViewsostituzione in long getCombinedChildId(long groupId, long childId)modo che restituisca un numero lungo positivo (l'implementazione predefinita nella classe BaseExpandableListAdapterrestituisce un numero negativo). Ecco alcuni esempi:

.

@Override
public long getChildId(int groupPosition, int childPosition) {
    return 0L | groupPosition << 12 | childPosition;
}

@Override
public long getCombinedChildId(long groupId, long childId) {
    return groupId << 32 | childId << 1 | 1;
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public long getCombinedGroupId(long groupId) {
    return (groupId & 0x7FFFFFFF) << 32;
}
  • se ListViewo ExpandableListViewviene utilizzato in un frammento non ricreare il frammento durante la ricreazione di attività (ad esempio dopo la rotazione dello schermo). Ottieni il frammento con il findFragmentByTag(String tag)metodo.
  • assicurati che ListViewabbia un android:ided è unico.

Per evitare il suddetto avvertimento con la prima voce dell'elenco, è possibile creare l'adattatore nel modo in cui restituisce una vista di altezza zero pixel fittizia speciale per la ListViewposizione at. Ecco il semplice esempio che mostra il progetto ListViewe ExpandableListViewripristina le posizioni di scorrimento fini mentre le posizioni di scorrimento non vengono salvate esplicitamente / restaurato. La posizione di scorrimento fine viene ripristinata perfettamente anche per gli scenari complessi con il passaggio temporaneo a qualche altra applicazione, la rotazione del doppio schermo e il ritorno all'applicazione di prova. Si noti che se si esce esplicitamente dall'applicazione (premendo il pulsante Indietro) la posizione di scorrimento non verrà salvata (così come tutte le altre viste non salveranno il loro stato). https://github.com/voromto/RestoreScrollPosition/releases


0

Per un'attività derivata da ListActivity che implementa LoaderManager.LoaderCallbacks utilizzando un SimpleCursorAdapter non ha funzionato per ripristinare la posizione in onReset (), poiché l'attività è stata quasi sempre riavviata e l'adattatore è stato ricaricato quando la vista dei dettagli è stata chiusa. Il trucco era ripristinare la posizione in onLoadFinished ():

in onListItemClick ():

// save the selected item position when an item was clicked
// to open the details
index = getListView().getFirstVisiblePosition();
View v = getListView().getChildAt(0);
top = (v == null) ? 0 : (v.getTop() - getListView().getPaddingTop());

in onLoadFinished ():

// restore the selected item which was saved on item click
// when details are closed and list is shown again
getListView().setSelectionFromTop(index, top);

in onBackPressed ():

// Show the top item at next start of the app
index = 0;
top = 0;

0

Nessuna delle soluzioni offerte qui sembrava funzionare per me. Nel mio caso, ho un ListViewin a Fragmentche sto sostituendo in a FragmentTransaction, quindi una nuova Fragmentistanza viene creata ogni volta che viene mostrato il frammento, il che significa che lo ListViewstato non può essere archiviato come membro delFragment .

Invece, ho finito per memorizzare lo stato nella mia Applicationclasse personalizzata . Il codice seguente dovrebbe darti un'idea di come funziona:

public class MyApplication extends Application {
    public static HashMap<String, Parcelable> parcelableCache = new HashMap<>();


    /* ... code omitted for brevity ... */
}

 

public class MyFragment extends Fragment{
    private ListView mListView = null;
    private MyAdapter mAdapter = null;


    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mAdapter = new MyAdapter(getActivity(), null, 0);
        mListView = ((ListView) view.findViewById(R.id.myListView));

        Parcelable listViewState = MyApplication.parcelableCache.get("my_listview_state");
        if( listViewState != null )
            mListView.onRestoreInstanceState(listViewState);
    }


    @Override
    public void onPause() {
        MyApplication.parcelableCache.put("my_listview_state", mListView.onSaveInstanceState());
        super.onPause();
    }

    /* ... code omitted for brevity ... */

}

L'idea di base è che si memorizzi lo stato al di fuori dell'istanza del frammento. Se non ti piace l'idea di avere un campo statico nella tua classe di applicazione, immagino che potresti farlo implementando un'interfaccia a frammenti e memorizzando lo stato nella tua attività.

Un'altra soluzione sarebbe quella di archiviarlo SharedPreferences, ma diventa un po 'più complicato e dovresti assicurarti di cancellarlo all'avvio dell'applicazione a meno che non desideri che lo stato venga mantenuto attraverso i lanci di app.

 

Inoltre, per evitare la "posizione di scorrimento non salvata quando è visibile il primo elemento", è possibile visualizzare un primo oggetto fittizio con 0pxaltezza. Ciò può essere ottenuto sostituendo getView()l'adattatore, in questo modo:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if( position == 0 ) {
        View zeroHeightView = new View(parent.getContext());
        zeroHeightView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
        return zeroHeightView;
    }
    else
        return super.getView(position, convertView, parent);
}

0

La mia risposta è per Firebase e la posizione 0 è una soluzione alternativa

Parcelable state;

DatabaseReference everybody = db.getReference("Everybody Room List");
    everybody.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            state = listView.onSaveInstanceState(); // Save
            progressBar.setVisibility(View.GONE);
            arrayList.clear();
            for (DataSnapshot messageSnapshot : dataSnapshot.getChildren()) {
                Messages messagesSpacecraft = messageSnapshot.getValue(Messages.class);
                arrayList.add(messagesSpacecraft);
            }
            listView.setAdapter(convertView);
            listView.onRestoreInstanceState(state); // Restore
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
        }
    });

e convertView

posizione 0 a aggiungere un elemento vuoto che non si sta utilizzando

public class Chat_ConvertView_List_Room extends BaseAdapter {

private ArrayList<Messages> spacecrafts;
private Context context;

@SuppressLint("CommitPrefEdits")
Chat_ConvertView_List_Room(Context context, ArrayList<Messages> spacecrafts) {
    this.context = context;
    this.spacecrafts = spacecrafts;
}

@Override
public int getCount() {
    return spacecrafts.size();
}

@Override
public Object getItem(int position) {
    return spacecrafts.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@SuppressLint({"SetTextI18n", "SimpleDateFormat"})
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        convertView = LayoutInflater.from(context).inflate(R.layout.message_model_list_room, parent, false);
    }

    final Messages s = (Messages) this.getItem(position);

    if (position == 0) {
        convertView.getLayoutParams().height = 1; // 0 does not work
    } else {
        convertView.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT;
    }

    return convertView;
}
}

Ho visto questo lavoro temporaneamente senza disturbare l'utente, spero che funzioni per te


0

usa questo codice qui sotto:

int index,top;

@Override
protected void onPause() {
    super.onPause();
    index = mList.getFirstVisiblePosition();

    View v = challengeList.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
}

e ogni volta che aggiorni i tuoi dati usa questo codice qui sotto:

adapter.notifyDataSetChanged();
mList.setSelectionFromTop(index, top);

0

Sto usando FirebaseListAdapter e non riesco a far funzionare nessuna delle soluzioni. Ho finito per farlo. Immagino che ci siano modi più eleganti ma questa è una soluzione completa e funzionante.

Prima di creare:

private int reset;
private int top;
private int index;

All'interno di FirebaseListAdapter:

@Override
public void onDataChanged() {
     super.onDataChanged();

     // Only do this on first change, when starting
     // activity or coming back to it.
     if(reset == 0) {
          mListView.setSelectionFromTop(index, top);
          reset++;
     }

 }

onStart:

@Override
protected void onStart() {
    super.onStart();
    if(adapter != null) {
        adapter.startListening();
        index = 0;
        top = 0;
        // Get position from SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        top = sharedPref.getInt("TOP_POSITION", 0);
        index = sharedPref.getInt("INDEX_POSITION", 0);
        // Set reset to 0 to allow change to last position
        reset = 0;
    }
}

onStop:

@Override
protected void onStop() {
    super.onStop();
    if(adapter != null) {
        adapter.stopListening();
        // Set position
        index = mListView.getFirstVisiblePosition();
        View v = mListView.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - mListView.getPaddingTop());
        // Save position to SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
        sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
    }
}

Dal momento che ho dovuto risolvere anche questo per FirebaseRecyclerAdapter sto pubblicando la soluzione anche qui per quello:

Prima di creare:

private int reset;
private int top;
private int index;

All'interno di FirebaseRecyclerAdapter:

@Override
public void onDataChanged() {
    // Only do this on first change, when starting
    // activity or coming back to it.
    if(reset == 0) {
        linearLayoutManager.scrollToPositionWithOffset(index, top);
        reset++;
    }
}

onStart:

@Override
protected void onStart() {
    super.onStart();
    if(adapter != null) {
        adapter.startListening();
        index = 0;
        top = 0;
        // Get position from SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        top = sharedPref.getInt("TOP_POSITION", 0);
        index = sharedPref.getInt("INDEX_POSITION", 0);
        // Set reset to 0 to allow change to last position
        reset = 0;
    }
}

onStop:

@Override
protected void onStop() {
    super.onStop();
    if(adapter != null) {
        adapter.stopListening();
        // Set position
        index = linearLayoutManager.findFirstVisibleItemPosition();
        View v = linearLayoutManager.getChildAt(0);
        top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
        // Save position to SharedPrefs
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPref.edit().putInt("TOP_POSITION" + "", top).apply();
        sharedPref.edit().putInt("INDEX_POSITION" + "", index).apply();
    }
}

0

Per chiarire l'eccellente risposta di Ryan Newsom e per adattarla ai frammenti e al solito caso in cui vogliamo passare da un frammento ListView "principale" a un frammento "dettagli" e quindi di nuovo al "principale"

    private View root;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
           if(root == null){
             root = inflater.inflate(R.layout.myfragmentid,container,false);
             InitializeView(); 
           } 
           return root; 
        }

    public void InitializeView()
    {
        ListView listView = (ListView)root.findViewById(R.id.listviewid);
        BaseAdapter adapter = CreateAdapter();//Create your adapter here
        listView.setAdpater(adapter);
        //other initialization code
    }

La "magia" qui è che quando torniamo indietro dal frammento di dettagli al frammento di ListView, la vista non viene ricreata, non impostiamo l'adattatore di ListView, quindi tutto rimane come l'abbiamo lasciato!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.