在使用大神(@bingoogolapple)的第三方控件時BGABanner-Android出現的兩小個bug記錄,issues中也有人遇到過,但無人回覆,坐等大神更新,以下爲個人處理方案,如有問題請指正。
CSDN:https://blog.csdn.net/lylddingHFFW/article/details/89212664
一 使用場景:使用BGABanner控件,當其寬度較小時,比如只有20dp,這樣是很容易滑動到左右邊界的。默認ViewPager左右是保留1個Item。
復現bug1:開啓無限輪播後,同時手指快速左滑動,一直滑動到viewpager Item不能移動,並鬆開時,出現anr。(復現概率很大)
復現bug2:開啓無限輪播後,同時手指多次快速左滑動,可能出現停止無限輪播現象。(復現機率一般)
二 bug分析及處理方案
1.1 bug1分析:
在BGABanner類代碼中,是利用onPageScrolled獲取滑動的位置mPageScrollPosition。當ActionUP時在handleAutoPlayActionUpOrCancel中判斷左滑還是右滑,來更新滑動位置。
@Override
public void handleAutoPlayActionUpOrCancel(float xVelocity) {
if (mViewPager != null) {
if (mPageScrollPosition < mViewPager.getCurrentItem()) {
// 往右滑
if (xVelocity > VEL_THRESHOLD || (mPageScrollPositionOffset < 0.7f && xVelocity > -VEL_THRESHOLD)) {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition, true);
} else {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition + 1, true);
}
} else{
// 往左滑
if (xVelocity < -VEL_THRESHOLD || (mPageScrollPositionOffset > 0.3f && xVelocity < VEL_THRESHOLD)) {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition + 1, true);
} else {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition, true);
}
}
}
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mPageScrollPosition = position;
mPageScrollPositionOffset = positionOffset;
.....
}
滑動現象:獲取滑動位置mPageScrollPosition。在滑動過程中onPageScrolled中返回的position,一直是左邊Item的位置,這個可以看ViewPager的原理。infoForCurrentScrollPosition返回當前滑動的位置。
因此正常現象如下:
1:只顯示當前Item時,mPageScrollPosition = = getCurrentItem();
2:向右滑動時:mPageScrollPosition < getCurrentItem()且mPageScrollPosition +1 = getCurrentItem();
3:向左滑動時:mPageScrollPosition == getCurrentItem();
/**
* @return Info about the page at the current scroll position.
* This can be synthetic for a missing middle page; the 'object' field can be null.
*/
private ItemInfo infoForCurrentScrollPosition() {
final int width = getClientWidth();
final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
int lastPos = -1;
float lastOffset = 0.f;
float lastWidth = 0.f;
boolean first = true;
ItemInfo lastItem = null;
//從左到右遍歷
for (int i = 0; i < mItems.size(); i++) {
ItemInfo ii = mItems.get(i);
float offset;
if (!first && ii.position != lastPos + 1) {
// Create a synthetic item for a missing page.
ii = mTempItem;
ii.offset = lastOffset + lastWidth + marginOffset;
ii.position = lastPos + 1;
ii.widthFactor = mAdapter.getPageWidth(ii.position);
i--;
}
offset = ii.offset;
final float leftBound = offset;
final float rightBound = offset + ii.widthFactor + marginOffset;
if (first || scrollOffset >= leftBound) {
if (scrollOffset < rightBound || i == mItems.size() - 1) {
return ii;
}
} else {
return lastItem;
}
first = false;
lastPos = ii.position;
lastOffset = offset;
lastWidth = ii.widthFactor;
lastItem = ii;
}
return lastItem;
}
問題出現: 但是手指快速左滑動,一直滑動到viewpager Item不能移動,並鬆開時,onPageScrolled中返回的position值又加1了。。。。
log如下:offset==0後手指還處於快速左滑狀態。
此時的異常現象 :mPageScrollPosition = getCurrentItem()+1且xVelocity 快速左滑速度超過閾值VEL_THRESHOLD ,代碼進入左滑的if()分支,傳入的值爲mPageScrollPosition + 1 即 getCurrentItem()+2;
那麼ViewPager默認是保留左右兩個Item的,現在要去找左邊第二個沒保存Item的position,那麼就會一直找不到這Item對應的position,由於無限循環次數爲Integer.MAX_VALUE,執行時間超10秒,就報ANR。
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition + 1, true);
1.2 bug1處理方案: 明確左右滑動條件,對異常情況進行處理。
if (mPageScrollPosition < mViewPager.getCurrentItem()) {
// 往右滑
if (xVelocity > VEL_THRESHOLD || (mPageScrollPositionOffset < 0.7f && xVelocity > -VEL_THRESHOLD)) {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition, true);
} else {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition + 1, true);
}
} else if (mPageScrollPosition == mViewPager.getCurrentItem()) {
// 往左滑
if (xVelocity < -VEL_THRESHOLD || (mPageScrollPositionOffset > 0.3f && xVelocity < VEL_THRESHOLD)) {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition + 1, true);
} else {
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition, true);
}
} else{
//其他
mViewPager.setBannerCurrentItemInternal(mPageScrollPosition, true);
}
2.1 bug2處理方案:
這個bug是在復現第一個bug1是偶現的,主要就是當多次快速滑動時,BGABanner控件dispatchTouchEvent有時返回false,因此只接受了ActionDown事件,即停止了無限循環。而後續的up和cancel事件沒有接受,即沒有從新開啓無限循環。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mAutoPlayAble) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//停止循環
stopAutoPlay();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//開啓循環
startAutoPlay();
break;
}
}
//處理偶然的不分發事件的情況handled ==false
boolean handled = super.dispatchTouchEvent(ev);
if (!handled && ev.getAction() != MotionEvent.ACTION_DOWN) {
startAutoPlay();
}
return handled;
}