我有一个应用程序,允许用户选择一个选项,某个列表显示在列表视图中。 我在保存和恢复状态时遇到问题。 我有一个list_mode,根据用户选择的选项进行设置。 到目前为止,我让它保存适配器的状态,但恢复它会导致问题(我知道它正在保存,因为当我在 onCreate 中检查 customAdapter.onRestoreInstanceState(savedInstanceState( 时,它根据选择显示正确的项目,但我很难将其恢复到列表视图。 它将在恢复时崩溃。它在getAllItems.clear和.addAll上崩溃。 我什至将其硬编码到其中一个列表中,但它也没有用。 当我注释掉该行时,它会在主活动中崩溃。 在Java和Android方面,我是一个菜鸟,并且仍在学习。 我不知道我离得有多近或多远。 任何人都可以提供关于我做错了什么以及如何让它发挥作用的见解吗?谢谢
列表适配器:
public class ListAdapter extends BaseAdapter {
private static final String KEY_ADAPTER_STATE = "ListAdapter.KEY_ADAPTER_STATE";
public enum ListMode {
IMAGES_AND_TEXT,
IMAGES_ONLY,
TEXT_ONLY
}
private ListMode mListMode = ListMode.IMAGES_AND_TEXT;
private ArrayList<Item> mItems;
private ArrayList<Item> mImages;
private ArrayList<Item> mTexts;
@Override
public int getCount() {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems == null ? 0 : mItems.size();
case IMAGES_ONLY:
return mImages == null ? 0 : mImages.size();
case TEXT_ONLY:
return mTexts == null ? 0 : mTexts.size();
}
return 0;
}
@Override
public Item getItem(int position) {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems == null ? null : mItems.get(position);
case IMAGES_ONLY:
return mImages == null ? null : mImages.get(position);
case TEXT_ONLY:
return mTexts == null ? null : mTexts.get(position);
}
return null;
}
public ArrayList getAllItems() {
switch (mListMode) {
case IMAGES_AND_TEXT:
return mItems;
case IMAGES_ONLY:
return mImages;
case TEXT_ONLY:
return mTexts;
}
return null;
}
@Override
public long getItemId(int position) {
return position; // not really used
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = null;
TextView tn = null;
ImageView img = null;
if (convertView == null) {
LayoutInflater vi;
vi = LayoutInflater.from(parent.getContext());
v = vi.inflate(R.layout.list, null);
} else {
v=convertView;
}
Item p = getItem(position);
tn = (TextView) v.findViewById(R.id.tvText);
img = (ImageView) v.findViewById(R.id.thumbnail);
if (p.getmType().equals("image")) {
img.setVisibility(View.VISIBLE);
Picasso.with(parent.getContext()).load(p.getmData()).error((R.drawable.placeholder_error)).placeholder(R.drawable.placeholder).resize(90,0).into(img);
tn.setText("ID: " + p.getmID()+"nTYPE: " + p.getmType() +"nDate: " + p.getmDate()+ "nImage URL: " + p.getmData());
} else {
img.setVisibility(View.GONE);
tn.setText("ID: " + p.getmID()+"nTYPE: " + p.getmType() +"nDate: " + p.getmDate()+ "nText Data: " + p.getmData());
}
return v;
}
public void setListMode(ListMode listMode) {
mListMode = listMode;
notifyDataSetChanged();
}
public void setItems(JSONArray jsonArray) throws JSONException {
mItems = new ArrayList<>();
mImages = new ArrayList<>();
mTexts = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
Item item = new Item((JSONObject) jsonArray.get(i));
mItems.add(item);
if (item.getmType().equals("image")) {
mImages.add(item);
}
if (item.getmType().equals("text")) {
mTexts.add(item);
}
}
notifyDataSetChanged();
}
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putParcelableArrayList("list_items", getAllItems());
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.containsKey("list_items")) {
//ArrayList<Item> objects = savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
//getAllItems().clear();
//getAllItems().addAll(objects);
//mImages.clear();
//mImages.addAll(objects);
}
}
}
主要活动:
myListView = (ListView) findViewById(R.id.listViewID);
customAdapter = new ListAdapter();
if(savedInstanceState!=null) {
//When I put a breakpoint here I see it's getting the correct list
customAdapter.onRestoreInstanceState(savedInstanceState);
}
if (savedInstanceState != null && savedInstanceState.containsKey("list_items")) {
//When I remove the .clear and .addAll it then crashes on this line
myListView.onRestoreInstanceState(savedInstanceState.getParcelable("list_items"));
myListView.setAdapter(customAdapter);
}
else{
if (isNetworkAvailable()) {
getData theJsonData = new getData();
theJsonData.execute();
}
else{
Toast.makeText(getApplicationContext(), "No internet connection", Toast.LENGTH_SHORT).show();
tvNoInet.setVisibility(View.VISIBLE);
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//Not sure what I should put here or if what I have is right
customAdapter.onSaveInstanceState(savedInstanceState);
}
//This is the code that sets the list_mode when the user selects an option
if (id == R.id.all) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_AND_TEXT);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.images) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.text) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.TEXT_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
还有我的物品类:
public class Item implements Parcelable{
private String mID;
private String mType;
private String mDate;
private String mData;
public Item(String mID,String mType, String mDate, String mData) {
this.mType = mType;
this.mID = mID;
this.mDate = mDate;
this.mData = mData;
}
public Item(JSONObject jsonItem) throws JSONException {
String itemID=null;
String itemType=null;
String itemDate=null;
String itemData=null;
if (jsonItem.has("id")) {
itemID=jsonItem.getString("id");
}
if (jsonItem.has("type")) {
itemType=jsonItem.getString("type");
}
if (jsonItem.has("date")){
itemDate=jsonItem.getString("date");
}
if (jsonItem.has("data")){
itemData=jsonItem.getString("data");
}
this.mID=itemID;
this.mType=itemType;
this.mDate=itemDate;
this.mData=itemData;
}
protected Item(Parcel in) {
String[] data = new String[4];
in.readStringArray(data);
this.mID = data[0];
this.mType = data[1];
this.mDate = data[2];
this.mData = data[3];
}
public static final Creator<Item> CREATOR = new Creator<Item>() {
@Override
public Item createFromParcel(Parcel in) {
return new Item(in);
}
@Override
public Item[] newArray(int size) {
return new Item[size];
}
};
public String getmID() {
return mID;
}
public String getmType() {
return mType;
}
public String getmDate() {
return mDate;
}
public String getmData() {
return mData;
}
@Override
public String toString() {
return "{" +
"ID='" + mID + ''' +
", Type='" + mType + ''' +
", Date='" + mDate + ''' +
", Data='" + mData + ''' +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
String[] data = new String[4];
data[0] = mID;
data[1] = mType;
data[2] = mDate;
data[3] = mData;
dest.writeStringArray(data);
}
}
更新:这就是代码现在的样子。 当我选择某个列表然后旋转屏幕时,它正在工作。 它保留在当前选定的列表中。 现在的问题是,当他们在设备旋转后选择新列表时,列表视图完全空白
if(savedInstanceState!=null) {
customAdapter.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.containsKey(KEY_LIST_VIEW_STATE)) {
myListView.onRestoreInstanceState(savedInstanceState.getParcelable(KEY_LIST_VIEW_STATE));
myListView.setAdapter(customAdapter);
}
}
else{
if (isNetworkAvailable()) {
getData theJsonData = new getData();
theJsonData.execute();
tvNoInet.setVisibility(View.GONE);
btnRetry.setVisibility(View.GONE);
} else {
Toast.makeText(getApplicationContext(), "No internet connection", Toast.LENGTH_SHORT).show();
tvNoInet.setVisibility(View.VISIBLE);
btnRetry.setVisibility(View.VISIBLE);
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
customAdapter.onSaveInstanceState(savedInstanceState);
savedInstanceState.putParcelable(KEY_LIST_VIEW_STATE, myListView.onSaveInstanceState());
}
和我更新的列表适配器:
private ListMode mListMode = ListMode.IMAGES_AND_TEXT;
private ArrayList<Item> mItems= new ArrayList<>();
private ArrayList<Item> mImages= new ArrayList<>();
private ArrayList<Item> mTexts= new ArrayList<>();
public void onSaveInstanceState(Bundle savedInstanceState) {
//savedInstanceState.putParcelableArrayList("list_items", getAllItems());
savedInstanceState.putParcelableArrayList(KEY_ADAPTER_STATE, getAllItems());
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.containsKey(KEY_ADAPTER_STATE)) {
ArrayList<Item> objects = savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
getAllItems().clear();
getAllItems().addAll(objects);
//mImages.clear();
//mImages.addAll(objects);
}
}
这是选项菜单中切换列表的代码。 如果我还没有旋转屏幕,这就可以了。 当我旋转屏幕并选择其他列表时,列表视图为空白。 如果我选择"全部",它应该显示所有项目,那么我之前选择的任何列表如果恢复。因此,如果我一开始选择了"仅图像",请旋转屏幕,则显示正确,如果我向后旋转屏幕并再次选择"仅文本"或"仅图像",则列表为空白,如果我选择"全部",则显示我第一次选择的任何列表(在本例中为仅图像(
if (id == R.id.all) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_AND_TEXT);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.images) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.IMAGES_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
if (id == R.id.text) {
item.setChecked(true);
customAdapter.setListMode(ListAdapter.ListMode.TEXT_ONLY);
myListView.setSelectionAfterHeaderView();
return true;
}
好吧,既然我把你弄得一团糟...... :)
在这种情况下,我所做的是将保存的实例状态Bundle
传递给适配器的构造函数。 既然你把你的Item
课Parcelable
,其他一切都应该很容易。
public ListAdapter(Bundle savedInstanceState) {
if (savedInstanceState != null) {
int ordinal = savedInstanceState.getInt("adapter_mode", 0);
mListMode = ListMode.values()[ordinal];
ArrayList<Item> items =
savedInstanceState.getParcelableArrayList(KEY_ADAPTER_STATE);
if (items != null) {
setItems(items);
}
}
}
public void onSaveInstanceState(Bundle outState) {
outState.putInt("adapter_mode", mListMode.ordinal());
outState.putParcelableArrayList(KEY_ADAPTER_STATE, mItems);
}
因此,在构造函数中使用保存的实例状态,不需要显式方法来还原适配器状态。
public void setItems(JSONArray jsonArray) throws JSONException {
List<Item> items = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
items.add(new Item((JSONObject) jsonArray.get(i)));
}
setItems(items);
}
private void setItems(List<Item> items) {
for (Item item : items) {
mItems.add(item);
if (item.getmType().equals("image")) {
mImages.add(item);
}
if (item.getmType().equals("text")) {
mTexts.add(item);
}
}
notifyDataSetChanged();
}
如果您在之前调用ListAdapter.getAllItems().clear()
而没有调用ListAdapter.setItems()
,您的应用程序将崩溃
原因:如果未初始化 mItems
、mImages
、 mTexts
,ListAdapter.getAllItems()
可能会返回 null。
一种解决方法可能是
public class ListAdapter extends ... {
...
private ArrayList<Item> mItems = new ArrayList<>();
private ArrayList<Item> mImages = new ArrayList<>();
private ArrayList<Item> mTexts = new ArrayList<>();
...
}