ViewPager刷新问题详解

ViewPager刷新问题详解

作者:李旺成

时间:2016年5月3日


一、PagerAdapter介绍

先看效率图

PageAdapter 使用示例

PagerAdapter简介

ListView 我们应该都很熟习吧!ListView 一般都亟需一个 艾达pter
来填充数据,如 ArrayAdapter、Simple艾达pter。PagerAdapter 就是 ViewPager
的 艾达(Ada)pter,与 ListView 的 艾达(Ada)pter 功用一样。

ViewPager->PageAdapter == ListView->BaseAdapter

先看下官方介绍

合法介绍

PageAdapter 类

PageAdapter 继承自
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

地方对 Page艾达(Ada)pter 的多少个抽象方法做了概括表达,下边看看哪些行使

概括利用

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

});

可以看到实现 Pager艾达(Ada)pter 与 Base艾达pter 很相近,只是 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);

}

APager艾达(Ada)pter 类模仿 ListView 的 BaseAdapter,抽象出一个 getView()方法,在里边采用 SparesArray 缓存所有显示过的
View。这样使用就很粗略了,继承 APager艾达pter,实现 getView()方法即可(能够参照:DemoPager艾达pter.java)。

PagerAdapter 刷新的题材

指出问题

在行使 ListView 的时候,我们一再习惯了革新 Adapter 的数据源,然后调用
艾达(Ada)pter 的 notifyDataSetChanged() 方法来刷新列表(有没多少 MVC
的感觉)。

PagerAdapter 也有 notifyDataSetChanged()方法,这大家依据这个流程来试试,看有没有怎么着问题。(ListView
的以身作则就不在这里演示了,感兴趣的可以团结去摸索,卓殊简单)

那么我的题材是:“ViewPager 的 Pager艾达pter
在数据源更新后,能否自行刷新视图?

带着题材,我们做一些尝试,下面实验的思路是:修改数据源,然后通知PagerAdapter 更新,查看视图的转变。

尝试环境准备

看看实验环境,上代码:

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>

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

Pager艾达pter 刷新实验

1、更新数据源中的某项

履新数据测试

对应代码:

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

题目讲述:在演示动画中得以看来,更新数据源之后视图并从未及时刷新,多滑行两遍重复再次回到更新的
Item 时才履新(这里先看题目,下面会细说)。

2、往数据源中添加数据

累加数据测试

对应代码:

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

题材讲述:没关系问题,数据源添加数据后文告 Pager艾达(Ada)pter
刷新,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
是否有问题,这里只是想引出问题来,下面会针对
Pager艾达(Ada)pter、FragmentPager艾达pter 以及 FragmentStatePagerAdapter
来分析问题由来和交由解决方案。

二、PagerAdapter

从下面的尝试可以看出 ViewPager 不同于 ListView,假设单独的调用
ViewPager.getAdapter().notifyDataSetChanged() 方法(即 Pager艾达(Ada)pter 的
notifyDataSetChanged()方法)页面并不曾刷新。

PagerAdapter 用于 ViewPager 的 Item 为常见
View的意况,那一个相对简单,所以首先介绍。

信任广大校友都搜过类似的题材 —— “Pager艾达pter 的 notifyDataSetChanged()不刷新?”。有的说这是 bug,有的则觉得 Google是专程这样设计的,个人倾向后一种看法(我以为那是 Google 为了 ViewPager
性能考虑而规划的,毕竟 ViewPager
需要显示“很多的”视图,而且要防备用户滑动时认为卡顿)。

ViewPager 刷新分析

先来打听下 ViewPager 的刷新过程:
1、刷新的序幕
ViewPager 的基础代谢是从调用其 Pager艾达(Ada)pter 的 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无刷新

下面在源码里面跟了一大圈是不是仍旧感觉并未明朗,因为还有一个很重点的类
—— Pager艾达pter 没有介绍,再给点耐心,继续。

6、PagerAdapter 的办事流程
骨子里就是 PagerAdapter 中艺术的举办各种,来探望
Leo8573
的辨析(个人感觉基本说成功了,所以直接拷过来了):

PagerAdapter 作为 ViewPager 的适配器,无论 ViewPager
有些许页,Pager艾达(Ada)pter 在最先化时也只起首化开首的2个
View,即调用2次instantiateItem 形式。而接下去每当 ViewPager
滑动时,PagerAdapter 都会调用 destroyItem
方法将离开该页2个涨幅以上的充分 View 销毁,以此保证 Pager艾达(Ada)pter
最五只管辖3个 View,且当前 View 是3个中的中间一个,假使当前 View
缺乏两边的 View,那么就 instantiateItem,如里有超过2个涨幅的就
destroyItem。

粗略图示:

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

眼下 View 为2号 View,所以 PagerAdapter 管辖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() 的基础代谢功效。

最简易的化解方案

这就是直接一刀切:重写 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);
}

最简方案的优化

此处提供一个思路,毕竟场景太多,相信我们了解了思路要兑现就很粗略了,闲话不多说。

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

注意:此处有几许要专注的是,当清空数据源的时候需要回到
POSITION_NONE,可用如下代码:

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

关于 Pager艾达pter 的介绍就到这里了,虽然 FragmentPagerAdapter 与
FragmentStatePagerAdapter 都是连续自
PagerAdapter。不过,这多少个是特别为以 Fragment 为 Item 的 ViewPager
所准备的,所以有其特殊性。且看上边的牵线。

三、FragmentPagerAdapter

简介

地点通过使 getItemPosition() 方法重返 POSITION_NONE
到达数据源变化(也就是调用
notifyDataSetChanged())时,刷新视图的目标。但是当我们采取 Fragment 作为
ViewPager 的 Item 时,就需要多着想部分了,而且一般是行使
FragmentPagerAdapter 或者 FragmentStatePager艾达pter。

这边不展开探讨 FragmentPagerAdapter 与 FragmentStatePagerAdapter
的异议和应用情况了,感兴趣的可以看看这篇小说:FragmentPagerAdapter与FragmentStatePagerAdapter区别

下面先来看望使用 FragmentPagerAdapter 时,怎么样在数据源发生变化时,刷新
Fragment 或者动态改变 Items 的数码。

方案:清除 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,这也得占用不少内存…

思路挺简单,这里不再赘言,这看看有没有哪些可以优化的。

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

先看效率:

FragmentPagerAdapter数据源刷新演示2

从上边的动图上得以看来,更新某一个 Fragment
没有问题,清空数据源的时候也远非,添加当然也没怎么问题;请留意删除的遵守,虽然,目标Fragment 确实从 ViewPager
中移除了,不过滑动前边的页面会发现并发了数量错乱。

解析一下优化的笔触

先来询问 FragmentPager艾达pter 中是何许管理 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,该”优化“方案并不适用,也不引进应用。

四、FragmentStatePagerAdapter

先看效果:

FragmentStatePagerAdapter数据源刷新演示

简介

FragmentStatePagerAdapter
与 FragmentPagerAdapter 类似,这多少个类都延续自 PagerAdapter。但是,和
FragmentPagerAdapter 不一样的是,FragmentStatePager艾达pter
只保留当前页面,当页面离开视线后,就会被免除,释放其资源;而在页面需要突显时,生成新的页面(这和
ListView
的兑现均等)。这种办法的功利就是当所有大量的页面时,不必在内存中占据大量的内存。(参考自:FragmentPagerAdapter与FragmentStatePagerAdapter区别

FragmentStatePagerAdapter 的落实与 FragmentPager艾达pter
有很大区别,假设照搬上述 FragmentPager艾达pter
刷新数据的办法,你会发现并未什么样问题(可以动用 FPager艾达pter11.java
测试)。

另一种思路

而是,我在类型中其实拔取的时候(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 也删除,然后刷新 艾达pter

小结

至于 ViewPager 数据源刷新相比较费心的地点是从数据源中删除数据的景观,这和
ViewPager
的落实情势有关,大家在缓解该问题的时候要分具体情形来使用两样的方案。

上边提供的方案也不是圆满的,还有好多欠缺,假诺您在动用的经过中遭遇了问题,那么请举报给本人,我们一块完善。

这边关键是探索关于 ViewPager 数据源刷新的题目,关于 ViewPager
的详细使用不是本文重点,这里就不关乎了。

品种地址

GitHub
村办博客

参考

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

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

ViewPager刷新单个页面的方法
ViewPager动态加载、删除页面
ViewPager+Fragment滑动界面,并做延迟加载【新版】
有关ViewPager的多寡更新问题总括
Viewpager+fragment数据更新问题分析
顺藤摸瓜源码解决android疑难有关题材1-Viewpager之notifyDataSetChanged无刷新
解决fragment+viewpager第二次进入的时候从不数据的问题
FragmentPager艾达(Ada)pter刷新fragment最周详解决方案
Viewpager+fragment数据更新问题浅析
FragmentPagerAdapter与FragmentStatePagerAdapter区别

发表评论

电子邮件地址不会被公开。 必填项已用*标注