欢迎指正错误O(∩_∩)O!
首先解决这件事需要了解android的事件分发机制,和事件分发的顺序,如何计算view的坐标。
计算坐标
假设已掌握 ~(≧▽≦)/~
事件分发机制
假设已掌握 ~(≧▽≦)/~
事件分发顺序
点击或滑动事件摊派到了某个view(设为v),v如何知道这个事件它能否处理?答案是通过view的几个事件响应方法得知,这几个事件相应方法分别是onTouch、onTouchEvent、onLongClick、onClick(可能还有,这里只列举这几个)。
onTouch:每个view都可以设置滑动监听器,只要实现onTouchLintener接口即可。
onTouchEvent:view的滑动事件响应方法,如果view没有实现onTouch接口,就由这个方法处理滑动事件,处理成功返回true,没有处理返回false,返回true则事件不再下发。
onLongClick:长按屏幕的响应方法,如果上面的方法返回true,这个方法就不会被执行。
onClick:同上,区别在于这个是点击事件,不是长按。
方法的执行顺序已给出,onTouch->onTouchEvent->onLongClick->onClick。这里有一个小坑,点击事件(带Click的)没有返回值,怎么知道事件是否被消费? 查看源码发现他们两的返回值其他方法隐藏了,其实他们是有返回值的。
代码为证:
relativeLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("onClick","relativeLayout");
Toast.makeText(MainActivity.this, "ajuergeirgbesrbglehasrbe",Toast.LENGTH_SHORT).show();
}
});
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("onClick","textView");
Toast.makeText(MainActivity.this, "asdfgwerg",Toast.LENGTH_SHORT).show();
}
});
代码中的textView是定义在relativeLayout中的,经测试证明Log.e(“onClick”,”relativeLayout”);这句话没有被输出。
滑动冲突解决
首先说明情况,我用继承HorizontalScrollView的方式实现了滑动菜单,右滑就会出现,同时主布局上的ListView也实现了滑动删除,这两个分开用都是好使的,但是合在一起,ListView的滑动就失灵了。
开始我怀疑滑动事件在HorizontalScrollView的层次上就被截住了,没有下发,但事实证明没有被截住,ListView的dispatchTouchEvent与onTouchEvent方法都正常响应了,但却只响应了DOWN动作,偶尔响应一两次MOVE动作,这让我非常蛋痛=。=
然后,我想到了一个改进办法,强行下发,就是从HorizontalScrollView层次直接下发到ListView上,跳过中间的所有view,像这样:
public class SlidingMenu extends HorizontalScrollView{
........
public boolean dispatchTouchEvent(MotionEvent event){
Log.e(LOG, " dispatchTouchEvent");
if(isOpen == false){
int downX;
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(LOG, " DOWN");
downX = (int)event.getX();
if(downX > mScreenWidth/6){
slideList.dispatchTouchEvent(event);
}
break;
case MotionEvent.ACTION_MOVE:
Log.e(LOG, " MOVE");
downX = (int)event.getX();
if(downX > mScreenWidth/6){
slideList.dispatchTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
Log.e(LOG, " UP");
downX = (int)event.getX();
if(downX > mScreenWidth/6){
slideList.dispatchTouchEvent(event);
}
break;
}
}
return super.dispatchTouchEvent(event);
}
........
}
结果光荣的空指针了,被强制结束。经查证是event的坐标无法定位到ListView上面:
case MotionEvent.ACTION_DOWN:
downX = (int) ev.getX();
downY = (int) ev.getY();
Log.e("SlideList", "X :" + downX + " Y :" + downY);
currentPosition = pointToPosition(downX, downY);
}
这是自写的listview中dispatchTouchEvent方法的其中一段,ev即从SlidingMenu传过来的event,错误在于通过ev的坐标计算不出listview的position,也就不知道是哪个item该滑动。
再然后,我发现
downX = (int) ev.getX();
downY = (int) ev.getY();
这样写获得的是滑动事件起点相对于父view原点的坐标,对于我的程序来说,就是应用程序原点(区别应用程序原点和屏幕原点的位置,两者不相等,Y轴上差了50,标题栏占25还有一个什么占25,所以应用程序原点应该是(0,50))。
小贴士:相对坐标 = ((x1-x2),(y1-y2)),即(x1,y1)相对于(x2,y2)的坐标。
pointToPosition这个方法可以计算坐标计算出点击位置在listview的哪个item上,但它只接受相对于listview的坐标,而ev的坐标确实相对于应用程序原点的,所以出错。
分析明白了,解决办法也就出现了,一道非常简单的数学题。只需要获取滑动事件对于屏幕原点的坐标:
downX = (int) ev.getRawX()
downY = (int) ev.getRawY()
同时获取listview对于屏幕原点的坐标:
int[] local = new int[2];
this.getLocationOnScreen(local);
// local[0] 即 X local 即 Y
他们两相减就OK了:
downX = (int) ev.getRawX() - local[0];
downY = (int) ev.getRawY() - local[1];
到此,问题解决,listview的滑动正常了。
但我又发现两个新问题,listview刷新卡顿与滑动判断不严谨(纵向滑动时横向滑动也再判断)。革命尚未成功同志仍需努力=。=