我的ViewPager数据改变后,在切换界面刷新数据时:OnPageChangeListener中的数据IndexOutOfBoundsException,我们来看源码探一下究竟;
代码时这样写的:
vpLoveCar.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { PreferenceUtil.set("OWNERCARVIN",carList.get(position).getVin_code());//记录当前索引,下拉刷新时更新该辆车;再次进入时显示该辆车; } @Override public void onPageScrollStateChanged(int state) { } });
各种使用都没有问题,但是当carList的集合长度变化时(size变大后),我再去切换就会崩溃,这个问题足足影响了一个星期,我一直以为是数据操作的问题;直到今天我看了addOnPageChangeListener的源码。。。。。。
ViewPager的addOnPageChangeListener的源码:
private List<OnPageChangeListener> mOnPageChangeListeners; private OnPageChangeListener mOnPageChangeListener;
/** * Add a listener that will be invoked whenever the page changes or is incrementally * scrolled. See {@link OnPageChangeListener}. * * <p>Components that add a listener should take care to remove it when finished. * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()} * to remove all attached listeners.</p> * * @param listener listener to add */ public void addOnPageChangeListener(@NonNull OnPageChangeListener listener) { if (mOnPageChangeListeners == null) { mOnPageChangeListeners = new ArrayList<>(); } mOnPageChangeListeners.add(listener); }
ViewPager的setOnPageChangeListener的源码:
/** * Set a listener that will be invoked whenever the page changes or is incrementally * scrolled. See {@link OnPageChangeListener}. * * @param listener Listener to set * * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)} * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead. */ @Deprecated public void setOnPageChangeListener(OnPageChangeListener listener) { mOnPageChangeListener = listener; }
由源码可知:addOnPageChangeListener这个方法,每调用一次都会添加一个OnPageChangeListener的监听,ViewPager会把这些监听存在一个List的集合中;所以我每次add一个监听,就会增加一个OnPageChangeListener,里面的数据也会有不同;会导致你有多个OnPageChangeListener,但是每个回调里面的position却是同样的,它去你集合长度小的那个里面OnPageChangeListener去获取数据,所以会下标越界异常;
源码提示让你在添加时去清空之前的OnPageChangeListener:
/** * Remove all listeners that are notified of any changes in scroll state or position. */ public void clearOnPageChangeListeners() { if (mOnPageChangeListeners != null) { mOnPageChangeListeners.clear(); } }
而setOnPageChangeListener这个方法,每调用一次都会新设置一个OnPageChangeListener的监听,ViewPager会替换你之前的监听; 用这个方法的话数据每次都是最新的,所以不会下表越界异常;
/** * Remove a listener that was previously added via * {@link #addOnPageChangeListener(OnPageChangeListener)}. * * @param listener listener to remove */ public void removeOnPageChangeListener(@NonNull OnPageChangeListener listener) { if (mOnPageChangeListeners != null) { mOnPageChangeListeners.remove(listener); } }
我最后的解决方式,有两种:
1、先清空在添加监听
vpLoveCar.clearOnPageChangeListeners(); vpLoveCar.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { PreferenceUtil.set("OWNERCARVIN",carList.get(position).getVin_code());//记录当前索引,下拉刷新时更新该辆车;再次进入时显示该辆车; } @Override public void onPageScrollStateChanged(int state) { } });
2、设置监听
vpLoveCar.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { PreferenceUtil.set("OWNERCARVIN",carList.get(position).getVin_code());//记录当前索引,下拉刷新时更新该辆车;再次进入时显示该辆车; } @Override public void onPageScrollStateChanged(int state) { } });
收获不小,我之前在使用这些方法没有出问题的时候,我一直认为addOnPageChangeListener和setOnPageChangeListener是一样的,知道今天改变数据后会下标越界异常;多去看一下源码还是有好处的;