我们知道,ViewPager 没有点击事件,许多时候,需要设置点击事件,比如轮询广告点击事件,这时候怎么处理呢?我总结了两种方式:
下面分别说明一下:
比如这种广告轮询,如果想要点击进入,就要给详情页面传入一个id,详情页通过网址+id(比如www.xxxxx/id)访问数据。那么,首先要说明的是这个是ViewPager+Fragment做的,适配器用的是PagerAdapter(后来我也尝试了用FragmentStatePagerAdapter,下面会说明)。
代码如下:
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
View view = viewList.get(position);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callBack.callBack(position);
}
});
container.addView(view);
return view;
}
直接给填充Fragment的视图设置点击事件,我这里用了一个回调(这个模式不懂的话自己查一下,不是很难),直接在在回调处做事件处理:
@Override
public void callBack(int position) {
Intent intent = new Intent(getActivity(), TeaDetailsActivity.class);
intent.putExtra("id", adList.get(position).getId());
startActivity(intent);
}
【注】
如果用的适配器是FragmentStatePagerAdapter,因为它没有instantiateItem()方法,则可以在Fragment的onCreateView()中设置点击事件。
首先说明,我参考了关于ViewPager的点击事件的处理(感谢作者),这篇文章思路是,点下屏幕时,设置一个flag为0,移动时为-1,抬起手指时,如果 flag==-1 说明是滑动事件(用于切换 ViewPgaer),此时不触发单击事件(用户滑动界面而非点击进入详情);如果 flag==0,则说明是单击事件。
说真的,我试了,结果不行。可能和机型有关吧,我用的是Android7.0系统,我 debug 的时候,发现无论怎么点击,都会触发移动的动作:
case MotionEvent.ACTION_MOVE:
flag = -1 ;
break ;
此时,我在 MotionEvent.ACTION_UP 里面写的代码都没有调用:
if (flag == 0) {//debug的时候发现 flag == -1
//代码操作
}
于是我尝试在 MotionEvent.ACTION_DOWN 和 MotionEvent.ACTION_MOVE 的时候分别打印当前的坐标 x,y 值,我发现 x,y 的值都没有变化,就是说,即使手指在屏幕上没有滑动,也会触发 MotionEvent.ACTION_MOVE 事件。
怎么办呢?我请教了我的基友(下面简称他为黄),黄先是查看ViewPager类中的源码,有一块是关于ViewPager点击事件的:
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
if (pointerIndex == -1) {
// A child has consumed some touch events and put us into an inconsistent state.
needsInvalidate = resetTouch();
break;
}
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
// Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
有一点长,其中有一个变量叫做 mTouchSlop ,对于它的处理很容易理解:
if (xDiff > mTouchSlop && xDiff > yDiff) {
//一些操作
}
xDiff 和 yDiff 是 x 和 y 方向上的移动距离(取绝对值),左右滑动时,x 方向上的变化值大于 mTouchSlop ,而且 xDiff 应该大于 yDiff (上下滑动视为无效),黄跟我说,他猜这个 mTouchSlop 应该就是判断ViewPager是否切换页面的滑动临界值,左右滑动超过这个值,页面就会滑到上一个或者下一个界面(看你往哪个方向滑了)。
至此,黄说对我说,如果不想干扰 ViewPager 的滑动事件,又想给 ViewPager 设置点击事件,可以在 mTouchSlop 上面做一些操作。简单的讨论之后,我们就搞定了:
headVp.setOnTouchListener(new View.OnTouchListener() {
int touchFlag = 0;
float x = 0, y = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchFlag = 0;
x = event.getX();
y = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float xDiff = Math.abs(event.getX() - x);
float yDiff = Math.abs(event.getY() - y);
if (xDiff < mTouchSlop && xDiff >= yDiff)
touchFlag = 0;
else
touchFlag = -1;
break;
case MotionEvent.ACTION_UP:
if (touchFlag == 0) {
int currentItem = headVp.getCurrentItem();
Intent it = new Intent();
it.setClass(getActivity(), NewsDetailsActivity.class);
it.putExtra("story_id", topStories.get(currentItem).getId());
startActivity(it);
}
break;
}
return false;
}
});
对了!黄还教了我如何取到 mTouchSlop 的值(也是源码当中的):
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
至此,问题就都解决了。最后附上项目代码:
茶百科
逼乎日报