需求如下:顶部UI(这里随便写的),一些标签(服务器给的,这里写死),切换标签展示不同内容,内容分页展示,要求可以下拉刷新(重要页面,没刷新太low了吧
),刷新之后还停在当前标签下面,但是内容也要刷新,切换内容也会如此。(插插更健康:github源码有ScrollableLayout三段式悬浮布局,相同需求)。demo效果如下:




需求分析
:看上去很简单的一个页面,但是做起来不一定那么顺畅,看我一步步分析。选用的控件毫无疑问是SwipeRefreshLayout+TabLayout+ViewPager+Fragment+FragmentStatePagerAdapter 。


        分析1:由于标签数量length不确定(比较多),Fragment有预加载机制,如果使用默认的setOffscreenPageLimit提前加载下一个fragment,切换的时候导致看过的fragment被销毁/detach,再重新切换回去的话又得重新加载页面,请求api,体验很不好。如果使用setOffscreenPageLimit(length)预加载所有的fragment,更浪费资源,也不合理。所以需要使用Fragment的懒加载了,具体实现看后面的代码。

        分析2:为什么不用FragmentPagerAdapter而是用FragmentStatePagerAdapter?

        FragmentPagerAdapter类内的每一个生成的 Fragment 都将保存在内存之中
,因此适用于那些相对静态的页,数量也比较少的那种。
Fragment在切换的时候,不会销毁,而只是调用事务中的detach方法,我们只会把我们的Fragment的view销毁,而保留了以前的Fragment对象。所以通过这种方式创建的Fragment一直不会被销毁。


         FragmentSatetePagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除
,仅保留状态信息,释放其资源;而在页面需要显示时,恢复状态信息,这样更节省内存。

        综上,根据需求选定FragmentStatePagerAdapter!


代码实现:

fragment懒加载基类:
import android.support.v4.app.Fragment; /** * Created by zrg on 2018/1/2. *
Fragment的懒加载模式 * 使用方法: * ***** 必须继承
FragmentPagerAdapter/FragmentStatePagerAdapter,才可以对setUserVisibleHint()调用 *
setUserVisibleHint() 设置Fragment的可见状态 * getUserVisibleHint() 获取Fragment的可见状态 *
mIsVisible 是父类的成员变量,子类不需要重写 * mIsprepared 子类需要在OnCreateView()中重写 *
mHasLoadedOnce 子类需要在lazyLoad()中重写 * <p> * 接入步骤: *
1:继承BaseLazyFragment,实现lazyLoad() * 2:在onCreateView()中,添加以下代码: * mIsprepared =
true; * lazyLoad(); * 3:重写lazyLoad(){ * if (!mIsprepared || !mIsVisible ||
mHasLoadedOnce) { * return; * } * mHasLoadedOnce = true; * //UI和业务逻辑 * } *
4:拓展方法 * stopLoad()://当视图已经对用户不可见并且加载过数据, * //如果需要在切换到其他页面时停止加载数据,可以覆写此方法 *
//此方法只在Fragment之间切换生效 * visibleToUser(): 用户可看见当前的Fragment (神策埋点需要) *
inVisibleToUser():用户看不见当前的Fragment,包含Fragment之间切换和跳转到Activity (神策埋点需要) */
public abstract class BaseLazyFragment extends Fragment { /** *
判断当前的Fragment是否可见(相对于其他的Fragment) */ protected boolean mIsVisible; /** *
标志位,标志已经初始化完成 */ protected boolean mIsprepared; /** * 是否已被加载过一次,第二次就不再去请求数据了 */
protected boolean mHasLoadedOnce; @Override public void
setUserVisibleHint(boolean isVisibleToUser) { //设置Fragment的可见状态
super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint())
{//getUserVisibleHint获取Fragment可见状态 mIsVisible = true; onVisible(); } else {
mIsVisible = false; onInvisible(); } if (isResumed()) {
onVisibilityChangedToUser(isVisibleToUser); } } /** * 可见 */ protected void
onVisible() { lazyLoad(); } /** * 不可见 */ protected void onInvisible() {
stopLoad(); } /** * 延迟加载 * 子类必须重写此方法 */ protected abstract void lazyLoad(); /**
* 当视图已经对用户不可见并且加载过数据,如果需要在切换到其他页面时停止加载数据,可以覆写此方法 */ protected void stopLoad() {
} //region 神策埋点需要,统计Fragement 可见时间 @Override public void onResume() {
super.onResume(); if (getUserVisibleHint()) { onVisibilityChangedToUser(true);
} } @Override public void onPause() { super.onPause(); if
(getUserVisibleHint()) { onVisibilityChangedToUser(false); } } /** *
当Fragment对用户的可见性发生了改变的时候就会回调此方法 * * @param isVisibleToUser
true:用户能看见当前Fragment;false:用户看不见当前Fragment */ public void
onVisibilityChangedToUser(boolean isVisibleToUser) { if (isVisibleToUser) {
visibleToUser(); } else { inVisibleToUser(); } } protected void visibleToUser()
{ } protected void inVisibleToUser() { } //endregion }
ViewPagerActivity
public class ViewPagerActivity extends AppCompatActivity { private String[]
TITLES = {"语文", "数学", "英语", "化学", "物理", "生物"}; SwipeRefreshLayout
mSwipeRefreshLayout; TabLayout mTabLayout; ViewPager mViewPager; private
ViewPagerAdapter mAdapter; private int mCurrentIndex; @Override protected void
onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager); mSwipeRefreshLayout =
(SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout); mTabLayout =
(TabLayout) findViewById(R.id.tab_layout); mViewPager = (ViewPager)
findViewById(R.id.view_pager); mAdapter = new
ViewPagerAdapter(getSupportFragmentManager(), TITLES);
mViewPager.setAdapter(mAdapter); mViewPager.setCurrentItem(0);
mViewPager.setOffscreenPageLimit(TITLES.length - 1);
mTabLayout.setupWithViewPager(mViewPager); final Handler handler = new
Handler(Looper.getMainLooper()); mSwipeRefreshLayout.setOnRefreshListener(new
SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() {
loadData(); handler.postDelayed(new Runnable() { @Override public void run() {
mSwipeRefreshLayout.setRefreshing(false); } }, 1000); } });
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override public void onPageScrolled(int position, float positionOffset, int
positionOffsetPixels) { } @Override public void onPageSelected(int position) {
mCurrentIndex = position; } @Override public void onPageScrollStateChanged(int
state) { } }); } private void loadData() { String[] titles = {"语文1", "数学1",
"英语1", "化学1", "物理1", "生物1"}; mAdapter.setTitles(titles); } }


activity_view_pager
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefreshLayout" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hp.viewpagerdemo.ViewPagerActivity"> <LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> <ImageView android:layout_width="match_parent"
android:layout_height="120dp" android:src="@mipmap/ic_launcher"/>
<android.support.design.widget.TabLayout android:id="@+id/tab_layout"
style="@style/MyTablayoutStyle" android:layout_width="match_parent"
android:layout_height="wrap_content"/> <android.support.v4.view.ViewPager
android:id="@+id/view_pager" android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout><!--Tablayout 设置属性--> <style
name="MyTablayoutStyle" parent="Widget.Design.TabLayout"> <item name=
"tabIndicatorColor">#ff4f47</item> <item name="tabSelectedTextColor">#4d4d4d</
item> <item name="tabBackground">@android:color/white</item> <item name=
"tabTextAppearance">@style/MyTabLayoutTextAppearance</item> </style>
<!--TabLayout 小标题--><style name="MyTabLayoutTextAppearance" parent=
"TextAppearance.AppCompat.Widget.ActionBar.Title"> <item name=
"android:textColor">#999999</item> <item name="android:textSize">14sp</item> </
style>
ViewPagerAdapter
public class ViewPagerAdapter extends FragmentStatePagerAdapter { private
String[] mTitles; public ViewPagerAdapter(FragmentManager fm, String[] titles)
{ super(fm); mTitles = titles; } @Override public Fragment getItem(int
position) { EmptyFragment fragment =
EmptyFragment.newInstance(mTitles[position]); Log.e("zrg", "getItem:
当前位置position=" + position); return fragment; } @Override public int getCount()
{ return mTitles == null ? 0 : mTitles.length; } @Override public Object
instantiateItem(ViewGroup container, int position) { Log.e("zrg",
"instantiateItem: 当前位置position=" + position); return
super.instantiateItem(container, position); } @Override public CharSequence
getPageTitle(int position) { return mTitles == null ? "" : mTitles[position]; }
@Override public int getItemPosition(Object object) { return POSITION_NONE; }
public void setTitles(String[] titles) { mTitles = titles;
notifyDataSetChanged(); } }
EmptyFragment
public class EmptyFragment extends BaseLazyFragment { private static final
String ARG_TITLE = "arg_title"; TextView mTvClick; private String mTitle;
public EmptyFragment() { } public static EmptyFragment newInstance(String
title) { EmptyFragment fragment = new EmptyFragment(); Bundle bundle = new
Bundle(); bundle.putString(ARG_TITLE, title); fragment.setArguments(bundle);
return fragment; } @Override public void onCreate(@Nullable Bundle
savedInstanceState) { super.onCreate(savedInstanceState); mTitle =
getArguments().getString(ARG_TITLE); } @Nullable @Override public View
onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable
Bundle savedInstanceState) { View view =
inflater.inflate(R.layout.fragment_empty, container, false); mTvClick =
(TextView) view.findViewById(R.id.tv_click); mIsprepared = true; lazyLoad();
return view; } @Override protected void lazyLoad() { if (!mIsprepared ||
!mIsVisible || mHasLoadedOnce) { return; } mHasLoadedOnce = true; //UI和业务逻辑
Log.e("zrg", "lazyLoad: 当前的fragment是:" + mTitle); mTvClick.setText(mTitle); }
public String getTitle() { return mTitle; } }fragment_empty
<android.support.constraint.ConstraintLayout
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">
<TextView android:id="@+id/tv_click" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
注意事项:

    1、更新fragment不能使用重新设置adapter的方式,只能通过adapter.setTitle(args...)让adapter完成更新。
private void errorLoadData(){ String[] titles = {"语文1", "数学1", "英语1", "化学1",
"物理1", "生物1"}; mAdapter = new ViewPagerAdapter(getSupportFragmentManager(),
titles); mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mCurrentIndex);
mViewPager.setOffscreenPageLimit(titles.length - 1);
mTabLayout.setupWithViewPager(mViewPager); }    

源码地址:https://github.com/zrg1215/ViewPagerRefreshDemo
<https://github.com/zrg1215/ViewPagerRefreshDemo>
源码包括ScrollableLayout三段式悬浮布局,实现方式大致相同,由于切换的时候需要知道当前fragment,源码里有
,如果不了解ScrollableLayout的童鞋,传送门 <https://github.com/cpoopc/ScrollableLayout>。