如何保留自定义对象列表的回收视图的状态?

How to retain the state of a recyclerview of list of custom objects?

主要目标:-

我有一份体育新闻列表。每个项目都包含一个运动名称和一些信息。单击它会显示有关该特定运动的最新消息。用户可以选择滑动以关闭新闻,如果他们不希望它出现在列表中,或者他们也可以拖放它,例如,如果他们想在其他新闻之上看到一些新闻。 列表中的每个项目都以编程方式表示为 Sport.java 对象。

我想在设备方向改变时保留列表的状态。

我试过的:-

对于列表,我有一个运动对象数组列表 (ArrayList)。我了解到要保存自定义对象列表,对象本身需要 Parcelable。为此,我实现了 Parcelable.java 接口,如下所示:

package com.example.android.materialme;

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

import androidx.annotation.NonNull;

/**
 * Data model for each row of the RecyclerView.
 */
class Sport implements Parcelable {

    //Member variables representing the title and information about the sport
    private String title;
    private String info;
    private String detail;
    private final int imageResource;

    /**
     * Constructor for the Sport data model
     * @param title The name if the sport.
     * @param info Information about the sport.
     */
    Sport(String title, String info, String detail, int imageResource) {
        this.title = title;
        this.info = info;
        this.detail = detail;
        this.imageResource = imageResource;
    }

    protected Sport(@NonNull Parcel in) {
        title = in.readString();
        info = in.readString();
        detail = in.readString();
        imageResource = in.readInt();
    }

    public static final Creator<Sport> CREATOR = new Creator<Sport>() {
        @Override
        public Sport createFromParcel(Parcel in) {
            return new Sport(in);
        }

        @Override
        public Sport[] newArray(int size) {
            return new Sport[size];
        }
    };

    String getTitle() {
        return title;
    }
  
    String getInfo() {
        return info;
    }

    int getImageResource(){
        return imageResource;
    }

    String getDetail(){
        return detail;
    }

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(title);
        parcel.writeString(info);
        parcel.writeString(detail);
        parcel.writeInt(imageResource);
    }
}

然后我用了

outState.putParcelableArrayList(KEY, sportsList);

但是,这不起作用。旋转设备时屏幕只是空白。

我尝试调试该应用程序,发现 arraylist 已正确传递且数据完好无损,只是该应用程序由于某种原因无法显示它。

此外,fab 按钮的实现方式是在单击时将整个列表重置为初始状态。 fab 正常工作,但如果方向改变一次,它就会停止工作(应用程序不会崩溃)。将方向改回也不能修复晶圆厂。因此,要再次获取任何其他测试的列表,我必须重新运行整个应用程序。

完整代码:-

MainActivity.java

package com.example.android.materialme;

import android.content.res.TypedArray;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.ArrayList;
import java.util.Collections;

public class MainActivity extends AppCompatActivity {

    //Member variables
    private RecyclerView mRecyclerView;
    private ArrayList<Sport> mSportsData;
    private SportsAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(view -> resetSports());

        //Initialize the RecyclerView
        mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);

        //Set the Layout Manager
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        //Initialize the ArrayLIst that will contain the data
        mSportsData = new ArrayList<>();

        //Initialize the adapter and set it ot the RecyclerView
        mAdapter = new SportsAdapter(this, mSportsData);
        mRecyclerView.setAdapter(mAdapter);

        initializeData(savedInstanceState);

        ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
                ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN,
                ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                int from = viewHolder.getAdapterPosition();
                int to = target.getAdapterPosition();

                Collections.swap(mSportsData, from, to);

                mAdapter.notifyItemMoved(from, to);
                return true;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                mSportsData.remove(viewHolder.getAdapterPosition());
                mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
            }
        });
        helper.attachToRecyclerView(mRecyclerView);
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putParcelableArrayList("state", mSportsData);
    }

    /**
     * Method for initializing the sports data from resources.
     */
    private void initializeData(Bundle savedInstanceState) {
        if(savedInstanceState!=null){
            mSportsData.clear();
            mSportsData = savedInstanceState.getParcelableArrayList("state");
        } else {
            //Get the resources from the XML file
            String[] sportsList = getResources().getStringArray(R.array.sports_titles);
            String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
            String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
            TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);

            //Clear the existing data (to avoid duplication)
            mSportsData.clear();

            //Create the ArrayList of Sports objects with the titles and information about each sport
            for (int i = 0; i < sportsList.length; i++) {
                mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
            }

            sportsImageResource.recycle();
        }
        //Notify the adapter of the change
        mAdapter.notifyDataSetChanged();
    }

    public void resetSports(){
        initializeData(null);
    }

}

应用图片:-
#1 初始列表
#2 更改列表
(运动篮球卡#2 被刷)

方向更改为横向:-

从 OnCreate 中删除此函数调用 initializeData(savedInstanceState); 并在 OnResume 中调用它

我无法使用 Parcelable 方式让它工作,所以我尝试做的(并且成功了)是将所有运动对象中包含的所有信息保存在单独的列表中,并将它们放在包中。 在 initialiseData() 中,我只是使用这些列表,然后用准确的数据创建一个新的运动对象列表。

这不应该是 的做法,但我的应用程序只是不加载保存的列表,如果它作为可打包的传递,我不知道为什么。

这是现在的工作代码:-

@Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);

        ArrayList<String> titles = new ArrayList<>();
        ArrayList<String> infos = new ArrayList<>();
        ArrayList<String> details = new ArrayList<>();
        ArrayList<Integer> imgs = new ArrayList<>();
        for(Sport sport: mSportsData){
            titles.add(sport.getTitle());
            infos.add(sport.getInfo());
            details.add(sport.getDetail());
            imgs.add(sport.getImageResource());
        }

        outState.putStringArrayList("title", titles);
        outState.putStringArrayList("info", infos);
        outState.putStringArrayList("detail", details);
        outState.putIntegerArrayList("img", imgs);
    }

    private void initializeData(Bundle savedInstanceState) {

        boolean hasData = false;
        if(savedInstanceState!=null){
            if(savedInstanceState.containsKey("title")){

                //Get the resources from the bundle
                ArrayList<String> sportsList = savedInstanceState.getStringArrayList("title");
                ArrayList<String> sportsInfo = savedInstanceState.getStringArrayList("info");
                ArrayList<String> sportsDetail = savedInstanceState.getStringArrayList("detail");
                ArrayList<Integer> sportsImageResource = savedInstanceState.getIntegerArrayList("img");

                //Clear the existing data (to avoid duplication)
                mSportsData.clear();

                //Create the ArrayList of Sports objects with the titles and information about each sport
                for (int i = 0; i < sportsList.size(); i++) {
                    mSportsData.add(new Sport(sportsList.get(i), sportsInfo.get(i), sportsDetail.get(i), sportsImageResource.get(i)));
                }

                hasData = true;
            }
        }

        if(!hasData) {
            //Get the resources from the XML file
            String[] sportsList = getResources().getStringArray(R.array.sports_titles);
            String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
            String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
            TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);

            //Clear the existing data (to avoid duplication)
            mSportsData.clear();

            //Create the ArrayList of Sports objects with the titles and information about each sport
            for (int i = 0; i < sportsList.length; i++) {
                mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
            }

            sportsImageResource.recycle();
        }
        //Notify the adapter of the change
        mAdapter.notifyDataSetChanged();
    }