Как сохранить позицию прокрутки RecyclerView с помощью RecyclerView.Государство?

У меня есть вопрос о Android RecyclerView.Государство.

Я использую RecyclerView, как я могу использовать и связать его с RecyclerView.Государство?

моя цель -сохранить позицию прокрутки RecyclerView.

10 ответов


как вы планируете сохранить последнюю сохраненную позицию с RecyclerView.State?

вы всегда можете положиться на хорошее состояние сохранения. Продлить RecyclerView и переопределить данные метода onsaveinstance() и 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.getChildCount();
              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];
            }
        };
    }

если вы используете LinearLayoutManager, он поставляется с предварительно построенным api сохранения linearLayoutManagerInstance.данные метода onsaveinstance() и восстановить api linearLayoutManagerInstance.onRestoreInstanceState(...)

С этим вы можете сохранить возвращенный parcelable в свой штат. например,

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

, и восстановить положение с состоянием сохранен. е.г,

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

чтобы завернуть все, ваш окончательный код будет выглядеть что-то как

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

Edit: вы также можете использовать те же API с GridLayoutManager, так как это подкласс LinearLayoutManager. Спасибо @wegsehen за предложение.

Edit: помните, что если вы также загружаете данные в фоновом потоке, вам нужно будет вызвать onRestoreInstanceState в вашем методе onPostExecute/onLoadFinished для восстановления позиции при изменении ориентации, например

@Override
protected void onPostExecute(ArrayList<Movie> movies) {
    mLoadingIndicator.setVisibility(View.INVISIBLE);
    if (movies != null) {
        showMoviePosterDataView();
        mDataAdapter.setMovies(movies);
      mRecyclerView.getLayoutManager().onRestoreInstanceState(mSavedRecyclerLayoutState);
        } else {
            showErrorMessage();
        }
    }

магазине

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

восстановить

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

и если это не сработает, попробуй

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

ставлю магазину в onPause() и восстановить в onResume()


Я устанавливаю переменные в onCreate (), сохраняю позицию прокрутки в onPause () и устанавливаю позицию прокрутки в 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);
    }
}

Я хотел сохранить положение прокрутки Recycler View при навигации от моей активности списка, а затем нажав кнопку "назад", чтобы вернуться. Многие из решений, предусмотренных для этой проблемы, были либо намного сложнее, чем нужно, либо не работали для моей конфигурации, поэтому я решил поделиться своим решением.

сначала сохраните состояние экземпляра в onPause, как показали многие. Я думаю, что здесь стоит подчеркнуть, что эта версия onSaveInstanceState является методом из RecyclerView.Класс LayoutManager выполняет.

private LinearLayoutManager mLayoutManager;
Parcelable state;

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

ключ к правильной работе - убедиться, что вы вызываете onRestoreInstanceState после подключения адаптера, как указано в других потоках. Однако фактический вызов метода намного проще, чем многие указали.

private void someMethod() {
    mVenueRecyclerView.setAdapter(mVenueAdapter);
    mLayoutManager.onRestoreInstanceState(state);
}

вам не нужно сохранять и восстанавливать состояние самостоятельно. Если вы установили уникальный идентификатор в xml и recyclerView.setSaveEnabled (true) (true по умолчанию) система сделает это автоматически. Вот еще об этом:http://trickyandroid.com/saving-android-view-state-correctly/


вот как я восстанавливаю позицию RecyclerView с GridLayoutManager после вращения, когда вам нужно перезагрузить данные из интернета с помощью AsyncTaskLoader.

сделайте глобальную переменную Parcelable и GridLayoutManager и статическую конечную строку:

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

сохранить состояние gridLayoutManager в onSaveInstance ()

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

восстановить в onRestoreInstanceState

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

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

затем, когда загрузчик извлекает данные из интернета, вы восстанавливаете recyclerview положение в onLoadFinished()

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

..конечно, вам нужно создать экземпляр gridLayoutManager внутри onCreate. Ура!--5-->


для меня проблема заключалась в том, что я настраивал новый layoutmanager каждый раз, когда я менял свой адаптер, теряя позицию прокрутки que на recyclerView.


вот мой подход, чтобы сохранить их и поддерживать состояние recyclerview во фрагменте. Во-первых, добавьте изменения конфигурации в родительское действие, как показано ниже кода.

android:configChanges="orientation|screenSize"

затем в вашем фрагменте объявите этот метод экземпляра

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

добавить ниже код в метод @onPause

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

добавить метод onConfigartionChanged, как показано ниже.

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

это approch будет reslove ваша проблема. если у вас есть diffrent layout manager, то просто замените верхний менеджер компоновки.


активности.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();

    }   

почему я использую OnSaveInstanceState() на onPause() означает, что при переключении на другую активность будет вызываться onPause.Он сохранит эту позицию прокрутки и восстановит позицию, когда мы вернемся из другой деятельности.