簡體   English   中英

從其他活動返回到同一片段時,如何保存和恢復片段中 RecyclerView 的滾動位置?

[英]How to save and restore scrolling position of the RecyclerView in a fragment when coming back from other activity to the same fragment?

對版主的要求:這不是一個重復的問題,請閱讀以下信息。

在問這個問題之前,我已經嘗試了幾乎所有可用的解決方案,但沒有一個對我有用,有些導致崩潰,有些根本不起作用(我是初學者,可能我已經在我的代碼中做錯了什么,我不會責怪任何人在 SO 上可用的答案在我的情況下不起作用)。 下面是我的代碼,請看:

fragment_tab1.xml(包含recyclerview):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:background="#333333"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="96dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="42dp"
            android:background="@color/colorPrimaryDark"

            android:id="@+id/sort1"
            android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/sort"
            android:layout_marginLeft="10dp"
            android:fontFamily="@font/quicksand"
            android:textStyle="bold"
            android:text="Sort by:"
            android:textColor="#ffffff"/>

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                style="@style/Widget.AppCompat.Button.Colored"
                android:text="Oldest first"/>
            <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
                style="@style/Widget.AppCompat.Button.Colored"
            android:text="Newest first"/>
        </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_below="@id/sort1"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:padding="5dp" />

    </RelativeLayout>

</LinearLayout>

片段表1.java

public class tab1  extends Fragment {
    RecyclerView mRecyclerView;
    FirebaseDatabase mFirebaseDatabase;
    DatabaseReference mRef;
    LinearLayoutManager manager;
    private static final String TAG = "tab1";
    ProgressDialog progressDialog;
    View rootView;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        //Returning the layout file after inflating
        //Change R.layout.tab1 in you classes
         rootView = inflater.inflate(R.layout.fragment_tab1, container, false);


return rootView;

    }


    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);



        manager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(manager);
        mRecyclerView.setItemViewCacheSize(20);
        mRecyclerView.setDrawingCacheEnabled(true);
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mRef = mFirebaseDatabase.getReference("memes");

        mFirebaseDatabase.getInstance().getReference().keepSynced(true);

        progressDialog = new ProgressDialog(getActivity());
        progressDialog.setMessage("Please Wait");
        progressDialog.show();

    }

    public void onStart() {

        super.onStart();


        FirebaseRecyclerAdapter<Model, ViewHolder> firebaseRecyclerAdapter =
                new FirebaseRecyclerAdapter<Model, ViewHolder>(
                        Model.class,
                        R.layout.row2,
                        ViewHolder.class,
                        mRef

                ) {



                    @Override
                    protected void populateViewHolder(ViewHolder viewHolder, Model model, int position) {
                        viewHolder.setDetails(getActivity(), model.getTitle(), model.getDesc(), model.getImage());
                        progressDialog.cancel();
                    }



                    @Override
                    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

                        ViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);

                        viewHolder.setOnClickListener(new ViewHolder.ClickListener(){




                            @Override
                            public void onItemClick(View view, int position){



                                //get data from firebase here
                                String mTitle = getItem(position).getTitle();
                                String mDesc= getItem(position).getDesc();
                                String mImage = getItem(position).getImage();

                                //pass this data to new activity
                                Intent intent = new Intent(view.getContext(), ArticleDetail.class);
                                intent.putExtra("title", mTitle);// put title
                                intent.putExtra("desc", mDesc);// put description
                                intent.putExtra("image", mImage); //put image urls

                                startActivity(intent);
                            }




                            @Override
                            public void onItemLongClick(View view, int position){

                                //TODO your own implementation on long item click


                            }

                        });

                        return viewHolder;
                    }
                };


        mRecyclerView.setAdapter(firebaseRecyclerAdapter);

    }

    @Override
    public void  onPause() {
        super.onPause();
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());

        View firstChild = mRecyclerView.getChildAt(0);
        int firstVisiblePosition = mRecyclerView.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();
    }


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

    }
    @Override
    public void onSaveInstanceState(Bundle outState) {


        super.onSaveInstanceState(outState);
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {


        super.onViewStateRestored(savedInstanceState);
    }

}

付出了這么多天,嘗試了很多解決方案,終於找到了解決我問題的可行方案,感謝https://stackoverflow.com/a/42563804/2128364

我已將工作解決方案移至另一種方法以使其在片段中工作。

也許這可以挽救某人的一天,這是它的工作原理:

  1. onConfigurationChanged(如原始解決方案中所建議)、onSaveInstanceState 或 onViewRestoredState 方法不起作用,因此您需要 onPause 和 onResume 方法分別保存和恢復狀態。
  2. 您必須在 onPause 方法中保存狀態:

     mBundleRecyclerViewState = new Bundle(); mListState = mRecyclerView.getLayoutManager().onSaveInstanceState(); mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
  3. 然后在 onResume 方法中恢復它:

     if (mBundleRecyclerViewState != null) { new Handler().postDelayed(new Runnable() { @Override public void run() { mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE); mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState); } }, 50); } mRecyclerView.setLayoutManager(staggeredGridLayoutManager);

如果您還沒有,請在build.gradle添加recyclerView依賴build.gradle

implementation "androidx.recyclerview:recyclerview:1.2.0"

不僅僅是在初始化列表適配器后設置stateRestorationPolicy

 listAdapter = ListAdapter()

 // Set scroll restore policy
 listdapter.stateRestorationPolicy =
      RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY

有關stateRestorationPolicy和不同類型政策的更多信息, 請訪問本文

您應該使用此方法返回可見的第一個項目:

int findFirstVisibleItemPosition();

那么你必須在 onPause() 保存這個索引,當用戶返回時檢索並在 onResume() 設置它:

void scrollToPosition (int position)

好的,最后我想我得到了未來編碼人員的答案 將 On Create View 中的所有代碼保留在片段中,如果不嘗試通過在類中聲明所有變量而不是在本地方法中如何在創建視圖中獲取上下文,它將工作最多次數工作

如果有人介意使用靜態對象Bundle mBundleRecyclerViewState接受它的答案,則可以改用ViewModel

public class MainViewModel extends ViewModel {
    private static final String RECYCLER_VIEW_STATE = "recyclerViewState";
    private final Bundle mRecyclerViewState;

    public MainViewModel() {
        mRecyclerViewState = new Bundle();
    }

    /**
     * Back up RecyclerView's state to ViewModel in Fragment's onDestroyView
     * or in Activity's onStop (or in somewhere else appropriate)
     *
     * @param layoutManager RecyclerView.LayoutManager object
     */
    public void saveRecyclerViewState(RecyclerView.LayoutManager layoutManager) {
        mRecyclerViewState.putParcelable(
            RECYCLER_VIEW_STATE, layoutManager.onSaveInstanceState()
        );
    }

    /**
     * Restore backed up RecyclerView's state from ViewModel in onCreate
     *
     * @param layoutManager RecyclerView.LayoutManager object
     */
    public void restoreRecyclerViewState(RecyclerView.LayoutManager layoutManager) {
        layoutManager.onRestoreInstanceState(
            mRecyclerViewState.getParcelable(RECYCLER_VIEW_STATE)
        );
    }
}

使用示例(在 Fragment 中):

private MainViewModel mViewModel;
private GridLayoutManager mLayoutManager;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mViewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);

    mLayoutManager = new GridLayoutManager(requireContext(), ITEMS_PER_ROW);
    mViewModel.restoreRecyclerViewState(mLayoutManager);
}

@Override
public void onDestroyView() {
    mViewModel.saveRecyclerViewState(mLayoutManager);

    super.onDestroyView();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM