diff options
-rw-r--r-- | core/java/android/view/TransformGestureDetector.java | 316 | ||||
-rw-r--r-- | core/java/android/view/VelocityTracker.java | 139 | ||||
-rw-r--r-- | tests/TransformTest/Android.mk | 10 | ||||
-rw-r--r-- | tests/TransformTest/AndroidManifest.xml | 28 | ||||
-rw-r--r-- | tests/TransformTest/res/drawable/logo.png | bin | 0 -> 13388 bytes | |||
-rw-r--r-- | tests/TransformTest/res/values/strings.xml | 19 | ||||
-rw-r--r-- | tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java | 171 |
7 files changed, 631 insertions, 52 deletions
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java new file mode 100644 index 0000000..196716a --- /dev/null +++ b/core/java/android/view/TransformGestureDetector.java @@ -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(); + } +} diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 5d89c46..9581080 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -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; + // 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); } - 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]; } } diff --git a/tests/TransformTest/Android.mk b/tests/TransformTest/Android.mk new file mode 100644 index 0000000..2d3637d --- /dev/null +++ b/tests/TransformTest/Android.mk @@ -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) diff --git a/tests/TransformTest/AndroidManifest.xml b/tests/TransformTest/AndroidManifest.xml new file mode 100644 index 0000000..5c9995f --- /dev/null +++ b/tests/TransformTest/AndroidManifest.xml @@ -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> diff --git a/tests/TransformTest/res/drawable/logo.png b/tests/TransformTest/res/drawable/logo.png Binary files differnew file mode 100644 index 0000000..4d717a8 --- /dev/null +++ b/tests/TransformTest/res/drawable/logo.png diff --git a/tests/TransformTest/res/values/strings.xml b/tests/TransformTest/res/values/strings.xml new file mode 100644 index 0000000..a0eb81f --- /dev/null +++ b/tests/TransformTest/res/values/strings.xml @@ -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> diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java new file mode 100644 index 0000000..52286d1 --- /dev/null +++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java @@ -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); + } + } +} |