如何保留自定义对象列表的回收视图的状态?
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();
}
主要目标:-
我有一份体育新闻列表。每个项目都包含一个运动名称和一些信息。单击它会显示有关该特定运动的最新消息。用户可以选择滑动以关闭新闻,如果他们不希望它出现在列表中,或者他们也可以拖放它,例如,如果他们想在其他新闻之上看到一些新闻。 列表中的每个项目都以编程方式表示为 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();
}