android开发:探索学习Android Touch事件分发传递机制(一)
想要把View的滑动事件冲突搞懂得先弄懂android的事件传递机制
滑动冲突主要有俩种方式:
1.外部滑动方向与内部方向不一致:像viewPager和listView,但是viewPager已经把这种冲突解决了。
2.外部方向与内部方向一致:像外部ScrollView嵌套一个listView。
今天要讲的例子就是ScrollView嵌套listView怎么解决滑动冲突:
如下图ScrollView直接嵌套listView滑动的效果:
很明显listView已经滑动不了,而我们希望的效果是:先滑动listView,listView滑动到底部后再滑动ScrollView
我们采用内部拦截法来解决:
1.自定义ScrollView,重写onInterceptTouchEvent(),除了点击事件外其他都拦截。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("父布局","onInterceptTouchEvent");
Boolean b;
if (ev.getAction()==MotionEvent.ACTION_DOWN)
{
b=super.onInterceptTouchEvent(ev);
} else {
b=true;
}
return b;
}
2.自定义listView,重写onTouchEvent()方法
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("子view点击", "onTouchEvent");
Log.e("子view点击", "" + this.getCount());
startX = (int) ev.getX();
startY = (int) ev.getY();
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
Log.e("子view移动", "" + getHeight());
Log.e("子view移动", "" + this.getLastVisiblePosition());
int up = (int) (ev.getY() - startY);
Log.e("子view移动", "" + up);
View view = (View) getChildAt(getChildCount() - 1);
//获取textView的高度,getBottom()是获取textView最初位置自身底部到父控件顶部的距离
int childViewHight = view.getBottom();
//getMeasuredHeight获取listview的高度,getScrollY获取listView上边界距离它内容的上边界距离
childViewHight -= (getMeasuredHeight() + getScrollY());
if (childViewHight == 0) {
Log.e("底部", "" + this.getLastVisiblePosition());
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return super.onTouchEvent(ev);
}
至于怎么判断listView什么时候是滑动到底部我们来了解一下:
上图可以很清楚的看到listView的状态:
getBottom():它的定义是获取view的初始位置底部到父控件顶部的距离。
getScrollY():它的定义是获取view的上边界到view的内容上边界距离,如果view是listView那它的内容就是textView.
View view = (View) getChildAt(getChildCount() - 1);
//获取textView的高度,getBottom()是获取textView最初位置自身底部到父控件顶部的距离
int childViewHight = view.getBottom();
//getMeasuredHeight获取listview的高度,getScrollY获取listView上边界距离它内容的上边界距离
childViewHight -= (getMeasuredHeight() + getScrollY());
最后理一下程序流程:
我们的父控件onInterceptTouchEvent()方法除了点击事件不拦截之外其他事件都进行拦截,所以我在屏幕上点击listView的时候,点击事件都能传递到listView,而我在listView 的onTouchEvent()方法中判断只要是点击事件就去去调用 getParent().requestDisallowInterceptTouchEvent(true);(这句话意思是告诉父控件:后面的事件不要拦截,事件交由我来消费,因此listView可以滑动),当listView获取了消费权后就可以自由滑动了,然后我在case MotionEvent.ACTION_MOVE:中去判断,只要listView滑动到底部后,就调用 getParent().requestDisallowInterceptTouchEvent(false);(通知让父控件进行拦截),这样子父控件就会拦截事件,因此ScrollView开始滚动。最后来看一下运行效果:
此外还有一种外部拦截法,在父控件拦截,满足条件则把事件下发: