19

水平および垂直のパン/ドラッグをサポートするビューを持つことは可能ですか? その上で、ピンチでズーム、ダブルタップでズームできるようにしたいです。このビューは Android に存在しますか、それとも存在するプロジェクトを誰かが知っていますか?

さらに難しくするには、別のビュー (Button、TextView、VideoView など) をビューに追加する必要があります。最初の/親ビューが拡大または移動されると、サブビュー (ボタン) は親と一緒に移動する必要があります。

複数のソリューションを試しましたが、探しているオプションがすべてありません。

4

9 に答える 9

27

あなたが望むものを達成することは可能だと思いますが、私が知る限り、そのためのソリューションが組み込まれています。Viewあなたの質問の2番目の部分から、ズーム可能ではなく、ViewGroup他のビュー(レイアウトなど)を含むことができるすべてのビューのスーパークラスであると思います。以下は、独自の ViewGroup を構築することから始めることができるいくつかのコードです。そのほとんどは、このブログ投稿からのものです。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.*;

public class ZoomableViewGroup extends ViewGroup {

    private static final int INVALID_POINTER_ID = 1;
    private int mActivePointerId = INVALID_POINTER_ID;

    private float mScaleFactor = 1;
    private ScaleGestureDetector mScaleDetector;
    private Matrix mScaleMatrix = new Matrix();
    private Matrix mScaleMatrixInverse = new Matrix();

    private float mPosX;
    private float mPosY;
    private Matrix mTranslateMatrix = new Matrix();
    private Matrix mTranslateMatrixInverse = new Matrix();

    private float mLastTouchX;
    private float mLastTouchY;

    private float mFocusY;

    private float mFocusX;

    private float[] mInvalidateWorkingArray = new float[6];
    private float[] mDispatchTouchEventWorkingArray = new float[2];
    private float[] mOnTouchEventWorkingArray = new float[2];


    public ZoomableViewGroup(Context context) {
        super(context);
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        mTranslateMatrix.setTranslate(0, 0);
        mScaleMatrix.setScale(1, 1);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight());
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.save();
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        mDispatchTouchEventWorkingArray[0] = ev.getX();
        mDispatchTouchEventWorkingArray[1] = ev.getY();
        mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
        ev.setLocation(mDispatchTouchEventWorkingArray[0],
                mDispatchTouchEventWorkingArray[1]);
        return super.dispatchTouchEvent(ev);
    }

    /**
     * Although the docs say that you shouldn't override this, I decided to do
     * so because it offers me an easy way to change the invalidated area to my
     * likening.
     */
    @Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {

        mInvalidateWorkingArray[0] = dirty.left;
        mInvalidateWorkingArray[1] = dirty.top;
        mInvalidateWorkingArray[2] = dirty.right;
        mInvalidateWorkingArray[3] = dirty.bottom;


        mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
        dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]),
                Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3]));

        location[0] *= mScaleFactor;
        location[1] *= mScaleFactor;
        return super.invalidateChildInParent(location, dirty);
    }

    private float[] scaledPointsToScreenPoints(float[] a) {
        mScaleMatrix.mapPoints(a);
        mTranslateMatrix.mapPoints(a);
        return a;
    }

    private float[] screenPointsToScaledPoints(float[] a){
        mTranslateMatrixInverse.mapPoints(a);
        mScaleMatrixInverse.mapPoints(a);
        return a;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mOnTouchEventWorkingArray[0] = ev.getX();
        mOnTouchEventWorkingArray[1] = ev.getY();

        mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

        ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
        mScaleDetector.onTouchEvent(ev);

        final int action = ev.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                final float x = ev.getX();
                final float y = ev.getY();

                mLastTouchX = x;
                mLastTouchY = y;

                // Save the ID of this pointer
                mActivePointerId = ev.getPointerId(0);
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                // Find the index of the active pointer and fetch its position
                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);

                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;

                mPosX += dx;
                mPosY += dy;
                mTranslateMatrix.preTranslate(dx, dy);
                mTranslateMatrix.invert(mTranslateMatrixInverse);

                mLastTouchX = x;
                mLastTouchY = y;

                invalidate();
                break;
            }

            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }

            case MotionEvent.ACTION_POINTER_UP: {
                // Extract the index of the pointer that left the touch sensor
                final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                }
                break;
            }
        }
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            if (detector.isInProgress()) {
                mFocusX = detector.getFocusX();
                mFocusY = detector.getFocusY();
            }
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
            mScaleMatrix.setScale(mScaleFactor, mScaleFactor,
                    mFocusX, mFocusY);
            mScaleMatrix.invert(mScaleMatrixInverse);
            invalidate();
            requestLayout();


            return true;
        }
    }
}

このクラスでできることは、コンテンツをドラッグしてピンチでズームできるようにすることです。現在、ダブルタップしてズームすることはできませんが、onTouchEventメソッドに簡単に実装できるはずです。

ViewGroup で子をレイアウトする方法について質問がある場合は、このビデオが非常に役立ちます。または、単一のメソッドがどのように機能するか、またはその他の質問がある場合は、コメントでお気軽にお問い合わせください。

于 2012-09-19T15:15:49.347 に答える
11

トーマスの答えはほぼ最高です(私の電話には位置バグが1つあります):ズームはすぐに開始され(アレックスのコードには当てはまりません)、ズームは正しいピボットポイントで行われます。

ただし、 Alexのコードに反して、「ダブルタップドラッグ」ジェスチャでズームすることはできません (よく知られたジェスチャではありませんが、Google Chrome や Google マップのように 1 本の指だけでズームできる非常に便利なジェスチャです)。アプリ)。したがって、これを可能にするためのThomasのコードの変更 (およびサブビューの位置のバグの修正) は次のとおりです。

public class ZoomableView extends ViewGroup {

    // States.
    private static final byte NONE = 0;
    private static final byte DRAG = 1;
    private static final byte ZOOM = 2;

    private byte mode = NONE;

    // Matrices used to move and zoom image.
    private Matrix matrix = new Matrix();
    private Matrix matrixInverse = new Matrix();
    private Matrix savedMatrix = new Matrix();

    // Parameters for zooming.
    private PointF start = new PointF();
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float[] lastEvent = null;
    private long lastDownTime = 0l;

    private float[] mDispatchTouchEventWorkingArray = new float[2];
    private float[] mOnTouchEventWorkingArray = new float[2];


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        mDispatchTouchEventWorkingArray[0] = ev.getX();
        mDispatchTouchEventWorkingArray[1] = ev.getY();
        mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
        ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
        return super.dispatchTouchEvent(ev);
    }

    public ZoomableView(Context context) {
        super(context);
        init(context);
    }

    public ZoomableView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ZoomableView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {

    }


    /**
     * Determine the space between the first two fingers
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * Calculate the mid point of the first two fingers
     */
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    private float[] scaledPointsToScreenPoints(float[] a) {
        matrix.mapPoints(a);
        return a;
    }

    private float[] screenPointsToScaledPoints(float[] a) {
        matrixInverse.mapPoints(a);
        return a;
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        float[] values = new float[9];
        matrix.getValues(values);
        canvas.save();
        canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
        canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // handle touch events here
        mOnTouchEventWorkingArray[0] = event.getX();
        mOnTouchEventWorkingArray[1] = event.getY();

        mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

        event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                mode = DRAG;
                lastEvent = null;
                long downTime = event.getDownTime();
                if (downTime - lastDownTime < 300l) {
                    float density = getResources().getDisplayMetrics().density;
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
                        savedMatrix.set(matrix);
                        mid.set(event.getX(), event.getY());
                        mode = ZOOM;
                        lastEvent = new float[4];
                        lastEvent[0] = lastEvent[1] = event.getX();
                        lastEvent[2] = lastEvent[3] = event.getY();
                    }
                    lastDownTime = 0l;
                } else {
                    lastDownTime = downTime;
                }
                start.set(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                final float density = getResources().getDisplayMetrics().density;
                if (mode == DRAG) {
                    matrix.set(savedMatrix);
                    float dx = event.getX() - start.x;
                    float dy = event.getY() - start.y;
                    matrix.postTranslate(dx, dy);
                    matrix.invert(matrixInverse);
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
                        lastDownTime = 0l;
                    }
                } else if (mode == ZOOM) {
                    if (event.getPointerCount() > 1) {
                        float newDist = spacing(event);
                        if (newDist > 10f * density) {
                            matrix.set(savedMatrix);
                            float scale = (newDist / oldDist);
                            matrix.postScale(scale, scale, mid.x, mid.y);
                            matrix.invert(matrixInverse);
                        }
                    } else {
                        matrix.set(savedMatrix);
                        float scale = event.getY() / start.y;
                        matrix.postScale(scale, scale, mid.x, mid.y);
                        matrix.invert(matrixInverse);
                    }
                }
                break;
        }

        invalidate();
        return true;
    }

}
于 2016-07-05T13:49:40.643 に答える
1

このカスタム ビューは、Android 標準の imageView のサブクラスであり、(マルチ) タッチ パンとズーム (およびダブルタップ ズーム) を追加します。

https://github.com/sephiroth74/ImageViewZoom

http://blog.sephiroth.it/2011/04/04/imageview-zoom-and-scroll/

これは、すでにご存知の MikeOrtiz の TouchImageView に似ていますが、いくつかの機能が追加されています。

ビュー「スタック」(Android FrameLayout など) 内で、必要な他の textView と一緒に使用できます。(私は、皿の山やカードの山など、ビューの「山」を意味します。つまり、Z 軸上で次から次へと積み重ねられたビューの山です。)

すべてのビューをまとめて移動するには、Android ジェスチャー (マルチタッチ) メカニズムを制御し、必要なコードを記述する必要があります。(非常に複雑な) 要件に対して、すぐに使用できるソリューションはありません。この記事を見てください:

http://android-developers.blogspot.it/2010/06/make-sense-of-multitouch.html

于 2012-10-18T15:17:10.870 に答える
0

アレックスのコードでズームのパフォーマンスを向上させるには、次の変更を追加します

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();
        if (detector.isInProgress()) {
            mFocusX = detector.getFocusX();
            mFocusY = detector.getFocusY();
        }

        mFocusX = (mFocusX + mLastTouchX)/2;  // get center of touch
        mFocusY = (mFocusY + mLastTouchY)/2;  // get center of touch

        mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 2.0f));
        mScaleMatrix.setScale(mScaleFactor, mScaleFactor,mFocusX, mFocusY);
        mScaleMatrix.invert(mScaleMatrixInverse);
        invalidate();
        requestLayout();

        return true;
    }
}
于 2015-11-27T16:42:35.083 に答える
0

LinearLayout のズーム/パンに興味がある人のために、アレックスが投稿したバージョンを修正して、物事を垂直にレイアウトし、パンを表示ビューに制限しました。これを PDFRenderer のビットマップに使用します。私はこれをテストしましたが、バグに気づいたら、私も知りたいので投稿してください!

注: QuickScale が機能するため、ダブルタップを実装しないことにしました。

public class ZoomableLinearLayout extends ViewGroup {

   private static final int INVALID_POINTER_ID = 1;
   private int mActivePointerId = INVALID_POINTER_ID;

   private float mScaleFactor = 1;
   private ScaleGestureDetector mScaleDetector;
   private Matrix mScaleMatrix = new Matrix();
   private Matrix mScaleMatrixInverse = new Matrix();

   private float mPosX;
   private float mPosY;
   private Matrix mTranslateMatrix = new Matrix();
   private Matrix mTranslateMatrixInverse = new Matrix();

   private float mLastTouchX;
   private float mLastTouchY;

   private float mFocusY;
   private float mFocusX;

   private int mCanvasWidth;
   private int mCanvasHeight;

   private float[] mInvalidateWorkingArray = new float[6];
   private float[] mDispatchTouchEventWorkingArray = new float[2];
   private float[] mOnTouchEventWorkingArray = new float[2];

   private boolean mIsScaling;

   public ZoomableLinearLayout(Context context) {
      super(context);
      mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
      mTranslateMatrix.setTranslate(0, 0);
      mScaleMatrix.setScale(1, 1);
   }

   public ZoomableLinearLayout(Context context, AttributeSet attributeSet) {
      super(context, attributeSet);
      mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
      mTranslateMatrix.setTranslate(0, 0);
      mScaleMatrix.setScale(1, 1);
   }

   @Override
   protected void onLayout(boolean changed, int l, int t, int r, int b) {
      int childCount = getChildCount();
      for (int i = 0; i < childCount; i++) {
         View child = getChildAt(i);
         if (child.getVisibility() != GONE) {
            child.layout(l, t, l+child.getMeasuredWidth(), t += child.getMeasuredHeight());
         }
      }
   }

   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);

      int height = 0;
      int width = 0;
      int childCount = getChildCount();
      for (int i = 0; i < childCount; i++) {
         View child = getChildAt(i);
         if (child.getVisibility() != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            height += child.getMeasuredHeight();
            width = Math.max(width, child.getMeasuredWidth());
         }
      }
      mCanvasWidth = width;
      mCanvasHeight = height;
   }

   @Override
   protected void dispatchDraw(Canvas canvas) {
      canvas.save();
      canvas.translate(mPosX, mPosY);
      canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
      super.dispatchDraw(canvas);
      canvas.restore();
   }

   @Override
   public boolean dispatchTouchEvent(MotionEvent ev) {
      mDispatchTouchEventWorkingArray[0] = ev.getX();
      mDispatchTouchEventWorkingArray[1] = ev.getY();
      mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
      ev.setLocation(mDispatchTouchEventWorkingArray[0],
            mDispatchTouchEventWorkingArray[1]);
      return super.dispatchTouchEvent(ev);
   }

   /**
    * Although the docs say that you shouldn't override this, I decided to do
    * so because it offers me an easy way to change the invalidated area to my
    * likening.
    */
   @Override
   public ViewParent invalidateChildInParent(int[] location, Rect dirty) {

      mInvalidateWorkingArray[0] = dirty.left;
      mInvalidateWorkingArray[1] = dirty.top;
      mInvalidateWorkingArray[2] = dirty.right;
      mInvalidateWorkingArray[3] = dirty.bottom;

      mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
      dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]),
            Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3]));

      location[0] *= mScaleFactor;
      location[1] *= mScaleFactor;
      return super.invalidateChildInParent(location, dirty);
   }

   private float[] scaledPointsToScreenPoints(float[] a) {
      mScaleMatrix.mapPoints(a);
      mTranslateMatrix.mapPoints(a);
      return a;
   }

   private float[] screenPointsToScaledPoints(float[] a){
      mTranslateMatrixInverse.mapPoints(a);
      mScaleMatrixInverse.mapPoints(a);
      return a;
   }

   @Override
   public boolean onTouchEvent(MotionEvent ev) {
      mOnTouchEventWorkingArray[0] = ev.getX();
      mOnTouchEventWorkingArray[1] = ev.getY();

      mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

      ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
      mScaleDetector.onTouchEvent(ev);

      final int action = ev.getAction();
      switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();

            mLastTouchX = x;
            mLastTouchY = y;

            // Save the ID of this pointer
            mActivePointerId = ev.getPointerId(0);
            break;
         }

         case MotionEvent.ACTION_MOVE: {
            // Find the index of the active pointer and fetch its position
            final int pointerIndex = ev.findPointerIndex(mActivePointerId);
            final float x = ev.getX(pointerIndex);
            final float y = ev.getY(pointerIndex);

            if (mIsScaling && ev.getPointerCount() == 1) {
               // Don't move during a QuickScale.
               mLastTouchX = x;
               mLastTouchY = y;

               break;
            }

            float dx = x - mLastTouchX;
            float dy = y - mLastTouchY;

            float[] topLeft = {0f, 0f};
            float[] bottomRight = {getWidth(), getHeight()};
            /*
             * Corners of the view in screen coordinates, so dx/dy should not be allowed to
             * push these beyond the canvas bounds.
             */
            float[] scaledTopLeft = screenPointsToScaledPoints(topLeft);
            float[] scaledBottomRight = screenPointsToScaledPoints(bottomRight);

            dx = Math.min(Math.max(dx, scaledBottomRight[0] - mCanvasWidth), scaledTopLeft[0]);
            dy = Math.min(Math.max(dy, scaledBottomRight[1] - mCanvasHeight), scaledTopLeft[1]);

            mPosX += dx;
            mPosY += dy;

            mTranslateMatrix.preTranslate(dx, dy);
            mTranslateMatrix.invert(mTranslateMatrixInverse);

            mLastTouchX = x;
            mLastTouchY = y;

            invalidate();
            break;
         }

         case MotionEvent.ACTION_UP: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
         }

         case MotionEvent.ACTION_CANCEL: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
         }

         case MotionEvent.ACTION_POINTER_UP: {
            // Extract the index of the pointer that left the touch sensor
            final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = ev.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
               // This was our active pointer going up. Choose a new
               // active pointer and adjust accordingly.
               final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
               mLastTouchX = ev.getX(newPointerIndex);
               mLastTouchY = ev.getY(newPointerIndex);
               mActivePointerId = ev.getPointerId(newPointerIndex);
            }
            break;
         }
      }
      return true;
   }

   private float getMaxScale() {
      return 2f;
   }

   private float getMinScale() {
      return 1f;
   }

   private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
      @Override
      public boolean onScaleBegin(ScaleGestureDetector detector) {
         mIsScaling = true;

         mFocusX = detector.getFocusX();
         mFocusY = detector.getFocusY();

         float[] foci = {mFocusX, mFocusY};
         float[] scaledFoci = screenPointsToScaledPoints(foci);

         mFocusX = scaledFoci[0];
         mFocusY = scaledFoci[1];

         return true;
      }

      @Override
      public void onScaleEnd(ScaleGestureDetector detector) {
         mIsScaling = false;
      }

      @Override
      public boolean onScale(ScaleGestureDetector detector) {
         mScaleFactor *= detector.getScaleFactor();
         mScaleFactor = Math.max(getMinScale(), Math.min(mScaleFactor, getMaxScale()));
         mScaleMatrix.setScale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
         mScaleMatrix.invert(mScaleMatrixInverse);
         invalidate();

         return true;
      }
   }

}
于 2016-12-18T19:54:51.467 に答える