最近需要實現一個功能,在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:
@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手掌抑制demo