¿Cómo guardar y restaurar la posición exacta de desplazamiento de RecyclerView?

Disculpa mi pobre ingles.

Quiero la posición exacta. Y después que la actividad / fragmento ha sido destruido (no importa si lo destruyo o el Sistema lo destruye), vuelvo a abrir la actividad / fragmento, el RecyclerView también puede como la misma posición que se ha destruido.

El "mismo" no significa "la posición del artículo", porque tal vez el elemento sólo mostrar en parte la última vez.

Quiero restaurar exactamente la misma posición.

He probado algunas maneras a continuación, pero nadie es perfecto.

¿Alguien puede ayudar?

1. Primer camino.

Utilizo onScrolled para calcular la posición exacta del desplazamiento, cuando la parada de desplazamiento, excepto la posición. Cuando spinner cambie el dataset o fragmento onCreat , restaure la posición del conjunto de datos elegido. Algunos conjuntos de datos pueden tener muchas muchas filas.

Se puede guardar y restaurar después de la aplicación se destruye, pero tal vez demasiado calculte?

Causará

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.

Y si scrollY es enorme, como 111111 , al abrir el fragmento, el RecyclerView mostrará primero la lista desde el primer elemento, y después de un cierto retraso, se desplaza a la posición scrollY .

¿Cómo hacer que no se demore?

  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. Segundo camino.

Esto funciona perfectamente, pero sólo puede funcionar en el ciclo de vida de los fragmentos.

Intento utilizar ObjectOutputStream y ObjectInputStream para guardar y restaurar el HashMap , pero no funciona.

¿Cómo guardarlo en la 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.Tercera manera.

Cambiar la segunda manera, usar el Bundle lugar de HashMap , pero tiene la misma pregunta, no funciona cuando está fuera del ciclo de vida del fragmento.

Uso manualmente serializar / deserializar el paquete android para guardar el bundle en la memoria, pero al restaurarlo, no obtiene el bundle válido.

============================================================================= ===================== La solución

¡Gracias a todos!

Finalmente resuelvo este problema, lo puedes ver en mi código , solo ve estos 2 métodos: saveRecyclerViewPosition y restoreWordRecyclerViewPosition .

One Solution collect form web for “¿Cómo guardar y restaurar la posición exacta de desplazamiento de RecyclerView?”

Así que ahora tuve tiempo de perder;) Aquí está una respuesta completa:

Diseño básico. Sí, podría ser optimizado. Ese no es el 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> 

Lo que quieres hacer es obtener el primer elemento visible y su posición. Si tienes ids fijos puedes agregar lógica para buscar id / position en el adaptador para id.

Algunos adaptadores básicos:

 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; } } 

Guarda su estado como quiera, utilicé SharedPreferences y lo volví a leer al iniciar. I postDelayed el pergamino, porque no funciona si se desplaza a la posición es seguido inmediatamente por ella. Es sólo un ejemplo básico, y probablemente haya una mejor manera de jugar con LayoutManager un poco más.

 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(); } } 

Lo que esto hará, se mostrará de nuevo el elemento correcto, y después de los 500 ms volver a la posición correcta se guardó.

¡Asegúrese de agregar controles nulos y los tipos!

¡Espero que esto ayude!

  • Recycler View Con Gridlayout Manager
  • Los fragmentos ViewPager hacen referencia al mismo RecyclerView y / o Adapter
  • Recyclerview (Obtener elemento en Recyclerview)
  • Android Visualización de datos de json a cardview en RecyclerView
  • Aplicar offset a elementos GridLayout y conservar el mismo tamaño entre todos los elementos
  • RecyclerView almacena / restaura el estado entre las actividades
  • Cómo inhabilitar el dibujo de la decoración del artículo de RecyclerView para la duración de las animaciones del artículo
  • RecyclerView y SwipeRefreshLayout
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.