2015-09-12 19 views
5

Ci scusiamo per il mio scarso inglese.Come salvare e ripristinare la posizione esatta di scorrimento di RecyclerView?

Voglio la posizione esatta. E dopo l'attività /frammento è stato distrutto (non importa distruggo o il sistema distrugge), ho riaprire l'attività/frammento, il RecyclerView può anche come lo stesso posizione come è stato distrutto.

Lo "lo stesso" non significa "posizione articolo", perché forse l'elemento viene visualizzato solo parzialmente l'ultima volta.

Voglio ripristinare la stessa identica posizione.

Ho provato alcuni modi sotto, ma nessuno è perfetto.

Qualcuno può aiutare?

1. Primo passo.

Io uso onScrolled per calcolare la posizione esatta di scorrimento, quando lo scorrimento si ferma, salvare la posizione. Quando lo spinner modifica il set di dati o frammento onCreat, ripristina la posizione del set di dati scelto. Alcuni set di dati possono avere molte molte righe.

Può salvare e ripristinare dopo che l'app è stata distrutta, ma forse ci sono troppi calcoli?

causerà

Skipped 60 frames! The application may be doing too much work on its main thread. attempt to finish an input event but the input event receiver has already been disposed. android total arena pages for jit.

E se scrollY è enorme, come 111111, quando è aperto il frammento, il RecyclerView in primo luogo mostrare la lista inizia dalla prima voce, e con un certo ritardo, scorre alla posizione scrollY.

Come fare senza indugio?

recyclerView.addOnScrollListener(new OnScrollListener() { 
     @Override 
     public void onScrolled(RecyclerView recyclerView, int dx, final int dy) { 
      super.onScrolled(recyclerView, dx, dy); 
      handler.post(new Runnable() { 

       @Override 
       public void run() { 
        if (isTableSwitched) { 
         scrollY = 0; 
         isTableSwitched = false; 
        } 
        scrollY = scrollY + dy; 
        Log.i("dy——scrollY——table", String.valueOf(dy) + "——" + String.valueOf(scrollY) + tableName); 
       } 
      }); 
     } 


     @Override 
     public void onScrollStateChanged(RecyclerView recyclerView, int newState) { 
      super.onScrollStateChanged(recyclerView, newState); 

      switch (newState) { 
      case RecyclerView.SCROLL_STATE_IDLE: 
       saveRecyclerPosition(tableName, scrollY); 
       break; 
      case RecyclerView.SCROLL_STATE_DRAGGING: 
       break; 
      case RecyclerView.SCROLL_STATE_SETTLING: 
       break; 
     } 
     } 

    }); 

public void saveRecyclerPosition(String tableName, int y) { 
    prefEditorSettings.putInt(KEY_SCROLL_Y + tableName, y); 
    prefEditorSettings.commit(); 
} 

public void restoreRecyclerViewState(final String tableName) { 
    handler.post(new Runnable() { 

     @Override 
     public void run() { 
      recyclerView.scrollBy(0, prefSettings.getInt(KEY_SCROLL_Y + tableName, 0)); 
     } 
    }); 
} 


//use Spinner to choose dataset 
spnWordbook.setOnItemSelectedListener(new OnItemSelectedListener() { 

     @Override 
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 
      isTableSwitched = true; 
      tableName= parent.getItemAtPosition(position).toString(); 
      prefEditorSettings.putString(KEY_SPINNER_SELECTED_TABLENAME, tableName); 
      prefEditorSettings.commit(); 
      wordsList = WordsManager.getWordsList(tableName); 
      wordCardAdapter.updateList(wordsList); 

      restoreRecyclerViewState(tableName); 
     } 

     @Override 
     public void onNothingSelected(AdapterView<?> parent) { 
     } 
    }); 

2.Seconda modo.

Questo funziona perfettamente, ma può funzionare solo nel ciclo di vita del frammento.

Provo a utilizzare ObjectOutputStream e ObjectInputStream per salvare e ripristinare il HashMap, ma non funziona.

Come salvarlo in memoria?

private HashMap <String, Parcelable> hmRecyclerViewState = new HashMap<String, Parcelable>(); 

public void saveRecyclerPosition(String tableName) {  
    recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState(); 
    hmRecyclerViewState.put(tableName, recyclerViewState);  
} 

public void restoreRecyclerViewState(final String tableName) { 
    if (hmRecyclerViewState.get(tableName) != null) { 
     recyclerViewState = hmRecyclerViewState.get(tableName); 
     recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState); 
     recyclerViewState = null; 
} 

3.Terza modo.

I cambiare il secondo modo, utilizzare Bundle anziché HashMap, ma ha la stessa domanda, non funziona quando fuori del ciclo di vita frammento.

Io uso manually serialize/deserialize android bundle per salvare bundle nella memoria, ma quando lo ripristino, non ottiene lo bundle valido.

========================================= ========================= La soluzione

Grazie a tutti!

Infine ho risolvere questo problema, è possibile vederlo in my code, basta vedere questi 2 metodi: saveRecyclerViewPosition e restoreWordRecyclerViewPosition.

+0

probabilmente ottieni solo il primo ID elemento visualizzato e il relativo offset dal layoutmanager, quindi puoi passare alla posizione su come ricreare e regolare lo scorrimento per l'offset. questo probabilmente funzionerà anche se il set di dati è cambiato nel frattempo. –

+0

@ bleeding182 grazie. Anch'io ho pensato in questo modo, ma non so come ottenere l'offset di un articolo? ogni elemento ha un'altezza diversa – DawnYu

+0

è possibile ottenere la vista dal layoutmanager, quindi leggere getTop() o getDecoratedTop(), la differenza tra quello e il recyclerview.top sarà l'offset;) E anche se non si ottiene l'offset giusto, sarai almeno nella giusta posizione senza saltare i fotogrammi –

risposta

4

Così ora ho avuto il tempo di sprecare;) Ecco una risposta completa:

il layout di base. Sì, potrebbe essere ottimizzato. Non è questo il punto;)

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:paddingBottom="@dimen/activity_vertical_margin" 
    android:paddingLeft="@dimen/activity_horizontal_margin" 
    android:paddingRight="@dimen/activity_horizontal_margin" 
    android:paddingTop="@dimen/activity_vertical_margin" 
    tools:context=".MainActivity"> 

    <android.support.v7.widget.RecyclerView 
     android:id="@+id/recycler" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent"/> 
</LinearLayout> 

Quello che vuoi fare è ottenere il primo oggetto visibile e la sua posizione. Se hai corretto gli ID puoi aggiungere la logica per cercare id/position in adapter per id.

Alcuni adattatore di base:

public class TestAdapter extends RecyclerView.Adapter { 
    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     return new RecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, parent, false)) { 
     }; 
    } 

    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
     ((TextView) holder.itemView).setText("Position " + position); 
    } 

    @Override 
    public int getItemCount() { 
     return 30; 
    } 
} 

Risparmi suo stato come più vi piace, ho usato SharedPreferences, e leggere di nuovo in partenza. Ho postato il rollover, perché non funziona se si passa alla posizione è immediatamente seguito da esso. È solo un esempio di base e probabilmente c'è un modo migliore se si gioca un po 'di più con LayoutManager.

import android.content.SharedPreferences; 
import android.os.Bundle; 
import android.os.Handler; 
import android.preference.PreferenceManager; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.LinearLayoutManager; 
import android.support.v7.widget.RecyclerView; 
import android.util.Log; 
import android.view.View; 

public class MainActivity extends AppCompatActivity { 

    private static final String TAG = "MainActivity"; 
    private RecyclerView recyclerView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     recyclerView = (RecyclerView) findViewById(R.id.recycler); 

     recyclerView.setLayoutManager(new LinearLayoutManager(this)); 
     recyclerView.setAdapter(new TestAdapter()); 

    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); 
     recyclerView.scrollToPosition(preferences.getInt("position", 0)); 
     new Handler().postDelayed(new Runnable() { 
      @Override 
      public void run() { 
       recyclerView.scrollBy(0, - preferences.getInt("offset", 0)); 
      } 
     }, 500); 
    } 

    @Override 
    protected void onPause() { 
     super.onPause(); 
     SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); 

     View firstChild = recyclerView.getChildAt(0); 
     int firstVisiblePosition = recyclerView.getChildAdapterPosition(firstChild); 
     int offset = firstChild.getTop(); 

     Log.d(TAG, "Postition: " + firstVisiblePosition); 
     Log.d(TAG, "Offset: " + offset); 

     preferences.edit() 
       .putInt("position", firstVisiblePosition) 
       .putInt("offset", offset) 
       .apply(); 
    } 
} 

Che cosa questo farà, verrà visualizzato di nuovo l'articolo corretto, e dopo quei 500 ms saltare di nuovo nella posizione corretta è stato salvato.

Assicurati di aggiungere assegni null e il tipo!

Spero che questo aiuti!

+0

yess !! eccellente – SergioLucas

Problemi correlati