简书链接:【原创】fragment隐藏显示方式来控制切换tab支持横竖屏记忆回收,切换过快又使用动画导致重叠问题解决
文章字数:516,阅读全文大约需要2分钟

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

static long lastClickTime = 0;

public static Fragment switchPagesByHide(Map<Integer, PairX<String, SoftReference<Fragment>>> map, FragmentManager fragmentManager, int fragment_container_id, int key) {

Fragment fragment;
ArrayList<Fragment> needHideFragments = new ArrayList<>();
for (Integer currentKey : map.keySet()) {
if (currentKey == key) {
continue;
}
PairX<String, SoftReference<Fragment>> pair = map.get(currentKey);
fragment = pair.second.get();
if (fragment == null) {
fragment = fragmentManager.findFragmentByTag(getFragmentTagName(pair.first));
if (fragment != null && fragment.isAdded()) {
needHideFragments.add(fragment);
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
Log.w(TAG, "from tag fragment :" + fragment.getClass().getSimpleName());
}
}
continue;
} else if (fragment.isAdded()) {
needHideFragments.add(fragment);
} else {
fragment = fragmentManager.findFragmentByTag(getFragmentTagName(fragment.getClass()));
if (fragment != null && fragment.isAdded()) {
needHideFragments.add(fragment);
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
Log.w(TAG, " and instance not add,from tag fragment :" + fragment.getClass().getSimpleName());
}
}
}
}
PairX<String, SoftReference<Fragment>> pair = map.get(key);

Fragment currentFragment = pair.second.get();
Fragment tagCurrentFragment = fragmentManager.findFragmentByTag(getFragmentTagName(currentFragment.getClass().getName()));
if (currentFragment == null) {
if (tagCurrentFragment != null) {
currentFragment = tagCurrentFragment;
} else {
try {
currentFragment = (Fragment) Class.forName(pair.first).newInstance();
pair.second = new SoftReference<>(currentFragment);
} catch (Throwable e) {
e.printStackTrace();
}
}
} else if (tagCurrentFragment != null && tagCurrentFragment.isAdded()) {//当前如果tag里面有还是需要隐藏它.
needHideFragments.add(tagCurrentFragment);
}

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (Fragment fragment_ : needHideFragments) {
//if (!fragment_.isHidden()) {
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
if (fragment_.isHidden()) {
Log.w(TAG, "REPEAT_HIDE_" + fragment_.getClass().getSimpleName());
} else {

Log.w(TAG, "HIDE_" + fragment_.getClass().getSimpleName());
}
}
fragmentTransaction.hide(fragment_);
// popBackStackImmediate
//}
}
if (!(currentFragment instanceof MyFragment) && !AppUtils.isPadBySizeLarge(AppContext.getInstance())) {//因为隐藏了actionbar,所以会出现白屏,不能动画
long currentMs = System.currentTimeMillis();
if (lastClickTime == 0 || currentMs - lastClickTime > 5500) {
currentFragment.setEnterTransition(createTransition());
currentFragment.setReenterTransition(createTransition());
lastClickTime = currentMs;
if(BuildConfig.DEBUG){
Log.w(TAG,"_fragment anim:"+currentFragment.getClass().getSimpleName());
}
}else{//必须设置,否则依然会因为切换过快导致重复.
currentFragment.setEnterTransition(null);
currentFragment.setReenterTransition(null);
}

}
if (currentFragment.isAdded()) {
if (BuildConfig.DEBUG) {
Log.w(TAG, "FRAGMENT_SHOW:"+currentFragment.getClass().getSimpleName()+",before_hidden:" + currentFragment.isHidden() + ",save:" + currentFragment.isStateSaved() + ",detach:" + currentFragment.isDetached() + ",add:" + currentFragment.isAdded());
}
fragmentTransaction.show(currentFragment);
} else {
if (BuildConfig.DEBUG) {
Log.w(TAG, "FRAGMENT_ADD:" + currentFragment.isHidden() + ",save:" + currentFragment.isStateSaved() + ",detach:" + currentFragment.isDetached() + ",add:" + currentFragment.isAdded() + "," + currentFragment);
}
fragmentTransaction.add(fragment_container_id == 0 ? R.id.fragment_space : fragment_container_id, currentFragment, getFragmentTagName(currentFragment.getClass()));
}
fragmentTransaction.commitAllowingStateLoss();

return currentFragment;
}

解决办法,频繁点击就直接清除动画,不使用动画

如果还是有重叠,最后经过修改的版本是这样的

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113


public static Fragment switchPagesByHide(Map<Integer, PairX<String, SoftReference<Fragment>>> map, FragmentManager fragmentManager, int fragment_container_id, int key, DialogUtils.INotify<Fragment> onHiddenNotify, DialogUtils.INotify<Fragment> onShowNotify) {
Fragment fragment;
ArrayList<Fragment> needHideFragments = new ArrayList<>();
for (Integer loopKey : map.keySet()) {
if (loopKey == key) {
continue;
}
PairX<String, SoftReference<Fragment>> pair = map.get(loopKey);
fragment = pair.second.get();
if (fragment == null) {
fragment = fragmentManager.findFragmentByTag(getFragmentTagName(pair.first));
if (fragment != null) {
// if (fragment != null && fragment.isAdded()) {
needHideFragments.add(fragment);
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
Log.w(TAG, "from tag fragment :" + fragment.getClass().getSimpleName());
}
}
continue;
/* } else if (!fragment.isHidden()) {
needHideFragments.add(fragment);*/
} else {
// fragment = fragmentManager.findFragmentByTag(getFragmentTagName(fragment.getClass()));
// if (fragment != null && !fragment.isHidden()) {
needHideFragments.add(fragment);
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
Log.w(TAG, " and instance not add,from tag fragment :" + fragment.getClass().getSimpleName());
}
// }
}
}
PairX<String, SoftReference<Fragment>> pair = map.get(key);
Fragment currentFragment = pair.second.get();
Fragment tagCurrentFragment = currentFragment == null ? null : fragmentManager.findFragmentByTag(getFragmentTagName(currentFragment.getClass().getName()));
if (currentFragment == null) {
if (tagCurrentFragment != null) {
currentFragment = tagCurrentFragment;
} else {
try {
currentFragment = (Fragment) Class.forName(pair.first).newInstance();
pair.second = new SoftReference<>(currentFragment);
} catch (Throwable e) {
e.printStackTrace();
}
}
} else if (tagCurrentFragment != null && tagCurrentFragment.isAdded()) {//当前如果tag里面有还是需要隐藏它.
if (BuildConfig.DEBUG) {
Log.w(TAG, "FIND REPEAT FROM TAG");
}
}

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (Fragment fragment_ : needHideFragments) {
//if (!fragment_.isHidden()) {
if (BuildConfig.DEBUG && AppContext.DEBUG_) {
if (fragment_.isHidden()) {
Log.w(TAG, "REPEAT_HIDE_" + fragment_.getClass().getSimpleName());
} else {

Log.w(TAG, "HIDE_" + fragment_.getClass().getSimpleName());
}
}
fragmentTransaction.hide(fragment_);
if (fragment_.isResumed()) {
onHiddenNotify.onNotify(fragment_);
}
// popBackStackImmediate
//}
}
if (!(currentFragment instanceof MyFragment) && !AppUtils.isPadBySizeLarge(AppContext.getInstance())) {//因为隐藏了actionbar,所以会出现白屏,不能动画
long currentMs = System.currentTimeMillis();
/* if (lastClickTime == 0 || currentMs - lastClickTime > 3000) {
currentFragment.setEnterTransition(createTransition());
currentFragment.setReenterTransition(createTransition());
lastClickTime = currentMs;
if (BuildConfig.DEBUG) {
Log.w(TAG, "_fragment anim:" + currentFragment.getClass().getSimpleName());
}
} else {//必须设置,否则依然会因为切换过快导致重复.
currentFragment.setEnterTransition(null);
currentFragment.setReenterTransition(null);
}*/

}
if (currentFragment.isAdded()) {
if (BuildConfig.DEBUG) {
Log.w(TAG, "FRAGMENT_SHOW:" + currentFragment.getClass().getSimpleName() + ",before_hidden:" + currentFragment.isHidden() + ",save:" + currentFragment.isStateSaved() + ",detach:" + currentFragment.isDetached() + ",add:" + currentFragment.isAdded());
}
// fragmentTransaction.remove(currentFragment).commitNowAllowingStateLoss();// Fragment no longer exists for key f#0: unique id 5893b1ef-2180-4110-ac1a-36ffc36df0b1 viewPager2.setSaveEnabled(false);
fragmentTransaction.show(currentFragment);

if (onShowNotify != null && currentFragment.isResumed()) {
onShowNotify.onNotify(currentFragment);
}
} else {
if (BuildConfig.DEBUG) {
Log.w(TAG, "FRAGMENT_ADD_NEW :currentStatus:is hidden:" + currentFragment.isHidden() + ",save:" + currentFragment.isStateSaved() + ",detach:" + currentFragment.isDetached() + ",add:" + currentFragment.isAdded() + "," + currentFragment);
}
fragmentTransaction.add(fragment_container_id == 0 ? R.id.fragment_space : fragment_container_id, currentFragment, getFragmentTagName(currentFragment.getClass()));
if (currentFragment.isHidden()) {
fragmentTransaction.show(currentFragment);
}
if (onShowNotify != null && !currentFragment.isResumed()) {
onShowNotify.onNotify(currentFragment);
}
}
fragmentTransaction.commitAllowingStateLoss();

return currentFragment;
}

其实分别屏蔽了2个分支的逻辑,因为同时返回有重叠,但是概率非常低,横竖屏方式无法测试出来,快速点击还是有概率出现 ,所以改成了最终的代码

测试回收的方法非常简单,就是不固定横屏或者竖屏旋转屏幕就可以让activity重建,

在onCreate中 是先添加进去,然后从savedInstanceState 中重新找出来,找到的进行替换。这样保证了顺序问题,

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

ArrayMap<Integer, FragmentUtil.PairX<String, SoftReference<Fragment>>> fragments = new ArrayMap<>();


fragments.put(R.id.navigation_home, pairHome);
fragments.put(R.id.navigation_app, new FragmentUtil.PairX<String, SoftReference<Fragment>>(AppModuleNewStyleFragment.class.getName(), new SoftReference<>(new AppModuleNewStyleFragment())));
fragments.put(R.id.navigation_reportform, new FragmentUtil.PairX<String, SoftReference<Fragment>>(ReportFormsFragment.class.getName(), new SoftReference<>(new ReportFormsFragment())));
fragments.put(R.id.navigation_notifications, new FragmentUtil.PairX<String, SoftReference<Fragment>>(NotificationsFragment.class.getName(), new SoftReference<>(new NotificationsFragment())));
fragments.put(R.id.navigation_my, new FragmentUtil.PairX<String, SoftReference<Fragment>>(MyFragment.class.getName(), new SoftReference<>(new MyFragment())));



if (savedInstanceState != null) {
int tab_id = savedInstanceState.getInt("tab_id");
for (Integer key : fragments.keySet()) {
Fragment fragment = getSupportFragmentManager().getFragment(savedInstanceState, key + "");
if (fragment != null) {
fragments.put(key, new FragmentUtil.PairX<String, SoftReference<Fragment>>(fragment.getClass().getName(), new SoftReference<>(fragment)));
if (BuildConfig.DEBUG) {
Log.w(TAG, "" + fragment.getClass().getName() + "从存储中恢复了");
}
}
}
binding.navView.setSelectedItemId(tab_id);
}

在保存状态方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.w(TAG, "onSaveInstanceState");
for (Map.Entry<Integer, FragmentUtil.PairX<String, SoftReference<Fragment>>> entry : fragments.entrySet()) {
FragmentUtil.PairX<String, SoftReference<Fragment>> pair = entry.getValue();
if (pair.second.get() != null) {
Fragment fragment = pair.second.get();
if (fragment != null && fragment.isAdded()) {
getSupportFragmentManager().putFragment(outState, entry.getKey() + "", fragment);
}
} else {

}
}
outState.putInt("tab_id", getBinding().navView.getSelectedItemId());
}

PairX类

1
2
3
4
5
6
7
8
9
10
11
public static class PairX<First, Second> {
public First first;
public Second second;

public PairX(First first, Second second) {
this.first = first;
this.second = second;
}
}


我这里用弱引用是为了解决被回收了,但是依然被activity持有的问题,不然我为什么不直接用replace方法呢,replace是我以前常用的方法,然后使用强引用,但是这样不会自动回收replace之前的fragment吧。

重叠的坑很多,我顺便给几个参考链接
各种坑的解决
https://blog.51cto.com/u_15303287/3089390
fragment自身法隐藏解决重叠
https://blog.csdn.net/weixin_33805557/article/details/92344513
https://www.jianshu.com/p/d9143a92ad94