背景:广告位用ViewPager装载,但是ViewPager适合数量固定的子View,在做切换tab动态刷新数量的时候出了各种问题,例如:
java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged!
这是出现次数最多的异常,网上虽然有各种各样的解答(主要是adapter的notifysetChange机制的问题),但是照做以后并没有任何卵用,于是准备将ViewPager直接换成ViewFlipper,关于ViewFlipper的介绍,此处不做过多说明,只讲当前项目中的具体实现
/**
* 给ViewFlipper装载子View
* */
private void startAD() {
vfAdvertisementsFragment.removeAllViews();
for (int i = 0; i < adList.size(); i++) {
ImageView iv = new ImageView(context);
iv.setImageBitmap(bitmaps.get(i));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
vfAdvertisementsFragment.addView(iv);
}
}
//开启轮播
public void startBanner() {
mHandler.removeCallbacks(r);
mHandler.postDelayed(r, 3000);
}
//中止轮播
public void stopBanner() {
mHandler.removeCallbacks(r);
}
//轮播任务
private void initBanner() {
r = new Runnable() {
@Override
public void run() {
showNextView();
mHandler.postDelayed(this, 3000);
}
};
}
/**
* 按下时停止轮播,比如滑动时不能让自动轮播任务和用户的滑动相冲
*/
@Override
public boolean onDown(MotionEvent e) {
stopBanner();
return false;
}
/**
* 子View的点击事件
*/
@Override
public boolean onSingleTapUp(MotionEvent e) {
Intent intent = new Intent(context, WebViewServiceActivity.class);
intent.putExtra("title", "专题活动");
intent.putExtra("urlAddress", adList.get(vfAdvertisementsFragment.getDisplayedChild()).srcURL);
startActivity(intent);
return false;
}
/**
* 长按事件(注意是还未up的状态),长按不动后需要继续轮播
*/
@Override
public void onLongPress(MotionEvent e) {
startBanner();
}
/**
* 滑动事件,down方法停止轮播后,滑完需要继续开启轮播
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean fling = false;
if (e1.getX() - e2.getX() > 120) {
showNextView();
fling = true;
} else if (e1.getX() - e2.getX() < -120) {
showPreviousView();
fling = true;
}
startBanner();
return fling;
}
/**
* 往前翻页,设置动画,并重置下方圆点状态,然后根据getDisplayedChild找到当前的index显示圆点
*/
private void showPreviousView() {
vfAdvertisementsFragment.setInAnimation(AnimationUtils.loadAnimation(context,
R.anim.slide_right_in));
vfAdvertisementsFragment.setOutAnimation(AnimationUtils.loadAnimation(context,
R.anim.slide_right_out));
vfAdvertisementsFragment.showPrevious();
resetDot();
dotList.get(vfAdvertisementsFragment.getDisplayedChild()).setSelected(true);
}
/**
* 往后翻页,同上
*/
private void showNextView() {
vfAdvertisementsFragment.setInAnimation(AnimationUtils.loadAnimation(context,
R.anim.slide_left_in));
vfAdvertisementsFragment.setOutAnimation(AnimationUtils.loadAnimation(context,
R.anim.slide_left_out));
vfAdvertisementsFragment.showNext();
resetDot();
dotList.get(vfAdvertisementsFragment.getDisplayedChild()).setSelected(true);
}
/**
* 当前fragment显示在activity顶层的时候开启轮播,否则终止轮播
*/
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) {
stopBanner();
} else {
startBanner();
}
}
/**
* activity在window顶层的时候开启轮播,onPause时终止轮播
* */
@Override
public void onResume() {
super.onResume();
startBanner();
}
@Override
public void onPause() {
super.onPause();
stopBanner();
}
以上是具体实现的代码,已加上了详细的注释,主要从以下几个点入手,一步一步更改
1.轮播是否自动
2.轮播时长设为x秒,是否正常
3.子View点击事件假设进入一个新的界面,再退出,是否继续轮播
4.切换tab再切回来,是否继续轮播
5.手势滑动是否正常,与自动轮播有无冲突
6.长按是否停止轮播,后又重新开始轮播
2.轮播时长设为x秒,是否正常
3.子View点击事件假设进入一个新的界面,再退出,是否继续轮播
4.切换tab再切回来,是否继续轮播
5.手势滑动是否正常,与自动轮播有无冲突
6.长按是否停止轮播,后又重新开始轮播
生命周期的调用顺序是onResume→onHiddenChanged
PS:说一下以上用法的缘由,之所以将点击事件写在onSingleTapUp而不是子View的OnClickListener里面,是因为onFling方法会与点击事件OnClick冲突,网上其他人给出的方案是将dispatchTouchEvent直接交由GestureDetector来处理,这样一来导致的问题就是,切换到轮播tab的时候,你就无法再次切换了,因为tab栏组件不属于当前的fragment下,当然因具体项目的不同,可能每个人 处理方案不同,在此只是提供我的一种解决方案