ViewPager刷新问题详解

作者:李旺成

时间:2016年5月3日


一、PagerAdapter介绍

先看功效图

PageAdapter 使用示例

PagerAdapter简介

ListView 大家应该都很领会吧!ListView 一般都亟需一个 Adapter
来填充数据,如 ArrayAdapter、Simple艾达pter。PagerAdapter 就是 ViewPager
的 Adapter,与 ListView 的 Adapter 成效一样。

ViewPager->PageAdapter == ListView->BaseAdapter

先看下合法介绍

法定介绍

PageAdapter 类

PageAdapter 继承自
Object,继承结构参考意义不大,这老实看文档。文档上未曾提供示范代码,只是说了下要自定义
Page艾达(Ada)pter 需要实现下边三个点子:

  • 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():2018正版葡京赌侠诗,回去当前行之有效视图的数额
  • 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);
    }

});

可以看来实现 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 的 Base艾达pter,抽象出一个 getView()方法,在里面采取 SparesArray 缓存所有彰显过的
View。那样使用就很粗略了,继承 APagerAdapter,实现 getView()方法即可(可以参见:DemoPager艾达(Ada)pter.java)。

Pager艾达(Ada)pter 刷新的问题

提出问题

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

PagerAdapter 也有 notifyDataSetChanged()方法,这我们遵照这多少个流程来尝试,看有没有什么问题。(ListView
的言传身教就不在这里演示了,感兴趣的能够协调去尝试,非凡简单)

这就是说自己的问题是:“ViewPager 的 PagerAdapter
在数据源更新后,能否自行刷新视图?

带着题材,我们做一些试行,下边实验的笔触是:修改数据源,然后通知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艾达(Ada)pter 刷新实验

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。

说明:先不要计较上边所写的 Pager艾达(Ada)pter
是否有题目,这里只是想引出问题来,下边会指向
PagerAdapter、FragmentPager艾达pter 以及 FragmentStatePager艾达(Ada)pter
来分析问题原因和提交解决方案。

二、PagerAdapter

从地点的试行可以看到 ViewPager 不同于 ListView,假使只有的调用
ViewPager.get艾达pter().notifyDataSetChanged() 方法(即 Pager艾达(Ada)pter 的
notifyDataSetChanged()方法)页面并从未刷新。

PagerAdapter 用于 ViewPager 的 Item 为普通
View的意况,这一个相对简便易行,所以开首介绍。

相信广丽水学都搜过类似的题目 —— “PagerAdapter 的 notifyDataSetChanged()不刷新?”。有的说这是 bug,有的则以为 Google是专门这样设计的,个人倾向后一种意见(我以为这是 Google 为了 ViewPager
性能考虑而计划的,毕竟 ViewPager
需要展现“很多的”视图,而且要严防用户滑动时以为卡顿)。

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无刷新

地点在源码里面跟了一大圈是不是依旧觉得没有明朗,因为还有一个很重点的类
—— Pager艾达(Ada)pter 没有介绍,再给点耐心,继续。

6、Pager艾达pter 的行事流程
其实就是 PagerAdapter 中方法的推行各样,来看望
Leo8573
的解析(个人感觉基本说成功了,所以直接拷过来了):

PagerAdapter 作为 ViewPager 的适配器,无论 ViewPager
有些许页,PagerAdapter 在开端化时也只开头化开头的2个
View,即调用2次instantiateItem 措施。而接下去每当 ViewPager
滑动时,Pager艾达(Ada)pter 都会调用 destroyItem
方法将离开该页2个涨幅以上的老大 View 销毁,以此保证 Pager艾达(Ada)pter
最三只管辖3个 View,且当前 View 是3个中的中间一个,假如当前 View
紧缺两边的 View,那么就 instantiateItem,如里有跨越2个涨幅的就
destroyItem。

简单的讲图示:

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

当前 View 为2号 View,所以 Pager艾达(Ada)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() 的刷新效率。

最简便易行的缓解方案

这就是一贯一刀切:重写 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 的牵线就到此处了,即使 FragmentPager艾达(Ada)pter 与
FragmentStatePager艾达pter 都是延续自
Pager艾达(Ada)pter。可是,这五个是专程为以 Fragment 为 Item 的 ViewPager
所准备的,所以有其特殊性。且看下面的牵线。

三、FragmentPagerAdapter

简介

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

此处不展开啄磨 FragmentPagerAdapter 与 FragmentStatePagerAdapter
的异同和行使情形了,感兴趣的可以看看这篇著作:FragmentPagerAdapter与FragmentStatePagerAdapter区别

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

方案:清除 FragmentManager 中缓存的 Fragment

先看效果:

FragmentPager艾达pter数据源刷新演示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艾达(Ada)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

先看效率:

FragmentStatePager艾达pter数据源刷新演示

简介

FragmentStatePagerAdapter
与 FragmentPager艾达pter 类似,这两个类都卫冕自 PagerAdapter。不过,和
FragmentPager艾达pter 不均等的是,FragmentStatePager艾达(Ada)pter
只保留当前页面,当页面离开视线后,就会被清除,释放其资源;而在页面需要呈现时,生成新的页面(这和
ListView
的贯彻均等)。这种措施的裨益就是当有着大量的页面时,不必在内存中据为己有大量的内存。(参考自:FragmentPagerAdapter与FragmentStatePagerAdapter区别

FragmentStatePager艾达(Ada)pter 的实现与 FragmentPagerAdapter
有很大分别,假诺照搬上述 FragmentPagerAdapter
刷新数据的措施,你会意识没有什么样问题(可以采取 FPagerAdapter11.java
测试)。

另一种思路

不过,我在项目中其实采取的时候(Fragment
相比复杂,里面有网络任务等)出现了 IllegalStateException,暴发在
”fragmentTransaction.remove(f);“
时。当时找了一部分稿子没有解决该问题,考虑到品种中的 Fragment
里面逻辑过多,就换思路,没有在这些方面继续追究了。

假诺,你也是这般使用 FragmentStatePager艾达(Ada)pter 来动态改变 ViewPager 中
Fragment,并且在 remove Fragment 时遭受了
IllegalStateException。那么,你可以考虑接纳下边的法门,先看代码(FSPager艾达(Ada)pter
.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,然后刷新 艾达pter

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

小结

有关 ViewPager 数据源刷新相比费心的地点是从数据源中删除数据的处境,这和
ViewPager
的贯彻格局有关,我们在化解该问题的时候要分具体情形来使用不同的方案。

地方提供的方案也不是无微不至的,还有许多不足,假若你在使用的过程中遭受了问题,那么请举报给自身,我们一道完善。

此处首假诺追究关于 ViewPager 数据源刷新的问题,关于 ViewPager
的详实使用不是本文重点,这里就不关乎了。

类型地址

GitHub
私家博客

参考

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

pager艾达(Ada)pter arrayList 数据清空,Item
不销毁的bug解决

ViewPager刷新单个页面的法子
ViewPager动态加载、删除页面
ViewPager+Fragment滑动界面,并做延迟加载【新版】
至于ViewPager的数额更新问题总括
Viewpager+fragment数据更新问题浅析
追溯源码解决android疑难有关问题1-Viewpager之notifyDataSetChanged无刷新
缓解fragment+viewpager第二次跻身的时候没有数量的题材
FragmentPager艾达pter刷新fragment最系数解决方案
Viewpager+fragment数据更新问题浅析
FragmentPagerAdapter与FragmentStatePagerAdapter区别

发表评论

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

网站地图xml地图