在Fragment onCreateView方法中缓存View
protected WeakReference mRootView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// detach/attach can lead to view recreate frequently
if (mRootView == null || mRootView.get() == null) {
View view=inflater.inflate(R.layout.tab_fragment, null);
mRootView = new WeakReference(view);
} else {
ViewGroup parent = (ViewGroup) mRootView.get().getParent();
if (parent != null) {
parent.removeView(mRootView.get());
}
}
return mRootView.get();
}
关联控件的时候可以使用 mRoorView.get()来关联
缓存 Tabs 页面切换不重新加载数据
转载:http://www.2cto.com/kf/201402/280717.html
网上搜索了很多文章都有说道保存 Fragment 数据和状态,
但是没有整整提到如何来保存他的当前view状态,不知道如何保存,当然实际上我还是没搞懂如何仅仅缓存 Fragment 的状态,
但对于 ViewPager 缓存 Tab 对应的 Fragment 还是找到了办法,之前花了很大功夫来自己实现,
后来偶然发现他居然有个自带的方法
1
2
|
// 设置缓存多少个 Tab对应的 fragment
mViewPager.setOffscreenPageLimit(
6
);
|
配置该项后,ViewPager在切换时将不会清理不可见的 Fragment,不会触发 Fragment 的任何事件,因此也就不会导致其重新加载。
6代表的含义是,当前页的左边预加载6页,当前页的右侧加载6页。
详细了解:
无论如何StatedFragment
会导致模式破坏,因为它的设计方式不同,因为Android的设计假设Android开发人员可能更容易理解片段的状态保存/还原,如果它的行为与活动一样(View的状态和实例状态同时处理)。所以我做了一个由发展中国家框架公司做的实验,看看进展如何。更容易理解吗?它的模式是否更有利于开发人员?
现在,经过两个月的实验,我想我已经得到了一个结果。虽然StatedFraume有点容易理解,但它也带来了一个相当大的问题,它打破了Android的View体系结构的模式设计。因此,我认为这可能会造成一个长期的问题,这是完全不好的。其实我自己也觉得自己的密码很奇怪.
出于这个原因,我决定从现在起把StatedFraage标记为废弃的。作为对错误的道歉,我写了这个博客,以展示真正的最佳实践,如何以Android的设计方式保存和恢复片段的状态。
活动时onSaveInstanceState
叫做。活动将自动收集视图的状态,从视图的每一个视图等级。请注意,只有实现了视图状态保存/内部恢复的视图才能收集数据。一次onRestoreInstanceState
叫做。活动将收集到的数据发送回视图层次中的视图,该视图提供了相同的android:id
因为它是一个接一个地收集的。
让我们从可视化的角度来看。
这就是为什么在EditText中键入的文本仍然持续存在的原因,尽管活动已经被销毁,而且我们没有做任何特别的事情。没有魔法。这些视图状态将自动收集并还原回来。
这也是为什么那些没有android:id
定义无法恢复它的视图状态。
虽然这些视图的状态是自动保存的,但是活动的成员变量不是。它们将随着活动而被摧毁。您必须通过手动保存和恢复它们。onSaveInstanceState
和onRestoreInstanceState
方法。
public class MainActivity extends AppCompatActivity {
// These variable are destroyed along with Activity
private int someVarA;
private String someVarB;
...
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("someVarA", someVarA);
outState.putString("someVarB", someVarB);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
someVarA = savedInstanceState.getInt("someVarA");
someVarB = savedInstanceState.getString("someVarB");
}
}
这就是恢复活动的实例状态和视图状态所必须做的一切。
以防碎片被系统破坏。一切都会和活动一样发生。
这意味着每个成员变量也被销毁。您必须通过以下方式手动保存和恢复这些变量onSaveInstanceState
和onActivityCreated
方法。请注意,片段中没有onRestoreInstanceState方法。
public class MainFragment extends Fragment {
// These variable are destroyed along with Activity
private int someVarA;
private String someVarB;
...
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("someVarA", someVarA);
outState.putString("someVarB", someVarB);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
someVarA = savedInstanceState.getInt("someVarA");
someVarB = savedInstanceState.getString("someVarB");
}
}
对于片段,有一些与活动不同的特殊情况,我认为您需要了解它。一旦从后台返回片段,它的视图就会被销毁和重新创建。
在这种情况下,碎片不会被破坏。只有查看内部片段。因此,没有任何实例状态保存发生。但是,上面展示的碎片生命周期新创建的视图会发生什么呢?
没问题。Android是这样设计的。在这种情况下,内部调用视图状态保存/恢复。因此,每个实现视图状态的视图都在内部保存/恢复,例如EditText
或TextView
带着android:freezeText="true"
,将自动保存和恢复状态。使其显示与前一次完全相同。
请注意,在这种情况下,只有View被销毁(并重新创建)。片段仍然存在,就像里面的成员变量一样,所以您不必对它们做任何事情。不需要任何额外的代码。
public class MainFragment extends Fragment {
// These variable still persist in this case
private int someVarA;
private String someVarB;
...
}
您可能已经注意到,如果在此片段中使用的每个视图都在内部实现了视图保存/还原。在这种情况下,您不需要做任何事情,因为View的状态将被自动恢复,片段中的成员变量也仍然持续存在。
因此碎片的状态保存/恢复最佳实践的第一个条件是.。
Android提供了一种机制,可以通过以下方式在内部保存和恢复视图状态onSaveInstanceState
和onRestoreInstanceState
方法。实现它是开发人员的任务。
public class CustomView extends View {
...
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
// Save current View's state here
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
// Restore View's state here
}
...
}
基本上,每个标准视图(如EditText、TextView、复选框等)都已经在内部实现了这些功能。无论如何,您可能需要为某些视图启用它,例如,您必须设置android:freezeText
为真TextView
使用这个特性。
但是如果我们说到第三方定制视图在互联网上的分布。我必须指出,其中许多代码还没有实现这部分代码,这可能会在实际使用中造成很大的问题。
如果您决定使用任何第三方自定义视图,则必须确保它已在内部实现视图状态保存/还原,或者必须创建从该自定义视图派生的子类并实现onSaveInstanceState
/onRestoreInstanceState
你自己。
//
// Assumes that SomeSmartButton is a 3rd Party view that
// View State Saving/Restoring are not implemented internally
//
public class SomeBetterSmartButton extends SomeSmartButton {
...
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
// Save current View's state here
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
// Restore View's state here
}
...
}
如果您创建了自己的自定义视图或自定义视图组,也不要忘记实现这两个方法。在这个部分实现应用程序中使用的每一种类型的视图是非常重要的。
也别忘了分配android:id
属性添加到放置在布局中的每个视图,以便启用视图状态保存和还原,否则它将根本无法恢复状态。
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/cbAgree"
android:text="I agree"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
我们已经走到一半了!
为了使您的代码干净和可伸缩,您必须将碎片状态和视图状态相互分离。如果有任何属性属于View,请执行状态保存/还原内部视图。如果任何属性属于碎片,则在片段内执行。以下是一个例子:
public class MainFragment extends Fragment {
...
private String dataGotFromServer;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("dataGotFromServer", dataGotFromServer);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
dataGotFromServer = savedInstanceState.getString("dataGotFromServer");
}
...
}
让我再重复一遍。不要在片段的onSaveInstanceState中保存View的状态,反之亦然。
仅此而已。这是关于如何保存/恢复活动的、片段的和视图的状态的最佳实践。希望你能找到这条有用的信息
本帅再次提醒:不要在Fragment的onSaveInstanceState方法里缓存View的状态,反之亦然。
好了,这就是Activity/Fragment 状态缓存和恢复的最佳实践,希望能对你有帮助。
Activity 中 onSaveInstanceState(Bundle outState) 调用的时机(activity可能被销毁时调用此方法来保存瞬态数据)
1. home键最小化时,在onPause后调用
2. 长按home键,在onPause后调用
3. 屏幕旋转时,在onPause后调用
4. 开启新的Activity,在onPause后调用
5. 补充:Android UI框架中几乎所有的UI控件都实现了onSaveInstanceState()方法,因此当activity被摧毁和重建时, 这些UI控件会自动保存和恢复状态数据,前提是你已经为这个控件指定过ID
Frament 中 onSaveInstanceState(Bundle outState) 调用的时机
6. 列表内容 home键最小化时,在onPause后调用 长
7. 按home键,在onPause后调用 按下电源键,在onPause后调用
8. 托管该Fragment的Activity 旋转时,在onPause后调用
9. 托管该Fragment的Activity开启新的Activity时,在onPause后调用 补充:即使该Fragment在回退栈中,当前展示的不是它,上面几种情况也是成立的
10. 补充:此方法被调用时,如果向outState里添加了key-value对,那么在和onCreate(Bundle savedInstanceState)和onViewCreated(View view, @Nullable Bundle savedInstanceState)中拿到的bundle中会有存入的key-value对
Activity 中 onRestoreInstanceState(Bundle savedInstanceState) 调用的时机(activity“确实”被销毁后重建,调用此方法,Fragment中无此方法)
11. 验证的时候,只有屏幕旋转后调用了此方法,在onStart()之后
12. onRestoreInstanceState()被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用。上面验证实验的其它4种情况Activity一般不会那么快被销毁,所以没有调用此方法
13. 关于屏幕旋转保存数据的补充:
Fragment 中 setRetainInstance方法介绍
17. Fragment还可以通过setRetainInstance(boolean)来保存自定义的对象数据(Fragment中都有此方法,Activity中无此方法), 当在Fragment的onCreate()方法中调用了setRetainInstance(true)后,Activity被系统销毁又重新创建时(如屏幕旋转),可以不完全销毁Fragment,Fragment中的成员变量值会保留,恢复时跳过了onDestroy()和onCreate()