问题所在:我们在数据源发生改变时,调用notifyDataSetChanged() 方法,发现界面没有变化
原因分析:
追踪到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;
}
...
}
...
官方给出了解释:
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的getItemPosition(Object object) 方法,将返回值固定为 POSITION_NONE。
上代码:
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
//如果使用的不是FragmentPagerAdapter,需要重写此方法
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// 把 Object 强转为 View,然后将 view 从 ViewGroup 中清除
container.removeView((View) object);
}
问题到此还没有结束,原因在于我是用的是FragmentPagerAdapter,在这里我们还得去跟源码,重点在于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;
}
在这里,并不会直接调用getItem(position)方法,而是先通过findFragmentByTag(name)去在缓存中寻找fragment,所以如果缓存存在的话,界面还是不会刷新,在这里采用一种偷懒的方式,直接清空缓存,代码如下:
package com.yueren.pyyx.adapters;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import com.yueren.pyyx.fragments.ImpressionDetailFragment;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ivan on 15/7/20.
*/
public class ImpressionDetailPagerAdapter extends FragmentPagerAdapter {
private List<Long> mIds;
private FragmentManager mFragmentManager;
private ArrayList<Fragment> mFragments;
private ImpressionDetailFragment.OnCommentTargetChangeListener mListener;
public PagerAdapter(FragmentManager fragmentManager, List<Long> ids) {
super(fragmentManager);
this.mFragmentManager = fragmentManager;
this.mFragments = new ArrayList<>();
this.mIds = ids == null ? new ArrayList<Long>() : ids;
}
public static PagerAdapter newInstance(FragmentManager fragmentManager, List<Long> ids) {
return new PagerAdapter(fragmentManager, ids);
}
@Override
public Fragment getItem(int position) {
long impId = mIds.get(position);
Fragment fragment = Fragment.newInstance(impId);
mFragments.add(fragment);
return fragment;
}
@Override
public int getCount() {
return mIds.size();
}
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
public long getImpressionId(int position) {
if (position < 0 || position >= getCount()) {
position = 0;
}
return mIds.isEmpty() ? -1 : mIds.get(position);
}
public void remove(int position) {
clearFragmentsCache();
mIds.remove(position);
mFragments.clear();
notifyDataSetChanged();
}
private void clearFragmentsCache() {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
for (Fragment fragment : mFragments) {
transaction.remove(fragment);
}
transaction.commit();
transaction = null;
mFragmentManager.executePendingTransactions();
}
}