在列表视图上恢复状态时遇到问题

Having trouble restoring state on listview

我有一个应用程序,允许用户select 一个选项并在列表视图中显示某个列表。我在让它保存和恢复状态时遇到问题。我有一个 list_mode,它根据用户 select 的选项进行设置。到目前为止,我已经保存了适配器的状态,但恢复它会导致问题(我知道它正在保存,因为当我在 onCreate 中检查 customAdapter.onRestoreInstanceState(savedInstanceState) 时,它显示了基于 select 的正确项目离子,但我很难将它恢复到列表视图。它会在恢复时崩溃。它在 getAllItems.clear 和 .addAll 上崩溃。我什至将其硬编码到列表之一,但它没有要么工作。当我评论那条线时,它在主要 activity 中崩溃。我是 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);
    }
}

}

主要Activity:

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;
    }

还有我的商品class:

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

更新:这就是代码现在的样子。当我 select 某个列表然后旋转屏幕时它正在工作。它保留在当前 selected 列表中。现在的问题是当他们 select 设备旋转后的新列表列表视图完全空白

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

这是切换列表的选项菜单中的代码。如果我还没有旋转屏幕,这就可以了。当我旋转屏幕并 select 一个不同的列表时,列表视图是空白的。如果我 select "All" 应该显示所有项目,那么如果恢复之前我 selected 的任何列表。因此,如果我首先 select 仅编辑图像,然后旋转屏幕,一切都会正确显示,如果我将屏幕旋转回来,然后再次 select 仅文本或仅图像,则列表为空,如果我 select "All" 然后显示我第一次选择的任何列表(在本例中为仅图像)

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;
    }

如果您调用 ListAdapter.getAllItems().clear() 而未在

之前调用 ListAdapter.setItems(),您的应用程序会崩溃

原因:如果mItemsmImagesmTexts未初始化,ListAdapter.getAllItems()可能return为null。

一种解决方法可能是

public class ListAdapter extends ... {
    ...
    private ArrayList<Item> mItems = new ArrayList<>();
    private ArrayList<Item> mImages = new ArrayList<>();
    private ArrayList<Item> mTexts = new ArrayList<>();
    ...
    }

好吧,自从我把你弄到这个烂摊子里……:)

在这种情况下,我所做的是将保存的实例状态 Bundle 传递给适配器的构造函数。既然你做了你的ItemclassParcelable,其他一切都应该很容易。

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