最近需要实现一个功能,在Activity中有一个手写区域,为了更好的用户体验,需要满足即使整个手掌放在屏幕上时(android平板,屏幕比较大)也只响应手写区域内的操作,即在支持多点触控的情况下,只响应指定的区域,我将这个功能称作“手掌抑制”,即在手写时,手掌放在屏幕上面不做任何响应。

初看这个功能很简单,按照之前处理listview、gridview里面的子view不能响应的方式,只要在activity层不拦截向手写view传递的消息即可实现想要的效果,但经过实际测试和对android消息机制的详细研究发现,要实现这个功能会有点小复杂。

一、android的消息传递机制:

1、基础知识:

(1)所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。

(2)事件类型分为ACTION_DOWN,ACTION_UP,ACTION_MOVE,ACTION_POINTER_DOWN,ACTION_POINTER_UP,ACTION_CANCEL,每个事件都是以ACTION_DOWN开始ACTION_UP结束。

(3)对事件的处理包括三类:

传递——dispatchTouchEvent()

拦截——onInterceptTouchEvent()

消费——onTouchEvent()和OnTouchListener

2、传递流程

(1)事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

(2)事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

(3)如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。

(4)如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来。

(5)OnTouchListener优先于onTouchEvent()对事件进行消费。

上面的消费即表示相应函数返回值为true。

3、实际情况:

能够响应事件处理方法的控件包括:ViewGroup、View、Activity,各类控件对三个事件响应处理方法的支持情况如下:

这三个控件,Activity是处于最外层的,消息的传递首先是系统回调消息给Activity,Activity将消息传递给每一个ViewGroup,然后ViewGroup会将消息传递给相应地子View。

本文所描述的手写控件是一个view,在有系统消息回调时只有上层控件将消息分发下来,它才能够消费和处理这些消息。

二、问题现象:

接着我们进入正题,按照我在开篇介绍的那种处理方式,写一个手写view,在Activity和ViewGroup(自定义一个Layout即可)层将消息分发给该view,目前的代码看上去是这样子的:

public class DrawView extends View {public DrawView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initPaintView( );}public DrawView(Context context, AttributeSet attrs) {super(context, attrs);initPaintView( );}public DrawView(Context context) {super(context);initPaintView( );}public void clear() {          if (null != mPath) {              mPath.reset();              invalidate();          }      }        private void initPaintView() {          mPaint.setAntiAlias(true);          mPaint.setColor(Color.WHITE);          mPaint.setStyle(Paint.Style.STROKE);          mPaint.setStrokeJoin(Paint.Join.ROUND);          mPaint.setStrokeWidth(5f);      }        @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    mViewWidth = MeasureSpec.getSize(widthMeasureSpec);   //获取ViewGroup宽度        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);  //获取ViewGroup高度    }      @Override      protected void onDraw(Canvas canvas) {          canvas.drawPath(mPath, mPaint);      }          public boolean inArea( float x, float y ){    return ( x >= 0 && x <= mViewWidth && y >= 0 && y <= mViewHeight)?true:false;    }  @Override      public boolean onTouchEvent(MotionEvent event) {    float eventX = event.getX( );          float eventY = event.getY( );            switch (event.getAction()) {        case MotionEvent.ACTION_DOWN: {              mPath.moveTo(eventX, eventY);            mLastTouchX = eventX;              mLastTouchY = eventY;            invalidate( );        }              return true;          case MotionEvent.ACTION_MOVE:{        drawView( event, eventX, eventY );        }        break;        case MotionEvent.ACTION_UP:{          drawView( event, eventX, eventY );        }              break;          default:              return false;          }            return true;      }        private void drawView( MotionEvent event, float eventX, float eventY ){    resetDirtyRect(eventX, eventY);          int historySize = event.getHistorySize();          for (int i = 0; i < historySize; i++) {              float historicalX = event.getHistoricalX(i);              float historicalY = event.getHistoricalY(i);              getDirtyRect(historicalX, historicalY);              mPath.lineTo(historicalX, historicalY);          }          mPath.lineTo(eventX, eventY);          invalidate((int) (mDirtyRect.left - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.top - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.right + HALF_STROKE_WIDTH),                  (int) (mDirtyRect.bottom + HALF_STROKE_WIDTH));                mLastTouchX = eventX;          mLastTouchY = eventY;    }      private void getDirtyRect(float historicalX, float historicalY) {          if (historicalX < mDirtyRect.left) {              mDirtyRect.left = historicalX;          } else if (historicalX > mDirtyRect.right) {              mDirtyRect.right = historicalX;          }          if (historicalY < mDirtyRect.top) {              mDirtyRect.top = historicalY;          } else if (historicalY > mDirtyRect.bottom) {              mDirtyRect.bottom = historicalY;          }      }        private void resetDirtyRect(float eventX, float eventY) {          mDirtyRect.left = Math.min(mLastTouchX, eventX);          mDirtyRect.right = Math.max(mLastTouchX, eventX);          mDirtyRect.top = Math.min(mLastTouchY, eventY);          mDirtyRect.bottom = Math.max(mLastTouchY, eventY);      }        private static final float STROKE_WIDTH = 5f;      private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;      private float mLastTouchX = 0;      private float mLastTouchY = 0;        private int mViewWidth = 0;    private int mViewHeight = 0;      private final RectF mDirtyRect = new RectF();      private Paint mPaint = new Paint();      private Path mPath = new Path();  }
你会发现,当你将activity的dispatchTouchEvent和viewgroup的dispatchTouchEvent、onInterceptTouchEvent方法的返回值都设置为false时,整个屏幕都不会响应任何消息了,当我们去掉activity和viewgroup的dispatchTouchEvent方法,只有viewgroup的onInterceptTouchEvent返回值被置为false时,手写区域能够响应,但当手掌靠在手写区域外,在手写区域内手写就失效了,其实和去掉viewgroupd的onInterceptTouchEvent方法效果是一样的,也就是说明这种处理方式是不可行的。

三、解决方案:

加了允许分发消息的方法,不拦截向下分发消息反而还不行,这个问题具体原因我没有找到,个人认为是系统针对activity的消息事件处理做了特殊处理,它的优先级是最高的,虽然可以复写它的消息分发dispatchTouchEvent方法,但是不管是返回true还是false结果都是屏幕不能响应任何操作,这一点有知道的大拿欢迎指点指点。

要实现本文想要的功能显然不能使用开篇讲到的方法,在研究这个问题的过程中,发现虽然activity的dispatchTouchEvent无法控制,但其onTouchEvent方法是有效的,只要在屏幕的任何一个地方操作,onTouchEvent里面都会有打印消息,仔细回顾上面提到的android消息分发机制会发现,只要我们在屏幕上操作时,模拟系统在activity的onTouchEvent方法里面向手写view派发消息即可实现想要的功能,具体方法如下:

1、向下派发消息的实现以及view和activity之间的坐标转换:

如上面所讲,当在屏幕上操作时监听activity的onTouchEvent方法,将在手写控件内的操作派发给手写view即可实现想要的功能。

(1)坐标转换:

本文的手写view基于activity居中的,它的坐标原点和activity的坐标原点不相同,为了判断在acitivity上操作的地方是否在手写view内,需要通过坐标转换之后才能判断:


如上图所示,在activity中判断一个点(x,y)是否在手写view里面时,转换成的坐标应该是(x-view.getLeft(),y-view.getTop())。

(2)目前的view和activity看起来是这样子的:

DrawView:

public class DrawView extends View {public DrawView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initPaintView( );}public DrawView(Context context, AttributeSet attrs) {super(context, attrs);initPaintView( );}public DrawView(Context context) {super(context);initPaintView( );}public void clear() {          if (null != mPath) {              mPath.reset();              invalidate();          }      }        private void initPaintView() {          mPaint.setAntiAlias(true);          mPaint.setColor(Color.WHITE);          mPaint.setStyle(Paint.Style.STROKE);          mPaint.setStrokeJoin(Paint.Join.ROUND);          mPaint.setStrokeWidth(5f);      }        @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    mViewWidth = MeasureSpec.getSize(widthMeasureSpec);   //获取ViewGroup宽度        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);  //获取ViewGroup高度    }      @Override      protected void onDraw(Canvas canvas) {          canvas.drawPath(mPath, mPaint);      }          public boolean inArea( float x, float y ){    return ( x >= 0 && x <= mViewWidth && y >= 0 && y <= mViewHeight)?true:false;    }  @Override      public boolean onTouchEvent(MotionEvent event) {    float eventX = -1;          float eventY = -1;        int pointId = 0;    int pointCnt = event.getPointerCount( );    for( int index = 0; index < pointCnt; index++ ){    if( inArea( event.getX( index ) - getLeft( ), event.getY( index ) - getTop( ) ) ){    pointId = index;    eventX = event.getX( index ) - getLeft( );    eventY = event.getY( index ) - getTop( );    break;    }    }            if( ( eventX == -1 || eventY == -1 ) || ( eventX == 0 || eventY == 0 ) ){    return false;    }            switch (event.getAction()) {        case MotionEvent.ACTION_DOWN: {              mPath.moveTo(eventX, eventY);            mLastTouchX = eventX;              mLastTouchY = eventY;            invalidate( );        }              return true;          case MotionEvent.ACTION_MOVE:{        drawView( event, eventX, eventY, pointId );        }        break;        case MotionEvent.ACTION_UP:{          drawView( event, eventX, eventY, pointId );        }              break;          default:              return false;          }            return true;      }        private void drawView( MotionEvent event, float eventX, float eventY, int pointId ){    resetDirtyRect(eventX, eventY);          int historySize = event.getHistorySize();          for (int i = 0; i < historySize; i++) {              float historicalX = event.getHistoricalX(pointId,i) - getLeft( );              float historicalY = event.getHistoricalY(pointId,i) - getTop( );              getDirtyRect(historicalX, historicalY);              mPath.lineTo(historicalX, historicalY);          }          mPath.lineTo(eventX, eventY);          invalidate((int) (mDirtyRect.left - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.top - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.right + HALF_STROKE_WIDTH),                  (int) (mDirtyRect.bottom + HALF_STROKE_WIDTH));                mLastTouchX = eventX;          mLastTouchY = eventY;    }      private void getDirtyRect(float historicalX, float historicalY) {          if (historicalX < mDirtyRect.left) {              mDirtyRect.left = historicalX;          } else if (historicalX > mDirtyRect.right) {              mDirtyRect.right = historicalX;          }          if (historicalY < mDirtyRect.top) {              mDirtyRect.top = historicalY;          } else if (historicalY > mDirtyRect.bottom) {              mDirtyRect.bottom = historicalY;          }      }        private void resetDirtyRect(float eventX, float eventY) {          mDirtyRect.left = Math.min(mLastTouchX, eventX);          mDirtyRect.right = Math.max(mLastTouchX, eventX);          mDirtyRect.top = Math.min(mLastTouchY, eventY);          mDirtyRect.bottom = Math.max(mLastTouchY, eventY);      }        private static final float STROKE_WIDTH = 5f;      private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;      private float mLastTouchX = 0;      private float mLastTouchY = 0;        private int mViewWidth = 0;    private int mViewHeight = 0;      private final RectF mDirtyRect = new RectF();      private Paint mPaint = new Paint();      private Path mPath = new Path();  }
Activity:

@Overridepublic boolean onTouchEvent(MotionEvent event) {int pointCnt = event.getPointerCount( );    for( int index = 0; index < pointCnt; index++ ){    if( mDrawView.inArea( event.getX( index ) - mDrawView.getLeft( ), event.getY( index ) - mDrawView.getTop( ) ) ){    mDrawView.onTouchEvent( event );    System.out.println( "action === pointIndex " + index );    return false;    }    }     //mDrawView.setInView( false );    return super.onTouchEvent(event);}
DrawView的inArea方法为核心方法,经过上面的处理后,能够实现当手掌放在手写区域外时在手写区域手写的功能,效果如下图所示,但你会发现当在手写区域手写的手指抬起再放下继续手写时,会直接画一条直线,通过接下来对android事件机制中ACTION_DOWN和ACTION_UP消息的分析,该问题将会得到解决。


2、android消息事件中ACTION_DOWN和ACTION_UP的触发时机即改善方案:

通过实际测试发现,android消息事件中ACTION_DOWN和ACTION_UP的触发时机分别为:

ACTION_DOWN:只要有手指接触屏幕即会触发;

ACTION_UP:当屏幕上没有任何触控操作时触发;

对于多点触控,当某个手指抬起或者松开时会分别触发:ACTION_POINTER_DOWN和ACTION_POINTER_UP,所以对于上面遇到的问题,是由于在多点触控的情况下,只点下或者松开某一根手指时,这两个消息不会触发导致,将ACTION_POINTER_DOWN和ACTION_POINTER_UP这两类消息在DrawView的onTouchEvent方法中一并处理即可解决,改善后的view代码是这样子的:

public class DrawView extends View {public DrawView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initPaintView( );}public DrawView(Context context, AttributeSet attrs) {super(context, attrs);initPaintView( );}public DrawView(Context context) {super(context);initPaintView( );}public void clear() {          if (null != mPath) {              mPath.reset();              invalidate();          }      }        private void initPaintView() {          mPaint.setAntiAlias(true);          mPaint.setColor(Color.WHITE);          mPaint.setStyle(Paint.Style.STROKE);          mPaint.setStrokeJoin(Paint.Join.ROUND);          mPaint.setStrokeWidth(5f);      }        @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    mViewWidth = MeasureSpec.getSize(widthMeasureSpec);   //获取ViewGroup宽度        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);  //获取ViewGroup高度    }      @Override      protected void onDraw(Canvas canvas) {          canvas.drawPath(mPath, mPaint);      }          public boolean inArea( float x, float y ){    return ( x >= 0 && x <= mViewWidth && y >= 0 && y <= mViewHeight)?true:false;    }  @Override      public boolean onTouchEvent(MotionEvent event) {    float eventX = -1;          float eventY = -1;        int pointId = 0;    int pointCnt = event.getPointerCount( );    for( int index = 0; index < pointCnt; index++ ){    if( inArea( event.getX( index ) - getLeft( ), event.getY( index ) - getTop( ) ) ){    pointId = index;    eventX = event.getX( index ) - getLeft( );    eventY = event.getY( index ) - getTop( );    break;    }    }            if( ( eventX == -1 || eventY == -1 ) || ( eventX == 0 || eventY == 0 ) ){    return false;    }            switch (event.getAction()) {        case MotionEvent.ACTION_POINTER_1_DOWN:        case MotionEvent.ACTION_POINTER_2_DOWN:        case MotionEvent.ACTION_POINTER_3_DOWN:        case MotionEvent.ACTION_DOWN: {              mPath.moveTo(eventX, eventY);            mLastTouchX = eventX;              mLastTouchY = eventY;            invalidate( );        }              return true;          case MotionEvent.ACTION_MOVE:{        drawView( event, eventX, eventY, pointId );        }        break;        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_POINTER_1_UP:        case MotionEvent.ACTION_POINTER_2_UP:        case MotionEvent.ACTION_POINTER_3_UP:{          drawView( event, eventX, eventY, pointId );        }              break;          default:              return false;          }            return true;      }        private void drawView( MotionEvent event, float eventX, float eventY, int pointId ){    resetDirtyRect(eventX, eventY);          int historySize = event.getHistorySize();          for (int i = 0; i < historySize; i++) {              float historicalX = event.getHistoricalX(pointId,i) - getLeft( );              float historicalY = event.getHistoricalY(pointId,i) - getTop( );              getDirtyRect(historicalX, historicalY);              mPath.lineTo(historicalX, historicalY);          }          mPath.lineTo(eventX, eventY);          invalidate((int) (mDirtyRect.left - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.top - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.right + HALF_STROKE_WIDTH),                  (int) (mDirtyRect.bottom + HALF_STROKE_WIDTH));                mLastTouchX = eventX;          mLastTouchY = eventY;    }      private void getDirtyRect(float historicalX, float historicalY) {          if (historicalX < mDirtyRect.left) {              mDirtyRect.left = historicalX;          } else if (historicalX > mDirtyRect.right) {              mDirtyRect.right = historicalX;          }          if (historicalY < mDirtyRect.top) {              mDirtyRect.top = historicalY;          } else if (historicalY > mDirtyRect.bottom) {              mDirtyRect.bottom = historicalY;          }      }        private void resetDirtyRect(float eventX, float eventY) {          mDirtyRect.left = Math.min(mLastTouchX, eventX);          mDirtyRect.right = Math.max(mLastTouchX, eventX);          mDirtyRect.top = Math.min(mLastTouchY, eventY);          mDirtyRect.bottom = Math.max(mLastTouchY, eventY);      }        private static final float STROKE_WIDTH = 5f;      private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;      private float mLastTouchX = 0;      private float mLastTouchY = 0;        private int mViewWidth = 0;    private int mViewHeight = 0;      private final RectF mDirtyRect = new RectF();      private Paint mPaint = new Paint();      private Path mPath = new Path();  }
效果也算比较理想了:



3、边界问题处理:

经过上面两步操作,基本上能够实现手掌抑制功能了,但经过仔细测试会发现,当多点触控屏幕时,某根手指从手写控件外移动到手写控件内时,会在手写区域边界直接绘制成直线的现象,如下图:


这是因为在android中一个完整的消息流程离不开ACTION_DOWN和ACTION_UP,当手指从手写区域外移动到手写区域内时,手写区域根本没有接收到ACTION_DOWN消息,针对这种情况,我们需要在activity中对view做特殊处理,即当检测到有手指在手写区域但没有触发ACTION_DOWN消息时,在ACTION_MOVE消息中处理ACTION_DOWN消息应该处理的事情。改善后的代码是这个样子的:

DrawView:

public class DrawView extends View {public DrawView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initPaintView( );}public DrawView(Context context, AttributeSet attrs) {super(context, attrs);initPaintView( );}public DrawView(Context context) {super(context);initPaintView( );}public void clear() {          if (null != mPath) {              mPath.reset();              invalidate();          }      }        private void initPaintView() {          mPaint.setAntiAlias(true);          mPaint.setColor(Color.WHITE);          mPaint.setStyle(Paint.Style.STROKE);          mPaint.setStrokeJoin(Paint.Join.ROUND);          mPaint.setStrokeWidth(5f);      }        @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    mViewWidth = MeasureSpec.getSize(widthMeasureSpec);   //获取ViewGroup宽度        mViewHeight = MeasureSpec.getSize(heightMeasureSpec);  //获取ViewGroup高度    }      @Override      protected void onDraw(Canvas canvas) {          canvas.drawPath(mPath, mPaint);      }          public boolean inArea( float x, float y ){    return ( x >= 0 && x <= mViewWidth && y >= 0 && y <= mViewHeight)?true:false;    }      @SuppressWarnings("deprecation")@Override      public boolean onTouchEvent(MotionEvent event) {    float eventX = -1;          float eventY = -1;        int pointId = 0;    int pointCnt = event.getPointerCount( );    for( int index = 0; index < pointCnt; index++ ){    if( inArea( event.getX( index ) - getLeft( ), event.getY( index ) - getTop( ) ) ){    pointId = index;    eventX = event.getX( index ) - getLeft( );    eventY = event.getY( index ) - getTop( );    break;    }    }            if( ( eventX == -1 || eventY == -1 ) || ( eventX == 0 || eventY == 0 ) ){    return false;    }            switch (event.getAction()) {        case MotionEvent.ACTION_POINTER_1_DOWN:        case MotionEvent.ACTION_POINTER_2_DOWN:        case MotionEvent.ACTION_POINTER_3_DOWN:        case MotionEvent.ACTION_DOWN: {              mPath.moveTo(eventX, eventY);            mLastTouchX = eventX;              mLastTouchY = eventY;            mInView = true;            invalidate( );        }              return true;          case MotionEvent.ACTION_MOVE:{        if( !mInView ){        mInView = true;        mLastTouchX = eventX;                  mLastTouchY = eventY;                mPath.moveTo(eventX, eventY);        }        drawView( event, eventX, eventY, pointId );        }        break;        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_POINTER_1_UP:        case MotionEvent.ACTION_POINTER_2_UP:        case MotionEvent.ACTION_POINTER_3_UP:{          drawView( event, eventX, eventY, pointId );        }              break;          default:              return false;          }            return true;      }        public void setInView( boolean inView ){    mInView = inView;    }        private void drawView( MotionEvent event, float eventX, float eventY, int pointId ){    resetDirtyRect(eventX, eventY);          int historySize = event.getHistorySize();          for (int i = 0; i < historySize; i++) {              float historicalX = event.getHistoricalX(pointId,i) - getLeft( );              float historicalY = event.getHistoricalY(pointId,i) - getTop( );              getDirtyRect(historicalX, historicalY);              mPath.lineTo(historicalX, historicalY);          }          mPath.lineTo(eventX, eventY);          invalidate((int) (mDirtyRect.left - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.top - HALF_STROKE_WIDTH),                  (int) (mDirtyRect.right + HALF_STROKE_WIDTH),                  (int) (mDirtyRect.bottom + HALF_STROKE_WIDTH));                mLastTouchX = eventX;          mLastTouchY = eventY;    }      private void getDirtyRect(float historicalX, float historicalY) {          if (historicalX < mDirtyRect.left) {              mDirtyRect.left = historicalX;          } else if (historicalX > mDirtyRect.right) {              mDirtyRect.right = historicalX;          }          if (historicalY < mDirtyRect.top) {              mDirtyRect.top = historicalY;          } else if (historicalY > mDirtyRect.bottom) {              mDirtyRect.bottom = historicalY;          }      }        private void resetDirtyRect(float eventX, float eventY) {          mDirtyRect.left = Math.min(mLastTouchX, eventX);          mDirtyRect.right = Math.max(mLastTouchX, eventX);          mDirtyRect.top = Math.min(mLastTouchY, eventY);          mDirtyRect.bottom = Math.max(mLastTouchY, eventY);      }        private static final float STROKE_WIDTH = 5f;      private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;      private float mLastTouchX = 0;      private float mLastTouchY = 0;        private boolean mInView = false;    private int mViewWidth = 0;    private int mViewHeight = 0;      private final RectF mDirtyRect = new RectF();      private Paint mPaint = new Paint();      private Path mPath = new Path();  }

activity也需要增加一句话:

@Overridepublic boolean onTouchEvent(MotionEvent event) {int pointCnt = event.getPointerCount( );    for( int index = 0; index < pointCnt; index++ ){    if( mDrawView.inArea( event.getX( index ) - mDrawView.getLeft( ), event.getY( index ) - mDrawView.getTop( ) ) ){    mDrawView.onTouchEvent( event );    System.out.println( "action === pointIndex " + index );    return false;    }    }     mDrawView.setInView( false );    return super.onTouchEvent(event);}

最后终于实现了我们想要的效果:



四、参考文档:

Android Touch事件传递机制

Android 编程下 Touch 事件的分发和消费机制


五、代码:

最后附上本文的代码:Android手掌抑制demo



更多相关文章

  1. 探讨android更新UI的几种方法
  2. Android(安卓)App压力测试(Monkey和ADB)
  3. eclipse paho包对于ActiveMQ持久化订阅者的设置
  4. Android(安卓)RxJava:这是一份全面 & 详细 的RxJava操作符 使用攻
  5. Android事件处理模型一(基于回调机制的事件处理)
  6. Android图解浅析事件拦截机制
  7. Android(安卓)RxJava实际应用案例讲解:使用RxJava的最佳开发场景
  8. Android里的Xmpp的理解(消息推送)
  9. AndroidUI学习

随机推荐

  1. WebView的简单使用
  2. 第21天 Android Touch事件学习 8 事件分
  3. 关于Android使用proguard进行代码混淆
  4. Android API 中文 (42) ―― ListView
  5. Android Studio插件之Android Strings.xm
  6. Android XML解析(Simple API for XML(SAX
  7. 记录一个小技巧-Android日志工具
  8. android双网卡
  9. Android实现简单的闹钟
  10. Android一个简易的登录界面