zoukankan      html  css  js  c++  java
  • Android手掌抑制功能的实现

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

            初看这个功能非常easy,依照之前处理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:

    @Override
    	public 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也须要添加一句话:

    @Override
    	public 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



  • 相关阅读:
    Spark2.4.5集群安装与本地开发
    Windows玩转Kubernetes系列4-搭建K8S Dashboard
    Windows玩转Kubernetes系列3-Centos安装K8S
    Windows玩转Kubernetes系列2-Centos安装Docker
    Windows玩转Kubernetes系列1-VirtualBox安装Centos
    Lock wait timeout exceeded?代码该优化了
    RocketMQ初入门踩坑记
    Java8虚拟机(JVM)内存溢出实战
    CentOS 7 下 JDK1.8+Maven+Nginx+MySql+Git+Redis环境安装
    消息中间件—SpringBoot下RabbitMQ实战
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6772395.html
Copyright © 2011-2022 走看看