am 8acdb201: Added TransformGestureDetector (still in progress) Modified VelocityTracker to track multiple pointers Added TransformTest
Merge commit '8acdb201bdad2cd03c07ebad9cda29f7971ed164' into eclair-plus-aosp * commit '8acdb201bdad2cd03c07ebad9cda29f7971ed164': Added TransformGestureDetector (still in progress)
This commit is contained in:
316
core/java/android/view/TransformGestureDetector.java
Normal file
316
core/java/android/view/TransformGestureDetector.java
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -55,12 +55,12 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
}
|
||||
}, 2));
|
||||
|
||||
final float mPastX[] = new float[NUM_PAST];
|
||||
final float mPastY[] = new float[NUM_PAST];
|
||||
final long mPastTime[] = new long[NUM_PAST];
|
||||
final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
|
||||
final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
|
||||
final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
|
||||
|
||||
float mYVelocity;
|
||||
float mXVelocity;
|
||||
float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
|
||||
float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
|
||||
|
||||
private VelocityTracker mNext;
|
||||
|
||||
@@ -105,7 +105,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
* Reset the velocity tracker back to its initial state.
|
||||
*/
|
||||
public void clear() {
|
||||
mPastTime[0] = 0;
|
||||
for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) {
|
||||
mPastTime[i][0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,18 +122,21 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
public void addMovement(MotionEvent ev) {
|
||||
long time = ev.getEventTime();
|
||||
final int N = ev.getHistorySize();
|
||||
for (int i=0; i<N; i++) {
|
||||
addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
|
||||
ev.getHistoricalEventTime(i));
|
||||
final int pointerCount = ev.getPointerCount();
|
||||
for (int p = 0; p < pointerCount; p++) {
|
||||
for (int i=0; i<N; i++) {
|
||||
addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i),
|
||||
ev.getHistoricalEventTime(i));
|
||||
}
|
||||
addPoint(p, ev.getX(p), ev.getY(p), time);
|
||||
}
|
||||
addPoint(ev.getX(), ev.getY(), time);
|
||||
}
|
||||
|
||||
private void addPoint(float x, float y, long time) {
|
||||
private void addPoint(int pos, float x, float y, long time) {
|
||||
int drop = -1;
|
||||
int i;
|
||||
if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
|
||||
final long[] pastTime = mPastTime;
|
||||
final long[] pastTime = mPastTime[pos];
|
||||
for (i=0; i<NUM_PAST; i++) {
|
||||
if (pastTime[i] == 0) {
|
||||
break;
|
||||
@@ -146,8 +151,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
drop = 0;
|
||||
}
|
||||
if (drop == i) drop--;
|
||||
final float[] pastX = mPastX;
|
||||
final float[] pastY = mPastY;
|
||||
final float[] pastX = mPastX[pos];
|
||||
final float[] pastY = mPastY[pos];
|
||||
if (drop >= 0) {
|
||||
if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
|
||||
final int start = drop+1;
|
||||
@@ -190,44 +195,48 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
* must be positive.
|
||||
*/
|
||||
public void computeCurrentVelocity(int units, float maxVelocity) {
|
||||
final float[] pastX = mPastX;
|
||||
final float[] pastY = mPastY;
|
||||
final long[] pastTime = mPastTime;
|
||||
|
||||
// Kind-of stupid.
|
||||
final float oldestX = pastX[0];
|
||||
final float oldestY = pastY[0];
|
||||
final long oldestTime = pastTime[0];
|
||||
float accumX = 0;
|
||||
float accumY = 0;
|
||||
int N=0;
|
||||
while (N < NUM_PAST) {
|
||||
if (pastTime[N] == 0) {
|
||||
break;
|
||||
for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) {
|
||||
final float[] pastX = mPastX[pos];
|
||||
final float[] pastY = mPastY[pos];
|
||||
final long[] pastTime = mPastTime[pos];
|
||||
|
||||
// Kind-of stupid.
|
||||
final float oldestX = pastX[0];
|
||||
final float oldestY = pastY[0];
|
||||
final long oldestTime = pastTime[0];
|
||||
float accumX = 0;
|
||||
float accumY = 0;
|
||||
int N=0;
|
||||
while (N < NUM_PAST) {
|
||||
if (pastTime[N] == 0) {
|
||||
break;
|
||||
}
|
||||
N++;
|
||||
}
|
||||
N++;
|
||||
// Skip the last received event, since it is probably pretty noisy.
|
||||
if (N > 3) N--;
|
||||
|
||||
for (int i=1; i < N; i++) {
|
||||
final int dur = (int)(pastTime[i] - oldestTime);
|
||||
if (dur == 0) continue;
|
||||
float dist = pastX[i] - oldestX;
|
||||
float vel = (dist/dur) * units; // pixels/frame.
|
||||
if (accumX == 0) accumX = vel;
|
||||
else accumX = (accumX + vel) * .5f;
|
||||
|
||||
dist = pastY[i] - oldestY;
|
||||
vel = (dist/dur) * units; // pixels/frame.
|
||||
if (accumY == 0) accumY = vel;
|
||||
else accumY = (accumY + vel) * .5f;
|
||||
}
|
||||
mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
|
||||
: Math.min(accumX, maxVelocity);
|
||||
mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
|
||||
: Math.min(accumY, maxVelocity);
|
||||
|
||||
if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
|
||||
+ mXVelocity + " N=" + N);
|
||||
}
|
||||
// Skip the last received event, since it is probably pretty noisy.
|
||||
if (N > 3) N--;
|
||||
|
||||
for (int i=1; i < N; i++) {
|
||||
final int dur = (int)(pastTime[i] - oldestTime);
|
||||
if (dur == 0) continue;
|
||||
float dist = pastX[i] - oldestX;
|
||||
float vel = (dist/dur) * units; // pixels/frame.
|
||||
if (accumX == 0) accumX = vel;
|
||||
else accumX = (accumX + vel) * .5f;
|
||||
|
||||
dist = pastY[i] - oldestY;
|
||||
vel = (dist/dur) * units; // pixels/frame.
|
||||
if (accumY == 0) accumY = vel;
|
||||
else accumY = (accumY + vel) * .5f;
|
||||
}
|
||||
mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity);
|
||||
mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity);
|
||||
|
||||
if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
|
||||
+ mXVelocity + " N=" + N);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +246,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
* @return The previously computed X velocity.
|
||||
*/
|
||||
public float getXVelocity() {
|
||||
return mXVelocity;
|
||||
return mXVelocity[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +256,32 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
|
||||
* @return The previously computed Y velocity.
|
||||
*/
|
||||
public float getYVelocity() {
|
||||
return mYVelocity;
|
||||
return mYVelocity[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last computed X velocity. You must first call
|
||||
* {@link #computeCurrentVelocity(int)} before calling this function.
|
||||
*
|
||||
* @param pos Which pointer's velocity to return.
|
||||
* @return The previously computed X velocity.
|
||||
*
|
||||
* @hide Pending API approval
|
||||
*/
|
||||
public float getXVelocity(int pos) {
|
||||
return mXVelocity[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the last computed Y velocity. You must first call
|
||||
* {@link #computeCurrentVelocity(int)} before calling this function.
|
||||
*
|
||||
* @param pos Which pointer's velocity to return.
|
||||
* @return The previously computed Y velocity.
|
||||
*
|
||||
* @hide Pending API approval
|
||||
*/
|
||||
public float getYVelocity(int pos) {
|
||||
return mYVelocity[pos];
|
||||
}
|
||||
}
|
||||
|
||||
10
tests/TransformTest/Android.mk
Normal file
10
tests/TransformTest/Android.mk
Normal file
@@ -0,0 +1,10 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
|
||||
LOCAL_PACKAGE_NAME := TransformTest
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
28
tests/TransformTest/AndroidManifest.xml
Normal file
28
tests/TransformTest/AndroidManifest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.test.transform">
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
|
||||
<application android:label="TransformTest">
|
||||
<activity android:name="TransformTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
BIN
tests/TransformTest/res/drawable/logo.png
Normal file
BIN
tests/TransformTest/res/drawable/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
19
tests/TransformTest/res/values/strings.xml
Normal file
19
tests/TransformTest/res/values/strings.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2007 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="act_title">TransformTest</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.google.android.test.transform;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.TransformGestureDetector;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class TransformTestActivity extends Activity {
|
||||
public TransformTestActivity() {
|
||||
super();
|
||||
init(false);
|
||||
}
|
||||
|
||||
public TransformTestActivity(boolean noCompat) {
|
||||
super();
|
||||
init(noCompat);
|
||||
}
|
||||
|
||||
public void init(boolean noCompat) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final LayoutInflater li = (LayoutInflater)getSystemService(
|
||||
LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
this.setTitle(R.string.act_title);
|
||||
LinearLayout root = new LinearLayout(this);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
TransformView view = new TransformView(getApplicationContext());
|
||||
Drawable drawable = getResources().getDrawable(R.drawable.logo);
|
||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicWidth());
|
||||
view.setDrawable(drawable);
|
||||
|
||||
root.addView(view);
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
private class TransformView extends View {
|
||||
private Drawable mDrawable;
|
||||
private float mPosX;
|
||||
private float mPosY;
|
||||
private float mScale = 1.f;
|
||||
private Matrix mMatrix;
|
||||
private TransformGestureDetector mDetector;
|
||||
|
||||
private class Listener implements TransformGestureDetector.OnTransformGestureListener {
|
||||
|
||||
public boolean onTransform(TransformGestureDetector detector) {
|
||||
Log.d("ttest", "Translation: (" + detector.getTranslateX() +
|
||||
", " + detector.getTranslateY() + ")");
|
||||
float scale = detector.getScaleFactor();
|
||||
Log.d("ttest", "Scale: " + scale);
|
||||
if (mScale * scale > 0.1f) {
|
||||
if (mScale * scale < 10.f) {
|
||||
mScale *= scale;
|
||||
} else {
|
||||
mScale = 10.f;
|
||||
}
|
||||
} else {
|
||||
mScale = 0.1f;
|
||||
}
|
||||
|
||||
mPosX += detector.getTranslateX();
|
||||
mPosY += detector.getTranslateY();
|
||||
|
||||
Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
|
||||
|
||||
float sizeX = mDrawable.getIntrinsicWidth()/2;
|
||||
float sizeY = mDrawable.getIntrinsicHeight()/2;
|
||||
float centerX = detector.getCenterX();
|
||||
float centerY = detector.getCenterY();
|
||||
float diffX = centerX - mPosX;
|
||||
float diffY = centerY - mPosY;
|
||||
diffX = diffX*scale - diffX;
|
||||
diffY = diffY*scale - diffY;
|
||||
mPosX -= diffX;
|
||||
mPosY -= diffY;
|
||||
mMatrix.reset();
|
||||
mMatrix.postTranslate(-sizeX, -sizeY);
|
||||
mMatrix.postScale(mScale, mScale);
|
||||
mMatrix.postTranslate(mPosX, mPosY);
|
||||
|
||||
invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onTransformBegin(TransformGestureDetector detector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onTransformEnd(TransformGestureDetector detector) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onTransformFling(TransformGestureDetector detector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TransformView(Context context) {
|
||||
super(context);
|
||||
mMatrix = new Matrix();
|
||||
mDetector = new TransformGestureDetector(context, new Listener());
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
mPosX = metrics.widthPixels/2;
|
||||
mPosY = metrics.heightPixels/2;
|
||||
}
|
||||
|
||||
public void setDrawable(Drawable d) {
|
||||
mDrawable = d;
|
||||
|
||||
float sizeX = mDrawable.getIntrinsicWidth()/2;
|
||||
float sizeY = mDrawable.getIntrinsicHeight()/2;
|
||||
mMatrix.reset();
|
||||
mMatrix.postTranslate(-sizeX, -sizeY);
|
||||
mMatrix.postScale(mScale, mScale);
|
||||
mMatrix.postTranslate(mPosX, mPosY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
boolean handled = mDetector.onTouchEvent(event);
|
||||
|
||||
int pointerCount = event.getPointerCount();
|
||||
Log.d("ttest", "pointerCount: " + pointerCount);
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
int saveCount = canvas.getSaveCount();
|
||||
canvas.save();
|
||||
canvas.concat(mMatrix);
|
||||
mDrawable.draw(canvas);
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user