如何在 Android 屏幕旋转上保存自定义 ArrayList?

Posted

技术标签:

【中文标题】如何在 Android 屏幕旋转上保存自定义 ArrayList?【英文标题】:How to save custom ArrayList on Android screen rotate? 【发布时间】:2012-09-12 07:12:51 【问题描述】:

我有一个带有自定义对象的ArrayList,我希望能够在屏幕旋转时保存和恢复这些对象。

我知道这可以用onSaveInstanceStateonRestoreInstanceState 来完成,如果我要让ArrayList 成为它自己的类,它实现ParcelableSerializable... 但是有没有办法在不创建另一个类的情况下这样做吗?

【问题讨论】:

【参考方案1】:

您不需要创建新类来传递自定义对象的 ArrayList。您应该为您的对象简单地实现 Parcelable 类,并在onSaveInstanceState()onRestoreInstanceState() 中使用Bundle#putParcelableArrayList()。这个方法会自己存储一个 Parcelables 的 ArrayList。


因为 Parcelables(以及 Serializables 和 Bundles)的主题有时让我头疼,这里有一个 ArrayList 的基本示例,其中包含存储在 Bundle 中的自定义 Parcelable 对象。 (这是可剪切和粘贴运行的,无需布局。)

实现 Parcelable

public class MyObject implements Parcelable 
    String color;
    String number;

    public MyObject(String number, String color) 
        this.color = color;
        this.number = number;
    

    private MyObject(Parcel in) 
        color = in.readString();
        number = in.readString();
    

    public int describeContents() 
        return 0;
    

    @Override
    public String toString() 
        return number + ": " + color;
    

    public void writeToParcel(Parcel out, int flags) 
        out.writeString(color);
        out.writeString(number);
    

    public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() 
        public MyObject createFromParcel(Parcel in) 
            return new MyObject(in);
        

        public MyObject[] newArray(int size) 
            return new MyObject[size];
        
    ;

保存/恢复状态

public class Example extends ListActivity 
    ArrayList<MyObject> list;

    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        if(savedInstanceState == null || !savedInstanceState.containsKey("key")) 
            String[] colors = "black", "red", "orange", "cyan", "green", "yellow", "blue", "purple", "magenta", "white";
            String[] numbers = "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten";

            list = new ArrayList<MyObject>();
            for(int i = 0; i < numbers.length; i++) 
                list.add(new MyObject(numbers[i], colors[i]));
        
        else 
            list = savedInstanceState.getParcelableArrayList("key");
        

        setListAdapter(new ArrayAdapter<MyObject>(this, android.R.layout.simple_list_item_1, list));
    

    @Override
    protected void onSaveInstanceState(Bundle outState) 
        outState.putParcelableArrayList("key", list);
        super.onSaveInstanceState(outState);
    

【讨论】:

谢谢!但是,我迷失了private MyObject(Parcel in) color = in.readString(); number = in.readString(); 位...这对字符串来说很容易,但是如何读取我的自定义数组列表? in.readArrayList(MyObject.class.getClassLoader()) 给我一个错误。 嗯,在您的问题中,您询问了暗示ArrayList&lt;MyObject&gt; 的“带有自定义对象的ArrayList”,这个答案在Parcelable.Creator 中处理;但是您的评论只是问“我如何阅读我的自定义数组列表”,暗示MyArrayList。你想扩展什么类? (抱歉,技术性很强,但我想尽快给你最好的答案。) 对不起,我的意思是 ArrayList 好的,那么你不需要在你的对象类中readArrrayList()。查看我发布的示例活动中的onCreate()。我检查:如果ArrayList&lt;MyObject&gt; 尚不存在,则创建它;如果确实存在,请在 ArrayList&lt;MyObject&gt;list = savedInstanceState.getParcelableArrayList("key"); 中读取。这有意义吗?如果有帮助,我可以轻松地更详细地评论我的示例。 @Sam 您如何将 Parcelable 与字符串的 ArrayList 一起使用,而不是自定义对象?【参考方案2】:

您可以使用onRetainNonConfigurationInstance()。它允许您在配置更改之前保存任何对象,并在之后使用getLastNonConfigurationInstanceState() 恢复它。

活动内部:

    @Override
    public Object onRetainNonConfigurationInstance() 
        return myArrayList;
    

内部onCreate()

    try
        ArrayList myArrayList = (ArrayList)getLastNonConfigurationInstance();
     catch(NullPointerException e) 

处理运行时更改:http://developer.android.com/guide/topics/resources/runtime-changes.html 文档:http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance%28%29

【讨论】:

哇,从来不知道这个功能!超级简单,正是我所需要的......但是,我看到它已被弃用...... 不幸的是,它仍然有效,并且没有其他简单的方法可以做到这一点。山姆的答案是我发现的唯一其他方法,但实施起来要困难得多。 如果你正在使用 Fragments,你可以使用 Fragment.setRetainInstance(true) 代替,这是它的更新版本(但仅适用于 Fragments)。文档:developer.android.com/reference/android/app/… 只有在不幸没有将片段添加到后台堆栈时才有效【参考方案3】:
public void onSaveInstanceState(Bundle outState) 
    super.onSaveInstanceState(outState);
    ArrayList<Integer> id=new ArrayList<>();
    ArrayList<String> title=new ArrayList<>();
    for(int i=0;i<arr.size();i++)
        id.add(arr.get(i).id);
        title.add(arr.get(i).title);
    
    outState.putIntegerArrayList("id",id);
    outState.putStringArrayList("title",title);

【讨论】:

欢迎来到 Stack Overflow。您能否详细说明一下此解决方案如何回答问题? 这可能有效。我还没有尝试过。不过,这真是个黑客。【参考方案4】:

是的,您可以将复合对象保存在共享首选项中。比方说:

Student mStudentObject = new Student();
     SharedPreferences appSharedPrefs = PreferenceManager
      .getDefaultSharedPreferences(this.getApplicationContext());
      Editor prefsEditor = appSharedPrefs.edit();
      Gson gson = new Gson();
      String json = gson.toJson(mStudentObject);
      prefsEditor.putString("MyObject", json);
      prefsEditor.commit(); 

现在您可以将对象检索为:

     SharedPreferences appSharedPrefs = PreferenceManager
     .getDefaultSharedPreferences(this.getApplicationContext());
     Editor prefsEditor = appSharedPrefs.edit();
     Gson gson = new Gson();
     String json = appSharedPrefs.getString("MyObject", "");
     Student mStudentObject = gson.fromJson(json, Student.class);

【讨论】:

这样做的坏主意 - 取决于要保存的列表的大小。 SharedPreferences 是一个 XML 文件,它不是为存储大量数据集而设计的。

以上是关于如何在 Android 屏幕旋转上保存自定义 ArrayList?的主要内容,如果未能解决你的问题,请参考以下文章

Android jetpack viewModel 屏幕旋转数据如何保存。

Android 片段不保存状态,在旋转/屏幕锁定/返回时崩溃

具有自定义表面的 Android CameraX

Android Exoplayer,在屏幕旋转时恢复视频

如何将屏幕设置为不旋转(android)? [复制]

Android屏幕旋转不生效