166 lines
5.2 KiB
Java
166 lines
5.2 KiB
Java
/*
|
|
* Copyright (C) 2008-2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.gesture;
|
|
|
|
import android.graphics.Color;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
/**
|
|
* TouchThroughGesturing implements the interaction behavior that allows a user
|
|
* to gesture over a regular UI widget such as ListView and at the same time,
|
|
* still allows a user to perform basic interactions (clicking, scrolling and panning)
|
|
* with the underlying widget.
|
|
*/
|
|
|
|
public class TouchThroughGesturing implements GestureListener {
|
|
public static final int SINGLE_STROKE = 0;
|
|
public static final int MULTIPLE_STROKE = 1;
|
|
|
|
private static final float STROKE_LENGTH_THRESHOLD = 30;
|
|
private static final float SQUARENESS_THRESHOLD = 0.275f;
|
|
private static final float ANGLE_THRESHOLD = 40;
|
|
|
|
private static final boolean STEAL_EVENTS = false;
|
|
|
|
public static final int DEFAULT_UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0);
|
|
|
|
private boolean mIsGesturing = false;
|
|
|
|
private float mTotalLength;
|
|
|
|
private float mX;
|
|
private float mY;
|
|
|
|
// TODO: Use WeakReference?
|
|
private View mModel;
|
|
|
|
private int mGestureType = SINGLE_STROKE;
|
|
private int mUncertainGestureColor = DEFAULT_UNCERTAIN_GESTURE_COLOR;
|
|
|
|
// TODO: Use WeakReferences
|
|
private final ArrayList<GestureActionListener> mActionListeners =
|
|
new ArrayList<GestureActionListener>();
|
|
|
|
public TouchThroughGesturing(View model) {
|
|
mModel = model;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param type SINGLE_STROKE or MULTIPLE_STROKE
|
|
*/
|
|
public void setGestureType(int type) {
|
|
mGestureType = type;
|
|
}
|
|
|
|
public void setUncertainGestureColor(int color) {
|
|
mUncertainGestureColor = color;
|
|
}
|
|
|
|
public void onStartGesture(GestureOverlay overlay, MotionEvent event) {
|
|
if (mGestureType == MULTIPLE_STROKE) {
|
|
overlay.cancelFadingOut();
|
|
}
|
|
|
|
mX = event.getX();
|
|
mY = event.getY();
|
|
mTotalLength = 0;
|
|
mIsGesturing = false;
|
|
|
|
if (mGestureType == SINGLE_STROKE || overlay.getCurrentGesture() == null
|
|
|| overlay.getCurrentGesture().getStrokesCount() == 0) {
|
|
overlay.setGestureColor(mUncertainGestureColor);
|
|
}
|
|
|
|
mModel.dispatchTouchEvent(event);
|
|
}
|
|
|
|
public void onGesture(GestureOverlay overlay, MotionEvent event) {
|
|
//noinspection PointlessBooleanExpression
|
|
if (!STEAL_EVENTS) {
|
|
mModel.dispatchTouchEvent(event);
|
|
}
|
|
|
|
if (mIsGesturing) {
|
|
return;
|
|
}
|
|
|
|
final float x = event.getX();
|
|
final float y = event.getY();
|
|
final float dx = x - mX;
|
|
final float dy = y - mY;
|
|
|
|
mTotalLength += (float)Math.sqrt(dx * dx + dy * dy);
|
|
mX = x;
|
|
mY = y;
|
|
|
|
if (mTotalLength > STROKE_LENGTH_THRESHOLD) {
|
|
final OrientedBoundingBox box =
|
|
GestureUtilities.computeOrientedBoundingBox(overlay.getCurrentStroke());
|
|
float angle = Math.abs(box.orientation);
|
|
if (angle > 90) {
|
|
angle = 180 - angle;
|
|
}
|
|
if (box.squareness > SQUARENESS_THRESHOLD || angle < ANGLE_THRESHOLD) {
|
|
mIsGesturing = true;
|
|
overlay.setGestureColor(GestureOverlay.DEFAULT_GESTURE_COLOR);
|
|
if (STEAL_EVENTS) {
|
|
event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(),
|
|
MotionEvent.ACTION_UP, x, y, event.getPressure(), event.getSize(),
|
|
event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
|
|
event.getDeviceId(), event.getEdgeFlags());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (STEAL_EVENTS) {
|
|
mModel.dispatchTouchEvent(event);
|
|
}
|
|
}
|
|
|
|
public void onFinishGesture(GestureOverlay overlay, MotionEvent event) {
|
|
if (mIsGesturing) {
|
|
overlay.clear(true);
|
|
|
|
final ArrayList<GestureActionListener> listeners = mActionListeners;
|
|
final int count = listeners.size();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
listeners.get(i).onGesturePerformed(overlay, overlay.getCurrentGesture());
|
|
}
|
|
} else {
|
|
mModel.dispatchTouchEvent(event);
|
|
overlay.clear(false);
|
|
}
|
|
}
|
|
|
|
public void addGestureActionListener(GestureActionListener listener) {
|
|
mActionListeners.add(listener);
|
|
}
|
|
|
|
public void removeGestureActionListener(GestureActionListener listener) {
|
|
mActionListeners.remove(listener);
|
|
}
|
|
|
|
public boolean isGesturing() {
|
|
return mIsGesturing;
|
|
}
|
|
}
|