android - RecyclerView 在设备方向更改时保存位置

标签 android android-fragments savestate android-recyclerview

在我的应用程序中,我有一个与适配器连接的 RecyclerView,但每次我的设备从横向变为纵向或其他方式时, View 都会重新加载,我会再次回到顶部。我想将我的滚动位置保存到我看到的最后一个项目

这是我尝试过的:

我有一个从另一个 fragment 延伸的 fragment ,状态应该保存在 MainPageFragment 中。我试图保存最后加载的位置并将其放入 savedState,它被正确检索但是如果我调用“scrollToPosition”没有任何反应。

package com.pr0.pr0grammreloaded.fragment;

/**
 * Created by Dominik on 22.02.2015.
 */
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.pr0.pr0grammreloaded.MainActivity;
import com.pr0.pr0grammreloaded.R;
import com.pr0.pr0grammreloaded.config.FilterConfig;
import com.pr0.pr0grammreloaded.util.FixedRecyclerView;
import com.pr0.pr0grammreloaded.util.MainPageSaveParcelable;
import com.pr0.pr0grammreloaded.util.MyLayoutManager;
import com.pr0.pr0grammreloaded.util.RecyclerViewDelegate;

import java.util.ArrayList;

import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout;
import uk.co.senab.actionbarpulltorefresh.library.ActionBarPullToRefresh;
import uk.co.senab.actionbarpulltorefresh.library.listeners.OnRefreshListener;
import uk.co.senab.actionbarpulltorefresh.library.viewdelegates.AbsListViewDelegate;

/**
 * Created by Dominik on 17.01.2015.
 */
public class MainPageFragment extends Pr0MainPageFragment {


    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static MainPageFragment newInstance(FilterConfig config) {
        MainPageFragment fragment = new MainPageFragment();
        fragment.config = config;
        return fragment;
    }

    public MainPageFragment() {

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        if(savedInstanceState != null){
            MainPageSaveParcelable saveParcelable = savedInstanceState.getParcelable("save");
            View rootView = inflater.inflate(R.layout.mainpage_fragment, container, false);

            recyclerView = (RecyclerView) rootView.findViewById(R.id.list);
            adapter = new ItemAdapter();
            itemList = saveParcelable.itemList;
            config = saveParcelable.config;
            // use better layout manager, maybe write our own?
            recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.getActivity(), 3));
            recyclerView.setAdapter(adapter);
            restorePositon = saveParcelable.position;

            mPullToRefreshLayout = (PullToRefreshLayout) rootView.findViewById(R.id.ptr_layout);

            // Now setup the PullToRefreshLayout
            ActionBarPullToRefresh.from(getActivity())
                    // Mark All Children as pullable
                    .allChildrenArePullable()
                            // Set a OnRefreshListener
                    .listener(new OnRefreshListener() {
                        @Override
                        public void onRefreshStarted(View view) {
                            if (!isBlocked()) {
                                FragmentManager fragmentManager = getFragmentManager();
                                fragmentManager.beginTransaction()
                                        .replace(R.id.container, MainPageFragment.newInstance(MainActivity.filterConfig))
                                        .commit();
                            }
                        }


                    })
                            // Finally commit the setup to our PullToRefreshLayout
                    .useViewDelegate(FixedRecyclerView.class, new RecyclerViewDelegate())
                    .setup(mPullToRefreshLayout);

            loadFeed();
//            recyclerView.scrollToPosition(saveParcelable.);
            return rootView;
        }else {
            View rootView = inflater.inflate(R.layout.mainpage_fragment, container, false);

            recyclerView = (RecyclerView) rootView.findViewById(R.id.list);

            adapter = new ItemAdapter();
            itemList = new ArrayList<>();
            // use better layout manager, maybe write our own?
            recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.getActivity(), 3));
            recyclerView.setAdapter(adapter);

            mPullToRefreshLayout = (PullToRefreshLayout) rootView.findViewById(R.id.ptr_layout);

            // Now setup the PullToRefreshLayout
            ActionBarPullToRefresh.from(getActivity())
                    // Mark All Children as pullable
                    .allChildrenArePullable()
                            // Set a OnRefreshListener
                    .listener(new OnRefreshListener() {
                        @Override
                        public void onRefreshStarted(View view) {
                            if (!isBlocked()) {
                                FragmentManager fragmentManager = getFragmentManager();
                                fragmentManager.beginTransaction()
                                        .replace(R.id.container, MainPageFragment.newInstance(MainActivity.filterConfig))
                                        .commit();
                            }
                        }


                    })
                            // Finally commit the setup to our PullToRefreshLayout
                    .useViewDelegate(FixedRecyclerView.class, new RecyclerViewDelegate())
                    .setup(mPullToRefreshLayout);

            loadFeed();
            return rootView;
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
    }

    @Override
    public void onSaveInstanceState(Bundle state){
        super.onSaveInstanceState(state);
        restorePositon = lastPositon;
        MainPageSaveParcelable saveParcelable = new MainPageSaveParcelable(itemList,firstChunk,config,restorePositon);
        state.putParcelable("save",saveParcelable);
    }


}

Pr0MainPageFragment

package com.pr0.pr0grammreloaded.fragment;

import android.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.pr0.pr0grammreloaded.MainActivity;
import com.pr0.pr0grammreloaded.R;
import com.pr0.pr0grammreloaded.api.Api;
import com.pr0.pr0grammreloaded.api.Feed;
import com.pr0.pr0grammreloaded.api.InstantDeserializer;
import com.pr0.pr0grammreloaded.config.FilterConfig;
import com.squareup.picasso.Picasso;

import org.joda.time.Instant;

import java.util.ArrayList;
import java.util.List;

import retrofit.RestAdapter;
import retrofit.converter.GsonConverter;
import rx.functions.Action1;
import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshLayout;

import static rx.android.observables.AndroidObservable.bindActivity;

/**
 * Created by Dominik on 22.02.2015.
 */
public class Pr0MainPageFragment extends Fragment {

    protected ItemAdapter adapter;
    protected boolean firstChunk = true;
    protected List<Feed.Item> itemList;
    protected FilterConfig config;
    private boolean blockLoading = false;
    protected PullToRefreshLayout mPullToRefreshLayout;
    protected int restorePositon = 0;
    protected int lastPositon = 0;
    protected RecyclerView recyclerView;
    protected boolean isRestore = false;

    /**
     * Loads the feed from pr0gramm. This should be put into some kind of service
     * that is injected into our activities.
     */
    protected void loadFeed() {
        if(!blockLoading) {
            blockLoading = true;
            Log.e("Pr0","Blocked loading!");
            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(Instant.class, new InstantDeserializer())
                    .create();

            Api api = new RestAdapter.Builder()
                    .setEndpoint("http://pr0gramm.com")
                    .setConverter(new GsonConverter(gson))
                    .setLogLevel(RestAdapter.LogLevel.BASIC)
                    .build()
                    .create(Api.class);

            // perform api request in the background and call
            // back to the main thread on finish

            if (firstChunk) {
                bindActivity(MainActivity.getActivity(), api.itemsGet(config.getFlag(), 1)).subscribe(new Action1<Feed>() {
                    @Override
                    public void call(Feed feed) {
                        // we are now back in the main thread
                        firstChunk = false;
                        handleFeedResponse(feed);
                    }
                });
            } else {
                //Log.e("Pr0", "Loading after ID : " + itemList.get(0).getId());
                for(int x = 0; x < itemList.size();x ++){
                    Log.e("Pr0", "POS: " + x + ", ID : " + itemList.get(x).getId());
                }
                bindActivity(MainActivity.getActivity(), api.olderGet(itemList.get(itemList.size() - 1).getPromoted(), config.getFlag(), 1)).subscribe(new Action1<Feed>() {
                    @Override
                    public void call(Feed feed) {
                        // we are now back in the main thread
                        handleFeedResponse(feed);
                    }
                });
            }
        }
    }

    /**
     * Display the elements from the feed
     *
     * @param feed The feed to display
     */
    private void handleFeedResponse(Feed feed) {
        // display feed now.
        //Log.i("MainActivity", "Number of items: " + feed.getItems().size());
        adapter.addItems(feed.getItems());
        mPullToRefreshLayout.setRefreshComplete();
        restorePostion();
    }


    protected class ItemAdapter extends RecyclerView.Adapter<ItemView> {


        ItemAdapter() {
            setHasStableIds(true);
        }

        @Override
        public ItemView onCreateViewHolder(ViewGroup viewGroup, int i) {
            LayoutInflater inflater = LayoutInflater.from(MainActivity.getActivity());
            View view = inflater.inflate(R.layout.item_view, viewGroup, false);
            return new ItemView(view);
        }

        @Override
        public void onBindViewHolder(ItemView itemView, int i) {
           // Log.e("Pr0","load id : " + i);
            //Log.e("Pr0",String.valueOf(itemList.get(i).getId()));
            String url = "http://thumb.pr0gramm.com/" + itemList.get(i).getThumb();
            Picasso.with(getActivity())
                    .setIndicatorsEnabled(true);
            Picasso.with(getActivity())
                    .load(url)
                    .into(itemView.image);
            lastPositon = i;
            Log.w("Pr0","last positon : " + i);
            if(i > itemList.size() - 5){
                //Log.e("Pr0","SIZE : " + itemList.size());
                //Log.e("Pr0","End Reached Load After ID : " + itemList.get(0).getId());
                loadFeed();
            }
        }

        @Override
        public int getItemCount() {
            return itemList.size();
        }

        public void addItems(List<Feed.Item> itemsToAdd) {

            int oldCount = itemList.size();
            itemList.addAll(itemsToAdd);
            notifyItemRangeInserted(oldCount, itemList.size());
           /*
            for(Feed.Item item : itemsToAdd) {
                //Log.e("Pr0","Added image ID : " + item.getId());
                int oldCount = itemList.size();
                itemList.add(item);
                notifyItemRangeInserted(oldCount, itemList.size());
            }
            */
            blockLoading = false;
            Log.e("Pr0","Unblocked Loading");
            //
        }

        @Override
        public long getItemId(int position) {
            return itemList.get(position).getId();
        }
    }

    /**
     * View holder for a view in the list of items
     */
    private class ItemView extends RecyclerView.ViewHolder {
        final ImageView image;

        public ItemView(View itemView) {
            super(itemView);
            image = (ImageView) itemView.findViewById(R.id.image);
        }
    }

    public boolean isBlocked(){
        return this.blockLoading;
    }

    public void reset(){
        itemList.clear();
        firstChunk = true;
    }

    public void restorePostion(){
        Log.w("Pr0", "Restore : " + restorePositon);
        recyclerView.scrollToPosition(restorePositon);
    }
}

这是我的包裹

package com.pr0.pr0grammreloaded.util;

import android.os.Parcel;
import android.os.Parcelable;

import com.pr0.pr0grammreloaded.account.User;
import com.pr0.pr0grammreloaded.api.Feed;
import com.pr0.pr0grammreloaded.config.FilterConfig;

import java.util.List;

/**
 * Created by Dominik on 27.02.2015.
 */
public class MainPageSaveParcelable implements Parcelable{

    public boolean firstChunk;
    public List<Feed.Item> itemList;
    public FilterConfig config;
    public int position;

    public MainPageSaveParcelable(List<Feed.Item> itemList, boolean firstChunk, FilterConfig config, int position){
        this.firstChunk = firstChunk;
        this.itemList = itemList;
        this.config = config;
        this.position = position;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeList(itemList);
        dest.writeValue(config);
        dest.writeValue(firstChunk);
        dest.writeInt(position);
    }

    /** Static field used to regenerate object, individually or as arrays */
    public static final Parcelable.Creator<MainPageSaveParcelable> CREATOR = new Parcelable.Creator<MainPageSaveParcelable>() {
        public MainPageSaveParcelable createFromParcel(Parcel pc) {
            return new MainPageSaveParcelable(pc);
        }
        public MainPageSaveParcelable[] newArray(int size) {
            return new MainPageSaveParcelable[size];
        }
    };

/**Ctor from Parcel, reads back fields IN THE ORDER they were written */
    public MainPageSaveParcelable(Parcel pc){
        pc.readList(itemList,List.class.getClassLoader());
        config        =  (FilterConfig) pc.readValue(FilterConfig.class.getClassLoader());
        firstChunk      = (boolean) pc.readValue(Boolean.class.getClassLoader());
        position = pc.readInt();
    }
}

最佳答案

您可以使用滚动监听器并在 SharedPreference 或任何您喜欢的滚动时保存第一个项目或 RecyclerView 状态

在您的 Activity 或 fragment 中将这些变量定义为私有(private)变量

    int firstCompleteVisibleItemPosition;
            int firstVisibleItemPosition;
            Parcelable recyclerViewState;

在滚动监听器上使用 recyclerview

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

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);


                firstCompleteVisibleItemPosition = recyclerView.getLayoutManager().findFirstCompletelyVisibleItemPosition();
                firstVisibleItemPosition = recyclerView.getLayoutManager().findFirstVisibleItemPosition();
                recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();


        }
    });

如果方向发生变化,您可以通过此方法保存的最后位置开始您的回收 View

linearLayoutManager.scrollToPositionWithOffset(firstVisibleItemPosition, 0);

或者通过recyclerview的最后状态

rv.getLayoutManager().onRestoreInstanceState(recyclerViewState);

关于android - RecyclerView 在设备方向更改时保存位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28775860/

相关文章:

java - 无法在android中保存实例状态

jQuery Datatable,保存表的状态,如分页、搜索等

Android Facebook 使用新的 Firebase 登录 : App gets redirected to the login screen even after allowing permission

android - 从android marketplace查询一个应用的价格

android - 我的 Android 应用程序的 fragment 或 Activity

android - 如何实现saveFragmentInstanceState?

java - Android 中指向相同对象或数据的 ArrayList

java - 如何使用 eclipse 在云上进行开发?

android - 在某些 fragment 中隐藏抽屉导航

android - UI元素是在Fragment还是FragmentActivity中发起的?