FileDocCategorySizeDatePackage
GestureOverlayView.javaAPI DocAndroid 5.1 API27107Thu Mar 12 22:22:10 GMT 2015android.gesture

GestureOverlayView

public class GestureOverlayView extends android.widget.FrameLayout
A transparent overlay for gesture input that can be placed on top of other widgets or contain other widgets.
attr
ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
attr
ref android.R.styleable#GestureOverlayView_fadeDuration
attr
ref android.R.styleable#GestureOverlayView_fadeOffset
attr
ref android.R.styleable#GestureOverlayView_fadeEnabled
attr
ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
attr
ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold
attr
ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold
attr
ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold
attr
ref android.R.styleable#GestureOverlayView_gestureStrokeType
attr
ref android.R.styleable#GestureOverlayView_gestureColor
attr
ref android.R.styleable#GestureOverlayView_orientation
attr
ref android.R.styleable#GestureOverlayView_uncertainGestureColor

Fields Summary
public static final int
GESTURE_STROKE_TYPE_SINGLE
public static final int
GESTURE_STROKE_TYPE_MULTIPLE
public static final int
ORIENTATION_HORIZONTAL
public static final int
ORIENTATION_VERTICAL
private static final int
FADE_ANIMATION_RATE
private static final boolean
GESTURE_RENDERING_ANTIALIAS
private static final boolean
DITHER_FLAG
private final android.graphics.Paint
mGesturePaint
private long
mFadeDuration
private long
mFadeOffset
private long
mFadingStart
private boolean
mFadingHasStarted
private boolean
mFadeEnabled
private int
mCurrentColor
private int
mCertainGestureColor
private int
mUncertainGestureColor
private float
mGestureStrokeWidth
private int
mInvalidateExtraBorder
private int
mGestureStrokeType
private float
mGestureStrokeLengthThreshold
private float
mGestureStrokeSquarenessTreshold
private float
mGestureStrokeAngleThreshold
private int
mOrientation
private final android.graphics.Rect
mInvalidRect
private final android.graphics.Path
mPath
private boolean
mGestureVisible
private float
mX
private float
mY
private float
mCurveEndX
private float
mCurveEndY
private float
mTotalLength
private boolean
mIsGesturing
private boolean
mPreviousWasGesturing
private boolean
mInterceptEvents
private boolean
mIsListeningForGestures
private boolean
mResetGesture
private Gesture
mCurrentGesture
private final ArrayList
mStrokeBuffer
private final ArrayList
mOnGestureListeners
private final ArrayList
mOnGesturePerformedListeners
private final ArrayList
mOnGesturingListeners
private boolean
mHandleGestureActions
private boolean
mIsFadingOut
private float
mFadingAlpha
private final android.view.animation.AccelerateDecelerateInterpolator
mInterpolator
private final FadeOutRunnable
mFadingOut
Constructors Summary
public GestureOverlayView(android.content.Context context)


       
        super(context);
        init();
    
public GestureOverlayView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle);
    
public GestureOverlayView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public GestureOverlayView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.GestureOverlayView, defStyleAttr, defStyleRes);

        mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
                mGestureStrokeWidth);
        mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1);
        mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
                mCertainGestureColor);
        mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
                mUncertainGestureColor);
        mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
        mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset);
        mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType,
                mGestureStrokeType);
        mGestureStrokeLengthThreshold = a.getFloat(
                R.styleable.GestureOverlayView_gestureStrokeLengthThreshold,
                mGestureStrokeLengthThreshold);
        mGestureStrokeAngleThreshold = a.getFloat(
                R.styleable.GestureOverlayView_gestureStrokeAngleThreshold,
                mGestureStrokeAngleThreshold);
        mGestureStrokeSquarenessTreshold = a.getFloat(
                R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold,
                mGestureStrokeSquarenessTreshold);
        mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled,
                mInterceptEvents);
        mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled,
                mFadeEnabled);
        mOrientation = a.getInt(R.styleable.GestureOverlayView_orientation, mOrientation);

        a.recycle();

        init();
    
Methods Summary
public voidaddOnGestureListener(android.gesture.GestureOverlayView$OnGestureListener listener)

        mOnGestureListeners.add(listener);
    
public voidaddOnGesturePerformedListener(android.gesture.GestureOverlayView$OnGesturePerformedListener listener)

        mOnGesturePerformedListeners.add(listener);
        if (mOnGesturePerformedListeners.size() > 0) {
            mHandleGestureActions = true;
        }
    
public voidaddOnGesturingListener(android.gesture.GestureOverlayView$OnGesturingListener listener)

        mOnGesturingListeners.add(listener);
    
public voidcancelClearAnimation()

        setPaintAlpha(255);
        mIsFadingOut = false;
        mFadingHasStarted = false;
        removeCallbacks(mFadingOut);
        mPath.rewind();
        mCurrentGesture = null;
    
public voidcancelGesture()

        mIsListeningForGestures = false;

        // add the stroke to the current gesture
        mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));

        // pass the event to handlers
        final long now = SystemClock.uptimeMillis();
        final MotionEvent event = MotionEvent.obtain(now, now,
                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);

        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
        int count = listeners.size();
        for (int i = 0; i < count; i++) {
            listeners.get(i).onGestureCancelled(this, event);
        }

        event.recycle();

        clear(false);
        mIsGesturing = false;
        mPreviousWasGesturing = false;
        mStrokeBuffer.clear();

        final ArrayList<OnGesturingListener> otherListeners = mOnGesturingListeners;
        count = otherListeners.size();
        for (int i = 0; i < count; i++) {
            otherListeners.get(i).onGesturingEnded(this);
        }
    
private voidcancelGesture(android.view.MotionEvent event)

        // pass the event to handlers
        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
        final int count = listeners.size();
        for (int i = 0; i < count; i++) {
            listeners.get(i).onGestureCancelled(this, event);
        }

        clear(false);
    
public voidclear(boolean animated)

        clear(animated, false, true);
    
private voidclear(boolean animated, boolean fireActionPerformed, boolean immediate)

        setPaintAlpha(255);
        removeCallbacks(mFadingOut);
        mResetGesture = false;
        mFadingOut.fireActionPerformed = fireActionPerformed;
        mFadingOut.resetMultipleStrokes = false;

        if (animated && mCurrentGesture != null) {
            mFadingAlpha = 1.0f;
            mIsFadingOut = true;
            mFadingHasStarted = false;
            mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;

            postDelayed(mFadingOut, mFadeOffset);
        } else {
            mFadingAlpha = 1.0f;
            mIsFadingOut = false;
            mFadingHasStarted = false;

            if (immediate) {
                mCurrentGesture = null;
                mPath.rewind();
                invalidate();
            } else if (fireActionPerformed) {
                postDelayed(mFadingOut, mFadeOffset);
            } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) {
                mFadingOut.resetMultipleStrokes = true;
                postDelayed(mFadingOut, mFadeOffset);
            } else {
                mCurrentGesture = null;
                mPath.rewind();
                invalidate();
            }
        }
    
public booleandispatchTouchEvent(android.view.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;
        }

        return super.dispatchTouchEvent(event);
    
public voiddraw(android.graphics.Canvas canvas)

        super.draw(canvas);

        if (mCurrentGesture != null && mGestureVisible) {
            canvas.drawPath(mPath, mGesturePaint);
        }
    
private voidfireOnGesturePerformed()

        final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners;
        final int count = actionListeners.size();
        for (int i = 0; i < count; i++) {
            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
        }
    
public java.util.ArrayListgetCurrentStroke()

        return mStrokeBuffer;
    
public longgetFadeOffset()

        return mFadeOffset;
    
public GesturegetGesture()

        return mCurrentGesture;
    
public intgetGestureColor()

        return mCertainGestureColor;
    
public android.graphics.PaintgetGesturePaint()

hide

        return mGesturePaint;
    
public android.graphics.PathgetGesturePath()

        return mPath;
    
public android.graphics.PathgetGesturePath(android.graphics.Path path)

        path.set(mPath);
        return path;
    
public floatgetGestureStrokeAngleThreshold()

        return mGestureStrokeAngleThreshold;
    
public floatgetGestureStrokeLengthThreshold()

        return mGestureStrokeLengthThreshold;
    
public floatgetGestureStrokeSquarenessTreshold()

        return mGestureStrokeSquarenessTreshold;
    
public intgetGestureStrokeType()

        return mGestureStrokeType;
    
public floatgetGestureStrokeWidth()

        return mGestureStrokeWidth;
    
public intgetOrientation()

        return mOrientation;
    
public intgetUncertainGestureColor()

        return mUncertainGestureColor;
    
private voidinit()

        setWillNotDraw(false);

        final Paint gesturePaint = mGesturePaint;
        gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
        gesturePaint.setColor(mCertainGestureColor);
        gesturePaint.setStyle(Paint.Style.STROKE);
        gesturePaint.setStrokeJoin(Paint.Join.ROUND);
        gesturePaint.setStrokeCap(Paint.Cap.ROUND);
        gesturePaint.setStrokeWidth(mGestureStrokeWidth);
        gesturePaint.setDither(DITHER_FLAG);

        mCurrentColor = mCertainGestureColor;
        setPaintAlpha(255);
    
public booleanisEventsInterceptionEnabled()

        return mInterceptEvents;
    
public booleanisFadeEnabled()

        return mFadeEnabled;
    
public booleanisGestureVisible()

        return mGestureVisible;
    
public booleanisGesturing()

        return mIsGesturing;
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();
        cancelClearAnimation();
    
private booleanprocessEvent(android.view.MotionEvent event)

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchDown(event);
                invalidate();
                return true;
            case MotionEvent.ACTION_MOVE:
                if (mIsListeningForGestures) {
                    Rect rect = touchMove(event);
                    if (rect != null) {
                        invalidate(rect);
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mIsListeningForGestures) {
                    touchUp(event, false);
                    invalidate();
                    return true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (mIsListeningForGestures) {
                    touchUp(event, true);
                    invalidate();
                    return true;
                }
        }

        return false;
    
public voidremoveAllOnGestureListeners()

        mOnGestureListeners.clear();
    
public voidremoveAllOnGesturePerformedListeners()

        mOnGesturePerformedListeners.clear();
        mHandleGestureActions = false;
    
public voidremoveAllOnGesturingListeners()

        mOnGesturingListeners.clear();
    
public voidremoveOnGestureListener(android.gesture.GestureOverlayView$OnGestureListener listener)

        mOnGestureListeners.remove(listener);
    
public voidremoveOnGesturePerformedListener(android.gesture.GestureOverlayView$OnGesturePerformedListener listener)

        mOnGesturePerformedListeners.remove(listener);
        if (mOnGesturePerformedListeners.size() <= 0) {
            mHandleGestureActions = false;
        }
    
public voidremoveOnGesturingListener(android.gesture.GestureOverlayView$OnGesturingListener listener)

        mOnGesturingListeners.remove(listener);
    
private voidsetCurrentColor(int color)

        mCurrentColor = color;
        if (mFadingHasStarted) {
            setPaintAlpha((int) (255 * mFadingAlpha));
        } else {
            setPaintAlpha(255);
        }
        invalidate();
    
public voidsetEventsInterceptionEnabled(boolean enabled)

        mInterceptEvents = enabled;
    
public voidsetFadeEnabled(boolean fadeEnabled)

        mFadeEnabled = fadeEnabled;
    
public voidsetFadeOffset(long fadeOffset)

        mFadeOffset = fadeOffset;
    
public voidsetGesture(Gesture gesture)

        if (mCurrentGesture != null) {
            clear(false);
        }

        setCurrentColor(mCertainGestureColor);
        mCurrentGesture = gesture;

        final Path path = mCurrentGesture.toPath();
        final RectF bounds = new RectF();
        path.computeBounds(bounds, true);

        // TODO: The path should also be scaled to fit inside this view
        mPath.rewind();
        mPath.addPath(path, -bounds.left + (getWidth() - bounds.width()) / 2.0f,
                -bounds.top + (getHeight() - bounds.height()) / 2.0f);

        mResetGesture = true;

        invalidate();
    
public voidsetGestureColor(int color)

        mCertainGestureColor = color;
    
public voidsetGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold)

        mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold;
    
public voidsetGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold)

        mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold;
    
public voidsetGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold)

        mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold;
    
public voidsetGestureStrokeType(int gestureStrokeType)

        mGestureStrokeType = gestureStrokeType;
    
public voidsetGestureStrokeWidth(float gestureStrokeWidth)

        mGestureStrokeWidth = gestureStrokeWidth;
        mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1);
        mGesturePaint.setStrokeWidth(gestureStrokeWidth);
    
public voidsetGestureVisible(boolean visible)

        mGestureVisible = visible;
    
public voidsetOrientation(int orientation)

        mOrientation = orientation;
    
private voidsetPaintAlpha(int alpha)

        alpha += alpha >> 7;
        final int baseAlpha = mCurrentColor >>> 24;
        final int useAlpha = baseAlpha * alpha >> 8;
        mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24));
    
public voidsetUncertainGestureColor(int color)

        mUncertainGestureColor = color;
    
private voidtouchDown(android.view.MotionEvent event)

        mIsListeningForGestures = true;

        float x = event.getX();
        float y = event.getY();

        mX = x;
        mY = y;

        mTotalLength = 0;
        mIsGesturing = false;

        if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) {
            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
            mResetGesture = false;
            mCurrentGesture = null;
            mPath.rewind();
        } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) {
            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
        }

        // if there is fading out going on, stop it.
        if (mFadingHasStarted) {
            cancelClearAnimation();
        } else if (mIsFadingOut) {
            setPaintAlpha(255);
            mIsFadingOut = false;
            mFadingHasStarted = false;
            removeCallbacks(mFadingOut);
        }

        if (mCurrentGesture == null) {
            mCurrentGesture = new Gesture();
        }

        mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
        mPath.moveTo(x, y);

        final int border = mInvalidateExtraBorder;
        mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);

        mCurveEndX = x;
        mCurveEndY = y;

        // pass the event to handlers
        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
        final int count = listeners.size();
        for (int i = 0; i < count; i++) {
            listeners.get(i).onGestureStarted(this, event);
        }
    
private android.graphics.RecttouchMove(android.view.MotionEvent event)

        Rect areaToRefresh = null;

        final float x = event.getX();
        final float y = event.getY();

        final float previousX = mX;
        final float previousY = mY;

        final float dx = Math.abs(x - previousX);
        final float dy = Math.abs(y - previousY);

        if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) {
            areaToRefresh = mInvalidRect;

            // start with the curve end
            final int border = mInvalidateExtraBorder;
            areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
                    (int) mCurveEndX + border, (int) mCurveEndY + border);

            float cX = mCurveEndX = (x + previousX) / 2;
            float cY = mCurveEndY = (y + previousY) / 2;

            mPath.quadTo(previousX, previousY, cX, cY);

            // union with the control point of the new curve
            areaToRefresh.union((int) previousX - border, (int) previousY - border,
                    (int) previousX + border, (int) previousY + border);

            // union with the end point of the new curve
            areaToRefresh.union((int) cX - border, (int) cY - border,
                    (int) cX + border, (int) cY + border);

            mX = x;
            mY = y;

            mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));

            if (mHandleGestureActions && !mIsGesturing) {
                mTotalLength += (float) Math.sqrt(dx * dx + dy * dy);

                if (mTotalLength > mGestureStrokeLengthThreshold) {
                    final OrientedBoundingBox box =
                            GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);

                    float angle = Math.abs(box.orientation);
                    if (angle > 90) {
                        angle = 180 - angle;
                    }

                    if (box.squareness > mGestureStrokeSquarenessTreshold ||
                            (mOrientation == ORIENTATION_VERTICAL ?
                                    angle < mGestureStrokeAngleThreshold :
                                    angle > mGestureStrokeAngleThreshold)) {

                        mIsGesturing = true;
                        setCurrentColor(mCertainGestureColor);

                        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
                        int count = listeners.size();
                        for (int i = 0; i < count; i++) {
                            listeners.get(i).onGesturingStarted(this);
                        }
                    }
                }
            }

            // pass the event to handlers
            final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
            final int count = listeners.size();
            for (int i = 0; i < count; i++) {
                listeners.get(i).onGesture(this, event);
            }
        }

        return areaToRefresh;
    
private voidtouchUp(android.view.MotionEvent event, boolean cancel)

        mIsListeningForGestures = false;

        // A gesture wasn't started or was cancelled
        if (mCurrentGesture != null) {
            // add the stroke to the current gesture
            mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));

            if (!cancel) {
                // pass the event to handlers
                final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
                int count = listeners.size();
                for (int i = 0; i < count; i++) {
                    listeners.get(i).onGestureEnded(this, event);
                }

                clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing,
                        false);
            } else {
                cancelGesture(event);

            }
        } else {
            cancelGesture(event);
        }

        mStrokeBuffer.clear();
        mPreviousWasGesturing = mIsGesturing;
        mIsGesturing = false;

        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
        int count = listeners.size();
        for (int i = 0; i < count; i++) {
            listeners.get(i).onGesturingEnded(this);
        }