接下来对我们前日的情节开展落到实处,ViewPager刷新难点详解

ViewPager刷新难点详解

后天我们来共同达成“爱阅”首页滑动切换分类浏览阅读的功效,并将ViewPager和TabLayout结合起来用以完成顶部导航栏的归类突显,并追加点击火速切换分类的作用。ViewPager+TabLayout也是现阶段最烜赫一时的重组形式,我会首先对基础的知识点做一些助教,然后对大家明天的始末进行落到实处。

作者:李旺成

先是看一下效益图:

时间:2016年5月3日


点此进入目录:[干货] 十天 教您从创意到上线APP

一、PagerAdapter介绍

先看功用图

PageAdapter 使用示例

1、ViewPager简介

ViewPager是android扩充包v4包中的类,那个类可以让用户左右切换当前的view

  • ViewPager类直接接轨了ViewGroup类,所以它是一个容器类,能够在里边添加其他的view类。
  • ViewPager类必要一个PagerAdapter适配器类给它提供数据。
  • ViewPager平时和Fragment一起利用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。

PagerAdapter简介

ListView 大家应该都很熟谙吧!ListView 一般都须要一个 Adapter
来填充数据,如 ArrayAdapter、SimpleAdapter。Pager艾达pter 就是 ViewPager
的 Adapter,与 ListView 的 Adapter 成效一样。

ViewPager->PageAdapter == ListView->BaseAdapter

先看下法定介绍

2、ViewPager的适配器

上文的简介中涉嫌了PagerAdapter,其实和RecyclerView、ListView等控件使用同一,ViewPager须求设置PagerAdapter来已毕页面和数目标绑定,那一个PagerAdapter是一个基类适配器,大家平日用它来贯彻App教导图,它的子类有FragmentPagerAdapter和FragmentStatePagerAdapter,那五个子类适配器用于和Fragment一起使用,在安卓运用中它们似乎RecyclerView一样出现的往往。

法定介绍

PageAdapter 类

Page艾达pter 继承自
Object,继承结构参考意义不大,那老实看文档。文档上尚未提供示范代码,只是说了下要自定义
PageAdapter 须求贯彻上面多个方式:

  • instantiateItem(ViewGroup container, int
    position):
    该办法的功用是创设指定地方的页面视图。适配器有任务扩张即将创立的
    View 视图到此处给定的 container 中,那是为着确保在
    finishUpdate(viewGroup) 再次来到时 this is be done!
    再次回到值:再次来到一个表示新增视图页面的
    Object(Key),那里没要求非要重返视图本身,也可以那么些页面的别样容器。其实自己的领会是可以象征当前页面的任意值,只要您能够与您扩充的
    View 一一对应即可,比如 position 变量也得以做为 Key
  • destroyItem(ViewGroup container, int position, Object
    object):
    该方式的成效是移除一个加以地方的页面。适配器有义务从容器中去除那么些视图,那是为了保险在
    finishUpdate(viewGroup) 重返时视图可以被移除
  • getCount():归来当前有效视图的多少
  • isViewFromObject(View view, Object object):该函数用来判定
    instantiateItem() 函数所重临来的 Key
    与一个页面视图是还是不是是代表的同一个视图(即它俩是不是是对应的,对应的象征同一个
    View)
    重临值:如果对应的是同一个View,重回 true,否则再次回到 false

地点对 PageAdapter 的五个抽象方法做了简约表达,上面看看哪些采纳

(1)完毕一个最大旨的PagerAdapter
    public class AdapterViewpager extends PagerAdapter {
        private List<View> mViewList;

        public AdapterViewpager(List<View> mViewList) {
            this.mViewList = mViewList;
        }

        @Override
        public int getCount() {//必须实现
            return mViewList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {//必须实现
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {//必须实现,实例化
            container.addView(mViewList.get(position));
            return mViewList.get(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {//必须实现,销毁
            container.removeView(mViewList.get(position));
        }
    }
  • instantiateItem()
    可以看来instantiateItem()做了两件事,第一:将眼前视图添加到container中,第二:再次回到当前View。也就是说instantiateItem()的机能是成立指定地方的页面视图,并且适配器有义务扩张即将创造的View视图添加到这里给定的container中。它的再次回到值代表新增视图页面的Object(Key),那里没要求非要再次回到视图本身,也得以回到可以代表当前页面的任意值,只要你能够与您增添的View一一对应即可,比如position变量也得以做为Key。

  • isViewFromObject()
    该函数用来判定 instantiateItem() 函数所重返来的 Key
    与一个页面视图是还是不是是代表的同一个视图(即它俩是不是是对应的,对应的象征同一个
    View),假如对应的是同一个View回 true,否则再次来到 false。

  • destroyItem()
    该措施的作用是移除一个加以地方的页面,适配器有权利从容器中删除这几个视图,那是为了保险在
    finishUpdate(viewGroup) 再次来到时视图可以被移除。

唯独说道destroyItem()这一个办法的时候就只好提及ViewPager的刷新难题了,因为ViewPager的基础代谢并不是大家早期想的调用一下notifyDataSetChanged()就到位了这么简单的,咱们会在后文表明“爱阅”中相遇的坑和解决办法。

粗略利用

mContentVP.setAdapter(new PagerAdapter() {
    @Override
    public int getCount() {
        return dataList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = View.inflate(SimpleDemoActivity.this, R.layout.item_vp_demopageradapter, null);
        TextView pageNumTV = (TextView) view.findViewById(R.id.tv_pagenum);
        pageNumTV.setText("DIY-PageNum-" + dataList.get(position));
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

});

能够见到落成 PagerAdapter 与 BaseAdapter 很接近,只是 PagerAdapter 的
isViewFromObject() 与 instantiateItem() 方法须要优异了然下。那里为了简化
PagerAdapter 的行使,我做了个简易的包装:

public abstract class APagerAdapter<T> extends PagerAdapter {

    protected LayoutInflater mInflater;
    protected List<T> mDataList;
    private SparseArray<View> mViewSparseArray;

    public APagerAdapter(Context context, List<T> dataList) {
        mInflater = LayoutInflater.from(context);
        mDataList = dataList;
        mViewSparseArray = new SparseArray<View>(dataList.size());
    }

    @Override
    public int getCount() {
        if (mDataList == null) return 0;
        return mDataList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = mViewSparseArray.get(position);
        if (view == null) {
            view = getView(position);
            mViewSparseArray.put(position, view);
        }
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mViewSparseArray.get(position));
    }

    public abstract View getView(int position);

}

APagerAdapter 类模仿 ListView 的 BaseAdapter,抽象出一个 getView()
方法,在其间使用 SparesArray 缓存所有突显过的
View。这样使用就很简短了,继承 APagerAdapter,完结 getView()
方法即可(可以参见:DemoPagerAdapter.java)。

(2)已毕一个最中央的FragmentPager艾达pter
/**
 * Created by   : WGH.
 */
public class ViewPagerAdapter extends FragmentPagerAdapter {
    private ArrayList<Category> mCategoryList;
    private Context mContext;
    private Fragment mFragment;

    public ViewPagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        mContext = context;
        mCategoryList = DataCacheHelper.getInstance().getPagerChildCategories();
    }

    public void onCategorysChange(String key) {
        if (key != null) {
            mCategoryList = DataCache.getInstance().getChildCategorys(key);
            notifyDataSetChanged();
        } else {
            DLog.e("onCategorysChange() Error!");
        }
    }

    @Override
    public Fragment getItem(int position) {// 必须实现
        return ViewPagerFragment.newInstance(mContext, mCategoryList.get(position));
    }

    @Override
    public int getCount() {// 必须实现
        if (mCategoryList != null) {
            return mCategoryList.size();
        } else {
            return 0;
        }
    }

    @Override
    public CharSequence getPageTitle(int position) {// 选择性实现
        return mCategoryList.get(position).getName();
    }

    public String getFragmentTag(int viewPagerId, int fragmentPosition) {
        return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
    }

    public String getFragmentTag(int viewPagerId, int fragmentPosition) {
        return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

FragmentStatePager艾达pter的兑现和FragmentPagerAdapter的兑现均等就不在写了。而那里的getItemPosition()之所以这么写同样和ViewPager的刷新有关,大家后文讲解。

PagerAdapter 刷新的标题

三个适配器的界别:

PagerAdapter是基类适配器是一个通用的ViewPager适配器,比较PagerAdapter,FragmentPagerAdapter和FragmentStatePager艾达pter更令人瞩目于每一页是Fragment的情景,而那四个子类适配器使用情形也是有分其他。FragmentPagerAdapter适用于页面比较少的动静,FragmentStatePagerAdapter适用于页面相比较多的气象,我们得以从八个适配器的源码得知。

  • FragmentStatePagerAdapter

@Override
  public Object instantiateItem(ViewGroup container, int position) {
      if (mFragments.size() > position) {
          Fragment f = mFragments.get(position);// fragment被释放后这里得到的null值
          if (f != null) {
              return f;
          }
      }

      if (mCurTransaction == null) {
          mCurTransaction = mFragmentManager.beginTransaction();
      }

      Fragment fragment = getItem(position);// fragment被释放后或者是初次进入页面拿到新的Fragment实例
      if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
      if (mSavedState.size() > position) {
          Fragment.SavedState fss = mSavedState.get(position);
          if (fss != null) {
              fragment.setInitialSavedState(fss);
          }
      }
      while (mFragments.size() <= position) {
          mFragments.add(null);
      }
      fragment.setMenuVisibility(false);
      fragment.setUserVisibleHint(false);
      mFragments.set(position, fragment);
      mCurTransaction.add(container.getId(), fragment);// 新的Fragment实例 是add上去的

      return fragment;
  }

 @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);// 真正释放了fragment实例

      mCurTransaction.remove(fragment);
  }
  • FragmentPagerAdapter

    @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);// 因为fragment实例没有被真正释放,所以可以直接attach效率高。
        } else {
            fragment = getItem(position);// 初始化页面的时候拿到fragment的实例
            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;
    }

    @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);// 并没有真正释放fragment对象只是detach
    }

从源码中大家能够看来:FragmentStatePagerAdapter中fragment实例在destroyItem的时候被真正自由,FragmentPagerAdapter中的fragment实例在destroyItem的时候并从未真的释放fragment对象只是detach。所以FragmentStatePagerAdapter省外存,而FragmentPagerAdapter会消耗愈多的内存,带来的好处就是功效更高一些。所以得出那样的下结论:FragmentPagerAdapter适用于页面相比少的情况,FragmentStatePagerAdapter适用于页面相比较多的景况,不过对质量有必要的意况下,推荐使用FragmentPagerAdapter。所以,“爱阅”为了卓绝品质,选用了FragmentPagerAdapter举办作业作用完毕。

提出难点

在动用 ListView 的时候,大家一再习惯了履新 Adapter 的数据源,然后调用
Adapter 的 notifyDataSetChanged() 方法来刷新列表(有没多少 MVC
的觉得)。

PagerAdapter 也有 notifyDataSetChanged()
方法,那我们按照这几个流程来试试,看有没有哪些难点。(ListView
的示范就不在那里演示了,感兴趣的能够友善去摸索,卓殊不难)

那就是说自己的标题是:“ViewPager 的 PagerAdapter
在数据源更新后,能如故不能自行刷新视图?

带着难点,我们做一些尝试,上面实验的思绪是:修改数据源,然后布告PagerAdapter 更新,查看视图的成形。

4、ViewPager的翻页动画

为ViewPager设置适配器后,就足以正常使用了,接下去我们为ViewPager扩展翻页动画,ViewPager提供了PageTransformer接口用于落到实处翻页动画。

尝试环境准备

看看实验环境,上代码:

private void initData() {
    // 数据源
    mDataList = new ArrayList<>(5);
    mDataList.add("Java");
    mDataList.add("Android");
    mDataList.add("C&C++");
    mDataList.add("OC");
    mDataList.add("Swift");

    // 很简单的一个 PagerAdapter
    this.mContentVP.setAdapter(mPagerAdapter = new PagerAdapter() {
        @Override
        public int getCount() {
            return mDataList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = View.inflate(SimpleDemoActivity.this, R.layout.item_vp_demopageradapter, null);
            TextView pageNumTV = (TextView) view.findViewById(R.id.tv_pagenum);
            pageNumTV.setText("DIY-PageNum-" + mDataList.get(position));
            container.addView(view);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

    });
}

ViewPager 的 Item:item_vp_demopageradapter.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center">
    <ImageView
        android:id="@+id/iv_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:src="@mipmap/ic_launcher" />
    <!-- 用于显示文本,数据更新体现在这里 -->
    <TextView
        android:id="@+id/tv_pagenum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="DIY-Page-" />
</LinearLayout>

很粗略的代码,并且加了诠释,直接往下看实验。

法定提供的PageTransformer落成例子
public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        Log.d("DepthPageTransformer", view.getTag() + " , " + position + "");
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.85f;
    private static final float MIN_ALPHA = 0.5f;

    @SuppressLint("NewApi")
    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        int pageHeight = view.getHeight();

        Log.e("TAG", view + " , " + position + "");

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 1) 
        { // [-1,1]
            // Modify the default slide transition to shrink the page as well
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            if (position < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }

            // Scale the page down (between MIN_SCALE and 1)
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

            // Fade the page relative to its size.
            view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE)
                    / (1 - MIN_SCALE) * (1 - MIN_ALPHA));

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

落到实处翻页动画的机要就是重写transformPage方法,方法里有四个参数view和position,驾驭那五个参数相当主要。假使有八个页面view1、view2、view3从左至右在viewPager中浮现:往左滑动时view1、view2、view3的position都是绵绵变小的;往右滑动时view1、view2、view3的position都是无休止变大的。当position是正负无穷大时view就离开显示屏视野了。因而最要旨的主宰逻辑是在[-1,0]和(0,1]那五个区间,通过安装透明度、平移、旋转、缩放等卡通组成可以兑现各式各个的页面变化意义。

PagerAdapter 刷新实验

1、更新数据源中的某项

创新数据测试

对应代码:

private void refresh() {
    mDataList.set(0, "更新数据源测试");
    mPagerAdapter.notifyDataSetChanged();
}

标题讲述:在示范动画中得以见见,更新数据源之后视图并不曾马上刷新,多滑行两回重复归来更新的
Item 时才履新(那里先看标题,上面会细说)。

2、往数据源中添加数据

拉长数据测试

对应代码:

private void add() {
    mDataList.add("这是新添加的Item");
    mPagerAdapter.notifyDataSetChanged();
}

难题讲述:不要紧难点,数据源添加数据后通报 PagerAdapter
刷新,ViewPager 中就多了一个 Item。

3、从数据源中删除数据

删除数据测试

private void delete() {
    mDataList.remove(0);
    mPagerAdapter.notifyDataSetChanged();
}

标题讲述:那一个题材就较多了,首先,假定是去除当前
Item,那么会晤到没有此外反应;其次,万一去除的不是现阶段
Item,会意识出现了数码错乱,并且前面有 Item
滑不过去,不过按住将来滑的时候可以看到后头的 Item。

4、将数据源清空

清空数据

private void clean() {
    mDataList.clear();
    mPagerAdapter.notifyDataSetChanged();
}

标题讲述:从上边的动图能够看到,清空数据源之后,会残留一个 Item。

说明:先不用计较上边所写的 PagerAdapter
是或不是有题目,那里只是想引出难题来,上边会指向
PagerAdapter、FragmentPagerAdapter 以及 FragmentStatePagerAdapter
来分析难题原因和付出解决方案。

5、ViewPager的高档用法

那里主要介绍的是ViewPager结合第三方库完毕小圆点提醒器效果和ViewPager结合design库完成tab切换,首先大家看下落成效益:

代码并不很复杂就只是多啰嗦了,我们可以到此处来赢得源码:ViewPager提示器效果

二、PagerAdapter

从地点的试行可以见到 ViewPager 差距于 ListView,即使仅仅的调用
ViewPager.getAdapter().notifyDataSetChanged() 方法(即 PagerAdapter 的
notifyDataSetChanged()方法)页面并不曾刷新。

PagerAdapter 用于 ViewPager 的 Item 为一般
View的情况,这些相对不难,所以首先介绍。

深信江西营班都搜过类似的难题 —— “PagerAdapter 的 notifyDataSetChanged()
不刷新?”。有的说那是 bug,有的则以为 谷歌(Google)是特地那样设计的,个人倾向后一种观点(我觉着这是 谷歌 为了 ViewPager
质量考虑而计划的,毕竟 ViewPager
须求出示“很多的”视图,而且要提防用户滑动时觉得卡顿)。

6、ViewPager使用中相遇的坑

在“爱阅”的开销进程中我赶上了那般的气象:更新数据源之后视图并没有即时刷新,多滑行四回重复归来更新的Item时才履新。

ViewPager 刷新分析

先来打探下 ViewPager 的基础代谢进程:
1、刷新的苗头
ViewPager 的基础代谢是从调用其 PagerAdapter 的 notifyDataSetChanged()
方法初步的,那先看看该格局的源码(在源码面前一切无所遁形…):

/**
 * This method should be called by the application if the data backing this adapter has changed
 * and associated views should update.
 */
public void notifyDataSetChanged() {
    synchronized (this) {
        if (mViewPagerObserver != null) {
            mViewPagerObserver.onChanged();
        }
    }
    mObservable.notifyChanged();
}

2、DataSetObservable 的 notifyChanged()
地点的形式中出现了七个关键的分子变量:

private final DataSetObservable mObservable = new DataSetObservable();
private DataSetObserver mViewPagerObserver;

观看者方式,有没有?先不急急分析那几个是或不是观察者格局,来看望
mObservable.notifyChanged() 做了些什么工作:

/**
 * Invokes {@link DataSetObserver#onChanged} on each observer.
 * Called when the contents of the data set have changed.  The recipient
 * will obtain the new contents the next time it queries the data set.
 */
public void notifyChanged() {
    synchronized(mObservers) {
        // since onChanged() is implemented by the app, it could do anything, including
        // removing itself from {@link mObservers} - and that could cause problems if
        // an iterator is used on the ArrayList {@link mObservers}.
        // to avoid such problems, just march thru the list in the reverse order.
        for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged(); 
        }
    }
}

notifyChanged() 方法中是很独立的观望者形式中遍历所有的 Observer,通告变化爆发了的代码。代码很简短,那根本是其一 mObservers 包括哪些 Observer
呢?

3、DataSetObserver
平素从 mObservers 点进去你会意识这一个:

protected final ArrayList<T> mObservers = new ArrayList<T>();

-_-‘,那是个泛型,坑了!还好 DataSetObservable 的 notifyChanged()
的诠释中写了这个 Observer 是 DataSetObserver。那去探访 DataSetObserver:

public abstract class DataSetObserver {
    /**
     * This method is called when the entire data set has changed,
     * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
     */
    public void onChanged() {
        // Do nothing
    }

    /**
     * This method is called when the entire data becomes invalid,
     * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
     * {@link Cursor}.
     */
    public void onInvalidated() {
        // Do nothing
    }
}

一个抽象类,里面多个空方法,这一个好办,找她的子类(AndroidStudio 中校光标放到类名上,按 F4):

DataSetObserver继承结构

归根结底找到你了,就是用红线框出来的那条,双击,定位过去。

4、PagerObserver 内部类
PagerObserver 是 ViewPager 中的一个内部类,完毕也很粗略,就是调用了
ViewPager 中的 dataSetChanged() 方法,真正的紧要性来了。

private class PagerObserver extends DataSetObserver {
    @Override
    public void onChanged() {
        dataSetChanged();
    }
    @Override
    public void onInvalidated() {
        dataSetChanged();
    }
}

5、ViewPager 的 dataSetChanged()
其一措施的兑现较长,里面的逻辑看上去挺复杂的,那里就不出示全体的源码了,列下关键点:

...
for (int i = 0; i < mItems.size(); i++) {
    final ItemInfo ii = mItems.get(i);
    final int newPos = mAdapter.getItemPosition(ii.object);

    if (newPos == PagerAdapter.POSITION_UNCHANGED) {
        continue;
    }

    if (newPos == PagerAdapter.POSITION_NONE) {
        ...
        continue;
    }
    ...
}
...

地点截取的代码中 for 循环之中有五个 continue
语句,那恐怕是相比较首要的代码,幸好不用我们一连深切了,官方给出精晓释:

Called when the host view is attempting to determine if an item’s
position has changed. Returns POSITION_UNCHANGED if the position of the
given item has not changed or POSITION_NONE if the item is no longer
present in the adapter.The default implementation assumes that items
will never change position and always returns POSITION_UNCHANGED.

大约的意趣是:
万一 Item 的职分借使没有暴发变化,则赶回 POSITION_UNCHANGED。借使回去了
POSITION_NONE,表示该职位的 Item 已经不设有了。默许的贯彻是一旦 Item
的岗位永远不会爆发变化,而回到
POSITION_UNCHANGED。(参考自:顺藤摸瓜源码解决android疑难有关题材1-Viewpager之notifyDataSetChanged无刷新

地方在源码里面跟了一大圈是还是不是依旧觉得并未明朗,因为还有一个很重大的类
—— PagerAdapter 没有介绍,再给点耐心,继续。

6、PagerAdapter 的行事流程
实在就是 PagerAdapter 中方法的实施各类,来看看
Leo8573
的辨析(个人感觉基本说成功了,所以直接拷过来了):

PagerAdapter 作为 ViewPager 的适配器,无论 ViewPager
有微微页,Pager艾达pter 在起始化时也只初始化先河的2个
View,即调用2次instantiateItem 格局。而接下去每当 ViewPager
滑动时,PagerAdapter 都会调用 destroyItem
方法将距离该页2个涨幅以上的不胜 View 销毁,以此保险 PagerAdapter
最四只管辖3个 View,且当前 View 是3个中的中间一个,如若当前 View
缺乏两边的 View,那么就 instantiateItem,如里有超越2个增幅的就
destroyItem。

简单来讲图示:

                 *
   ------+---+---+---+------
     ... 0 | 1 | 2 | 3 | 4 ...
   ------+---+---+---+------

时下 View 为2号 View,所以 Pager艾达pter 管辖1、2、3多个View,接下去向左滑动–>

                 *
   ------+---+---+---+------
     ... 1 | 2 | 3 | 4 | 5 ...
   ------+---+---+---+------

滑动后,当前 View 变为3号 View,PagerAdapter 会 destroyItem
0号View,instantiateItem 5号 View,所以 PagerAdapter 管辖2、3、4三个
View。(参考自:有关ViewPager的数量更新难点总括

总计一下: Viewpager 的基础代谢进程是那样的,在历次调用 PagerAdapter 的
notifyDataSetChanged() 方法时,都会激活 getItemPosition(Object object)
方法,该方法会遍历 ViewPager 的具有 Item(由缓存的 Item
数量控制,默许为当前页和其左右加起来共3页,那几个可以自行设定,不过起码会缓存2页),为各个Item 重临一个状态值(POSITION_NONE/POSITION_UNCHANGED),如果是
POSITION_NONE,那么该 Item 会被 destroyItem(ViewGroup container, int
position, Object object) 方法 remove 掉,然后重新加载,借使是
POSITION_UNCHANGED,就不会重新加载,默许是
POSITION_UNCHANGED,所以借使不重写 getItemPosition(Object
object),修改重返值,就不能看出 notifyDataSetChanged() 的刷新功用。

相应刷新部分的代码编写是那般的:
    public void onCategorysChange(String key) {
        if (key != null) {
            mCategoryList = DataCache.getInstance().getChildCategorys(key);
            notifyDataSetChanged();
        } else {
            DLog.e("onCategorysChange() Error!");
        }
    }

同时不仅仅是翻新数据,在一味的丰富和删除数据的时候同样会出现这么的难题。那么到底是怎样来头吗?大家对上文提到的二种适配器分别做出方案解答。

最简便易行的化解方案

那就是一贯一刀切:重写 PagerAdapter 的 getItemPosition(Object object)
方法,将重临值固定为 POSITION_NONE。

先看看效果:

![最简便解决方案示例](http://upload-images.jianshu.io/upload\_images/1233754-0071612440ec3200.gif?imageMogr2/auto-orient/strip
”最简单易行解决方案示例“)

上代码(PagerAdapterActivity.java):

@Override
public int getItemPosition(Object object) {
    // 最简单解决 notifyDataSetChanged() 页面不刷新问题的方法
    return POSITION_NONE;
}

该方案的弱项:有个很让人侧目标老毛病,那就是会刷新所有的
Item,那将导致系统资源的浪费,所以那种艺术不切合数据量较大的风貌。

注意:
那种办法还有一个必要小心的地点,就是重写 destoryItem() 方法:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    // 把 Object 强转为 View,然后将 view 从 ViewGroup 中清除
    container.removeView((View) object);
}

(1)PagerAdapter的解决方案

先来精晓下 ViewPager 的基础代谢进程:

  • 刷新的原初
    ViewPager 的基础代谢是从调用其 PagerAdapter 的 notifyDataSetChanged()
    方法开端的,这先看看该办法的源码:

public void notifyDataSetChanged() {
    synchronized (this) {
        if (mViewPagerObserver != null) {
            mViewPagerObserver.onChanged();
        }
    }
    mObservable.notifyChanged();
}
  • DataSetObservable 的 notifyChanged()
    上面的艺术中出现了多个主要的成员变量:

private final DataSetObservable mObservable = new DataSetObservable();
private DataSetObserver mViewPagerObserver;

察觉那是大家熟稔的寓目者格局,接下去看看 mObservable.notifyChanged()
做了些什么工作:

public void notifyChanged() {
    synchronized(mObservers) {
        for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged(); 
        }
    }
}

notifyChanged() 方法中是很独立的观看者格局中遍历所有的
Observer,布告变化发生了的代码,接下去看看这些 mObservers 包括怎么样Observer 。

  • DataSetObserver
    一贯从 mObservers 点进去你会意识这一个:

protected final ArrayList<T> mObservers = new ArrayList<T>();

public abstract class DataSetObserver {
    public void onChanged() {
        // Do nothing
    }
    public void onInvalidated() {
        // Do nothing
    }
}
  • PagerObserver 内部类
    PagerObserver 是 ViewPager 中的一个里边类,完结就是调用了 ViewPager
    中的 dataSetChanged() 方法,真正的重大来了:

private class PagerObserver extends DataSetObserver {
    @Override
    public void onChanged() {
        dataSetChanged();
    }
    @Override
    public void onInvalidated() {
        dataSetChanged();
    }
}
  • ViewPager 的 dataSetChanged()
    本条点子的落到实处较长,里面的逻辑看上去挺复杂的,那里就不突显全体的源码了,列下关键点:

for (int i = 0; i < mItems.size(); i++) {
    final ItemInfo ii = mItems.get(i);
    final int newPos = mAdapter.getItemPosition(ii.object);

    if (newPos == PagerAdapter.POSITION_UNCHANGED) {
        continue;
    }

    if (newPos == PagerAdapter.POSITION_NONE) {
        ...
        continue;
    }
}

地方截取的代码中 for 循环之中有七个 continue
语句,那或者是相比较主要的代码,幸好不用我们后续深切了,官方给出了表明:如果Item 的地方假若没有暴发变化,则赶回 POSITION_UNCHANGED。倘若回去了
POSITION_NONE,表示该岗位的 Item 已经不设有了。默许的落到实处是只要 Item
的职位永远不会暴发变化,而回到 POSITION_UNCHANGED。

最简方案的优化

此处提供一个思路,毕竟场景太多,相信大家清楚了思路要兑现就很简短了,闲话不多说。

思路:在 instantiateItem() 方法中给每个 View 添加 tag(使用 setTag()
方法),然后在 getItemPosition() 方法中经过 View.getTag()
来判断是不是是要求刷新的页面,是就回到 POSITION_NONE,否就重临POSITION_UNCHANGED。
(参考自:ViewPager刷新单个页面的方法

注意:那里有少数要注意的是,当清空数据源的时候需求回到
POSITION_NONE,可用如下代码:

if (mDataList != null && mDataList.size()==0) {
    return POSITION_NONE;
}

至于 PagerAdapter 的牵线就到此处了,即使 FragmentPager艾达pter 与
FragmentStatePagerAdapter 都是持续自
PagerAdapter。不过,那七个是专门为以 Fragment 为 Item 的 ViewPager
所准备的,所以有其特殊性。且看下面的介绍。

切磋那里,大家必要精晓下 Viewpager 的基础代谢进度:

在每趟调用 PagerAdapter 的 notifyDataSetChanged() 方法时,都会激活
getItemPosition(Object object) 方法,该方法会遍历 ViewPager 的享有
Item(由缓存的 Item
数量控制,默许为当前页和其左右加起来共3页,那几个可以自动设定,可是至少会缓存2页),为每个
Item 再次回到一个状态值(POSITION_NONE/POSITION_UNCHANGED),如果是
POSITION_NONE,那么该 Item 会被 destroyItem(ViewGroup container, int
position, Object object) 方法 remove 掉然后再行加载,如若是
POSITION_UNCHANGED就不会重复加载。默许是
POSITION_UNCHANGED,所以要是不重写 getItemPosition(Object
object)并修改重回值,就不能看出 notifyDataSetChanged() 的刷新功用。

三、FragmentPagerAdapter

最简便的化解方案:

那就是一贯重写PagerAdapter的getItemPosition(Object
object)方法,将重回值固定为POSITION_NONE。正如前文所写的那么:

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

简介

上面通过使 getItemPosition() 方法再次来到 POSITION_NONE
到达数据源变化(也就是调用
notifyDataSetChanged())时,刷新视图的目标。不过当我们采取 Fragment 作为
ViewPager 的 Item 时,就要求多考虑部分了,而且貌似是选取FragmentPagerAdapter 或者 FragmentStatePagerAdapter。

那里不展开切磋 FragmentPagerAdapter 与 FragmentStatePagerAdapter
的异同和使用情状了,感兴趣的可以看看那篇作品:FragmentPagerAdapter与FragmentStatePagerAdapter区别

下边先来探视使用 FragmentPagerAdapter 时,怎么样在数据源发生变化时,刷新
Fragment 或者动态改变 Items 的多寡。

该方案的通病:

有个很明确的弱项,那就是会刷新所有的
Item,那将导致系统资源的浪费,所以那种方式不吻合数据量较大的气象。

方案:清除 FragmentManager 中缓存的 Fragment

先看效果:

FragmentPagerAdapter数据源刷新演示1

兑现上图效果的重点代码:
1、FPagerAdapter1Activity.java

private void refresh() {
    if (checkData()) return;
    mDataList.set(0, 7); // 修改数据源
    mPagerAdapter.updateData(mDataList); // 通知 Adapter 更新
}

private void add() {
    mDataList.add(7);
    mPagerAdapter.updateData(mDataList);
}

private void delete() {
    if (checkData()) return;
    mDataList.remove(0);
    mPagerAdapter.updateData(mDataList);
}

private void clear() {
    if (checkData()) return;
    mDataList.clear();
    mPagerAdapter.updateData(mDataList);
}

2、FPagerAdapter1.java

public class FPagerAdapter1 extends FragmentPagerAdapter {

    private ArrayList<Fragment> mFragmentList;
    private FragmentManager mFragmentManager;

    public FPagerAdapter1(FragmentManager fm, List<Integer> types) {
        super(fm);
        this.mFragmentManager = fm;
        mFragmentList = new ArrayList<>();
        for (int i = 0, size = types.size(); i < size; i++) {
            mFragmentList.add(FragmentTest.instance(i));
        }
        setFragments(mFragmentList);
    }

    public void updateData(List<Integer> dataList) {
        ArrayList<Fragment> fragments = new ArrayList<>();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            Log.e("FPagerAdapter1", dataList.get(i).toString());
            fragments.add(FragmentTest.instance(dataList.get(i)));
        }
        setFragments(fragments);
    }

    private void setFragments(ArrayList<Fragment> mFragmentList) {
        if(this.mFragmentList != null){
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            for(Fragment f:this.mFragmentList){
                fragmentTransaction.remove(f);
            }
            fragmentTransaction.commit();
            mFragmentManager.executePendingTransactions();
        }
        this.mFragmentList = mFragmentList;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return this.mFragmentList.size();
    }

    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }
}

3、思路分析
地点的代码思路很简短,就是当数据源暴发变化时,先将 FragmentManger
里面装有缓存的 Fragment 全部排除,然后再一次创设,那样达到刷新视图的目标。

然则,那样做有一个毛病,那就是会导致不必要的荒废,会潜移默化属性。还有就是必须使用一个
List 缓存所有的 Fragment,那也得占用不少内存…

思路挺简单,那里不再赘言,这看看有没有怎么样可以优化的。

注意:

那种措施还有一个须要小心的地点,就是重写 destoryItem() 方法:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    // 把 Object 强转为 View,然后将 view 从 ViewGroup 中清除
    container.removeView((View) object);
}

优化:通过 Tag 获取缓存的 Fragment

先看功能:

FragmentPagerAdapter数据源刷新演示2

从上边的动图上可以看来,更新某一个 Fragment
不成难题,清空数据源的时候也远非,添加当然也没怎么难题;请留意删除的效应,尽管,目标Fragment 确实从 ViewPager
中移除了,但是滑动后边的页面会发现出现了多少错乱。

解析一下优化的思绪

先来明白 FragmentPagerAdapter 中是怎么着管理 Fragment 的,那里涉及到
FragmentPagerAdapter 中的 instantiateItem() 方法:

@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;
}

从源码中可以寓目在从 FragmentManager 中取出 Fragment 时调用了
findFragmentByTag() 方法,而那么些 Tag 是由 makeFragmentName()
方法生成的。继续往下得以看出每一个 Fragment 都打上了一个标签(在
mCurTransaction.add() 方法中)。

也就是说是 FragmentManager 通过 Tag 找相应的 Fragment,从而完结缓存
Fragment 的目标。如果可以找到,就不会创设新的 Fragment,Fragment 的
onCreate()、onCreateView() 等办法都不会重新调用。

优化的笔触就有了:
首先,须求缓存所有 Fragment 的 Tag,代码如下:

private List<String> mTagList; // 用来存放所有的 Tag

// 生成 Tag
// 直接从 FragmentPageAdapter 源码里拷贝 Fragment 生成 Tag 的方法
private String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

// 将 Tag 缓存到 List 中
@Override
public Object instantiateItem(ViewGroup container, int position) {
    mTagList.add(position, makeFragmentName(container.getId(),
            (int) getItemId(position)));
    return super.instantiateItem(container, position);
}

其次,在创新 Fragment 时,使用相应的 Tag 去 FragmentManamager
中找相应的 Fragment,如若存在,就平素更新,代码如下:

public void update(int position, String str) {
    Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));
    if (fragment == null) return;
    if (fragment instanceof FragmentTest) {
        ((FragmentTest)fragment).update(str);
    }
    notifyDataSetChanged();
}

该方法需求活动在 Fragment 中提供。

最后,对此动态改变 ViewPager 中 Fragment
的数据,假如是增加,那不要首要小心的;不过删除有点吃力。

在上头的动态上观望,删除一个 Fragment
后会现身紊乱,那里没有进一步去商讨了,那里仅提供一个演示供参考(那么些示例代码有难题,仅供参考)

public void remove(int position) {
    mDataList.remove(position);
    isDataSetChange = true;
    Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));
    mTagList.remove(position);
    if (fragment == null) {
        notifyDataSetChanged();
        return;
    }
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    fragmentTransaction.remove(fragment);
    fragmentTransaction.commit();
    mFragmentManager.executePendingTransactions();
    notifyDataSetChanged();
}

注意:
本条”优化“示例,仅仅适用于在只要求更新某个 Fragment 的场所,关于动态删除
Fragment,该”优化“方案并不适用,也不推荐使用。

最简方案的优化

在 instantiateItem() 方法中给各类 View 添加 tag(使用 setTag()
方法),然后在 getItemPosition() 方法中经过 View.getTag()
来判断是或不是是须要刷新的页面,是就回到 POSITION_NONE,否就再次来到POSITION_UNCHANGED。

四、FragmentStatePagerAdapter

先看效果:

FragmentStatePagerAdapter数据源刷新演示

注意:

那边有一些要小心的是,当清空数据源的时候需求回到
POSITION_NONE,可用如下代码:

if (mDataList != null && mDataList.size()==0) {
    return POSITION_NONE;
}

至于 PagerAdapter 的介绍就到那里了,固然 FragmentPagerAdapter 与
FragmentStatePagerAdapter 都是三番五次自 PagerAdapter。可是那五个是专程为以
Fragment 为 Item 的 ViewPager 所准备的,所以有其特殊性,我们上边来介绍。

简介

FragmentStatePagerAdapter
与 FragmentPagerAdapter 类似,那七个类都无冕自 PagerAdapter。可是,和
FragmentPagerAdapter 差距的是,FragmentStatePager艾达pter
只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需求出示时,生成新的页面(那和
ListView
的贯彻平等)。那种措施的益处就是当有着大量的页面时,不必在内存中占据多量的内存。(参考自:FragmentPagerAdapter与FragmentStatePagerAdapter区别

FragmentStatePagerAdapter 的兑现与 FragmentPagerAdapter
有很大分别,借使照搬上述 FragmentPagerAdapter
刷新数据的法子,你会发现并未什么样问题(可以选用 FPagerAdapter11.java
测试)。

(2)FragmentPagerAdapter的缓解方案

上边通过使 getItemPosition() 方法再次回到 POSITION_NONE
到达数据源变化(也就是调用
notifyDataSetChanged())时刷新视图的目标。但是当大家运用 Fragment 作为
ViewPager 的 Item 时,就要求多考虑部分了,而且一般是使用
FragmentPagerAdapter 或者 FragmentStatePagerAdapter。

另一种思路

只是,我在类型中实际上利用的时候(Fragment
比较复杂,里面有网络义务等)出现了 IllegalStateException,发生在
”fragmentTransaction.remove(f);“
时。当时找了一部分稿子没有缓解该难题,考虑到品种中的 Fragment
里面逻辑过多,就换思路,没有在那个方面继续深究了。

假诺,你也是这样使用 FragmentStatePagerAdapter 来动态改变 ViewPager 中
Fragment,并且在 remove Fragment 时相遇了
IllegalStateException。那么,你可以考虑采纳下边的格局,先看代码(FSPagerAdapter
.java):

public class FSPagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<Fragment> mFragmentList;

    public FSPagerAdapter(FragmentManager fm, List<Integer> types) {
        super(fm);
        updateData(types);
    }

    public void updateData(List<Integer> dataList) {
        ArrayList<Fragment> fragments = new ArrayList<>();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            Log.e("FPagerAdapter1", dataList.get(i).toString());
            fragments.add(FragmentTest.instance(dataList.get(i)));
        }
        setFragmentList(fragments);
    }

    private void setFragmentList(ArrayList<Fragment> fragmentList) {
        if(this.mFragmentList != null){
            mFragmentList.clear();
        }
        this.mFragmentList = fragmentList;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return this.mFragmentList.size();
    }

    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }
}

相应的测试 Activity 见 FSPagerAdapterActivity.java

上边的代码挺不难,稍微解释一下达成思路:
1、缓存所有的 Fragment
接纳一个 List 将数据源对应的 Fragment 都缓存起来

2、更新数据源,刷新 Fragment
当有数据源更新的时候,从 List 中取出相应的 Fragment,然后刷新 Adapter

3、删除数据时,删除 List 中对应的 Fragment
当数码源中删除某项时,将 List 中对应的 Fragment 也删除,然后刷新 Adapter

解决方案:清除 FragmentManager 中缓存的 Fragment

当数据源暴发变化时,先将 FragmentManger 里面有着缓存的 Fragment
全体清除,然后再一次创制,那样达到刷新视图的目标。下边给出宗旨代码:

public class FPagerAdapter1 extends FragmentPagerAdapter {

    private ArrayList<Fragment> mFragmentList;
    private FragmentManager mFragmentManager;

    public FPagerAdapter1(FragmentManager fm, List<Integer> types) {
        super(fm);
        this.mFragmentManager = fm;
        mFragmentList = new ArrayList<>();
        for (int i = 0, size = types.size(); i < size; i++) {
            mFragmentList.add(FragmentTest.instance(i));
        }
        setFragments(mFragmentList);
    }

    public void updateData(List<Integer> dataList) {
        ArrayList<Fragment> fragments = new ArrayList<>();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            Log.e("FPagerAdapter1", dataList.get(i).toString());
            fragments.add(FragmentTest.instance(dataList.get(i)));
        }
        setFragments(fragments);
    }

    private void setFragments(ArrayList<Fragment> mFragmentList) {
        if(this.mFragmentList != null){
            FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
            for(Fragment f:this.mFragmentList){
                fragmentTransaction.remove(f);
            }
            fragmentTransaction.commit();
            mFragmentManager.executePendingTransactions();
        }
        this.mFragmentList = mFragmentList;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return this.mFragmentList.size();
    }

    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }
}

不过,那样做有一个欠缺,那就是会促成不须要的荒废,会潜移默化属性。还有就是必须利用一个
List 缓存所有的 Fragment,那也得占用不少内存,那接下去看看怎么着去优化。

小结

有关 ViewPager 数据源刷新相比较劳累的地点是从数据源中删除数据的事态,那和
ViewPager
的兑现格局有关,大家在解决该难点的时候要分具体处境来行使差其余方案。

下面提供的方案也不是健全的,还有很多相差,倘若您在运用的经过中碰到了难题,那么请举报给本人,大家一块儿完善。

那边重借使探索关于 ViewPager 数据源刷新的题材,关于 ViewPager
的详实使用不是本文重点,那里就不关乎了。

优化方案:通过 Tag 获取缓存的 Fragment

率先大家应该精晓 FragmentManager 是透过 Tag 找相应的
Fragment,从而达到缓存 Fragment 的目标。假设得以找到,就不会创建新的
Fragment,Fragment 的 onCreate()、onCreateView()
等方式都不会重复调用。那优化的思绪就有了:

体系地址

GitHub
民用博客

第一,需求缓存所有 Fragment 的 Tag:
private List<String> mTagList; // 用来存放所有的 Tag

// 生成 Tag:直接从 FragmentPageAdapter 源码里拷贝 Fragment 生成 Tag 的方法
private String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

// 将 Tag 缓存到 List 中
@Override
public Object instantiateItem(ViewGroup container, int position) {
    mTagList.add(position, makeFragmentName(container.getId(),
            (int) getItemId(position)));
    return super.instantiateItem(container, position);
}

参考

ViewPager
详解(二)—详解四大函数

pagerAdapter arrayList 数据清空,Item
不销毁的bug解决

ViewPager刷新单个页面的格局
ViewPager动态加载、删除页面
ViewPager+Fragment滑动界面,并做延迟加载【新版】
至于ViewPager的多少更新难点统计
Viewpager+fragment数据更新难点分析
追溯源码解决android疑难有关题材1-Viewpager之notifyDataSetChanged无刷新
解决fragment+viewpager第二次跻身的时候从不数量的题材
FragmentPagerAdapter刷新fragment最周到解决方案
Viewpager+fragment数据更新问题浅析
FragmentPagerAdapter与FragmentStatePagerAdapter区别

说不上,在创新 Fragment 时使用相应的 Tag 去 FragmentManamager 中找相应的 Fragment,如若存在就一贯更新:
public void update(int position, String str) {
    Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));
    if (fragment == null) return;
    if (fragment instanceof FragmentTest) {
        ((FragmentTest)fragment).update(str);
    }
    notifyDataSetChanged();
}

该方法必要活动在 Fragment 中提供。

终极,对于动态改变 ViewPager 中 Fragment 的多少,假诺是添加那没什么要留心的,不过删除就有点困难,那里给出实例代码:
public void remove(int position) {
    mDataList.remove(position);
    isDataSetChange = true;
    Fragment fragment = mFragmentManager.findFragmentByTag(mTagList.get(position));
    mTagList.remove(position);
    if (fragment == null) {
        notifyDataSetChanged();
        return;
    }
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    fragmentTransaction.remove(fragment);
    fragmentTransaction.commit();
    mFragmentManager.executePendingTransactions();
    notifyDataSetChanged();
}

(3)“爱阅”中的解决方案

“爱阅”中的处理方式和上述优化的方法很像,可是又做了越发的优化,代码如下所示:

    public void updateView(String pagerKey) {
        for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
            Fragment fragment = getSupportFragmentManager().findFragmentByTag(mViewPagerAdapter.getFragmentTag(R.id.viewpager_main, i));
            if (null != fragment) {
                String categoryKey = DataCache.getInstance().getCategory(pagerKey).getNextKey(i);
                if (categoryKey != null) {
                    Category viewPagerCategory = DataCache.getInstance().getCategory(categoryKey);
                    ViewPagerFragment viewPagerFragment = (ViewPagerFragment) fragment;
                    viewPagerFragment.setCategoryData(viewPagerCategory);
                }
            }
        }
    }
    public String getFragmentTag(int viewPagerId, int fragmentPosition) {
        return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
    }

可以看来,大家经过getSupportFragmentManager().findFragmentByTag()方法去ViewPager中找到当前缓存的Fragment,经过非空判断后到多少源中获取要更新的数据,然后把相应的Fragment中的数据举办翻新,以此来促成界面的翻新。那样做的好处是:不用ViewPager举办操作,从而使数码的拍卖轻量级,不必因为刷新界面而消耗过多的CPU资源。

(4)FragmentStatePagerAdapter的缓解方案

FragmentStatePagerAdapter 与 FragmentPagerAdapter 类似,这多少个类都一连自
PagerAdapter。不过和 FragmentPagerAdapter
不雷同的是,FragmentStatePagerAdapter
只保留当前页面,当页面离开视线后就会被免去并释放其资源;而在页面须求浮现时,生成新的页面(那和
ListView
的兑现均等)。那种办法的裨益就是当所有多量的页面时,不必在内存中据为己有大批量的内存。

public class FSPagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<Fragment> mFragmentList;

    public FSPagerAdapter(FragmentManager fm, List<Integer> types) {
        super(fm);
        updateData(types);
    }

    public void updateData(List<Integer> dataList) {
        ArrayList<Fragment> fragments = new ArrayList<>();
        for (int i = 0, size = dataList.size(); i < size; i++) {
            Log.e("FPagerAdapter1", dataList.get(i).toString());
            fragments.add(FragmentTest.instance(dataList.get(i)));
        }
        setFragmentList(fragments);
    }

    private void setFragmentList(ArrayList<Fragment> fragmentList) {
        if(this.mFragmentList != null){
            mFragmentList.clear();
        }
        this.mFragmentList = fragmentList;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return this.mFragmentList.size();
    }

    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }
}

上述代码是对该情状下的一种缓解方法,对应的表明如下:

1、缓存所有的 Fragment

选用一个 List 将数据源对应的 Fragment 都缓存起来

2、更新数据源,刷新 Fragment

当有数据源更新的时候,从 List 中取出相应的 Fragment,然后刷新 Adapter

3、删除数据时,删除 List 中对应的 Fragment

当数码源中删除某项时,将 List 中对应的 Fragment 也删除,然后刷新 Adapter

总结

有关 ViewPager 的选择和 ViewPager
数据源刷新的难点到此大家就已毕完工了,其中 ViewPager
数据源刷新的题材相比较勤奋的地点是从数据源中删除数据的事态,那和 ViewPager
的贯彻格局有关,大家在解决该难点的时候要分具体景况来使用不一样的方案。

由来为止,大家后日一半的工作量就已经到位了,接下去的年月大家一道去结合
TabLayout 已毕顶部的领航功用,我们下篇见!

联系方式:

简书:WillFlow
CSDN:WillFlow
微信公众号:WillFlow

微信公众号:威尔Flow

相关文章