Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

今日长缨在手,何时缚住苍龙。这篇文章主要讲述Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment相关的知识,希望能为你提供帮助。
该文章是一个系列文章,是本人在android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!
前言上一篇文章中我们使用底部导航+Fragment的方式实现了Android主流App中大都存在的设计。并命名其为“Fragment最佳实践”,作为想到单独使用Fragment的用户来说,这个说法并不夸大,它解决了许多用户在使用Fragment时产生的这样那样可见或不可见的问题。不过Fragment还有其他的使用方式,就是我们本章要介绍的。(本来是介绍ListView的,等着ListView的读者不好意思了,我会很快更新的。)
注:为什么临时插入这一章,因为有读者在上一篇文章中评论了,我觉得大有道理,感谢

Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

文章图片

这里我就不打码了,,哈哈哈哈
TabLayout TabLayout的静态使用TabLayout是Android 5.0之后Google提供的一系列Material Design设计规范中的一个控件。我们在布局文件中可以这样使用

< android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" app:tabIndicatorHeight="0dp" app:tabSelectedTextColor="@color/colorPrimary" > < android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab 1"/> < android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab 2"/> < android.support.design.widget.TabItem android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tab 3"/> < /android.support.design.widget.TabLayout>

TabLayout间接继承于ViewGroup,其内可包含0到n个TabItem,这个TabItem就是我们经常使用的标签,其是个自定义View
,这样我们就定义了一个包含3个标签页的TabLayout。其运行结果如下图:
Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

文章图片

TabLayout的动态使用在布局文件中我们可以很方便定义顶部/底部 导航的布局。我们来看一下在代码中的使用
public class TabActivity extends AppCompatActivity { @BindView(R.id.tab_layout) TabLayout mTabLayout; @BindView(R.id.view_pager) ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab); ButterKnife.bind(this); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1")); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2")); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3")); //为TabLayout添加Tab选择事件监听 mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) {//当标签被选择时回调}@Override public void onTabUnselected(TabLayout.Tab tab) {//当标签从选择变为非选择时回调}@Override public void onTabReselected(TabLayout.Tab tab) {//当标签被重新选择时回调} }); } }

关于运行结果我就不上图了,跟上面的运行结果是一样的。
TabLayout的更多属性关于TabLayout的更多属性以及使用的说明请查看其官方文档。在这里我们只关心TabLayout+ViewPager的化学反应,这个组合也是我们平常在开发中使用最多的。在此之前我们先介绍ViewPager
ViewPager先看看官方对ViewPager的说明
/* Layout manager that allows the user to flip left and right through pages of data.You supply an implementation of a {@link PagerAdapter} to generate the pages that the view shows.ViewPager is most often used in conjunction with {@link android.app.Fragment} There are standard adapters implemented for using fragments with the ViewPager, which cover the most common use cases.These are {@link android.support.v4.app.FragmentPagerAdapter} and {@link android.support.v4.app.FragmentStatePagerAdapter}; */public class ViewPager extends ViewGroup { }

上面英文的大致意思是ViewPager是一个布局管理类,这个类呢允许用户左右翻转页面。你必须实现一个PagerAdapter来生成这些显示的页面。ViewPager经常和Fragment一起使用。而且呢Google非常贴心的提供了两个类FragmentPagerAdapter和FragmentStatePagerAdapter来应付那些一般场景。
其实从ViewPager的说明中,我们基本上就能知道ViewPager是什么以及如何使用了。
PagerAdapterViewPager继承于ViewGroup,官方指导中就说了,你要自己实现PagerAdapter来生成显示的页面,那么我们来看看这个PagerAdapter
/** * Base class providing the adapter to populate pages inside of * a {@link ViewPager}.You will most likely want to use a more * specific implementation of this, such as * {@link android.support.v4.app.FragmentPagerAdapter} or * {@link android.support.v4.app.FragmentStatePagerAdapter}. * * < p> When you implement a PagerAdapter, you must override the following methods * at minimum:< /p> * < ul> * < li> {@link #instantiateItem(ViewGroup, int)}< /li> * < li> {@link #destroyItem(ViewGroup, int, Object)}< /li> * < li> {@link #getCount()}< /li> * < li> {@link #isViewFromObject(View, Object)}< /li> * < /ul> * / public abstract class PagerAdapter { }

其实我们在看一个不太了解的类的时候,通过源码上的关于这个类的说明就可以知道很多信息了。关于PagerAdapter的说明就是如此。
先说了一下PagerAdapter的作用,是一个基类提供适配器给ViewPager中的页面,如果你想使用特定的实现类,那么你可以看两个类FragmentPagerAdapter和FragmentStatePagerAdapter,这两个类继承了PagerAdapter,并实现了其抽象方法。
后面一段的意思是你如果想自定义你自己的PagerAdapter,那么你最少要实现这4个方法
  1. instantiateItem(ViewGroup, int)
  2. destroyItem(ViewGroup, int, Object)
  3. getCount()
  4. isViewFromObject(View, Object)
下面我们以代码的形式,说明这4个方法的含义以及如何使用
private class MyViewPagerAdapter extends PagerAdapter {/** * 获取View的总数 * * @return View总数 */ @Override public int getCount() { return 0; }/** * 为给定的位置创建相应的View。创建View之后,需要在该方法中自行添加到container中。 * * @param container ViewPager本身 * @param position给定的位置 * @return 提交给ViewPager进行保存的实例对象 */ @Override public Object instantiateItem(ViewGroup container, int position) { return super.instantiateItem(container, position); }/** * 给定的位置移除相应的View。 * * @param container ViewPager本身 * @param position给定的位置 * @param object在instantiateItem中提交给ViewPager进行保存的实例对象 */ @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); } /** * 确认View与实例对象是否相互对应。ViewPager内部用于获取View对应的ItemInfo。 * * @param viewViewPager显示的View内容 * @param object 在instantiateItem中提交给ViewPager进行保存的实例对象 * @return 是否相互对应 */ @Override public boolean isViewFromObject(View view, Object object) { return false; }}

这4个方法是必须的,,另外还有一些不是必须,但是可能会用到的
/** * 当ViewPager的内容有所变化时,进行调用。 * * @param container ViewPager本身 */ @Override public void startUpdate(ViewGroup container) { super.startUpdate(container); }/** * ViewPager调用该方法来通知PageAdapter当前ViewPager显示的主要项,提供给用户对主要项进行操作的方法。 * * @param container ViewPager本身 * @param position给定的位置 * @param object在instantiateItem中提交给ViewPager进行保存的实例对象 */ @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { super.setPrimaryItem(container, position, object); }/** * 较多的用于Design库中的TabLayout与ViewPager进行绑定时,提供显示的标题。 * * @param position 给定的位置 * @return 显示的标题 */ @Override public CharSequence getPageTitle(int position) { return super.getPageTitle(position); }

FragmentPagerAdapter上面呢只是列举说明了一下PagerAdapter,看起来有些枯燥,都是些说明,那么我们来看一下实践,ViewPager通畅跟Fragment一起使用,即其所管理的页面通畅是Fragment,所以Google提供了两个适配器FragmentPagerAdapter和FragmentStatePagerAdapter,我们这节分析FragmentPagerAdapter。
/** *真是不看不知道,一看吓一跳。FragmentPagerAdapter也是个抽象类, * */ public abstract class FragmentPagerAdapter extends PagerAdapter { private static final String TAG = "FragmentPagerAdapter"; private static final boolean DEBUG = false; private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; private Fragment mCurrentPrimaryItem = null; public FragmentPagerAdapter(FragmentManager fm) { mFragmentManager = fm; }/** *抽象方法,看来这个函数要子类自己实现了 * * @param position ViewPager中Item的位置 * @return 位置相关联的Fragment */ public abstract Fragment getItem(int position); @Override public void startUpdate(ViewGroup container) { if (container.getId() == View.NO_ID) { throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id"); } }/** * 为给定的位置创建相应的fragment。创建fragment之后,需要在该方法中自行添加到container中。 * * @param container ViewPager本身 * @param position给定的位置 * @return 提交给ViewPager进行保存的实例对象,这里是Fragment */ @Override public Object instantiateItem(ViewGroup container, int position) {if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); }final long itemId = getItemId(position); 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; }/** * 移除给定的位置相应的fragment。 * * @param container ViewPager本身 * @param position给定的位置 * @param object在instantiateItem中提交给ViewPager进行保存的实例对象 */ @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment)object).getView()); mCurTransaction.detach((Fragment)object); }@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; } }@Override public void finishUpdate(ViewGroup container) { if (mCurTransaction != null) { mCurTransaction.commitNowAllowingStateLoss(); mCurTransaction = null; } }@Override public boolean isViewFromObject(View view, Object object) { return ((Fragment)object).getView() == view; }@Override public Parcelable saveState() { return null; }@Override public void restoreState(Parcelable state, ClassLoader loader) { }/** * @param position ViewPager中Item的位置 * @return 唯一的ItemID */ public long getItemId(int position) { return position; }private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id; } }

代码比较少,总共也就100多行,逻辑也比较清晰明了,我们来着重分析instantiateItem和destroyItem
/** * 为给定的位置创建相应的fragment。创建fragment之后,需要在该方法中自行添加到container中。 * * @param container ViewPager本身 * @param position给定的位置 * @return 提交给ViewPager进行保存的实例对象,这里是Fragment */ @Override public Object instantiateItem(ViewGroup container, int position) { //开启事务 if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } //得到指定位置Item的ID final long itemId = getItemId(position); //根据id和ViewPager的ID生成item的name String name = makeFragmentName(container.getId(), itemId); //以name为Tag查找对应的Fragment Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) {//如果找到了 if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); //调用事务的attach mCurTransaction.attach(fragment); } else {//没找到 //通过我们重写的getItem方法得到相应fragment fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); //调用事务的add方法,并设置Tag mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } //如果frament不等于当前主要的Itemif (fragment != mCurrentPrimaryItem) { //设置其Menu不可见 fragment.setMenuVisibility(false); //设置其不可见 fragment.setUserVisibleHint(false); }return fragment; }

instantiateItem方法主要功能是为ViewPager生成Item。
那么destroyItem方法的主要功能是销毁ViwePager内的Item
@Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); }//调用事务的detach方法 mCurTransaction.detach((Fragment)object); }

FragmentStatePagerAdapter关于FragmentStatePagerAdapter,读者可自行分析,代码也不长。需要注意的地方是,两者对于destroyItem的不同实现
@Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)object).getView()); while (mSavedState.size() < = position) { mSavedState.add(null); } mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null); mFragments.set(position, null); //调用事务的remove方法 mCurTransaction.remove(fragment); }

小结ViewPager是个ViewGroup,与其他布局LinearLayout或者其他任意的ViewGroup并无本质的不同,它被Google建议与Fragment结伴使用,也是说ViewPager所包裹的是Fragment布局。ViewPager需要适配器PagerAdapter操作Fragment,这一点就像ListView需要适配器操作其内部的Item一样。
适配器PagerAdapter是个抽象类,并且依照官方说明,我们必须至少实现其4个重要方法。4个方法可能太多,所以Google提供了FragmentPagerAdapter以及FragmentStatePagerAdapter,这两个也是抽象类,不过我们的自定义Adapter只需要实现其中的getItem(int position)方法即可。
关于FragmentPagerAdapter以及FragmentStatePagerAdapter的不同,我这里再总结一下。FragmentPagerAdapter销毁item的时候最终调用FragmentTransaction的detach()方法,使用detach()会将view从viewtree中删除,和FragmentStatePagerAdapter中使用的remove()不同,此时fragment的状态依然保持着,在使用attach()时会再次调用onCreateView()来重绘视图,注意使用detach()后fragment.isAdded()方法将返回false。
实例更改后的TabActivity对应的布局文件
< ?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"< !--ViewPager--> < android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" > < /android.support.v4.view.ViewPager> < !--分割线--> < ImageView android:id="@+id/image_1" android:layout_width="match_parent" android:layout_height="1dp" android:background="#919292" android:layout_above="@+id/tab_layout"/> < !--TabLayout--> < android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" app:tabIndicatorHeight="0dp" app:tabSelectedTextColor="@color/colorPrimary" > < /android.support.design.widget.TabLayout> < /RelativeLayout>

更改后的TabActivity
public class TabActivity extends AppCompatActivity { @BindView(R.id.tab_layout) TabLayout mTabLayout; @BindView(R.id.view_pager) ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tab); ButterKnife.bind(this); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 1")); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 2")); mTabLayout.addTab(mTabLayout.newTab().setText("Tab 3")); //自定义的Adapter继承自FragmentPagerAdapter final PagerAdapter adapter = new PagerAdapter (getSupportFragmentManager(), mTabLayout.getTabCount()); //ViewPager设置Adapter mViewPager.setAdapter(adapter); //为ViewPager添加页面改变监听 mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout)); //为TabLayout添加Tab选择监听mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { mViewPager.setCurrentItem(tab.getPosition()); }@Override public void onTabUnselected(TabLayout.Tab tab) {}@Override public void onTabReselected(TabLayout.Tab tab) {} }); }}

而我们自定义的MyPagerAdapter也非常简单
public class MyPagerAdapter extends FragmentPagerAdapter { //fragment的数量 int nNumOfTabs; public MyPagerAdapter(FragmentManager fm, int nNumOfTabs) { super(fm); this.nNumOfTabs=nNumOfTabs; }/** * 重写getItem方法 * * @param position 指定的位置 * @return 特定的Fragment */ @Override public Fragment getItem(int position) { switch(position) { case 0: GoodsFragment tab1=new GoodsFragment(); return tab1; case 1: CategoryFragment tab2=new CategoryFragment(); return tab2; case 2: TaskFragment tab3=new TaskFragment(); return tab3; } return null; }/** * 重写getCount方法 * * @return fragment的数量 */ @Override public int getCount() { return nNumOfTabs; } }

ViewPager预加载与网络请求 ViewPager的预加载机制ViewPager可通过setOffscreenPageLimit(int limit)函数设置ViewPager预加载的View数目
public void setOffscreenPageLimit(int limit) { //DEFAULT_OFFSCREEN_PAGES=1if (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(); } }

可以看到该函数的源码,当我们传入的limit< 1时,limit还是被设置为1,当limit与成员变量mOffscreenPageLimit的值不同时(成员变量mOffscreenPageLimit的默认值为1),更新成员变量mOffscreenPageLimit的值,然后调用populate()函数。
而这个populate()函数就是给我们的ViewPager准备缓存页面并显示当前页面用的。
假如说我采用下面的方法调用setOffscreenPageLimit(2),此时ViewPager的简单示意图
Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

文章图片

注:从上面的代码也可以看出ViewPager最少会预加载一个页面。在本例中,也是我们在显示TAB1的时候,ViewPager已经加载了TAB2,具体方式是通过instantiateItem方法,该方法内部调用了我们重写的getItem方法,TAB2所表示的Fragment的onCreateView等相关生命周期方法会被回调。
ViewPager的网络请求ViewPager的预加载机制其实在某些时候是个很让人不爽的问题,比如我们在Fragment做网络请求数据的时候,我们网络请求的代码通常会放在onCreateView中,我们只是打开第1个Fragment,但是由于ViewPager会加载第2个Fragment,可能也执行了第2个Fragment的网络请求代码。
而避免上述问题的主要依靠
public void setUserVisibleHint(boolean isVisibleToUser)

setUserVisibleHint(boolean isVisibleToUser)是Fragment中的一个回调函数。当前Fragment可见时,setUserVisibleHint()回调,其中isVisibleToUser=true。当前Fragment由可见到不可见或实例化时,setUserVisibleHint()回调,其中isVisibleToUser=false。
setUserVisibleHint(boolean isVisibleToUser)调用时机
  1. 在Fragment实例化,即在ViewPager中,由于ViewPager默认会预加载左右两个页面。此时预加载页面回调的生命周期流程:setUserVisibleHint() --> onAttach() --> onCreate()--> onCreateView()--> onActivityCreate() --> onStart() --> onResume()
    此时,setUserVisibleHint() 中的参数为false,因为不可见。
  2. 在Fragment可见时,即ViewPager中滑动到当前页面时,因为已经预加载过了,之前生命周期已经走到onResume() ,所以现在只会回调:setUserVisibleHint()。
    此时,setUserVisibleHint() 中的参数为true,因为可见。
  3. 在Fragment由可见变为不可见,即ViewPager由当前页面滑动到另一个页面,因为还要保持当前页面的预加载过程,所以只会回调:setUserVisibleHint()。
    此时,setUserVisibleHint() 中的参数为false,因为不可见。
  4. 由TabLayout直接跳转到一个未预加载的页面,此时生命周期的回调过程:setUserVisibleHint() --> setUserVisibleHint() --> onAttach() --> onCreate()--> onCreateView()--> onActivityCreate() --> onStart()
    --> onResume()
    此时回调了两次setUserVisibleHint() ,一次代表初始化时,传入参数是false,一次代表可见时,传入参数是true。这种情况比较特殊。
总结:无论何时,setUserVisibleHint()都是先于其他生命周期的调用,并且初始化时调用,可见时调用,由可见转换成不可见时调用,一共三次时机。
ViewPager的网络请求的优化实现
我们在使用ViewPager+Fragment显示数据的时候,我们通常会把网络请求的操作放在onCreateView-> onResume之间的生命周期内。这可能带来的问题我们上面已经探讨了。那么怎么解决这个问题呢?
本篇总结我们在本篇博客中比较详细的探讨了TabLayout+ViewPager+Fragment的使用,我们在许多主流App中都能看到这种顶部、底部导航的效果,并且在此基础上我们探讨了TabLayout+ViewPager+Fragment网络数据加载问题。
我们希望Fragment可见时加载网络数据,不可见时不进行或者取消网络请求。
public abstract class BaseFragment extends Fragment { protected View rootView; private Unbinder mUnbinder; //当前Fragment是否处于可见状态标志,防止因ViewPager的缓存机制而导致回调函数的触发 private boolean isFragmentVisible; //是否是第一次开启网络加载 public boolean isFirst; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (rootView == null) rootView = inflater.inflate(getLayoutResource(), container, false); mUnbinder = ButterKnife.bind(this, rootView); initView(); //可见,但是并没有加载过 if (isFragmentVisible & & !isFirst) { onFragmentVisibleChange(true); } return rootView; }//获取布局文件 protected abstract int getLayoutResource(); //初始化view protected abstract void initView(); @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { isFragmentVisible = true; } if (rootView == null) { return; } //可见,并且没有加载过 if (!isFirst& & isFragmentVisible) { onFragmentVisibleChange(true); return; } //由可见——> 不可见 已经加载过 if (isFragmentVisible) { onFragmentVisibleChange(false); isFragmentVisible = false; } }@Override public void onDestroyView() { super.onDestroyView(); mUnbinder.unbind(); }/** * 当前fragment可见状态发生变化时会回调该方法 * * 如果当前fragment是第一次加载,等待onCreateView后才会回调该方法,其它情况回调时机跟 {@link #setUserVisibleHint(boolean)}一致 * 在该回调方法中你可以做一些加载数据操作,甚至是控件的操作. * * @param isVisible true不可见 -> 可见 *false 可见-> 不可见 */ protected void onFragmentVisibleChange(boolean isVisible) {}}

我们设计抽象基类BaseFragment,所有的公共行为我们都可以在这个基类中定义,那么我们的Fragment是否可见就是其中的一种行为,所以我们上面重写了Fragment的setUserVisibleHint方法。
public class GoodsFragment extends BaseFragment {@Override protected void onFragmentVisibleChange(boolean isVisible) { if(isVisible){ //可见,并且是第一次加载 lazyLoad(); }else{ //取消加载 } }private void lazyLoad() { if (!isFirst) { isFirst = true; } }@Override protected int getLayoutResource() { return R.layout.fragment_goods; }@Override protected void initView() {} }

我们设计GoodsFragment继承BaseFragment并重写其onFragmentVisibleChange以控制自身的网络请求。
本篇总结本篇为读者介绍了另外一种导航页切换的实现,我们使用TabLayout+ViewPager+Fragment的方式,其中读者需要重点理解以下几点
  1. ViewPager是个ViewGroup,它所关机的布局就是通常是我们的Fragment布局。
  2. ViewPager的预加载机制、可能带来的问题及如何解决。
  3. 理解PagerAdapter,以及如何实现它
  4. 理解Google提供了两个特定场景的PagerAdapter实现类FragmentPagerAdapter以及FragmentStatePagerAdapter,以及他们的主要区别
下篇预告下篇打算往Fragment中加点东西,ListView
【Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment】此致,敬礼









    推荐阅读