人生得意须尽欢,
桃花坞里桃花庵。
Question:
最近在 《开发一款商业级Banner控件》,其中碰到了 调用 PageAdapter.notifyDataSetChanged() 页面不刷新问题,让我非常之郁闷,具体的问题是这样的:当我改变数据源时 调用notifyDataSetChanged时,当前页面以及缓存页面的内容不刷新。
如图: 在数据改变的情况下,我们的界面并没有刷新。 没办法 去看看源码吧。
Reson:
那么我们就先来看看notifyDataSetChanged()的源码吧。
1、刷新开始
PageAdapter.class
public void notifyDataSetChanged() {
synchronized (this) {
if (mViewPagerObserver != null) {
mViewPagerObserver.onChanged();
}
}
mObservable.notifyChanged();
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mObservable.unregisterObserver(observer);
}
void setViewPagerObserver(DataSetObserver observer) {
synchronized (this) {
mViewPagerObserver = observer;
}
}
ok,看到这就知道这是个观察者模式,而且成员变量mViewPagerObserver
是在 setViewPagerObserver
方法中设置进来的, 那么现在 找到这个'mViewPagerObserver' 是个什么东西以及mViewPagerObserver.onChanged()
方法都做了些什么
2、PagerObserver 以及 dataSetChanged()
可以看到 在ViewPager
当中我们创建了 PagerObserver
并调用了 mAdapter.setViewPagerObserver(mObserver);
所以我们的 mViewPagerBoserver
是PagerObserver
; 它又调用了dataSetChanged()
方法
ViewPager.class
public void setAdapter(PagerAdapter adapter) {
....
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
//监听adapter
mAdapter.setViewPagerObserver(mObserver);
}
// viewPager内部类
private class PagerObserver extends DataSetObserver {
PagerObserver() {
}
@Override
public void onChanged() {
// 数据改变时调用这个方法
dataSetChanged();
}
@Override
public void onInvalidated() {
dataSetChanged();
}
}
3、dataSetChanged
void dataSetChanged() {
// This method only gets called if our observer is attached, so mAdapter is non-null. 只给observer调用
// 拿到数据数量
final int adapterCount = mAdapter.getCount();
mExpectedAdapterCount = adapterCount;
//判断是否需要填充
boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
&& mItems.size() < adapterCount;
//拿到当前选中的item下标
int newCurrItem = mCurItem;
boolean isUpdating = false;
//循环mitems
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) {
mItems.remove(i);
i--;
if (!isUpdating) {
mAdapter.startUpdate(this);
isUpdating = true;
}
mAdapter.destroyItem(this, ii.position, ii.object);
// 需要填充
needPopulate = true;
if (mCurItem == ii.position) {
// Keep the current item in the valid range
newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
// 需要填充
needPopulate = true;
}
continue;
}
...
}
可以看到 大致的意思是:循环遍历视图上所有的item, 并通过adapter.getgetItemPosition
方法 拿到一个newPos
,该值有两种状态 一种:PagerAdapter.POSITION_UNCHANGED
和PagerAdapter.POSITION_NONE
,如果为PagerAdapter.POSITION_UNCHANGED
状态那么就不刷新itemView,否则 先销毁现有的itemView再重新创建一个新的itemVIew;
所以看到这里 就明白了 为什么调用notifyDataSetChanged
没有刷新页面。 直到这里那么我们就可以解决我们的问题了。
solve
这里的解决思路是,在我调用notifyDataSetChanged
方法时 ,将 getgetItemPosition
的返回值设置为PagerAdapter.POSITION_NONE
, 这样所有的 item视图都会刷新了。
具体代码是这样:
自定义PageAdapter
public boolean myNotify = false;
@Override
public int getItemPosition(Object object) {
if (!myNotify) {
return super.getItemPosition(object);
} else {
return POSITION_NONE;
}
}
public void notifyDataSetChanged(boolean isRefresh){
myNotify=isRefresh;
super.notifyDataSetChanged();
myNotify=false;
}
调用时
mAdapter.notifyDataSetChanged(true);
Question2:
强制刷新后transformer错乱问题
还是看源码【Android源码解读】ViewPager; 终于找到了问题
最后排查原因,是因为强制刷新后,调用 transformer 在requestLayout之前,而我们新建的view,在onLayout之前 view.left=0;所以 调用transformer时数据是不对的。
最后如下解决:
// ViewPager
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (transformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.isDecor) continue;
final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
transformer.transformPage(child, transformPos);
}
}
}
希望我的文章不会误导在观看的你,如果有异议的地方欢迎讨论和指正。
如果能给观看的你带来收获,那就是最好不过了。