【问题标题】:How to retain the state of a recyclerview of list of custom objects?如何保留自定义对象列表的回收视图的状态?
【发布时间】:2022-07-22 00:47:23
【问题描述】:

主要目标:-

我有一份体育新闻列表。每个项目都包含一个运动名称和一些信息。单击它将显示有关该特定运动的最新消息。用户可以选择滑动以关闭新闻,如果他们不希望它在列表中,或者他们也可以拖放它,例如,如果他们想在其他新闻之上看到一些新闻。 列表中的每一项都以编程方式表示为一个 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);

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

我尝试调试应用程序,发现数组列表正确传递,数据完整,只是应用程序由于某种原因无法显示它。

此外,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被刷)

方向更改为横向:-

【问题讨论】:

    标签: java android android-recyclerview android-orientation onsaveinstancestate


    【解决方案1】:

    从 OnCreate 中移除这个函数调用 initializeData(savedInstanceState); 并在 OnResume 中调用它

    【讨论】:

    • 这不起作用。我重写了 onResume() 但由于它没有 savedInstanceState 作为参数,所以我将 bundle 设置为实例变量,并在每次调用 onCreate() 时将值存储在其中。
    【解决方案2】:

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

    这不应该是 这样做的方式,但是如果它作为一个 parcelable 传递并且我不知道为什么,我的应用程序不会加载保存的列表。

    这是现在的工作代码:-

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

    【讨论】:

      【解决方案3】:

      即使问题已经 4 个月了,而且您可能不再需要答案:

      问题是您使用 mSportsData 初始化适配器,但稍后在 initializeData() 中为该变量重新分配了另一个值。绑定到适配器的 ArrayList 仍然是它初始化时使用的空列表。

      解决此问题的一种方法是,如果 savedInstanceState 为 null 或保存的值,则使用新的 ArrayList 初始化 mSportsData,并且仅在 savedInstanceState 为 null 时调用 initializeData。您可以从 initalizeData() 中完全删除参数和 if。

      // Initialize the ArrayList that will contain the data.
      if (savedInstanceState == null) {
          mSportsData = new ArrayList<>();
      } else {
          mSportsData = savedInstanceState.getParcelableArrayList("state");
      }
      
      //Initialize the adapter and set it ot the RecyclerView (nothing changed here)
      mAdapter = new SportsAdapter(this, mSportsData);
      mRecyclerView.setAdapter(mAdapter);
      
      if (savedInstanceState == null) {
              initializeData();
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多