工作中在做首页轮播图时有一个需求, 当用户滑动轮播图时暂停自动轮播, 用户手指离开屏幕时重新开始自动轮播. 此时我想到了使用setOnTouchListener 方法设置一个监听在用户ACTION_DWON和ACTION_UP时, 停止和开始自动轮播, 但跑起来后发现onTouch方法压根没有执行. 测试后发现, 如果onTouch方法返回值return false, 则监听收不到事件, 如果onTouch方法返回true, 则ViewPager不能正常滑动.
然后我突然想到addOnPageChangeListener方法,其中的onPageScrolled回调在viewPager滑动时可以通过参数positionOffset实时监听滑动距离:
addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.i("DEFAULT","position:"+position+" positionOffset:"+positionOffset+" positionOffsetPixels:"+positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
突发奇想打印Log看看:
当setCurrentItem(1,false)
执行时打印Log如下:
当”setCurrentItem(1,true)”或者手指向左滑动手指从按下到离开中间所经历的Log
分析Log后, 发现在不考虑 setCurrentItem 方法执行的情况下, 当positionOffset为0时永远代表ACTION_UP事件, 当positionOffset不为0时, 除第一次非零数据是ACTION_DOWN事件外, 其余都是ACTION_MOVE事件, 当然拿不到点击位置, 这也是没有办法的事.
于是我想到了继承ViewPager, 通过该监听事件, 模拟出onTouchListener监听.
具体代码如下, 定义三个状态 up move down, 默认状态是up, 通过监听onPageScrolled方法, positionOffset 为0时改变状态为up, 非0时如果状态为up则改变为down, 如果状态为down则改变状态为move, 设置开关setCurrentItemNoAnimStart来防止setCurrentItem(1,false|true)
导致误触发事件, 虽然最终结果接收到down 和 up事件时有迟缓并且得不到按下位置, 但能够得到 down up move 三种状态已经完美解决了轮播图的需求.
希望这篇博文也能帮到正在看的你.
补充: 最近看了一些博客, 找到一种更好的方案解决 ViewPager监听问题, 做法是重写 viewPager dispatchTouchEvent 方法, 在该方法里写一个监听事件, 简单高效, 能更快更准确的获取到监听, 不得不承认比我的方法好.
// 大神的方案...
public class SuperViewPager extends ViewPager{
public static final int STATE_DOWN = MotionEvent.ACTION_DOWN;
public static final int STATE_UP = MotionEvent.ACTION_UP;
public static final int STATE_MOVE = MotionEvent.ACTION_MOVE;
int state = STATE_UP;
public SuperViewPager(Context context) {
super(context);
public SuperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(mOnTouchListener != null)
mOnTouchListener.onTouch(ev.getAction());
return super.dispatchTouchEvent(ev);
}
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
OnTouchListener mOnTouchListener;
interface OnTouchListener {
void onTouch(int state);
}
}
// 我的方案
public class SuperViewPager extends ViewPager{
public static final int STATE_DOWN = 0;
public static final int STATE_UP = 1;
public static final int STATE_MOVE = -1;
int state = STATE_UP;
boolean setCurrentItemNoAnimStart;
private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Log.i(Util.getTag(null),"position:"+position+" positionOffset:"+positionOffset+" positionOffsetPixels:"+positionOffsetPixels);
//... 相当于 viewpager onTouchListener
if(positionOffset == 0){
// Log.i(Util.getTag(null),"HAHA_position:"+position);
if(setCurrentItemNoAnimStart){
setCurrentItemNoAnimStart = false;
}else {
state = STATE_UP;
if (mOnTouchListener != null)
mOnTouchListener.onTouch(STATE_UP);
}
}else{
if(!setCurrentItemNoAnimStart){
if (state == STATE_UP) {
state = STATE_DOWN;
if (mOnTouchListener != null)
mOnTouchListener.onTouch(STATE_DOWN);
} else if(state == STATE_DOWN) {
state = STATE_MOVE;
if (mOnTouchListener != null)
mOnTouchListener.onTouch(STATE_MOVE);
}
}
}
//...
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
public SuperViewPager(Context context) {
super(context);
}
public SuperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
addOnPageChangeListener(mOnPageChangeListener);
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {
if(!smoothScroll)
setCurrentItemNoAnimStart = true;
super.setCurrentItem(item, smoothScroll);
}
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener = l;
}
OnTouchListener mOnTouchListener;
interface OnTouchListener {
void onTouch(int state);
}
}