在用fragmenttabhost做页面切换的时候,发现只要一来回切换fragment,fragment页面就会重新初始化,也就是执行onCreateView()方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态

解决方案:

在Fragment onCreateView方法中缓存View
protected WeakReference<View> 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>(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 还是找到了办法,之前花了很大功夫来自己实现,

后来偶然发现他居然有个自带的方法


?



// 设置缓存多少个 Tab对应的 fragment        





         mViewPager.setOffscreenPageLimit(         6         );


我测试时用了 6个 listView 加载图片列表数据,切换动画也没有任何卡顿现象,非常流畅,就这么简单一句就搞定了。

配置该项后,ViewPager在切换时将不会清理不可见的 Fragment,不会触发 Fragment 的任何事件,因此也就不会导致其重新加载。

6代表的含义是,当前页的左边预加载6页,当前页的右侧加载6页。

详细了解:

无论如何StatedFragment会导致模式破坏,因为它的设计方式不同,因为Android的设计假设Android开发人员可能更容易理解片段的状态保存/还原,如果它的行为与活动一样(View的状态和实例状态同时处理)。所以我做了一个由发展中国家框架公司做的实验,看看进展如何。更容易理解吗?它的模式是否更有利于开发人员?

现在,经过两个月的实验,我想我已经得到了一个结果。虽然StatedFraume有点容易理解,但它也带来了一个相当大的问题,它打破了Android的View体系结构的模式设计。因此,我认为这可能会造成一个长期的问题,这是完全不好的。其实我自己也觉得自己的密码很奇怪.

出于这个原因,我决定从现在起把StatedFraage标记为废弃的。作为对错误的道歉,我写了这个博客,以展示真正的最佳实践,如何以Android的设计方式保存和恢复片段的状态。

了解在保存/恢复活动状态时发生了什么

活动时onSaveInstanceState叫做。活动将自动收集视图的状态,从视图的每一个视图等级。请注意,只有实现了视图状态保存/内部恢复的视图才能收集数据。一次onRestoreInstanceState叫做。活动将收集到的数据发送回视图层次中的视图,该视图提供了相同的android:id因为它是一个接一个地收集的。

让我们从可视化的角度来看。

android fragment状态保存 isInLayout fragment 缓存 view_视图状态

android fragment状态保存 isInLayout fragment 缓存 view_ide_02

这就是为什么在EditText中键入的文本仍然持续存在的原因,尽管活动已经被销毁,而且我们没有做任何特别的事情。没有魔法。这些视图状态将自动收集并还原回来。

这也是为什么那些没有android:id定义无法恢复它的视图状态。虽然这些视图的状态是自动保存的,但是活动的成员变量不是。它们将随着活动而被摧毁。您必须通过手动保存和恢复它们。onSaveInstanceStateonRestoreInstanceState方法。

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

}

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

}

这就是恢复活动的实例状态和视图状态所必须做的一切。

了解碎片状态保存/恢复时发生的情况

以防碎片被系统破坏。一切都会和活动一样发生。

android fragment状态保存 isInLayout fragment 缓存 view_android_03

android fragment状态保存 isInLayout fragment 缓存 view_ide_04

这意味着每个成员变量也被销毁。您必须通过以下方式手动保存和恢复这些变量onSaveInstanceStateonActivityCreated方法。请注意,片段中没有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");
    }

}

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 fragment状态保存 isInLayout fragment 缓存 view_android_05

在这种情况下,碎片不会被破坏。只有查看内部片段。因此,没有任何实例状态保存发生。但是,上面展示的碎片生命周期新创建的视图会发生什么呢?

没问题。Android是这样设计的。在这种情况下,内部调用视图状态保存/恢复。因此,每个实现视图状态的视图都在内部保存/恢复,例如EditTextTextView带着android:freezeText="true",将自动保存和恢复状态。使其显示与前一次完全相同。

android fragment状态保存 isInLayout fragment 缓存 view_android_06

请注意,在这种情况下,只有View被销毁(并重新创建)。片段仍然存在,就像里面的成员变量一样,所以您不必对它们做任何事情。不需要任何额外的代码。

public class MainFragment extends Fragment {

    // These variable still persist in this case
    private int someVarA;
    private String someVarB;

    ...

}

public class MainFragment extends Fragment {

    // These variable still persist in this case
    private int someVarA;
    private String someVarB;

    ...

}

您可能已经注意到,如果在此片段中使用的每个视图都在内部实现了视图保存/还原。在这种情况下,您不需要做任何事情,因为View的状态将被自动恢复,片段中的成员变量也仍然持续存在。

因此碎片的状态保存/恢复最佳实践的第一个条件是.。

应用程序中使用的每个视图都必须在内部实现状态保存/还原

Android提供了一种机制,可以通过以下方式在内部保存和恢复视图状态onSaveInstanceStateonRestoreInstanceState方法。实现它是开发人员的任务。

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
    }

    ...

}

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
    }

    ...

}

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

    ...

}

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,Fragment中onSaveInstanceState(Bundle outState)的调用时机


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. 关于屏幕旋转保存数据的补充:

  1. Object onRetainCustomNonConfigurationInstance()旋转时会调用此方法,在onStop()后调用,可以重写此方法返回一个对象(Fragment中无此方法)
  2. Activity重建后可以在onCreate方法中通过getLastCustomNonConfigurationInstance()拿到上边保存的对象(Fragment中无此方法)
  3. AndroidManifest.xml中设置android:configChanges=”orientation|screenSize”后,旋转屏幕将不会重新调用各个生命周期,当然也不会调用保存数据的方法,只会调用onConfigurationChanged(Configuration newConfig)方法

Fragment 中 setRetainInstance方法介绍 
17. Fragment还可以通过setRetainInstance(boolean)来保存自定义的对象数据(Fragment中都有此方法,Activity中无此方法), 当在Fragment的onCreate()方法中调用了setRetainInstance(true)后,Activity被系统销毁又重新创建时(如屏幕旋转),可以不完全销毁Fragment,Fragment中的成员变量值会保留,恢复时跳过了onDestroy()和onCreate()