1 ViewPager布局之因
![](https://img.haomeiwen.com/i3171298/7e07178b09b7e66f.png)
- 原因:
根据源码的onMeasure()方法,由于他先测量的自己,其MeasureSpec值和自身没有关系,只和父控件的限制有关.
//ViewPager源码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// For simple implementation, our internal size is always 0.
// We depend on the container to specify the layout size of
// our view. We can't really know what it is since we will be
// adding and removing different arbitrary views and do not
// want the layout to change as this happens.
setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
getDefaultSize(0, heightMeasureSpec));
...
}
- 解决方案
重写ViewPager 的onMeasure()方法,测量子view的height,将其中的最大值设置为ViewPager的heightMeasureSpec后,再调用super.onMeasure(widthMeasureSpec, heightMeasureSpec);
传入参数
public class MyViewPager extends ViewPager {
private static final String TAG = "MyViewPager";
public MyViewPager(@NonNull Context context) {
super(context);
}
public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > height) {
height = h;
}
Log.d(TAG, "onMeasure: " + h);
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
2 缓存机制源码层解析
![](https://img.haomeiwen.com/i3171298/5c9e7a4072e004e1.png)
- 为什么设置的缓存页面数不能低于1
//源码
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
DEFAULT_OFFSCREEN_PAGES
的值=1,如果设置的值小于1,则源码中设置为1.
-
2 populate()方法进行布局和缓存
populate()生命周期和adapter函数紧密绑定.意味着adapter所有流程都由populate()控制->每个item的管理都由populate()->缓存由populate()控制
image.png
3 ViewPager常规懒加载
![](https://img.haomeiwen.com/i3171298/31348d08a376fd49.png)
![](https://img.haomeiwen.com/i3171298/5129b2782856c3bf.png)
-
生命周期
image.png
image.png
-
Framement三种状态分发
image.png
![](https://img.haomeiwen.com/i3171298/4120dc611fab31e7.png)
4 ViewPager深层嵌套懒加载
-
问题复查
image.png
![](https://img.haomeiwen.com/i3171298/0d77455674e3890f.png)
-
解决方案
image.png
5 懒加载的BaseFragment基类示例
public abstract class LazyFragment3 extends Fragment {
private static final String TAG = "LazyFragment3";
//fragment 生命周期:
// onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume -> onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
//对于 ViewPager + Fragment 的实现我们需要关注的几个生命周期有:
//onCreatedView + onActivityCreated + onResume + onPause + onDestroyView
protected View rootView = null;
boolean isViewCreated = false;
boolean currentVisibleState = false;
boolean mIsFirstVisible = true;
FragmentDelegater mFragmentDelegater;
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (rootView == null) {
rootView = inflater.inflate(getLayoutRes(), container, false);
}
initView(rootView);
isViewCreated = true;
logD("onCreateView: ");
//初始化的时候,判断当前fragment可见状态
//todo, isHidden()什么时候调用?原理
if (!isHidden() && getUserVisibleHint()) {
dispatchUserVisibleHint(true);
}
return rootView;
}
protected abstract int getLayoutRes();
protected abstract void initView(View view);
//修改fragment的可见性
//setUserVisibleHint 被调用有两种情况:1) 在切换tab的时候,会先于所有fragment的其他生命周期,先调用这个函数,可以看log
//2)对于之前已经调用过setUserVisibleHint 方法的fragment后,让fragment从可见到不可见之间状态的变化
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
logD("setUserVisibleHint: " + isVisibleToUser);
// 对于情况1)不予处理,用 isViewCreated 进行判断,如果isViewCreated false,说明它没有被创建
if (isViewCreated) {
//对于情况2)要分情况考虑,如果是不可见->可见是下面的情况 2.1),如果是可见->不可见是下面的情况2.2)
//对于2.1)我们需要如何判断呢?首先必须是可见的(isVisibleToUser 为true)而且只有当可见状态进行改变的时候才需要切换,否则会出现反复调用的情况
//从而导致事件分发带来的多次更新
if (isVisibleToUser && !currentVisibleState ) {
dispatchUserVisibleHint(true);
} else if (!isVisibleToUser && currentVisibleState) {
dispatchUserVisibleHint(false);
}
}
}
/**
* 统一处理用户可见信息分发
* @param isVisible
*/
private void dispatchUserVisibleHint(boolean isVisible) {
logD( "dispatchUserVisibleHint: " + isVisible);
//事实上作为父 Fragment 的 BottomTabFragment2 并没有分发可见事件,
// 他通过 getUserVisibleHint() 得到的结果为 false,首先我想到的
// 是能在负责分发事件的方法中判断一下当前父 fragment 是否可见,
// 如果父 fragment 不可见我们就不进行可见事件的分发
if (isVisible && isParentInvisible()) {
return;
}
//为了代码严谨
if (currentVisibleState == isVisible) {
return;
}
currentVisibleState = isVisible;
if (isVisible) {
if (mIsFirstVisible) {
mIsFirstVisible = false;
onFragmentFirstVisible();
}
onFragmentResume();
//在双重ViewPager嵌套的情况下,第一次滑到Frgment 嵌套ViewPager(fragment)的场景的时候
//此时只会加载外层Fragment的数据,而不会加载内嵌viewPager中的fragment的数据,因此,我们
//需要在此增加一个当外层Fragment可见的时候,分发可见事件给自己内嵌的所有Fragment显示
// dispatchChildVisibleState(true);
} else {
onFragmentPause();
// dispatchChildVisibleState(false);
}
}
private boolean isParentInvisible() {
Fragment parentFragment = getParentFragment();
if (parentFragment instanceof LazyFragment3) {
LazyFragment3 fragment = (LazyFragment3)parentFragment;
return !fragment.isSupportVisible();
}
return false;
}
private boolean isSupportVisible() {
return currentVisibleState;
}
private void dispatchChildVisibleState(boolean visible) {
FragmentManager fragmentManager = getChildFragmentManager();
List<Fragment> fragments = fragmentManager.getFragments();
if (fragments != null) {
for (Fragment fragment: fragments) {
if (fragment instanceof LazyFragment3 &&
!fragment.isHidden() &&
fragment.getUserVisibleHint()) {
((LazyFragment3)fragment).dispatchUserVisibleHint(visible);
}
}
}
}
/**
*
* 用FragmentTransaction来控制fragment的hide和show时,
* 那么这个方法就会被调用。每当你对某个Fragment使用hide
* 或者是show的时候,那么这个Fragment就会自动调用这个方法。
* https://blog.csdn.net/u013278099/article/details/72869175
* @param hidden
*/
@Override
public void onHiddenChanged(boolean hidden) {
logD("onHiddenChanged: " + hidden);
super.onHiddenChanged(hidden);
if (hidden) {
dispatchUserVisibleHint(false);
} else {
dispatchUserVisibleHint(true);
}
}
protected abstract void onFragmentFirstVisible();
protected void onFragmentResume() {
logD("onFragmentResume " + " 真正的resume,开始相关操作耗时");
}
protected void onFragmentPause() {
logD("onFragmentPause" + " 真正的Pause,结束相关操作耗时");
}
public void setFragmentDelegater(FragmentDelegater fragmentDelegater) {
mFragmentDelegater = fragmentDelegater;
}
@Override
public void onResume() {
super.onResume();
logD( "onResume: ");
//在滑动或者跳转的过程中,第一次创建fragment的时候均会调用onResume方法,类似于在tab1 滑到tab2,此时tab3会缓存,这个时候会调用tab3 fragment的
//onResume,所以,此时是不需要去调用 dispatchUserVisibleHint(true)的,因而出现了下面的if
if (!mIsFirstVisible) {
//由于Activit1 中如果有多个fragment,然后从Activity1 跳转到Activity2,此时会有多个fragment会在activity1缓存,此时,如果再从activity2跳转回
//activity1,这个时候会将所有的缓存的fragment进行onResume生命周期的重复,这个时候我们无需对所有缓存的fragnment 调用dispatchUserVisibleHint(true)
//我们只需要对可见的fragment进行加载,因此就有下面的if
if (!isHidden() && !currentVisibleState && getUserVisibleHint()) {
dispatchUserVisibleHint(true);
}
}
}
/**
* 只有当当前页面由可见状态转变到不可见状态时才需要调用 dispatchUserVisibleHint
* currentVisibleState && getUserVisibleHint() 能够限定是当前可见的 Fragment
* 当前 Fragment 包含子 Fragment 的时候 dispatchUserVisibleHint 内部本身就会通知子 Fragment 不可见
* 子 fragment 走到这里的时候自身又会调用一遍
*/
@Override
public void onPause() {
super.onPause();
logD( "onPause: ");
if (currentVisibleState && getUserVisibleHint()) {
dispatchUserVisibleHint(false);
}
}
@Override
public void onStop() {
super.onStop();
logD("onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
logD("onDestroyView");
isViewCreated = false;
mIsFirstVisible = false;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onDetach() {
super.onDetach();
}
private void logD(String infor) {
if (mFragmentDelegater != null) {
mFragmentDelegater.dumpLifeCycle(infor);
}
}
}
6 参考
https://www.jianshu.com/p/b0830f9b44bb
https://www.jianshu.com/p/ea5de4925b36
https://www.jianshu.com/p/204efa98a18d
网友评论