Android原理——SavedState

View.BaseSavedState


引言

还在用public static T 保存View状态?

Android官方早已想到了:

使用View的BaseSavedState来保存状态。

  1. 在一个activity被销毁前,不一定会调用onSaveInstanceState()这个方法,因为不是所有情况都需要去存储activity的状态(例如当用户按回退键退出你的activity的时候,因为用户指定关掉这个activity)。
  2. 如果这个方法被调用,它一定会在 onStop()方法之前,可能会在onPause()方法之前。
  3. 布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,将不会存储它的状态。
  4. 由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息时,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。
  5. 由于onSaveInstanceState()方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在onPause()方法中存储持久化数据(例如应该被存储到数据库中的数据)。

类概述

BaseSavedState

Base class for derived classes that want to save and restore their own state in onSaveInstanceState().

直接子类

TextView.SavedState

ViewPager.SavedState


How To Use

读取保存状态

onRestoreInstanceState(Parcelable state)

保存状态

Parcelable onSaveInstanceState()

举个ViewPager.SavedState的示例

    /**
     * 读取保存状态
     * */
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState)state;
        super.onRestoreInstanceState(savedState.getSuperState());
        mCurrentPage = savedState.currentPage;
        mSnapPage = savedState.currentPage;
        requestLayout();
    }
    /**
     * 保存状态
     * */
    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState savedState = new SavedState(superState);
        savedState.currentPage = mCurrentPage;
        return savedState;
    }

    /**
     * 保存界面状态
     * */
    static class SavedState extends BaseSavedState {
        //当前页
        int currentPage;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            currentPage = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(currentPage);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }


BaseSavedState源码

我们看一下它的构造方法

public static class BaseSavedState extends AbsSavedState {
        /**
         * Constructor used when reading from a parcel. Reads the state of the superclass.
         *
         * @param source
         */
        public BaseSavedState(Parcel source) {
            super(source);
        }

        /**
         * Constructor called by derived classes when creating their SavedState objects
         *
         * @param superState The state of the superclass of this view
         */
        public BaseSavedState(Parcelable superState) {
            super(superState);
        }

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

            public BaseSavedState[] newArray(int size) {
                return new BaseSavedState[size];
            }
        };
    }

而其父类实现了Parcelable借口

/**
 * A {@link Parcelable} implementation that should be used by inheritance
 * hierarchies to ensure the state of all classes along the chain is saved.
 */
public abstract class AbsSavedState implements Parcelable {
    public static final AbsSavedState EMPTY_STATE = new AbsSavedState() {};

    private final Parcelable mSuperState;

    /**
     * Constructor used to make the EMPTY_STATE singleton
     */
    private AbsSavedState() {
        mSuperState = null;
    }

    /**
     * Constructor called by derived classes when creating their SavedState objects
     *
     * @param superState The state of the superclass of this view
     */
    protected AbsSavedState(Parcelable superState) {
        if (superState == null) {
            throw new IllegalArgumentException("superState must not be null");
        }
        mSuperState = superState != EMPTY_STATE ? superState : null;
    }

    /**
     * Constructor used when reading from a parcel. Reads the state of the superclass.
     *
     * @param source
     */
    protected AbsSavedState(Parcel source) {
        // FIXME need class loader
        Parcelable superState = source.readParcelable(null);

        mSuperState = superState != null ? superState : EMPTY_STATE;
    }

    final public Parcelable getSuperState() {
        return mSuperState;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(mSuperState, flags);
    }

    public static final Parcelable.Creator<AbsSavedState> CREATOR
        = new Parcelable.Creator<AbsSavedState>() {

        public AbsSavedState createFromParcel(Parcel in) {
            Parcelable superState = in.readParcelable(null);
            if (superState != null) {
                throw new IllegalStateException("superState must be null");
            }
            return EMPTY_STATE;
        }

        public AbsSavedState[] newArray(int size) {
            return new AbsSavedState[size];
        }
    };
}

补充

最后再举一个定义SeekBar的

3   import android.content.Context;
4   import android.content.res.TypedArray;
5   import android.os.Parcel;
6   import android.os.Parcelable;
7   import android.util.AttributeSet;
8   import android.view.View;
9   import android.view.ViewGroup;
10  import android.view.ViewParent;
11
12  import com.WazaBe.HoloEverywhere.LayoutInflater;
13  import com.WazaBe.HoloEverywhere.R;
14  import com.WazaBe.HoloEverywhere.widget.SeekBar;
15
16  public class SeekBarDialogPreference extends DialogPreference {
17      private static class SavedState extends BaseSavedState {
18          @SuppressWarnings("unused")
19          public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
20              @Override
21              public SavedState createFromParcel(Parcel in) {
22                  return new SavedState(in);
23              }
24
25              @Override
26              public SavedState[] newArray(int size) {
27                  return new SavedState[size];
28              }
29          };
30
31          protected int mValue, mMaxValue;
32
33          public SavedState(Parcel source) {
34              super(source);
35              mValue = source.readInt();
36              mMaxValue = source.readInt();
37          }
38
39          public SavedState(Parcelable superState) {
40              super(superState);
41          }
42
43          @Override
44          public void writeToParcel(Parcel dest, int flags) {
45              super.writeToParcel(dest, flags);
46              dest.writeInt(mValue);
47              dest.writeInt(mMaxValue);
48          }
49      }
50
51      private final SeekBar mSeekBar;
52      private int mValue = Integer.MIN_VALUE, mMaxValue = Integer.MIN_VALUE;
53
54      public SeekBarDialogPreference(Context context) {
55          this(context, null);
56      }
57
58      public SeekBarDialogPreference(Context context, AttributeSet attrs) {
59          this(context, attrs, R.attr.seekBarDialogPreferenceStyle);
60      }
61
62      public SeekBarDialogPreference(Context context, AttributeSet attrs,
63              int defStyle) {
64          super(context, attrs, defStyle);
65          TypedArray a = context.obtainStyledAttributes(attrs,
66                  R.styleable.SeekBarDialogPreference, defStyle,
67                  R.style.Holo_PreferenceDialog_SeekBarDialogPreference);
68          int maxValue = a.getInt(R.styleable.SeekBarDialogPreference_max, 100);
69          a.recycle();
70          mSeekBar = onCreateSeekBar();
71          setMaxValue(maxValue);
72      }
73
74      public int getMaxValue() {
75          return mMaxValue;
76      }
77
78      public SeekBar getSeekBar() {
79          return mSeekBar;
80      }
81
82      public int getValue() {
83          return mValue;
84      }
85
86      @Override
87      protected void onBindDialogView(View view) {
88          super.onBindDialogView(view);
89          synchronized (mSeekBar) {
90              ViewParent oldParent = mSeekBar.getParent();
91              if (oldParent != view) {
92                  if (oldParent != null) {
93                      ((ViewGroup) oldParent).removeView(mSeekBar);
94                  }
95                  ((ViewGroup) view).addView(mSeekBar);
96              }
97          }
98      }
99
100     protected SeekBar onCreateSeekBar() {
101         return (SeekBar) LayoutInflater.inflate(getContext(),
102                 R.layout.preference_dialog_seekbar_widget);
103     }
104
105     @Override
106     protected void onDialogClosed(boolean positiveResult) {
107         super.onDialogClosed(positiveResult);
108         final int value;
109         synchronized (mSeekBar) {
110             value = mSeekBar.getProgress();
111         }
112         if (positiveResult && callChangeListener(value)) {
113             setValue(value);
114         }
115     }
116
117     @Override
118     protected Integer onGetDefaultValue(TypedArray a, int index) {
119         return a.getInt(index, 0);
120     }
121
122     @Override
123     protected void onRestoreInstanceState(Parcelable state) {
124         if (state == null || !(state instanceof SavedState)) {
125             super.onRestoreInstanceState(state);
126             return;
127         }
128         SavedState ss = (SavedState) state;
129         super.onRestoreInstanceState(ss.getSuperState());
130         setValue(ss.mValue);
131         setMaxValue(ss.mMaxValue);
132     }
133
134     @Override
135     protected Parcelable onSaveInstanceState() {
136         final Parcelable superState = super.onSaveInstanceState();
137         if (isPersistent()) {
138             return superState;
139         }
140         final SavedState myState = new SavedState(superState);
141         myState.mValue = mValue;
142         myState.mMaxValue = mMaxValue;
143         return myState;
144     }
145
146     @Override
147     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
148         int def = defaultValue instanceof Integer ? (Integer) defaultValue
149                 : defaultValue == null ? 0 : Integer.parseInt(defaultValue
150                         .toString());
151         mSeekBar.setProgress(restoreValue ? getPersistedInt(def) : def);
152     }
153
154     public void setMaxValue(int maxValue) {
155         if (mMaxValue == maxValue) {
156             return;
157         }
158         final boolean wasBlocking = shouldDisableDependents();
159         mMaxValue = maxValue;
160         mSeekBar.setMax(maxValue);
161         if (shouldDisableDependents() != wasBlocking) {
162             notifyDependencyChange(!wasBlocking);
163         }
164     }
165
166     public void setValue(int value) {
167         if (mValue == value) {
168             return;
169         }
170         final boolean wasBlocking = shouldDisableDependents();
171         mValue = value;
172         mSeekBar.setProgress(value);
173         persistInt(value);
174         if (shouldDisableDependents() != wasBlocking) {
175             notifyDependencyChange(!wasBlocking);
176         }
177     }
178 }
时间: 2024-08-09 12:37:44

Android原理——SavedState的相关文章

Android(java)学习笔记95:Android原理揭秘系列之View、ViewGroup

作过Android 应用开发的朋友都知道,Android的UI界面都是由View和ViewGroup及其派生类组合而成的.其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的.AndroidUI界面的一般结构可参见下面的示意图: 可见,作为容器的ViewGroup可以包含作为叶子节点的View,也可以包含作为更低层次的子ViewGroup,而子ViewGroup又可以包含下一层的叶子节点的View和ViewGroup.事实上,这种灵活的Vi

Android原理——自定义Toast原理

自定义Toast原理 概要 1. 使用Toast遇到的问题 2. Toast源码及原理 3. 我的单例类 T.java 使用Toast遇到的问题 原生的Toast真的很难看不是吗 多个Toast依次显示,程序都结束了还在不停的显示呢 解决办法:自定义Toast + 单例类 Toast源码及原理 Toast的源码不多,只有423行 有些我们常用的方法,想必不用多说,例如: public Toast(Context context) public void show() public void ca

Android原理——回弹ScrollView

回弹的ScrollView 网上看到的通常是ElasticScrollView, 有一个Bug:点击子控件滑动时,滑动无效, 所以针对此问题,我对ElasticScrollView做了改进. 原理图 代码 我在注释中做了详细的说明 import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; impor

Android原理揭秘系列之一动态墙纸

Livewallpaper,即动态墙纸,是Android的一大3D特色功能,用户可以在桌面选择加载动态墙纸,让自己的手机桌面背景旋动起来. 相对于静态桌面壁纸,动态墙纸可以展示各种动态变化的背景,而与传统手机系统采用GIF作为动态背景不同的是,Android的动态墙纸并不是GIF图片,而是一个标准的Android应用程序,也就是APK.既然是应用程序,当然意味着天生具有GIF图片不具备的功能——能与用户发生交互,而且动态的背景变化绝不仅仅局限于GIF图片那般只能是固定的几张图片的循环播放. 需要

Android原理——动态代码布局

动态代码布局 如何添加代码布局 代码布局注意的问题 代码布局和XML布局的性能比较 如何添加代码布局 for example -- 简单布局LinearLayout LinearLayout llayout = new LinearLayout(mContext); llayout.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParam

Android原理——回调机制

Android回调机制 回调函数可以把调用者与被调用者分开,调用者不关心谁是被调用者,调用者只需知道具有特定原型和限制条件的被调函数. 1.定义一个回调函数: 2.提供函数实现的一方在初始化的时候,将回调函数的实例化接口提交给调用者: 3.当特定的事件或条件发生的时候,调用者使用接口调用回调函数对事件进行处理. 看到过一句写的比较好的描述:A类中调用B类的某个方法C,然后B类中反过来调用A类的方法D,D这个方法就叫回调方法 先引用一个Android源码中经典的例子: //接口类, B类实现接口,

Android全平台书籍

<Android Database Programming>:全书研究Android平台下的数据库技术. <Android Application Programming with OpenCV3>:全书旨在Android平台下开发能够捕获.操作和跟踪2D.3D环境中物体的应用程序. <Android高级编程>:提供Android应用程序设计的建议. <Android多媒体开发高级编程---为智能手机和平板电脑开发图形.音乐.视频和富媒体应用>:全书包括三个

Android 开源项目分类汇总

目前包括: Android 开源项目第一篇--个性化控件(View)篇  包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar.TextView.ScrollView.TimeView.TipView.FlipView.ColorPickView.GraphView.UI Style.其他Android 开源项目第二篇--工具库篇  包括依赖注入.图片缓存.网络请求.数据库 ORM 工具包.Andro

Android常用酷炫控件(开源项目)github地址汇总

转载一个很牛逼的控件收集贴... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar.TextView.ScrollView.TimeView.TipView.FlipView.ColorPickView.GraphView.UI Style 等等. 一.ListView android-pulltorefresh一个强大的拉动