-
Notifications
You must be signed in to change notification settings - Fork 20
/
LazyLoadBaseFragment.java
214 lines (177 loc) · 7.36 KB
/
LazyLoadBaseFragment.java
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
package com.wangshijia.www.fragmentliftcycle;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* @author wangshijia
* @date 2018/2/2
* Fragment 第一次可见状态应该在哪里通知用户 在 onResume 以后?
*/
public abstract class LazyLoadBaseFragment extends BaseLifeCircleFragment {
protected View rootView = null;
private boolean mIsFirstVisible = true;
private boolean isViewCreated = false;
private boolean currentVisibleState = false;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (rootView == null) {
rootView = inflater.inflate(getLayoutRes(), container, false);
}
initView(rootView);
return rootView;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
// 对于默认 tab 和 间隔 checked tab 需要等到 isViewCreated = true 后才可以通过此通知用户可见
// 这种情况下第一次可见不是在这里通知 因为 isViewCreated = false 成立,等从别的界面回到这里后会使用 onFragmentResume 通知可见
// 对于非默认 tab mIsFirstVisible = true 会一直保持到选择则这个 tab 的时候,因为在 onActivityCreated 会返回 false
if (isViewCreated) {
if (isVisibleToUser && !currentVisibleState) {
dispatchUserVisibleHint(true);
} else if (!isVisibleToUser && currentVisibleState) {
dispatchUserVisibleHint(false);
}
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewCreated = true;
// !isHidden() 默认为 true 在调用 hide show 的时候可以使用
if (!isHidden() && getUserVisibleHint()) {
// 这里的限制只能限制 A - > B 两层嵌套
dispatchUserVisibleHint(true);
}
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
LogUtils.e(getClass().getSimpleName() + " onHiddenChanged dispatchChildVisibleState hidden " + hidden);
if (hidden) {
dispatchUserVisibleHint(false);
} else {
dispatchUserVisibleHint(true);
}
}
@Override
public void onResume() {
super.onResume();
if (!mIsFirstVisible) {
if (!isHidden() && !currentVisibleState && getUserVisibleHint()) {
dispatchUserVisibleHint(true);
}
}
}
@Override
public void onPause() {
super.onPause();
// 当前 Fragment 包含子 Fragment 的时候 dispatchUserVisibleHint 内部本身就会通知子 Fragment 不可见
// 子 fragment 走到这里的时候自身又会调用一遍 ?
if (currentVisibleState && getUserVisibleHint()) {
dispatchUserVisibleHint(false);
}
}
private boolean isFragmentVisible(Fragment fragment) {
return !fragment.isHidden() && fragment.getUserVisibleHint();
}
/**
* 统一处理 显示隐藏
*
* @param visible
*/
private void dispatchUserVisibleHint(boolean visible) {
//当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment getUserVisibleHint = true
//但当父 fragment 不可见所以 currentVisibleState = false 直接 return 掉
// LogUtils.e(getClass().getSimpleName() + " dispatchUserVisibleHint isParentInvisible() " + isParentInvisible());
// 这里限制则可以限制多层嵌套的时候子 Fragment 的分发
if (visible && isParentInvisible()) return;
//
// //此处是对子 Fragment 不可见的限制,因为 子 Fragment 先于父 Fragment回调本方法 currentVisibleState 置位 false
// // 当父 dispatchChildVisibleState 的时候第二次回调本方法 visible = false 所以此处 visible 将直接返回
if (currentVisibleState == visible) {
return;
}
currentVisibleState = visible;
if (visible) {
if (mIsFirstVisible) {
mIsFirstVisible = false;
onFragmentFirstVisible();
}
onFragmentResume();
dispatchChildVisibleState(true);
} else {
dispatchChildVisibleState(false);
onFragmentPause();
}
}
/**
* 用于分发可见时间的时候父获取 fragment 是否隐藏
*
* @return true fragment 不可见, false 父 fragment 可见
*/
private boolean isParentInvisible() {
LazyLoadBaseFragment fragment = (LazyLoadBaseFragment) getParentFragment();
return fragment != null && !fragment.isSupportVisible();
}
private boolean isSupportVisible() {
return currentVisibleState;
}
/**
* 当前 Fragment 是 child 时候 作为缓存 Fragment 的子 fragment 的唯一或者嵌套 VP 的第一 fragment 时 getUserVisibleHint = true
* 但是由于父 Fragment 还进入可见状态所以自身也是不可见的, 这个方法可以存在是因为庆幸的是 父 fragment 的生命周期回调总是先于子 Fragment
* 所以在父 fragment 设置完成当前不可见状态后,需要通知子 Fragment 我不可见,你也不可见,
* <p>
* 因为 dispatchUserVisibleHint 中判断了 isParentInvisible 所以当 子 fragment 走到了 onActivityCreated 的时候直接 return 掉了
* <p>
* 当真正的外部 Fragment 可见的时候,走 setVisibleHint (VP 中)或者 onActivityCreated (hide show) 的时候
* 从对应的生命周期入口调用 dispatchChildVisibleState 通知子 Fragment 可见状态
*
* @param visible
*/
private void dispatchChildVisibleState(boolean visible) {
FragmentManager childFragmentManager = getChildFragmentManager();
List<Fragment> fragments = childFragmentManager.getFragments();
if (!fragments.isEmpty()) {
for (Fragment child : fragments) {
if (child instanceof LazyLoadBaseFragment && !child.isHidden() && child.getUserVisibleHint()) {
((LazyLoadBaseFragment) child).dispatchUserVisibleHint(visible);
}
}
}
}
public void onFragmentFirstVisible() {
LogUtils.e(getClass().getSimpleName() + " 对用户第一次可见");
}
public void onFragmentResume() {
LogUtils.e(getClass().getSimpleName() + " 对用户可见");
}
public void onFragmentPause() {
LogUtils.e(getClass().getSimpleName() + " 对用户不可见");
}
@Override
public void onDestroyView() {
super.onDestroyView();
isViewCreated = false;
mIsFirstVisible = true;
}
/**
* 返回布局 resId
*
* @return layoutId
*/
protected abstract int getLayoutRes();
/**
* 初始化view
*
* @param rootView
*/
protected abstract void initView(View rootView);
}