Merge "TransformGestureDetector is now ScaleGestureDetector - scope reduced. N1 screen can't reliably handle translation and scaling at the same time." into eclair
This commit is contained in:
360
core/java/android/view/ScaleGestureDetector.java
Normal file
360
core/java/android/view/ScaleGestureDetector.java
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 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 android.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects transformation gestures involving more than one pointer ("multitouch")
|
||||||
|
* using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
|
||||||
|
* callback will notify users when a particular gesture event has occurred.
|
||||||
|
* This class should only be used with {@link MotionEvent}s reported via touch.
|
||||||
|
*
|
||||||
|
* To use this class:
|
||||||
|
* <ul>
|
||||||
|
* <li>Create an instance of the {@code ScaleGestureDetector} for your
|
||||||
|
* {@link View}
|
||||||
|
* <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
|
||||||
|
* {@link #onTouchEvent(MotionEvent)}. The methods defined in your
|
||||||
|
* callback will be executed when the events occur.
|
||||||
|
* </ul>
|
||||||
|
* @hide Pending API approval
|
||||||
|
*/
|
||||||
|
public class ScaleGestureDetector {
|
||||||
|
/**
|
||||||
|
* The listener for receiving notifications when gestures occur.
|
||||||
|
* If you want to listen for all the different gestures then implement
|
||||||
|
* this interface. If you only want to listen for a subset it might
|
||||||
|
* be easier to extend {@link SimpleOnScaleGestureListener}.
|
||||||
|
*
|
||||||
|
* An application will receive events in the following order:
|
||||||
|
* <ul>
|
||||||
|
* <li>One {@link OnScaleGestureListener#onScaleBegin()}
|
||||||
|
* <li>Zero or more {@link OnScaleGestureListener#onScale()}
|
||||||
|
* <li>One {@link OnScaleGestureListener#onTransformEnd()}
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public interface OnScaleGestureListener {
|
||||||
|
/**
|
||||||
|
* Responds to scaling events for a gesture in progress.
|
||||||
|
* Reported by pointer motion.
|
||||||
|
*
|
||||||
|
* @param detector The detector reporting the event - use this to
|
||||||
|
* retrieve extended info about event state.
|
||||||
|
* @return Whether or not the detector should consider this event
|
||||||
|
* as handled. If an event was not handled, the detector
|
||||||
|
* will continue to accumulate movement until an event is
|
||||||
|
* handled. This can be useful if an application, for example,
|
||||||
|
* only wants to update scaling factors if the change is
|
||||||
|
* greater than 0.01.
|
||||||
|
*/
|
||||||
|
public boolean onScale(ScaleGestureDetector detector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to the beginning of a scaling gesture. Reported by
|
||||||
|
* new pointers going down.
|
||||||
|
*
|
||||||
|
* @param detector The detector reporting the event - use this to
|
||||||
|
* retrieve extended info about event state.
|
||||||
|
* @return Whether or not the detector should continue recognizing
|
||||||
|
* this gesture. For example, if a gesture is beginning
|
||||||
|
* with a focal point outside of a region where it makes
|
||||||
|
* sense, onScaleBegin() may return false to ignore the
|
||||||
|
* rest of the gesture.
|
||||||
|
*/
|
||||||
|
public boolean onScaleBegin(ScaleGestureDetector detector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to the end of a scale gesture. Reported by existing
|
||||||
|
* pointers going up. If the end of a gesture would result in a fling,
|
||||||
|
* {@link onTransformFling()} is called instead.
|
||||||
|
*
|
||||||
|
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
|
||||||
|
* and {@link ScaleGestureDetector#getFocusY()} will return the location
|
||||||
|
* of the pointer remaining on the screen.
|
||||||
|
*
|
||||||
|
* @param detector The detector reporting the event - use this to
|
||||||
|
* retrieve extended info about event state.
|
||||||
|
*/
|
||||||
|
public void onScaleEnd(ScaleGestureDetector detector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience class to extend when you only want to listen for a subset
|
||||||
|
* of scaling-related events. This implements all methods in
|
||||||
|
* {@link OnScaleGestureListener} but does nothing.
|
||||||
|
* {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
|
||||||
|
* {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
|
||||||
|
* {@code true}.
|
||||||
|
*/
|
||||||
|
public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
|
||||||
|
|
||||||
|
public boolean onScale(ScaleGestureDetector detector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||||
|
// Intentionally empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final float PRESSURE_THRESHOLD = 0.67f;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private OnScaleGestureListener mListener;
|
||||||
|
private boolean mGestureInProgress;
|
||||||
|
|
||||||
|
private MotionEvent mPrevEvent;
|
||||||
|
private MotionEvent mCurrEvent;
|
||||||
|
|
||||||
|
private float mFocusX;
|
||||||
|
private float mFocusY;
|
||||||
|
private float mPrevFingerDiffX;
|
||||||
|
private float mPrevFingerDiffY;
|
||||||
|
private float mCurrFingerDiffX;
|
||||||
|
private float mCurrFingerDiffY;
|
||||||
|
private float mCurrLen;
|
||||||
|
private float mPrevLen;
|
||||||
|
private float mScaleFactor;
|
||||||
|
private float mCurrPressure;
|
||||||
|
private float mPrevPressure;
|
||||||
|
private long mTimeDelta;
|
||||||
|
|
||||||
|
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
|
||||||
|
mContext = context;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
final int action = event.getAction();
|
||||||
|
boolean handled = true;
|
||||||
|
|
||||||
|
if (!mGestureInProgress) {
|
||||||
|
if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
|
||||||
|
action == MotionEvent.ACTION_POINTER_2_DOWN) &&
|
||||||
|
event.getPointerCount() >= 2) {
|
||||||
|
// We have a new multi-finger gesture
|
||||||
|
|
||||||
|
// Be paranoid in case we missed an event
|
||||||
|
reset();
|
||||||
|
|
||||||
|
mPrevEvent = MotionEvent.obtain(event);
|
||||||
|
mTimeDelta = 0;
|
||||||
|
|
||||||
|
setContext(event);
|
||||||
|
mGestureInProgress = mListener.onScaleBegin(this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Transform gesture in progress - attempt to handle it
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_POINTER_1_UP:
|
||||||
|
case MotionEvent.ACTION_POINTER_2_UP:
|
||||||
|
// Gesture ended
|
||||||
|
setContext(event);
|
||||||
|
|
||||||
|
// Set focus point to the remaining finger
|
||||||
|
int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
|
||||||
|
>> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
|
||||||
|
mFocusX = event.getX(id);
|
||||||
|
mFocusY = event.getY(id);
|
||||||
|
|
||||||
|
mListener.onScaleEnd(this);
|
||||||
|
mGestureInProgress = false;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
mListener.onScaleEnd(this);
|
||||||
|
mGestureInProgress = false;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
setContext(event);
|
||||||
|
|
||||||
|
// Only accept the event if our relative pressure is within
|
||||||
|
// a certain limit - this can help filter shaky data as a
|
||||||
|
// finger is lifted.
|
||||||
|
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
|
||||||
|
final boolean updatePrevious = mListener.onScale(this);
|
||||||
|
|
||||||
|
if (updatePrevious) {
|
||||||
|
mPrevEvent.recycle();
|
||||||
|
mPrevEvent = MotionEvent.obtain(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setContext(MotionEvent curr) {
|
||||||
|
if (mCurrEvent != null) {
|
||||||
|
mCurrEvent.recycle();
|
||||||
|
}
|
||||||
|
mCurrEvent = MotionEvent.obtain(curr);
|
||||||
|
|
||||||
|
mCurrLen = -1;
|
||||||
|
mPrevLen = -1;
|
||||||
|
mScaleFactor = -1;
|
||||||
|
|
||||||
|
final MotionEvent prev = mPrevEvent;
|
||||||
|
|
||||||
|
final float px0 = prev.getX(0);
|
||||||
|
final float py0 = prev.getY(0);
|
||||||
|
final float px1 = prev.getX(1);
|
||||||
|
final float py1 = prev.getY(1);
|
||||||
|
final float cx0 = curr.getX(0);
|
||||||
|
final float cy0 = curr.getY(0);
|
||||||
|
final float cx1 = curr.getX(1);
|
||||||
|
final float cy1 = curr.getY(1);
|
||||||
|
|
||||||
|
final float pvx = px1 - px0;
|
||||||
|
final float pvy = py1 - py0;
|
||||||
|
final float cvx = cx1 - cx0;
|
||||||
|
final float cvy = cy1 - cy0;
|
||||||
|
mPrevFingerDiffX = pvx;
|
||||||
|
mPrevFingerDiffY = pvy;
|
||||||
|
mCurrFingerDiffX = cvx;
|
||||||
|
mCurrFingerDiffY = cvy;
|
||||||
|
|
||||||
|
mFocusX = cx0 + cvx * 0.5f;
|
||||||
|
mFocusY = cy0 + cvy * 0.5f;
|
||||||
|
mTimeDelta = curr.getEventTime() - prev.getEventTime();
|
||||||
|
mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
|
||||||
|
mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
if (mPrevEvent != null) {
|
||||||
|
mPrevEvent.recycle();
|
||||||
|
mPrevEvent = null;
|
||||||
|
}
|
||||||
|
if (mCurrEvent != null) {
|
||||||
|
mCurrEvent.recycle();
|
||||||
|
mCurrEvent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if a two-finger scale gesture is in progress.
|
||||||
|
* @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isInProgress() {
|
||||||
|
return mGestureInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the X coordinate of the current gesture's focal point.
|
||||||
|
* If a gesture is in progress, the focal point is directly between
|
||||||
|
* the two pointers forming the gesture.
|
||||||
|
* If a gesture is ending, the focal point is the location of the
|
||||||
|
* remaining pointer on the screen.
|
||||||
|
* If {@link isInProgress()} would return false, the result of this
|
||||||
|
* function is undefined.
|
||||||
|
*
|
||||||
|
* @return X coordinate of the focal point in pixels.
|
||||||
|
*/
|
||||||
|
public float getFocusX() {
|
||||||
|
return mFocusX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Y coordinate of the current gesture's focal point.
|
||||||
|
* If a gesture is in progress, the focal point is directly between
|
||||||
|
* the two pointers forming the gesture.
|
||||||
|
* If a gesture is ending, the focal point is the location of the
|
||||||
|
* remaining pointer on the screen.
|
||||||
|
* If {@link isInProgress()} would return false, the result of this
|
||||||
|
* function is undefined.
|
||||||
|
*
|
||||||
|
* @return Y coordinate of the focal point in pixels.
|
||||||
|
*/
|
||||||
|
public float getFocusY() {
|
||||||
|
return mFocusY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current distance between the two pointers forming the
|
||||||
|
* gesture in progress.
|
||||||
|
*
|
||||||
|
* @return Distance between pointers in pixels.
|
||||||
|
*/
|
||||||
|
public float getCurrentSpan() {
|
||||||
|
if (mCurrLen == -1) {
|
||||||
|
final float cvx = mCurrFingerDiffX;
|
||||||
|
final float cvy = mCurrFingerDiffY;
|
||||||
|
mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
|
||||||
|
}
|
||||||
|
return mCurrLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the previous distance between the two pointers forming the
|
||||||
|
* gesture in progress.
|
||||||
|
*
|
||||||
|
* @return Previous distance between pointers in pixels.
|
||||||
|
*/
|
||||||
|
public float getPreviousSpan() {
|
||||||
|
if (mPrevLen == -1) {
|
||||||
|
final float pvx = mPrevFingerDiffX;
|
||||||
|
final float pvy = mPrevFingerDiffY;
|
||||||
|
mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
|
||||||
|
}
|
||||||
|
return mPrevLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the scaling factor from the previous scale event to the current
|
||||||
|
* event. This value is defined as
|
||||||
|
* ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
|
||||||
|
*
|
||||||
|
* @return The current scaling factor.
|
||||||
|
*/
|
||||||
|
public float getScaleFactor() {
|
||||||
|
if (mScaleFactor == -1) {
|
||||||
|
mScaleFactor = getCurrentSpan() / getPreviousSpan();
|
||||||
|
}
|
||||||
|
return mScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the time difference in milliseconds between the previous
|
||||||
|
* accepted scaling event and the current scaling event.
|
||||||
|
*
|
||||||
|
* @return Time difference since the last scaling event in milliseconds.
|
||||||
|
*/
|
||||||
|
public long getTimeDelta() {
|
||||||
|
return mTimeDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the event time of the current event being processed.
|
||||||
|
*
|
||||||
|
* @return Current event time in milliseconds.
|
||||||
|
*/
|
||||||
|
public long getEventTime() {
|
||||||
|
return mCurrEvent.getEventTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 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 android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detects transformation gestures involving more than one pointer ("multitouch")
|
|
||||||
* using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
|
|
||||||
* will notify users when a particular gesture event has occurred. This class
|
|
||||||
* should only be used with {@link MotionEvent}s reported via touch.
|
|
||||||
*
|
|
||||||
* To use this class:
|
|
||||||
* <ul>
|
|
||||||
* <li>Create an instance of the {@code TransformGestureDetector} for your
|
|
||||||
* {@link View}
|
|
||||||
* <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
|
|
||||||
* {@link #onTouchEvent(MotionEvent)}. The methods defined in your
|
|
||||||
* callback will be executed when the events occur.
|
|
||||||
* </ul>
|
|
||||||
* @hide Pending API approval
|
|
||||||
*/
|
|
||||||
public class TransformGestureDetector {
|
|
||||||
/**
|
|
||||||
* The listener for receiving notifications when gestures occur.
|
|
||||||
* If you want to listen for all the different gestures then implement
|
|
||||||
* this interface. If you only want to listen for a subset it might
|
|
||||||
* be easier to extend {@link SimpleOnGestureListener}.
|
|
||||||
*
|
|
||||||
* An application will receive events in the following order:
|
|
||||||
* One onTransformBegin()
|
|
||||||
* Zero or more onTransform()
|
|
||||||
* One onTransformEnd() or onTransformFling()
|
|
||||||
*/
|
|
||||||
public interface OnTransformGestureListener {
|
|
||||||
/**
|
|
||||||
* Responds to transformation events for a gesture in progress.
|
|
||||||
* Reported by pointer motion.
|
|
||||||
*
|
|
||||||
* @param detector The detector reporting the event - use this to
|
|
||||||
* retrieve extended info about event state.
|
|
||||||
* @return true if the event was handled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean onTransform(TransformGestureDetector detector);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to the beginning of a transformation gesture. Reported by
|
|
||||||
* new pointers going down.
|
|
||||||
*
|
|
||||||
* @param detector The detector reporting the event - use this to
|
|
||||||
* retrieve extended info about event state.
|
|
||||||
* @return true if the event was handled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean onTransformBegin(TransformGestureDetector detector);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to the end of a transformation gesture. Reported by existing
|
|
||||||
* pointers going up. If the end of a gesture would result in a fling,
|
|
||||||
* onTransformFling is called instead.
|
|
||||||
*
|
|
||||||
* @param detector The detector reporting the event - use this to
|
|
||||||
* retrieve extended info about event state.
|
|
||||||
* @return true if the event was handled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean onTransformEnd(TransformGestureDetector detector);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to the end of a transformation gesture that begins a fling.
|
|
||||||
* Reported by existing pointers going up. If the end of a gesture
|
|
||||||
* would not result in a fling, onTransformEnd is called instead.
|
|
||||||
*
|
|
||||||
* @param detector The detector reporting the event - use this to
|
|
||||||
* retrieve extended info about event state.
|
|
||||||
* @return true if the event was handled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean onTransformFling(TransformGestureDetector detector);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
private static final int INITIAL_EVENT_IGNORES = 2;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private float mTouchSizeScale;
|
|
||||||
private OnTransformGestureListener mListener;
|
|
||||||
private int mVelocityTimeUnits;
|
|
||||||
private MotionEvent mInitialEvent;
|
|
||||||
|
|
||||||
private MotionEvent mPrevEvent;
|
|
||||||
private MotionEvent mCurrEvent;
|
|
||||||
private VelocityTracker mVelocityTracker;
|
|
||||||
|
|
||||||
private float mCenterX;
|
|
||||||
private float mCenterY;
|
|
||||||
private float mTransX;
|
|
||||||
private float mTransY;
|
|
||||||
private float mPrevFingerDiffX;
|
|
||||||
private float mPrevFingerDiffY;
|
|
||||||
private float mCurrFingerDiffX;
|
|
||||||
private float mCurrFingerDiffY;
|
|
||||||
private float mRotateDegrees;
|
|
||||||
private float mCurrLen;
|
|
||||||
private float mPrevLen;
|
|
||||||
private float mScaleFactor;
|
|
||||||
|
|
||||||
// Units in pixels. Current value is pulled out of thin air for debugging only.
|
|
||||||
private float mPointerJumpLimit = 30;
|
|
||||||
|
|
||||||
private int mEventIgnoreCount;
|
|
||||||
|
|
||||||
public TransformGestureDetector(Context context, OnTransformGestureListener listener,
|
|
||||||
int velocityTimeUnits) {
|
|
||||||
mContext = context;
|
|
||||||
mListener = listener;
|
|
||||||
mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
|
|
||||||
mVelocityTimeUnits = velocityTimeUnits;
|
|
||||||
mEventIgnoreCount = INITIAL_EVENT_IGNORES;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
|
|
||||||
this(context, listener, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
final int action = event.getAction();
|
|
||||||
boolean handled = true;
|
|
||||||
|
|
||||||
if (mInitialEvent == null) {
|
|
||||||
// No transform gesture in progress
|
|
||||||
if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
|
|
||||||
action == MotionEvent.ACTION_POINTER_2_DOWN) &&
|
|
||||||
event.getPointerCount() >= 2) {
|
|
||||||
// We have a new multi-finger gesture
|
|
||||||
mInitialEvent = MotionEvent.obtain(event);
|
|
||||||
mPrevEvent = MotionEvent.obtain(event);
|
|
||||||
mVelocityTracker = VelocityTracker.obtain();
|
|
||||||
handled = mListener.onTransformBegin(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Transform gesture in progress - attempt to handle it
|
|
||||||
switch (action) {
|
|
||||||
case MotionEvent.ACTION_POINTER_1_UP:
|
|
||||||
case MotionEvent.ACTION_POINTER_2_UP:
|
|
||||||
// Gesture ended
|
|
||||||
handled = mListener.onTransformEnd(this);
|
|
||||||
|
|
||||||
reset();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
handled = mListener.onTransformEnd(this);
|
|
||||||
|
|
||||||
reset();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
setContext(event);
|
|
||||||
|
|
||||||
// Our first few events can be crazy from some touchscreens - drop them.
|
|
||||||
if (mEventIgnoreCount == 0) {
|
|
||||||
mVelocityTracker.addMovement(event);
|
|
||||||
handled = mListener.onTransform(this);
|
|
||||||
} else {
|
|
||||||
mEventIgnoreCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPrevEvent.recycle();
|
|
||||||
mPrevEvent = MotionEvent.obtain(event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setContext(MotionEvent curr) {
|
|
||||||
mCurrEvent = MotionEvent.obtain(curr);
|
|
||||||
|
|
||||||
mRotateDegrees = -1;
|
|
||||||
mCurrLen = -1;
|
|
||||||
mPrevLen = -1;
|
|
||||||
mScaleFactor = -1;
|
|
||||||
|
|
||||||
final MotionEvent prev = mPrevEvent;
|
|
||||||
|
|
||||||
float px0 = prev.getX(0);
|
|
||||||
float py0 = prev.getY(0);
|
|
||||||
float px1 = prev.getX(1);
|
|
||||||
float py1 = prev.getY(1);
|
|
||||||
float cx0 = curr.getX(0);
|
|
||||||
float cy0 = curr.getY(0);
|
|
||||||
float cx1 = curr.getX(1);
|
|
||||||
float cy1 = curr.getY(1);
|
|
||||||
|
|
||||||
// Some touchscreens do weird things with pointer values where points are
|
|
||||||
// too close along one axis. Try to detect this here and smooth things out.
|
|
||||||
// The main indicator is that we get the X or Y value from the other pointer.
|
|
||||||
final float dx0 = cx0 - px0;
|
|
||||||
final float dy0 = cy0 - py0;
|
|
||||||
final float dx1 = cx1 - px1;
|
|
||||||
final float dy1 = cy1 - py1;
|
|
||||||
|
|
||||||
if (cx0 == cx1) {
|
|
||||||
if (Math.abs(dx0) > mPointerJumpLimit) {
|
|
||||||
cx0 = px0;
|
|
||||||
} else if (Math.abs(dx1) > mPointerJumpLimit) {
|
|
||||||
cx1 = px1;
|
|
||||||
}
|
|
||||||
} else if (cy0 == cy1) {
|
|
||||||
if (Math.abs(dy0) > mPointerJumpLimit) {
|
|
||||||
cy0 = py0;
|
|
||||||
} else if (Math.abs(dy1) > mPointerJumpLimit) {
|
|
||||||
cy1 = py1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final float pvx = px1 - px0;
|
|
||||||
final float pvy = py1 - py0;
|
|
||||||
final float cvx = cx1 - cx0;
|
|
||||||
final float cvy = cy1 - cy0;
|
|
||||||
mPrevFingerDiffX = pvx;
|
|
||||||
mPrevFingerDiffY = pvy;
|
|
||||||
mCurrFingerDiffX = cvx;
|
|
||||||
mCurrFingerDiffY = cvy;
|
|
||||||
|
|
||||||
final float pmidx = px0 + pvx * 0.5f;
|
|
||||||
final float pmidy = py0 + pvy * 0.5f;
|
|
||||||
final float cmidx = cx0 + cvx * 0.5f;
|
|
||||||
final float cmidy = cy0 + cvy * 0.5f;
|
|
||||||
|
|
||||||
mCenterX = cmidx;
|
|
||||||
mCenterY = cmidy;
|
|
||||||
mTransX = cmidx - pmidx;
|
|
||||||
mTransY = cmidy - pmidy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reset() {
|
|
||||||
if (mInitialEvent != null) {
|
|
||||||
mInitialEvent.recycle();
|
|
||||||
mInitialEvent = null;
|
|
||||||
}
|
|
||||||
if (mPrevEvent != null) {
|
|
||||||
mPrevEvent.recycle();
|
|
||||||
mPrevEvent = null;
|
|
||||||
}
|
|
||||||
if (mCurrEvent != null) {
|
|
||||||
mCurrEvent.recycle();
|
|
||||||
mCurrEvent = null;
|
|
||||||
}
|
|
||||||
if (mVelocityTracker != null) {
|
|
||||||
mVelocityTracker.recycle();
|
|
||||||
mVelocityTracker = null;
|
|
||||||
}
|
|
||||||
mEventIgnoreCount = INITIAL_EVENT_IGNORES;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getCenterX() {
|
|
||||||
return mCenterX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getCenterY() {
|
|
||||||
return mCenterY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getTranslateX() {
|
|
||||||
return mTransX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getTranslateY() {
|
|
||||||
return mTransY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getCurrentSpan() {
|
|
||||||
if (mCurrLen == -1) {
|
|
||||||
final float cvx = mCurrFingerDiffX;
|
|
||||||
final float cvy = mCurrFingerDiffY;
|
|
||||||
mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
|
|
||||||
}
|
|
||||||
return mCurrLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPreviousSpan() {
|
|
||||||
if (mPrevLen == -1) {
|
|
||||||
final float pvx = mPrevFingerDiffX;
|
|
||||||
final float pvy = mPrevFingerDiffY;
|
|
||||||
mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
|
|
||||||
}
|
|
||||||
return mPrevLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getScaleFactor() {
|
|
||||||
if (mScaleFactor == -1) {
|
|
||||||
mScaleFactor = getCurrentSpan() / getPreviousSpan();
|
|
||||||
}
|
|
||||||
return mScaleFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getRotation() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.TransformGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
@@ -49,9 +48,6 @@ public class TransformTestActivity extends Activity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
final LayoutInflater li = (LayoutInflater)getSystemService(
|
|
||||||
LAYOUT_INFLATER_SERVICE);
|
|
||||||
|
|
||||||
this.setTitle(R.string.act_title);
|
this.setTitle(R.string.act_title);
|
||||||
LinearLayout root = new LinearLayout(this);
|
LinearLayout root = new LinearLayout(this);
|
||||||
root.setOrientation(LinearLayout.VERTICAL);
|
root.setOrientation(LinearLayout.VERTICAL);
|
||||||
@@ -71,15 +67,19 @@ public class TransformTestActivity extends Activity {
|
|||||||
private float mPosY;
|
private float mPosY;
|
||||||
private float mScale = 1.f;
|
private float mScale = 1.f;
|
||||||
private Matrix mMatrix;
|
private Matrix mMatrix;
|
||||||
private TransformGestureDetector mDetector;
|
private ScaleGestureDetector mDetector;
|
||||||
|
|
||||||
private class Listener implements TransformGestureDetector.OnTransformGestureListener {
|
private float mLastX;
|
||||||
|
private float mLastY;
|
||||||
|
|
||||||
public boolean onTransform(TransformGestureDetector detector) {
|
private class Listener implements ScaleGestureDetector.OnScaleGestureListener {
|
||||||
Log.d("ttest", "Translation: (" + detector.getTranslateX() +
|
|
||||||
", " + detector.getTranslateY() + ")");
|
public boolean onScale(ScaleGestureDetector detector) {
|
||||||
float scale = detector.getScaleFactor();
|
float scale = detector.getScaleFactor();
|
||||||
|
|
||||||
Log.d("ttest", "Scale: " + scale);
|
Log.d("ttest", "Scale: " + scale);
|
||||||
|
|
||||||
|
// Limit the scale so our object doesn't get too big or disappear
|
||||||
if (mScale * scale > 0.1f) {
|
if (mScale * scale > 0.1f) {
|
||||||
if (mScale * scale < 10.f) {
|
if (mScale * scale < 10.f) {
|
||||||
mScale *= scale;
|
mScale *= scale;
|
||||||
@@ -90,15 +90,12 @@ public class TransformTestActivity extends Activity {
|
|||||||
mScale = 0.1f;
|
mScale = 0.1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPosX += detector.getTranslateX();
|
|
||||||
mPosY += detector.getTranslateY();
|
|
||||||
|
|
||||||
Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
|
Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
|
||||||
|
|
||||||
float sizeX = mDrawable.getIntrinsicWidth()/2;
|
float sizeX = mDrawable.getIntrinsicWidth()/2;
|
||||||
float sizeY = mDrawable.getIntrinsicHeight()/2;
|
float sizeY = mDrawable.getIntrinsicHeight()/2;
|
||||||
float centerX = detector.getCenterX();
|
float centerX = detector.getFocusX();
|
||||||
float centerY = detector.getCenterY();
|
float centerY = detector.getFocusY();
|
||||||
float diffX = centerX - mPosX;
|
float diffX = centerX - mPosX;
|
||||||
float diffY = centerY - mPosY;
|
float diffY = centerY - mPosY;
|
||||||
diffX = diffX*scale - diffX;
|
diffX = diffX*scale - diffX;
|
||||||
@@ -115,24 +112,20 @@ public class TransformTestActivity extends Activity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTransformBegin(TransformGestureDetector detector) {
|
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTransformEnd(TransformGestureDetector detector) {
|
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||||
return true;
|
mLastX = detector.getFocusX();
|
||||||
|
mLastY = detector.getFocusY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onTransformFling(TransformGestureDetector detector) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TransformView(Context context) {
|
public TransformView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
mMatrix = new Matrix();
|
mMatrix = new Matrix();
|
||||||
mDetector = new TransformGestureDetector(context, new Listener());
|
mDetector = new ScaleGestureDetector(context, new Listener());
|
||||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||||
mPosX = metrics.widthPixels/2;
|
mPosX = metrics.widthPixels/2;
|
||||||
mPosY = metrics.heightPixels/2;
|
mPosY = metrics.heightPixels/2;
|
||||||
@@ -151,12 +144,37 @@ public class TransformTestActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
boolean handled = mDetector.onTouchEvent(event);
|
mDetector.onTouchEvent(event);
|
||||||
|
|
||||||
int pointerCount = event.getPointerCount();
|
// Handling single finger pan
|
||||||
Log.d("ttest", "pointerCount: " + pointerCount);
|
if (!mDetector.isInProgress()) {
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
mLastX = event.getX();
|
||||||
|
mLastY = event.getY();
|
||||||
|
break;
|
||||||
|
|
||||||
return handled;
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
final float x = event.getX();
|
||||||
|
final float y = event.getY();
|
||||||
|
mPosX += x - mLastX;
|
||||||
|
mPosY += y - mLastY;
|
||||||
|
mLastX = x;
|
||||||
|
mLastY = y;
|
||||||
|
|
||||||
|
float sizeX = mDrawable.getIntrinsicWidth()/2;
|
||||||
|
float sizeY = mDrawable.getIntrinsicHeight()/2;
|
||||||
|
|
||||||
|
mMatrix.reset();
|
||||||
|
mMatrix.postTranslate(-sizeX, -sizeY);
|
||||||
|
mMatrix.postScale(mScale, mScale);
|
||||||
|
mMatrix.postTranslate(mPosX, mPosY);
|
||||||
|
invalidate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user