草庐IT

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

coder 2023-12-04 原文

在我的应用程序中,我有一个与适配器连接的 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/

有关android - RecyclerView 在设备方向更改时保存位置的更多相关文章

  1. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby - Capistrano 3 在任务中更改 ssh_options - 2

    我尝试使用不同的ssh_options在同一阶段运行capistranov.3任务。我的production.rb说:set:stage,:productionset:user,'deploy'set:ssh_options,{user:'deploy'}通过此配置,capistrano与用户deploy连接,这对于其余的任务是正确的。但是我需要将它连接到服务器中配置良好的an_other_user以完成一项特定任务。然后我的食谱说:...taskswithoriginaluser...task:my_task_with_an_other_userdoset:user,'an_othe

  4. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  5. ruby - 更改 ActiveRecord 中对象的类 - 2

    假设我有一个FireNinja我的数据库中的对象,使用单表继承存储。后来才知道他真的是WaterNinja.将他更改为不同的子类的最干净的方法是什么?更好的是,我很想创建一个新的WaterNinja对象并替换旧的FireNinja在数据库中,保留ID。编辑我知道如何创建新的WaterNinja来self现有FireNinja的对象,我也知道我可以删除旧的并保存新的。我想做的是改变现有项目的类别。我是通过创建一个新对象并执行一些ActiveRecord魔法来替换行,还是通过对对象本身做一些疯狂的事情,或者甚至通过删除它并使用相同的ID重新插入来做到这一点,这是问题的一部分。

  6. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  7. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  8. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  9. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  10. ruby-on-rails - 有没有一种工具可以在编码时自动保存对文件的增量更改? - 2

    我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功

随机推荐