安卓使用Fragment出现重叠现象解决办法

来源:互联网 发布:怎么注册邮箱登录淘宝 编辑:程序博客网 时间:2024/06/05 21:05
转载自:http://blog.csdn.net/jiangxuqaz/article/details/51707031

参考:

http://stackoverflow.com/questions/16189088/overlapping-hidden-fragments-after-application-gets-killed-and-restored

http://stackoverflow.com/questions/18274732/android-fragments-overlapping-issue?answertab=active#tab-top

Fragment重叠问题相信很多开发者都遇到个这个问题,也解决个这个问题,前段时间偶然发现,公司项目偶然出现了Fragment重叠的Bug,心里不由一紧,赶紧去stackoverflow搜索了一番,找到了好几种解决方案,最终问题是解决了,不过心里留下了很多疑问(为什么会出现重叠?为什么这么处理之后可以解决问题?这样写会不会引发其他问题?),带着我决定写个Demo去分析下每种解决方法的原理以及可能带来的负面影响。

一、问题重现

Demo是常见的用Fragment实现的Tab切换,拿了网上现成的Demo改了一下,先给个Fragment重叠的效果图:

这里写图片描述

在Fragment切换时,采用的show/hide的方式,原理是显示某个Fragment时,先把其他几个Fragment先隐藏掉:

private void setTabSelection(int index) {        clearSelection();        FragmentTransaction transaction = fragmentManager.beginTransaction();        hideFragments(transaction);        switch (index) {            case 0:                messageImage.setImageResource(R.drawable.message_selected);                messageText.setTextColor(Color.WHITE);                if (messageFragment == null) {                    messageFragment = new NormalListFragment();                    transaction.add(R.id.content, messageFragment);                } else {                    transaction.show(messageFragment);                }                break;            case 1:                contactsImage.setImageResource(R.drawable.contacts_selected);                contactsText.setTextColor(Color.WHITE);                if (contactsFragment == null) {                    contactsFragment = new ContactsFragment();                    transaction.add(R.id.content, contactsFragment);                } else {                    transaction.show(contactsFragment);                }                break;            case 2:                newsImage.setImageResource(R.drawable.news_selected);                newsText.setTextColor(Color.WHITE);                if (newsFragment == null) {                    newsFragment = new NewsFragment();                    transaction.add(R.id.content, newsFragment);                } else {                    transaction.show(newsFragment);                }                break;            case 3:            default:                settingImage.setImageResource(R.drawable.setting_selected);                settingText.setTextColor(Color.WHITE);                if (settingFragment == null) {                    settingFragment = new SettingFragment();                    transaction.add(R.id.content, settingFragment);                } else {                    transaction.show(settingFragment);                }                break;        }        transaction.commit();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

由于Fragment重叠问题是发生在某种特定的情况下,所以在常规环境下很难复现,所以需要在Android 手机开发者选项中把不保留活动这个选项打开,这样每次进入新的Activity,旧的Activity就会马上销毁。

这里写图片描述

问题分析

假设现在处在第一个Tab 图片列表(NormalListFragment),然后点击某个Item进入详情页,由于不保留活动,Fragment所在的Activity会销毁掉。然后,我们从详情页返回到图片列表,Activity会重建,Fragment会重新绑定, 整个过程Activity和Fragment的生命周期方法调用Log如下:

06-18 21:35:36.479 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onPause06-18 21:35:36.479 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onPause06-18 21:35:36.893 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onSaveInstanceState   Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624006</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624007=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624008</span>=android<span class="hljs-preprocessor">.support</span><span class="hljs-preprocessor">.v</span>7<span class="hljs-preprocessor">.widget</span><span class="hljs-preprocessor">.Toolbar</span>$SavedState@bda98cc, 2131624009=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624052</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624053=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624054</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624055=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624056</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624057=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624058</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624059=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624060</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624061=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624062</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624063=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624064</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e}, android:focusedViewId=2131624102}], android:support:fragments=android.support.v4.app.FragmentManagerState@696715}]06-18 21:35:36.893 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onStop06-18 21:35:36.893 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onStop06-18 21:35:36.910 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onDestroyView06-18 21:35:36.914 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onDestroy06-18 21:35:36.914 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onDetach06-18 21:35:36.914 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onDestroy06-18 21:35:39.527 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onAttach06-18 21:35:39.527 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onCreate06-18 21:35:39.527 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity   onCreate   Bundle[{android:viewHierarchyState=Bundle[mParcelledData.dataSize=1512], android:support:fragments=android.support.v4.app.FragmentManagerState@e7e75b8}]06-18 21:35:39.666 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment   onCreateView   Bundle[{android:view_state={2131624102=AbsListView.SavedState{402bf7e selectedId=-9223372036854775808 firstId=-1 viewTop=0 position=0 height=1557 filter=null checkState=null}}}]06-18 21:35:39.672 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment   onActivityCreated   Bundle[{android:view_state={2131624102=AbsListView.SavedState{402bf7e selectedId=-9223372036854775808 firstId=-1 viewTop=0 position=0 height=1557 filter=null checkState=null}}}]06-18 21:35:39.672 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onAttach06-18 21:35:39.672 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onCreate06-18 21:35:39.673 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment   onCreateView   null06-18 21:35:39.675 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment   onActivityCreated   null06-18 21:35:39.676 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onStart06-18 21:35:39.676 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onStart06-18 21:35:39.676 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onStart06-18 21:35:39.679 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onRestoreInstanceState  Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624006</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624007=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624008</span>=android<span class="hljs-preprocessor">.support</span><span class="hljs-preprocessor">.v</span>7<span class="hljs-preprocessor">.widget</span><span class="hljs-preprocessor">.Toolbar</span>$SavedState@80d4056, 2131624009=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624052</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624053=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624054</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624055=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624056</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624057=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624058</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624059=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624060</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624061=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624062</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624063=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624064</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e}, android:focusedViewId=2131624102}], android:support:fragments=android.support.v4.app.FragmentManagerState@e7e75b8}]06-18 21:35:39.680 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onResume06-18 21:35:39.680 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onResume06-18 21:35:39.680 3004-3004/com.jx.androiddemos I/FragmentLearn: NormalListFragment  onResume
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

从上面的Log可以发现,重新创建Activity时,NormalListFragment每个周期方法都走了两遍。这意味着同时创建了两个NormalListFragment实例,这个两个NormalListFragment一个是我代码里面主动创建的,另外一个则是上次Activity异常销毁时保存的,因为恢复的这个Fragment没有拿到引用,所以无法去做操作的(隐藏显示),这意味着我切换到其他tab时,这个Fragment会一直显示,这正是Fragment重叠问题的根源所在。

下面从源码角度证实Activity在异常情况下销毁时,会保存Fragment。

从Log第三行打印的Bundle的值我们可以发现,Activity在异常销毁时会调用onSaveInstanceState方法,系统会默认保存一些数据,包括Fragment

06-18 21:35:36.893 3004-3004/com.jx.androiddemos I/FragmentLearn: FragmentMainActivity  onSaveInstanceState   Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624006</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624007=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624008</span>=android<span class="hljs-preprocessor">.support</span><span class="hljs-preprocessor">.v</span>7<span class="hljs-preprocessor">.widget</span><span class="hljs-preprocessor">.Toolbar</span>$SavedState@bda98cc, 2131624009=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624052</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624053=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624054</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624055=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624056</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624057=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624058</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624059=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624060</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624061=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624062</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e, 2131624063=android.view.AbsSavedState$1<span class="hljs-localvars">@6962</span>f3e, <span class="hljs-number">2131624064</span>=android<span class="hljs-preprocessor">.view</span><span class="hljs-preprocessor">.AbsSavedState</span>$1@6962f3e}, android:focusedViewId=2131624102}], android:support:fragments=android.support.v4.app.FragmentManagerState@696715}]
  • 1
  • 2
  • 1
  • 2

这个我们从Activity源码中的onSaveInstanceState方法也能够确认:

 protected void onSaveInstanceState(Bundle outState) {        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());        Parcelable p = mFragments.saveAllState();        if (p != null) {            outState.putParcelable(FRAGMENTS_TAG, p);        }        getApplication().dispatchActivitySaveInstanceState(this, outState);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在onSaveInstanceState方法中,首先调用mFragments(FragmentManager)的saveAllState方法把Fragment数据保存到Parcelable 变量中,然后通过调用outState.putParcelable(FRAGMENTS_TAG, p);
保存到Bundle 中,FRAGMENTS_TAG这个常量

static final String FRAGMENTS_TAG = "android:support:fragments";
  • 1
  • 1

也正是上面Log Bundle 数据中 键值对的一个键:

android:support:fragments=android.support.v4.app.FragmentManagerState@696715}
  • 1
  • 1

Activity异常销毁时保存Fragment已经可以确认,那么这个保存Fragment在重新创建Activity时,怎么恢复的了?这就需要研究Activity的onCreate方法了

 protected void onCreate(@Nullable Bundle savedInstanceState) {        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);        if (mLastNonConfigurationInstances != null) {            mAllLoaderManagers = mLastNonConfigurationInstances.loaders;        }        if (mActivityInfo.parentActivityName != null) {            if (mActionBar == null) {                mEnableDefaultActionBarUp = true;            } else {                mActionBar.setDefaultDisplayHomeAsUpEnabled(true);            }        }        if (savedInstanceState != null) {            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null                    ? mLastNonConfigurationInstances.fragments : null);        }        mFragments.dispatchCreate();        getApplication().dispatchActivityCreated(this, savedInstanceState);        if (mVoiceInteractor != null) {            mVoiceInteractor.attachActivity(this);        }        mCalled = true;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

ActivityonCreate方法中和Fragment相关的应该是这几句:

 if (savedInstanceState != null) {            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null                    ? mLastNonConfigurationInstances.fragments : null);        }        mFragments.dispatchCreate();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

当savedInstanceState 不为空时,意味着Activity上次销毁时保存了数据,会掉用FragmentManager 的 restoreAllState 方法,这个方法比较长,就不贴出来了,这个方法主要作用就是从savedInstanceState 把保存的Fragment都取出来,实例化,绑定到当前Activity。
通过上面的分析,对Fragment的保存和恢复应该有了比较清楚的理解,也找到的Fragment重叠的根源所在,那么下一步就是如何解决问题。

解决方法

我在网上找到3中比较有代表性的解决方法,当然,实际可能有更多的解决方法,但每种方法的核心思想都是一样的,就是如何处理Activity异常销毁时保存的Fragment。

方法一、重写 onSaveInstanceState方法

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    ......    if (savedInstanceState != null) {        mCustomVariable = savedInstanceState.getInt("variable", 0);    }}@Overrideprotected void onSaveInstanceState(Bundle outState) {    //super.onSaveInstanceState(outState);    outState.putInt("variable", mCustomVariable);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这样重写之后,相当于不会调用Activity的onSaveInstanceState的方法保存系统默认数据,只保存自己需要的数据。Activity异常销毁时不会保存Fragment,当然也就不会再有重叠的问题出现。
不过这样处理是可能出现问题的,Activity的onSaveInstanceState方法不仅仅只是保存Fragment,还会保存获取焦点的View的状态,ActionBar,以及调用View的onSaveInstanceState 保存View的相关数据。

方法二、给每个Fragment根布局设置背景,拦截点击事件

 android:background="@android:color/white" android:clickable="true"
  • 1
  • 2
  • 1
  • 2

Fragment背景默认是透明的,所以我们能看到两个Fragment重叠在一起。当我们为每个Fragment添加背景之后,即使两个Fragment叠加在一起,我们也只看到一个。至于为什么要设置clickable=”true”,是因为两个Fragment叠加在一起,虽然我们只能看到上面那个,但是下面那个仍然能接收到事件。设置clickable=”true”时,上面的Fragment会拦截掉所有事件。
这样处理,能从视觉上解决问题,但是Activity异常销毁时,同一个Fragment同时出现两个实例的客观事实没有改变。有时你会发现Fragment中的某个网络接口明明应该只调用一次,Log却打印调用两次,其实是Fragment创建了两个实例。

方法三、用replace替代show/hide

用replace实现Tab切换的写法:

private void setTabSelection2(int index) {        clearSelection();        FragmentTransaction transaction = fragmentManager.beginTransaction();        switch (index) {            case 0:                messageImage.setImageResource(R.drawable.message_selected);                messageText.setTextColor(Color.WHITE);                if (messageFragment == null) {                    messageFragment = new NormalListFragment();                }                transaction.replace(R.id.content, messageFragment);                break;            case 1:                contactsImage.setImageResource(R.drawable.contacts_selected);                contactsText.setTextColor(Color.WHITE);                if (contactsFragment == null) {                    contactsFragment = new ContactsFragment();                }                transaction.replace(R.id.content, contactsFragment);                break;            case 2:                newsImage.setImageResource(R.drawable.news_selected);                newsText.setTextColor(Color.WHITE);                if (newsFragment == null) {                    newsFragment = new NewsFragment();                }                transaction.replace(R.id.content, newsFragment);                break;            case 3:            default:                settingImage.setImageResource(R.drawable.setting_selected);                settingText.setTextColor(Color.WHITE);                if (settingFragment == null) {                    settingFragment = new SettingFragment();                }                transaction.replace(R.id.content, settingFragment);                break;        }        transaction.commit();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

replace的做法是每次把Activity(准确的说是Activity添加Fragment的布局)的Fragment先全部移除掉,再添加新的Fragment,这样操作确保Activity的布局容器每次只会存在一个Fragment。当然不会出现重叠问题。
replace Fragment操作的源码,很容易看出是先移除容器包含的Fragment,然后再添加:

 case OP_REPLACE: {                    Fragment f = op.fragment;                    if (mManager.mAdded != null) {                        for (int i = 0; i < mManager.mAdded.size(); i++) {                            Fragment old = mManager.mAdded.get(i);                            if (FragmentManagerImpl.DEBUG) {                                Log.v(TAG,                                        "OP_REPLACE: adding=" + f + " old=" + old);                            }                            if (f == null || old.mContainerId == f.mContainerId) {                                if (old == f) {                                    op.fragment = f = null;                                } else {                                    if (op.removed == null) {                                        op.removed = new ArrayList<Fragment>();                                    }                                    op.removed.add(old);                                    old.mNextAnim = op.exitAnim;                                    if (mAddToBackStack) {                                        old.mBackStackNesting += 1;                                        if (FragmentManagerImpl.DEBUG) {                                            Log.v(TAG, "Bump nesting of "                                                    + old + " to " + old.mBackStackNesting);                                        }                                    }                                    mManager.removeFragment(old, mTransition, mTransitionStyle);                                }                            }                        }                    }                    if (f != null) {                        f.mNextAnim = op.enterAnim;                        mManager.addFragment(f, false);                    }                }                break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

当然,这样处理后每次点击Tab之后,每个Fragment都要重新创建实例,走周期方法,加载数据。具体能不能这样做,还要看业务上允不允许每次重新加载数据。

(function () {('pre.prettyprint code').each(function () { var lines = (this).text().split(\n).length;varnumbering = $('
    ').addClass('pre-numbering').hide(); (this).addClass(hasnumbering).parent().append(numbering); for (i = 1; i
    0 0
    原创粉丝点击