[AS3.0.1]关于ViewPager、FragmentPagerAdapter、Fragment源码研究 |
您所在的位置:网站首页 › 太阳系实时运行位置图片 › [AS3.0.1]关于ViewPager、FragmentPagerAdapter、Fragment源码研究 |
前言
关于fragment的生命周期大家几乎都懂了。不过还是贴下图吧!这张图挺不错的比较直观。 fragment的恢复是从onDestroyView到onCreateView 然后我们使用ViewPager的时候都知道,他会自动加载下一个fragment,以达到流畅的滑动,但是也会因为数据过多或者不断的刷新页面导致应用卡顿等问题。 所以想要让fragment不提前加载,查了下百度,大部分方案有以下两个 1.将v4包内的ViewPager拷贝出来将private static final int DEFAULT_OFFSCREEN_PAGES = 1;改为0就可以实现单独加载一个fragment的效果。 2.通过fragment中的public void setUserVisibleHint(boolean isVisibleToUser)方法来进行判断是否展示了fragment从而选择是否加载数据。 原理探究第二种方法让我决定看下ViewPager、FragmentPagerAdapter、Fragment三个之间的流程 log测试首先我们创建一个TestFragment用来打印所以的生命周期log,看一下具体的日志 TestFragment.java public class TestFragment extends Fragment { private View view; private String name = ""; public static TestFragment newInstance(String name) { TestFragment fragment = new TestFragment(); Bundle bundle = new Bundle(); bundle.putString("name", name); fragment.setArguments(bundle); return fragment; } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); try { name = getArguments().getString("name", ""); } catch (Exception e) { e.printStackTrace(); } Log.e("-s-", "name = " + name); if (getUserVisibleHint()) { Log.e("-s-sssssss", name + "fragment is show."); } else { Log.e("-s-sssssss", name + "fragment is hide."); } } @Override public void onAttach(Context context) { super.onAttach(context); Log.e("-s-", name + "fragment is onAttach."); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("-s-", name + "fragment is onCreate."); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (view == null) { Log.e("-s-", name + "fragment is onCreateView."); view = inflater.inflate(R.layout.fragment_test, null); TextView tv = view.findViewById(R.id.tv_test_fm); tv.setText(name); } else { Log.e("-s-", name + "fragment is onCreateView ===> oldView"); } ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) { parent.removeView(view); } return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.e("-s-", name + "fragment is onActivityCreated."); } @Override public void onStart() { super.onStart(); Log.e("-s-", name + "fragment is onStart."); } @Override public void onResume() { super.onResume(); Log.e("-s-", name + "fragment is onResume."); } @Override public void onPause() { super.onPause(); Log.e("-s-", name + "fragment is onPause."); } @Override public void onStop() { super.onStop(); Log.e("-s-", name + "fragment is onStop."); } @Override public void onDestroyView() { Log.e("-s-", name + "fragment is onDestroyView."); super.onDestroyView(); } @Override public void onDestroy() { Log.e("-s-", name + "fragment is onDestroy."); super.onDestroy(); } @Override public void onDetach() { Log.e("-s-", name + "fragment is onDetach."); super.onDetach(); } }Activity正常调用FragmentPagerAdapter和ViewPager 打开app初始如下log E/-s-: name = first E/-s-sssssss: firstfragment is hide. E/-s-: name = second E/-s-sssssss: secondfragment is hide. E/-s-: name = first E/-s-sssssss: firstfragment is show. E/-s-: firstfragment is onAttach. E/-s-: firstfragment is onCreate. E/-s-: secondfragment is onAttach. E/-s-: secondfragment is onCreate. E/-s-: firstfragment is onCreateView. E/-s-: firstfragment is onActivityCreated. E/-s-: firstfragment is onStart. E/-s-: firstfragment is onResume. E/-s-: secondfragment is onCreateView. E/-s-: secondfragment is onActivityCreated. E/-s-: secondfragment is onStart. E/-s-: secondfragment is onResume.发现开始最先被调用的方法其实是setUserVisibleHint而且确实创建了两个fragment,并且都执行到了onResume 之后我们滑动ViewPager将页面滑动到第二页,新log如下 E/-s-: name = third E/-s-sssssss: thirdfragment is hide. E/-s-: name = first E/-s-sssssss: firstfragment is hide. E/-s-: name = second E/-s-sssssss: secondfragment is show. E/-s-: thirdfragment is onAttach. E/-s-: thirdfragment is onCreate. E/-s-: thirdfragment is onCreateView. E/-s-: thirdfragment is onActivityCreated. E/-s-: thirdfragment is onStart. E/-s-: thirdfragment is onResume.因为ViewPager的预加载将第三页也加载出来了,并且我们看到第一页被隐藏掉了。 在继续滑到第三页,log如下 E/-s-: name = four E/-s-sssssss: fourfragment is hide. E/-s-: name = second E/-s-sssssss: secondfragment is hide. E/-s-: name = third E/-s-sssssss: thirdfragment is show. E/-s-: fourfragment is onAttach. E/-s-: fourfragment is onCreate. E/-s-: firstfragment is onPause. E/-s-: firstfragment is onStop. E/-s-: firstfragment is onDestroyView. E/-s-: fourfragment is onCreateView. E/-s-: fourfragment is onActivityCreated. E/-s-: fourfragment is onStart. E/-s-: fourfragment is onResume.由于默认的ViewPager设置的setOffscreenPageLimit是1所以目前页面加载到是第三页,保存的fragment有第二页(被隐藏)第三页(正在显示)第四页(预加载,被隐藏),而第一页就被关闭掉了。 最后再将第三页滑动回第二页,log如下 E/-s-: name = first E/-s-sssssss: firstfragment is hide. E/-s-: name = third E/-s-sssssss: thirdfragment is hide. E/-s-: name = second E/-s-sssssss: secondfragment is show. E/-s-: firstfragment is onCreateView ===> oldView E/-s-: firstfragment is onActivityCreated. E/-s-: fourfragment is onPause. E/-s-: fourfragment is onStop. E/-s-: fourfragment is onDestroyView. E/-s-: firstfragment is onStart. E/-s-: firstfragment is onResume.至此我得到如下结论 1. ViewPager必定会预加载左右两边的fragment 2. 当出现过的fragment多于设置的setOffscreenPageLimit数量就会将除了预加载和前一页的fragment释放掉 源码排查关于上面的log之后我们就可以看下源码方法的调用情况 首先先点击setUserVisibleHint看下调用的情况 很直观的可以看到在FragmentPagerAdapter中有3次调用,进到调用的地方看下源码 主要在两个方法中设置的fragment的是否显示instantiateItem和setPrimaryItem 以下是源码 @SuppressWarnings("ReferenceEquality") @Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } @SuppressWarnings("ReferenceEquality") @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment)object; if (fragment != mCurrentPrimaryItem) { if (mCurrentPrimaryItem != null) { mCurrentPrimaryItem.setMenuVisibility(false); mCurrentPrimaryItem.setUserVisibleHint(false); } if (fragment != null) { fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; } }第一个instantiateItem很明显我们知道是Adapter添加fragment的代码 而后面一个setPrimaryItem设置属性的方法可以看出来是用于判断,当前页面的fragment是否是ViewPager滑动到的fragment,我们在看下setPrimaryItem的调用 可以看到就在ViewPager中Adapter使用了一次是在populate方法内,看方法名,我们可以知道这个ViewPager在填充fragment的方法。 至此我们就可以知道setUserVisibleHint的具体逻辑了。 首先先向FragmentPagerAdapter中加入多个fragment对象 ViewPager通过setOffscreenPageLimit中的值来确认需要填充的fragment数量,即调用populate方法 populate方法先确认需要添加的个数让Adapter调用instantiateItem方法加入fragment 最后让Adapter调用setPrimaryItem方法来设置fragment的setUserVisibleHint属性 这样就有了我们一开始看到的log 开始需要预加载1个fragment就调用了2次instantiateItem,然后再设置setPrimaryItem来设置显示与否。 之后滑到第二页是调用了1次instantiateItem,然后设置setPrimaryItem这个时候有第一页和第二页,所以设置第一页隐藏,第二页显示。 总结通过逻辑我们可以知道为什么可以在setUserVisibleHint方法中来控制是否加载fragment网络数据,即百度到的fragment懒加载。 本篇就算是一段记录吧! 资料Fragment的setUserVisibleHint详解 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |