Wie speichere ich die Bildlaufposition von RecyclerView mit RecyclerView.State?

107

Ich habe eine Frage zu Android RecyclerView.State .

Ich verwende ein RecyclerView. Wie kann ich es verwenden und mit RecyclerView.State binden?

Mein Zweck ist es , die Bildlaufposition von RecyclerView zu speichern .

陈文涛
quelle

Antworten:

37

Wie wollen Sie die zuletzt gespeicherte Position mit speichern? RecyclerView.State ?

Sie können sich immer auf den guten Speicherzustand verlassen. Erweitern RecyclerViewund überschreiben Sie onSaveInstanceState () und onRestoreInstanceState():

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        LayoutManager layoutManager = getLayoutManager();
        if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
            mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
        }
        SavedState newState = new SavedState(superState);
        newState.mScrollPosition = mScrollPosition;
        return newState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        super.onRestoreInstanceState(state);
        if(state != null && state instanceof SavedState){
            mScrollPosition = ((SavedState) state).mScrollPosition;
            LayoutManager layoutManager = getLayoutManager();
            if(layoutManager != null){
              int count = layoutManager.getItemCount();
              if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
                  layoutManager.scrollToPosition(mScrollPosition);
              }
            }
        }
    }

    static class SavedState extends android.view.View.BaseSavedState {
        public int mScrollPosition;
        SavedState(Parcel in) {
            super(in);
            mScrollPosition = in.readInt();
        }
        SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(mScrollPosition);
        }
        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
Nikola Despotoski
quelle
1
Jetzt habe ich noch eine Frage: Wie, nein, was kann RecyclerView.State?
19.
4
funktioniert das wirklich Ich habe dies versucht, erhalte jedoch immer wieder eine Laufzeitausnahme wie diese: MyRecyclerView $ SavedState kann nicht in android.support.v7.widget.RecyclerView $ SavedState umgewandelt werden
Daivid
2
Im Status der Wiederherstellungsinstanz müssen wir den SuperState wie folgt an seine Superklasse (die Recytcler-Ansicht) übergeben: super.onRestoreInstanceState (((ScrollPositionSavingRecyclerView.SavedState) -Status) .getSuperState ());
Micnoy
5
Dies funktioniert nicht, wenn Sie die RecyclerView erweitern. Sie erhalten eine BadParcelException.
EpicPandaForce
1
Nichts davon ist notwendig: Wenn Sie etwas wie LinearLayoutManager / GridLayoutManager verwenden, wird die "Bildlaufposition" bereits automatisch für Sie gespeichert. (Es ist die mAnchorPosition in LinearLayoutManager.SavedState). Wenn Sie das nicht sehen, stimmt etwas nicht mit der Art und Weise, wie Sie Daten erneut anwenden.
Brian Yencho
228

Aktualisieren

Ab der recyclerview:1.2.0-alpha02VeröffentlichungStateRestorationPolicy wurde eingeführt. Es könnte eine bessere Herangehensweise an das gegebene Problem sein.

Dieses Thema wurde in einem mittleren Artikel für Android-Entwickler behandelt .

Außerdem hat @ rubén-viguera in der folgenden Antwort weitere Details mitgeteilt. https://stackoverflow.com/a/61609823/892500

Alte Antwort

Wenn Sie LinearLayoutManager verwenden, wird die vorgefertigte API zum Speichern von linearLayoutManagerInstance.onSaveInstanceState () und zum Wiederherstellen der API linearLayoutManagerInstance.onRestoreInstanceState (...) mitgeliefert.

Damit können Sie das zurückgegebene Paket in Ihrem Außenstaat speichern. z.B,

outState.putParcelable("KeyForLayoutManagerState", linearLayoutManagerInstance.onSaveInstanceState());

und stellen Sie die Wiederherstellungsposition mit dem von Ihnen gespeicherten Status wieder her. z.B,

Parcelable state = savedInstanceState.getParcelable("KeyForLayoutManagerState");
linearLayoutManagerInstance.onRestoreInstanceState(state);

Zum Abschluss sieht Ihr endgültiger Code ungefähr so ​​aus

private static final String BUNDLE_RECYCLER_LAYOUT = "classname.recycler.layout";

/**
 * This is a method for Fragment. 
 * You can do the same in onCreate or onRestoreInstanceState
 */
@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);

    if(savedInstanceState != null)
    {
        Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        recyclerView.getLayoutManager().onRestoreInstanceState(savedRecyclerLayoutState);
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
}

Bearbeiten: Sie können dieselbe API auch mit dem GridLayoutManager verwenden, da es sich um eine Unterklasse von LinearLayoutManager handelt. Danke @wegsehen für den Vorschlag.

Bearbeiten: Denken Sie daran, dass Sie, wenn Sie auch Daten in einen Hintergrundthread laden, onRestoreInstanceState in Ihrer onPostExecute / onLoadFinished-Methode aufrufen müssen, damit die Position bei einer Änderung der Ausrichtung wiederhergestellt werden kann, z

@Override
protected void onPostExecute(ArrayList<Movie> movies) {
    mLoadingIndicator.setVisibility(View.INVISIBLE);
    if (movies != null) {
        showMoviePosterDataView();
        mDataAdapter.setMovies(movies);
      mRecyclerView.getLayoutManager().onRestoreInstanceState(mSavedRecyclerLayoutState);
        } else {
            showErrorMessage();
        }
    }
Gökhan Barış Aker
quelle
21
Dies ist eindeutig die beste Antwort. Warum wird dies nicht akzeptiert? Beachten Sie, dass jeder LayoutManager dies hat, nicht nur LinearLayoutManager, sondern auch GridLayoutManager, und wenn nicht, ist es eine falsche Implementierung.
Paul Woitaschek
linearLayoutManager.onInstanceState () gibt immer null zurück -> überprüfe den Quellcode. Funktioniert leider nicht.
Luckyhandler
1
Führt RecyclerView nicht das Speichern des Status seines LayoutManagers in seinem eigenen onSaveInstanceState durch? Mit Blick auf v24 der Support-
Bibliothek
3
Dies funktioniert auf einer einzelnen Seite, RecyclerViewaber nicht, wenn Sie auf jeder Seite eine ViewPagermit einer RecycerViewhaben.
Kris B
1
@ Ivan, ich denke, es funktioniert in einem Fragment (nur überprüft), aber nicht in onCreateView, sondern nach dem Laden der Daten. Siehe @ Farmaker Antwort hier.
CoolMind
81

Geschäft

lastFirstVisiblePosition = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();

Wiederherstellen

((LinearLayoutManager) rv.getLayoutManager()).scrollToPosition(lastFirstVisiblePosition);

und wenn das nicht funktioniert, versuchen Sie es

((LinearLayoutManager) rv.getLayoutManager()).scrollToPositionWithOffset(lastFirstVisiblePosition,0)

Speichern onPause() und wiederherstellenonResume()

Gillis Haasnoot
quelle
Dies funktioniert, aber Sie müssen möglicherweise das zurücksetzen lastFirstVisiblePosition, 0nachdem Sie es wiederhergestellt haben. Dieser Fall ist nützlich, wenn Sie ein Fragment / eine Aktivität haben, die verschiedene Daten in eine lädt RecyclerView, z. B. eine Datei-Explorer-App.
Blueware
Ausgezeichnet! Es erfüllt sowohl das Zurückkehren von einer Detailaktivität zur Liste als auch das Speichern des Status bei der Bildschirmrotation
Javi
Was tun, wenn sich mein recyclerView in befindet SwipeRefreshLayout?
Morozov
2
Warum sollte die erste Wiederherstellungsoption nicht funktionieren, die zweite jedoch?
Lucidbrot
1
@MehranJalili es scheint einRecyclerView
Vlad
22

Ich wollte die Bildlaufposition von Recycler View speichern, wenn ich von meiner Listenaktivität weg navigiere und dann auf die Schaltfläche "Zurück" klicke, um zurück zu navigieren. Viele der für dieses Problem bereitgestellten Lösungen waren entweder viel komplizierter als erforderlich oder funktionierten für meine Konfiguration nicht. Daher dachte ich, ich würde meine Lösung teilen.

Speichern Sie zuerst Ihren Instanzstatus in onPause, wie viele gezeigt haben. Ich denke, es ist hier hervorzuheben, dass diese Version von onSaveInstanceState eine Methode aus der RecyclerView.LayoutManager-Klasse ist.

private LinearLayoutManager mLayoutManager;
Parcelable state;

        @Override
        public void onPause() {
            super.onPause();
            state = mLayoutManager.onSaveInstanceState();
        }

Der Schlüssel, damit dies ordnungsgemäß funktioniert, besteht darin, sicherzustellen, dass Sie onRestoreInstanceState aufrufen, nachdem Sie Ihren Adapter angeschlossen haben, wie einige in anderen Threads angegeben haben. Der eigentliche Methodenaufruf ist jedoch viel einfacher als von vielen angegeben.

private void someMethod() {
    mVenueRecyclerView.setAdapter(mVenueAdapter);
    mLayoutManager.onRestoreInstanceState(state);
}
Braden Holt
quelle
1
Das ist die genaue Lösung. : D
Afjalur Rahman Rana
1
Für mich wird onSaveInstanceState nicht aufgerufen, daher scheint das Speichern des Status in onPause eine bessere Option zu sein. Ich knalle auch den Backstack, um zum Fragment zurückzukehren.
filthy_wizard
für die Last in Zustand. was ich in onViewCreated mache. es scheint nur zu funktionieren, wenn ich im Debug-Modus bin und Haltepunkte benutze? oder wenn ich eine Verzögerung hinzufüge und die Ausführung in eine Coroutine setze.
filthy_wizard
21

I Setzen Sie Variablen in onCreate (), speichern Sie die Bildlaufposition in onPause () und setzen Sie die Bildlaufposition in onResume ()

public static int index = -1;
public static int top = -1;
LinearLayoutManager mLayoutManager;

@Override
public void onCreate(Bundle savedInstanceState)
{
    //Set Variables
    super.onCreate(savedInstanceState);
    cRecyclerView = ( RecyclerView )findViewById(R.id.conv_recycler);
    mLayoutManager = new LinearLayoutManager(this);
    cRecyclerView.setHasFixedSize(true);
    cRecyclerView.setLayoutManager(mLayoutManager);

}

@Override
public void onPause()
{
    super.onPause();
    //read current recyclerview position
    index = mLayoutManager.findFirstVisibleItemPosition();
    View v = cRecyclerView.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - cRecyclerView.getPaddingTop());
}

@Override
public void onResume()
{
    super.onResume();
    //set recyclerview position
    if(index != -1)
    {
        mLayoutManager.scrollToPositionWithOffset( index, top);
    }
}
farhad.kargaran
quelle
Behalten Sie auch den Versatz des Artikels bei. Nett. Vielen Dank!
bis
14

Sie müssen den Status nicht mehr selbst speichern und wiederherstellen. Wenn Sie eine eindeutige ID in XML festlegen und recyclerView.setSaveEnabled (true) (standardmäßig true), wird dies vom System automatisch ausgeführt. Hier ist mehr dazu: http://trickyandroid.com/saving-android-view-state-correctly/

AppiDevo
quelle
13

Alle in der Support-Bibliothek enthaltenen Layout-Manager wissen bereits, wie die Bildlaufposition gespeichert und wiederhergestellt wird.

Die Bildlaufposition wird beim ersten Layoutdurchlauf wiederhergestellt, wenn die folgenden Bedingungen erfüllt sind:

  • Ein Layout-Manager ist an die angehängt RecyclerView
  • Ein Adapter ist an die angeschlossen RecyclerView

In der Regel legen Sie den Layout-Manager in XML fest, sodass Sie jetzt nur noch etwas tun müssen

  1. Laden Sie den Adapter mit Daten
  2. Dann befestigen Sie den Adapter an dieRecyclerView

Sie können dies jederzeit tun, es ist nicht auf Fragment.onCreateViewoder beschränktActivity.onCreate .

Beispiel: Stellen Sie sicher, dass der Adapter bei jeder Aktualisierung der Daten angeschlossen ist.

viewModel.liveData.observe(this) {
    // Load adapter.
    adapter.data = it

    if (list.adapter != adapter) {
        // Only set the adapter if we didn't do it already.
        list.adapter = adapter
    }
}

Die gespeicherte Bildlaufposition wird aus folgenden Daten berechnet:

  • Adapterposition des ersten Elements auf dem Bildschirm
  • Pixelversatz vom oberen Rand des Elements vom oberen Rand der Liste (für vertikales Layout)

Daher können Sie leichte Abweichungen erwarten, z. B. wenn Ihre Artikel im Hoch- und Querformat unterschiedliche Abmessungen haben.


Das RecyclerView/ ScrollView/ was auch immer muss ein haben, android:iddamit sein Zustand gespeichert wird.

Eugen Pechanec
quelle
Das war es für mich. Ich habe den Adapter zunächst eingestellt und dann seine Daten aktualisiert, sofern verfügbar. Wenn Sie, wie oben erläutert, die Daten so lange belassen, bis sie verfügbar sind, wird automatisch zurück zu ihrem ursprünglichen Standort gescrollt.
NeilS
Danke @Eugen. Gibt es eine Dokumentation zum Speichern und Wiederherstellen der Bildlaufposition von LayoutManager?
Adam Hurwitz
@AdamHurwitz Welche Art von Dokumentation würden Sie erwarten? Was ich beschrieben habe, ist ein Implementierungsdetail von LinearLayoutManager. Lesen Sie also den Quellcode.
Eugen Pechanec
1
Hier ist ein Link zu LinearLayoutManager.SavedState: cs.android.com/androidx/platform/frameworks/support/+/…
Eugen Pechanec
Danke, @EugenPechanec. Dies funktionierte bei mir für Gerätedrehungen. Ich verwende auch eine untere Navigationsansicht. Wenn ich zu einer anderen unteren Registerkarte wechsle und zurückkomme, beginnt die Liste erneut von vorne. Irgendwelche Ideen, warum das so ist?
schv09
4

Hier ist mein Ansatz, um sie zu speichern und den Status der Recyclingübersicht in einem Fragment beizubehalten. Fügen Sie zunächst Konfigurationsänderungen in Ihrer übergeordneten Aktivität wie im folgenden Code hinzu.

android:configChanges="orientation|screenSize"

Deklarieren Sie dann in Ihrem Fragment diese Instanzmethoden

private  Bundle mBundleRecyclerViewState;
private Parcelable mListState = null;
private LinearLayoutManager mLinearLayoutManager;

Fügen Sie den folgenden Code in Ihre @ onPause-Methode ein

@Override
public void onPause() {
    super.onPause();
    //This used to store the state of recycler view
    mBundleRecyclerViewState = new Bundle();
    mListState =mTrailersRecyclerView.getLayoutManager().onSaveInstanceState();
    mBundleRecyclerViewState.putParcelable(getResources().getString(R.string.recycler_scroll_position_key), mListState);
}

Fügen Sie die onConfigartionChanged-Methode wie unten hinzu.

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    //When orientation is changed then grid column count is also changed so get every time
    Log.e(TAG, "onConfigurationChanged: " );
    if (mBundleRecyclerViewState != null) {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                mListState = mBundleRecyclerViewState.getParcelable(getResources().getString(R.string.recycler_scroll_position_key));
                mTrailersRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);

            }
        }, 50);
    }
    mTrailersRecyclerView.setLayoutManager(mLinearLayoutManager);
}

Dieser Ansatz wird Ihr Problem beheben. Wenn Sie einen anderen Layout-Manager haben, ersetzen Sie einfach den oberen Layout-Manager.

Pawan Kumar Sharma
quelle
Möglicherweise benötigen Sie keinen Handler, um Daten zu speichern. Lebenszyklusmethoden reichen zum Speichern / Laden aus.
Mahi
@sayaMahi Kannst du es richtig beschreiben? Ich habe auch gesehen, dass onConfigurationChanged verhindert, dass die destroy-Methode aufgerufen wird. Es ist also keine richtige Lösung.
Pawan Kumar Sharma
3

Auf diese Weise stelle ich die RecyclerView-Position mit GridLayoutManager nach der Drehung wieder her, wenn Sie mit AsyncTaskLoader Daten aus dem Internet neu laden müssen.

Erstellen Sie eine globale Variable aus Parcelable und GridLayoutManager sowie eine statische endgültige Zeichenfolge:

private Parcelable savedRecyclerLayoutState;
private GridLayoutManager mGridLayoutManager;
private static final String BUNDLE_RECYCLER_LAYOUT = "recycler_layout";

Speichern Sie den Status von gridLayoutManager in onSaveInstance ()

 @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, 
            mGridLayoutManager.onSaveInstanceState());
        }

In onRestoreInstanceState wiederherstellen

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        //restore recycler view at same position
        if (savedInstanceState != null) {
            savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
        }
    }

Wenn der Loader Daten aus dem Internet abruft, stellen Sie die Position der Recycling-Ansicht in onLoadFinished () wieder her.

if(savedRecyclerLayoutState!=null){
                mGridLayoutManager.onRestoreInstanceState(savedRecyclerLayoutState);
            }

..natürlich müssen Sie gridLayoutManager in onCreate instanziieren. Prost

Landwirt
quelle
3

Ich hatte die gleichen Anforderungen, aber die Lösungen hier haben mich aufgrund der Datenquelle für die recyclerView nicht ganz über die Linie gebracht.

Ich habe den LinearLayoutManager-Status von RecyclerViews in onPause () extrahiert.

private Parcelable state;

Public void onPause() {
    state = mLinearLayoutManager.onSaveInstanceState();
}

Das Paket statewird gespeichert onSaveInstanceState(Bundle outState)und erneut extrahiert onCreate(Bundle savedInstanceState), wennsavedInstanceState != null .

Der recyclerView-Adapter wird jedoch von einem ViewModel LiveData-Objekt gefüllt und aktualisiert, das von einem DAO-Aufruf an eine Room-Datenbank zurückgegeben wird.

mViewModel.getAllDataToDisplay(mSelectedData).observe(this, new Observer<List<String>>() {
    @Override
    public void onChanged(@Nullable final List<String> displayStrings) {
        mAdapter.swapData(displayStrings);
        mLinearLayoutManager.onRestoreInstanceState(state);
    }
});

Ich fand schließlich heraus, dass das Wiederherstellen des Instanzstatus direkt nach dem Festlegen der Daten im Adapter den Bildlaufstatus über Rotationen hinweg beibehalten würde.

Ich vermute, das liegt auch daran, dass die LinearLayoutManager dass der wiederhergestellte Status überschrieben wurde, als die Daten vom Datenbankaufruf zurückgegeben wurden, oder dass der wiederhergestellte Status für einen 'leeren' LinearLayoutManager bedeutungslos war.

Wenn die Adapterdaten direkt verfügbar sind (dh bei einem Datenbankaufruf nicht relevant sind), kann der Instanzstatus im LinearLayoutManager wiederhergestellt werden, nachdem der Adapter in der recyclerView festgelegt wurde.

Die Unterscheidung zwischen den beiden Szenarien hat mich ewig aufgehalten.

MikeP
quelle
3

Auf der Android-API-Ebene 28 stelle ich einfach sicher, dass ich meine LinearLayoutManagerund RecyclerView.Adaptermeine Fragment#onCreateViewMethode sowie alles, was gerade funktioniert hat, eingerichtet habe. Ich brauche nicht zu tun onSaveInstanceStateoder zu onRestoreInstanceStatearbeiten.

Die Antwort von Eugen Pechanec erklärt, warum dies funktioniert.

Heidegrenzen
quelle
2

Ab Version 1.2.0-alpha02 der androidx recyclerView-Bibliothek wird diese nun automatisch verwaltet. Fügen Sie es einfach hinzu mit:

implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"

Und benutze:

adapter.stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY

Die Aufzählung StateRestorationPolicy bietet drei Optionen:

  • ALLOW - Der Standardstatus, der den RecyclerView-Status im nächsten Layoutdurchlauf sofort wiederherstellt
  • PREVENT_WHEN_EMPTY - Stellt den RecyclerView-Status nur wieder her, wenn der Adapter nicht leer ist (adapter.getItemCount ()> 0). Wenn Ihre Daten asynchron geladen werden, wartet RecyclerView, bis Daten geladen sind, und erst dann wird der Status wiederhergestellt. Wenn Sie Standardelemente wie Header oder Ladefortschrittsanzeigen als Teil Ihres Adapters haben, sollten Sie die Option PREVENT verwenden, es sei denn, die Standardelemente werden mit MergeAdapter hinzugefügt . MergeAdapter wartet darauf, dass alle Adapter bereit sind, und stellt erst dann den Status wieder her.
  • PREVENT - Die Wiederherstellung des gesamten Status wird verschoben, bis Sie ALLOW oder PREVENT_WHEN_EMPTY festlegen.

Beachten Sie, dass sich die recyclerView-Bibliothek zum Zeitpunkt dieser Antwort noch in alpha03 befindet, die alpha-Phase jedoch nicht für Produktionszwecke geeignet ist .

Rubén Viguera
quelle
So viel Überarbeitung gespart. Andernfalls mussten wir den Listenklickindex speichern, Elemente wiederherstellen, das Neuzeichnen in onCreateView verhindern, scrollToPosition anwenden (oder andere Antworten verwenden), und dennoch konnte der Benutzer den Unterschied nach seiner Rückkehr
feststellen
Auch wenn ich die RecyclerView-Position nach der Navigation durch die App speichern möchte ... gibt es ein Tool dafür?
StayCool
@RubenViguera Warum ist Alpha nicht für Produktionszwecke gedacht? Ich meine, ist das wirklich schlimm?
StayCool
1

Für mich bestand das Problem darin, dass ich jedes Mal, wenn ich meinen Adapter wechselte, einen neuen Layoutmanager einrichtete und die Position des Bildlaufs in recyclerView verlor.

Ronaldo Albertini
quelle
1

Ich möchte nur die jüngste Situation mit RecyclerView teilen. Ich hoffe, dass jeder, der das gleiche Problem hat, davon profitieren wird.

Meine Projektanforderung: Ich habe also eine RecyclerView, in der einige anklickbare Elemente in meiner Hauptaktivität (Aktivität A) aufgelistet sind. Wenn Sie auf den Artikel klicken, wird eine neue Aktivität mit den Artikeldetails (Aktivität-B) angezeigt.

Ich habe im Manifest implementiert Datei dass Aktivität-A das übergeordnete Element von Aktivität-B ist. Auf diese Weise habe ich eine Zurück- oder Startschaltfläche in der Aktionsleiste von Aktivität-B

Problem: Jedes Mal, wenn ich die Zurück- oder Home-Taste in der Aktionsleiste von Aktivität B gedrückt habe, wechselt Aktivität A ab onCreate () in den vollständigen Aktivitätslebenszyklus.

Obwohl ich ein onSaveInstanceState () implementiert habe, das die Liste des RecyclerView-Adapters speichert, ist saveInstanceState in der onCreate () -Methode immer null, wenn Activity-A seinen Lebenszyklus startet.

Beim weiteren Durchsuchen des Internets stieß ich auf das gleiche Problem, aber die Person bemerkte, dass die Schaltfläche Zurück oder Startseite unter dem Anroid-Gerät (Standard-Schaltfläche Zurück / Startseite), Aktivität-A, nicht in den Aktivitätslebenszyklus eingeht.

Lösung:

  1. Ich habe das Tag für übergeordnete Aktivität im Manifest für Aktivität B entfernt
  2. Ich habe die Home- oder Back-Taste in Aktivität B aktiviert

    Fügen Sie unter der Methode onCreate () diese Zeile hinzu. SupportActionBar? .SetDisplayHomeAsUpEnabled (true)

  3. Bei der Methode overide fun onOptionsItemSelected () habe ich nach dem Element gesucht. Element-ID, auf die das Element basierend auf der ID geklickt wird. Die ID für die Zurück-Schaltfläche lautet

    android.R.id.home

    Implementieren Sie dann einen Funktionsaufruf finish () im android.R.id.home

    Dies beendet die Aktivität B und bringt Aktivität A, ohne den gesamten Lebenszyklus zu durchlaufen.

Für meine Anforderung ist dies die bisher beste Lösung.

     override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_show_project)

    supportActionBar?.title = projectName
    supportActionBar?.setDisplayHomeAsUpEnabled(true)

}


 override fun onOptionsItemSelected(item: MenuItem?): Boolean {

    when(item?.itemId){

        android.R.id.home -> {
            finish()
        }
    }

    return super.onOptionsItemSelected(item)
}
Andy Parinas
quelle
1

Ich hatte das gleiche Problem mit einem kleinen Unterschied, ich wurde mit Databinding , und ich war recyclerView Adapter und Layoutmanager in den Bindeverfahren setzen.

Das Problem war , dass die Daten-Bindung wurde nach geschieht onResume so war es mein Layout nach onResume Zurücksetzen und das bedeutet , es wurde jedes Mal zurück zum ursprünglichen Platz zu scrollen. Wenn ich die Einstellung von layoutManager in den Datenbindungsadaptermethoden beendet habe, funktioniert dies wieder einwandfrei.

Amin Keshavarzian
quelle
Wie haben Sie es erreicht?
Kedar Sukerkar
0

In meinem Fall habe ich die RecyclerViews layoutManagersowohl in XML als auch in festgelegt onViewCreated. Das Entfernen der Zuordnung wurde onViewCreatedbehoben.

with(_binding.list) {
//  layoutManager =  LinearLayoutManager(context)
    adapter = MyAdapter().apply {
        listViewModel.data.observe(viewLifecycleOwner,
            Observer {
                it?.let { setItems(it) }
            })
    }
}
Maxbeaudoin
quelle
-1

Activity.java:

public RecyclerView.LayoutManager mLayoutManager;

Parcelable state;

    mLayoutManager = new LinearLayoutManager(this);


// Inside `onCreate()` lifecycle method, put the below code :

    if(state != null) {
    mLayoutManager.onRestoreInstanceState(state);

    } 

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

        if (state != null) {
            mLayoutManager.onRestoreInstanceState(state);
        }
    }   

   @Override
    protected void onPause() {
        super.onPause();

        state = mLayoutManager.onSaveInstanceState();

    }   

Warum ich OnSaveInstanceState()in onPause()Mitteln verwende, während der Wechsel zu einer anderen Aktivität onPause aufgerufen wird. Diese Bildlaufposition wird gespeichert und die Position wiederhergestellt, wenn wir von einer anderen Aktivität zurückkehren.

Steve
quelle
2
Das öffentliche statische Feld ist nicht gut. Globale Daten und Singletons sind nicht gut.
Weston
@weston Ich habe es jetzt verstanden. Ich habe den Speicherstatus in "Pause" geändert, um "Global" zu entfernen.
Steve