简单灵活解决 Viewgroup嵌套 产生的手势冲突问题

来源:互联网 发布:淘宝店怎么样提高销量 编辑:程序博客网 时间:2024/06/05 16:46

          转载请标明原文地址:简单灵活解决 Viewgroup嵌套 产生的手势冲突问题

       这是接着上一篇Android 下拉刷新上拉加载 多种应用场景 超级大放送(上)的,这里介绍一下怎么 简单灵活解决Viewgroup嵌套产生的手势冲突问题。虽然这里只是以ViewPager为例,但是提供了一种解决此类问题的通用思路。

       先来看一下网易新闻客户端的界面效果:

  • 当手势方向为蓝色箭头区域方向时,响应ListView的滑动
  • 当手势方向为黄色箭头区域方向时,响应ViewPager的图片滑动

 

       上一篇实现的Demo效果图,手势效果响应和上图一样


       关于Viewgroup嵌套产生的手势冲突问题主要分为两类:

  1. 当手势动作作用于子View时,我们希望能够响应父Viewgroup的相应事件
  2. 当手势动作作用于子View时,我们希望能够响应子View的相应事件

       对于1  一种简单粗暴的解决方案:在父Viewgroup的onInterceptTouchEvent方法中,对子View直接进行拦截
       对于2  还是简单粗暴的解决方案:在子View的dispatchTouchEvent方法中,通过requestDisallowInterceptTouchEvent(true) 告诉父Viewgroup,不要拦截我。
      上面的方案带来的手势交互效果有时候并不那么友好自然,在项目的实际情况下,要复杂一点。比如说,在ListView 嵌套图片轮播的ViewPager时,希望手势在与水平方向<45°的时候,响应ViewPager的图片轮播,在与垂直方向<45°的时候,响应ListView的滑动。当然,你也可以设置 根据 手势 在X方向,Y方向移动一段 设定的 最小距离值 再响应 不同的事件。

       这里提供一种 解决以上问题的通用方案:

  1. 手势动作作用于子View时,无论是希望响应父Viewgroup,还是响应子View,都交给子View处理;
  2. 在子View的dispatchTouchEvent方法中,通过GestureDetector将MotionEvent事件交给监听器OnGestureListener
  3. 根据监听的不用手势结果,再决定到底是响应父Viewgroup事件,还是响应子View事件
       先写一个小测试Demo,定义一个子View
public class SubView1 extends View {private static String TAG = SubView1.class.getSimpleName();private GestureDetector mGestureDetector;private Paint paint = new Paint();public SubView1(Context context) {super(context);}public SubView1(Context context, AttributeSet attrs) {super(context, attrs);// 为mGestureDetector设置监听器mGestureDetector = new GestureDetector(context, new MTouchDetector());// 保证能触发相应的事件setFocusable(true);setClickable(true);setEnabled(true);setLongClickable(true);}protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 指定宽和高setMeasuredDimension(600, 200);// super.onMeasure(widthMeasureSpec, heightMeasureSpec);}@Overridepublic void onDraw(Canvas canvas) {// TODO Auto-generated method stubsuper.onDraw(canvas);paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));canvas.drawColor((Color.parseColor("#FFA07A")));canvas.drawText("SubView1", 100, 100, paint);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:// L.i(TAG, "dispatchTouchEvent ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:// L.i(TAG, "dispatchTouchEvent ACTION_MOVE");break;case MotionEvent.ACTION_UP:// L.i(TAG, "dispatchTouchEvent ACTION_UP");break;default:break;}getParent().requestDisallowInterceptTouchEvent(true); // 先告诉父Viewgroup,不要拦截我我mGestureDetector.onTouchEvent(event); // 通过GestureDetector将MotionEvent事件交给监听器OnGestureListenerreturn super.dispatchTouchEvent(event);}class MTouchDetector extends SimpleOnGestureListener {// Touch down时触发public boolean onDown(MotionEvent e) {L.i(TAG, "onDown");return super.onDown(e);}public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {//tan(60°),手势与垂直方向大于30°,相应子View的onScroll,父Viewgroup的onScroll则不会响应if (1.732*Math.abs(distanceX) >= Math.abs(distanceY)) {L.i(TAG, "不要拦截我");getParent().requestDisallowInterceptTouchEvent(true);// 告诉父Viewgroup不要拦截我,事件我自行处理return true;} else {getParent().requestDisallowInterceptTouchEvent(false);// 告诉父Viewgroup拦截我,响应父Viewgroup的onScrollL.i(TAG, "拦截我");return false;}}public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {L.i(TAG, "onFling");return super.onFling(e1, e2, velocityX, velocityY);}// 双击的第二下Touch down时触发public boolean onDoubleTap(MotionEvent e) {L.i(TAG, "onDoubleTap");return super.onDoubleTap(e);}public boolean onDoubleTapEvent(MotionEvent e) {L.i(TAG, "onDoubleTapEvent");return super.onDoubleTapEvent(e);}}}
       再定义父ViewGroup
public class MLinearLayout extends LinearLayout {private static String TAG = MLinearLayout.class.getSimpleName();// private static String TAG = SubView1.class.getSimpleName();private GestureDetector mGestureDetector;@SuppressLint("ClickableViewAccessibility")public MLinearLayout(Context context, AttributeSet attrs) {super(context, attrs);mGestureDetector = new GestureDetector(context, new MTouchDetector());this.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {return mGestureDetector.onTouchEvent(event);}});setFocusable(true);setClickable(true);setEnabled(true);setLongClickable(true);}@SuppressLint("NewApi")public MLinearLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public MLinearLayout(Context context) {super(context);}class MTouchDetector extends SimpleOnGestureListener {public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {L.i(TAG, "onScroll");return true;}}@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {// TODO Auto-generated method stub mGestureDetector.onTouchEvent(event);return false;}}<span style="font-size: 11.9999990463257px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(254, 254, 254);">      </span>
       外面是MLinearLayout,里面是SubView1
 
       当在SubView1滑动时,手势与垂直方向>30°(比较水平),则会告知告诉父Viewgroup不要拦截我,事件我自行处理,父Viewgroup的onScroll不会执行。
       当在SubView1滑动时,手势与垂直方向<30°(比较竖直),则会告知告诉父Viewgroup截我,父Viewgroup的onScroll执行。
 
       接着解决ListView 嵌套图片轮播的ViewPager的问题,思路和上面一样,重写ViewPager。
/** * @author wen_er * */public class MyViewPager extends ViewPager {private static String TAG = MyViewPager.class.getSimpleName();private GestureDetector mGestureDetector;public MyViewPager(Context context) {super(context);mGestureDetector = new GestureDetector(context, new ScrollDetector());}public MyViewPager(Context context, AttributeSet attrs) {super(context, attrs);// mGestureDetector = new GestureDetector(context, new}class ScrollDetector extends SimpleOnGestureListener {@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {if (Math.abs(distanceX) >= Math.abs(distanceY)) {getParent().requestDisallowInterceptTouchEvent(true);// 告诉父viewGroup不要拦截我return true;} else {getParent().requestDisallowInterceptTouchEvent(false);// 告诉父viewGroupw拦截我return false;}}}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {L.i(TAG, "dispatchTouchEvent");getParent().requestDisallowInterceptTouchEvent(true);mGestureDetector.onTouchEvent(event);return super.dispatchTouchEvent(event);}}

       是不是很简单,其效果就是前面截图,手势与垂直方向>45°,相应ViewPager的图片轮播效果,手势与垂直方向<45°,响应ListView的滑动。当然,你可以随意更改角度,再加上一个最小响应的距离值,让手势交互效果更自然。
       Demo源码会在后面一并给出。

       说好的优化后再上传,最近实在太忙,就把以前的代码放出来了,Sorry……

CSDN下载地址http://download.csdn.net/detail/yalinfendou/9187925



3 0
原创粉丝点击