017-Android自定义View(12):手势绘制及GestureOverlayView事件浅析

1,185 阅读2分钟

1.基础

  1. 掌握View体系事件分发与处理,参考Android自定义View(9):事件分发&处理
  2. 掌握基础的手势监听及手势动作有关的回调时机,参考Android自定义View(10):手势监听&处理
  3. 了解GestureLibrary和GestureOverlayView的api使用,参考官方API文档

2.绘制

绘制简述:

(具体用法及要点见代码注释)

1.首先在布局文件中添加GestureOverlayView控件;
2.然后在代码中获取控件对象;
3.初始化绘制画笔;
4.设置监听;
5.若关联activity,则在onDestroy()解除。

2.1 xml代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <android.gesture.GestureOverlayView
        android:id="@+id/gesture_overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:orientation="vertical"
            android:gravity="center"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </android.gesture.GestureOverlayView>
</RelativeLayout>

2.2 activity代码

package com.cupster.android_x_frame;

import android.gesture.Gesture;
import android.gesture.GestureOverlayView;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class DrawGestureActivity extends AppCompatActivity implements GestureOverlayView.OnGesturePerformedListener, GestureOverlayView.OnGesturingListener {

    GestureOverlayView mGestureOverlayView;
    TextView mTvHint;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_draw_gesture);
        mGestureOverlayView = findViewById(R.id.gesture_overlay);
        mTvHint = findViewById(R.id.text);

        mGestureOverlayView.setFadeOffset(800);//默认420ms,设置为800ms
        mGestureOverlayView.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE);//默认单指连续绘制,设置为多指绘制
        mGestureOverlayView.setGestureStrokeWidth(10);//绘制线宽
        mGestureOverlayView.setGestureColor(Color.parseColor("#3ca1f9"));//绘制颜色
        mGestureOverlayView.setUncertainGestureColor(Color.parseColor("#a7dbf7"));//渐成色
        //设置监听
        mGestureOverlayView.addOnGesturePerformedListener(this);
        mGestureOverlayView.addOnGesturingListener(this);
    }

    @Override
    protected void onDestroy() {
        mGestureOverlayView.removeOnGesturePerformedListener(this);
        mGestureOverlayView.removeOnGesturingListener(this);
        super.onDestroy();
    }

    //==============================================
    //=========GesturePerformedListener=============
    //==============================================
    @Override
    public void onGesturePerformed(GestureOverlayView gestureOverlayView, Gesture gesture) {
        mTvHint.append("\n");
        mTvHint.append("--> 所有绘制路径,识别完成");
    }

    //===============================================
    //=========GesturingListener=====================
    //===============================================
    @Override
    public void onGesturingStarted(GestureOverlayView gestureOverlayView) {
        mTvHint.append("\n");
        mTvHint.append("--> 单笔手势绘制开始");
    }

    @Override
    public void onGesturingEnded(GestureOverlayView gestureOverlayView) {
        mTvHint.append("\n");
        mTvHint.append("--> 单笔手势绘制结束");
    }
}

2.3 源码简析

2.3.1 结论

  1. GestureOverlayView继承FrameLayout,FrameLayout继承ViewGroup(继承View);
  2. GestureOverlayView重写View的dispatchTouchEvent方法
  3. GestureOverlayView.setEnabled()默认设置为true,即夺取事件分发;
  4. GestureOverlayView.setEnabled(false)时,可视为FrameLayout,即不响应手势绘制;

验证代码:

//测试禁用,mGestureOverlayView默认启用绘制==读取手势事件
        mGestureOverlayView.setEnabled(false);
        mTvHint.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                mTvHint.append("\n");
                mTvHint.append("绘制未启用,事件正常分发");
                return true;
            }
        });

2.3.2 源码

解析 见代码注释

public class GestureOverlayView extends FrameLayout {
...
 @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (isEnabled()) {
            final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null &&
                    mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) &&
                    mInterceptEvents;
 
            processEvent(event);//即夺取事件分发
 
            if (cancelDispatch) {
                event.setAction(MotionEvent.ACTION_CANCEL);
            }
 
            super.dispatchTouchEvent(event);
 
            return true;//回复上级已处理事件,不下发
        }
        //isEnabled== false ,可视为FrameLayout,正常分发事件
        return super.dispatchTouchEvent(event);
    }
...
}
...
 private boolean processEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchDown(event);//这里回调onGestureStarted
                invalidate();
                return true;
            case MotionEvent.ACTION_MOVE:
                if (mIsListeningForGestures) {
                    Rect rect = touchMove(event);//这里回调onGesture
                    if (rect != null) {
                        invalidate(rect);
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsListeningForGestures) {
                    touchUp(event, false);//回调onGestureEnded
                    invalidate();
                    return true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (mIsListeningForGestures) {
                    touchUp(event, true);//标志位true,代表取消,清除绘制的手势
                    invalidate();
                    return true;
                }
        }
 
        return false;
    }
...
...
/**
* 每次执行完touchDown、touchMove方法后都会调用invalidate()、invalidate(rect)刷新手势
*/
@Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        
        if (mCurrentGesture != null && mGestureVisible) {
            canvas.drawPath(mPath, mGesturePaint);
        }
    }
...