|
安卓开发过程中滑动冲突的情形主要有三类:
① 父view与子view的滑动方向不同,如:父view左右滑动,子view上下滑动或相反;
这种情形是比较简单的,只需要根据不同的滑动动作进行相应的拦截与处理即可。
② 父view与子view的滑动方向相同,即,父view左右,子view也左右,父view上下,子view也是上下;
这种情形需要根据具体情况来进行拦截处理,比如父View在出现子View滑动到边缘的情况才进行
拦截处理或者其他情况。
③ 以上两种情形,多个View的嵌套。
这种情形虽比较复杂,但根据上面两种情形的处理法则分开进行处理即可。
滑动冲突的解决策略的理论基础为安卓的事件分发机制,不甚了解的朋友可以去先补补这方面的知识。
针对滑动冲突的解决策略有以下两种:
一种是外部拦截法:即是当事件满足滑动条件,通过父View的onInterceptTouchEvent方法对其进行
拦截,拦截之后将直接进入父View的onTouchEvent进行事件消费,不会再传入下级view;
二种是内部拦截法:即是通过子View的dispatchTouchEvent方法接收到down事件,然后获取父View的requestDisallowInterceptTouchEvent方法禁止其onInterceptTouchEvent拦截,当满足父View滑动条件的
时候才允许。
第二种方法需要父View不拦截down事件,一般情况下也是不拦截down事件的,因为拦截了down事件,
所有子元素点击事件都会失效。建议采用第一种方法,易于理解,不容易出错。
针对第一种方法,下面 :
一个下拉刷新的SwipeRefreshLayout里面嵌套了左右滑动、并且在pullToRefreshListView头的viewPage
和上下滑动pullToRefreshListView(这里解释一下,pullToRefreshListView禁掉了它的下拉刷新功能,只用它
的上拉刷新),若不做解决事件冲突处理,就会出现只能父View下拉刷新,viewPager左右滑动无效,并且当
ListView往下滑动后不能再上滑,因为上滑时候的下拉事件被父view拦截了,所以这里有了两种冲突情形。
首先先分析viewPage与父view SwipeRefreshLayout的滑动冲突,一个上下,一个左右,那好办,只要重写SwipeRefreshLayout的onInterceptTouchEvent对滑动事件进行事件判断,如果是左右滑动的那么返回false不
进行拦截,事件将直接传到子View,即传给了viewPager,实现其左右滑动的操作。然后说说pullToRefreshListView
与父view SwipeRefreshLayout的滑动冲突,因为两者都要上滑即手势下拉的操作,所以产生了冲突。而这里我
们只是想当pullToRefreshListView滑动到顶部才使能SwipeRefreshLayout的下拉刷新,所以思路就是当没滑动到
顶部的时候SwipeRefreshLayout的onInterceptTouchEvent方法返回false
不进行拦截,把事件传给他的子View pullToRefreshListView,从而实现其上滑操作,直到滑动到了顶部父View才对事件进行拦截,如此便解决了
事件的冲突。
获取子View pullToRefreshListView,并开启滑动监听,判断是否处于顶部
<span style="font-size:14px;"> if(getChildAt(0).getId() == R.id.pullToRefreshListView){
((PullToRefreshListView)getChildAt(0)).getRefreshableView().setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(firstVisibleItem==0){
isFirst = true;
}else{
isFirst = false;
}
}
});
}</span>
重写onInterceptTouchEvent进行条件判断
<span style="font-size:14px;"> @Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
if(!isFirst){
return false;
}
}
return super.onInterceptTouchEvent(event);
}</span>
为了精准判断滑动的方式,我们在自定义viewgroup里面重写onInterceptTouchEvent时,需要对滑动
的xy轴距离判断大小,若竖直方向距离大则认为是竖直滑动,否则认为是水平滑动。
而第二种方法一般代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if("满足条件")
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return super.dispatchTouchEvent(ev);
}
|