9

私はこの問題に 8 時間も悩まされていたので、助けを求める時が来たと考えました。

問題を説明する前に、このサイトと Google を調べた結果、どれも役に立たなかったことをお伝えしておきます。(これは 1 つ、もう1 つ、もう1 つです。)

これが取り引きです:私は拡張するクラスを持っていますSurfaceView(それを と呼びましょうMySurface)そしてその中の多くのメソッドをオーバーライドします。通常、いくつかの四角形とテキスト ボックスを描画しますが、これで問題ありません。ユーザーがタッチを開始するとすぐに に変換されBitmap、ユーザーが離すまで各フレームを描画します。

ここにこすりがあります: ユーザーが画面に 2 本の指を置き、ピンチしてズームし、パンすることができるような機能を実装したいと考えています (ただし、2 本の指でのみ)。

ピンチツーズームの実装をいくつか見つけ、次の方法でそれらをCanvasオブジェクトに適合させました。MySurface

public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();

    canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below
    canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine

    // Start draw code

    // ...

    // End draw code

    canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float factor = detector.getScaleFactor();
        if (Math.abs(factor - 1.0f) >= 0.0075f) {
            mScaleVector.z *= factor;
            mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM));
        }

        // ...

        invalidate();

        return true;
    }
}
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction() & MotionEvent.ACTION_MASK;
    int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
    if (event.getPointerCount() == 2) {
        if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) {
            // The various pivot coordinate codes would belong here
        }
    }

    detector.onTouchEvent(event); // Calls the Scale Gesture Detector
    return true;
}

両方の要素 (前後のスクロールとピンチによるズーム) は正常に機能しますが、大きな問題が 1 つあります。ピンチ ツー ズームを使用すると0,0、指のポイントではなくポイントにズームインします。

これを修正するために多くの方法を試しました:

  • を使用してcanvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);; 明らかに、mScaleVectorx と y の値が 0 オフセットであるため、これは望ましくない結果をもたらします。
  • translate()メソッドと同じオフセットを使用する「ピボット」座標を管理しますが、これにより同じ0,0問題が発生するか、ビューに触れたときにジャンプします。
  • 他の多くのこと...前述のピボット座標を使用して多くのことを行い、その位置をユーザーの最初のタッチに基づいて、連続する各ジェスチャでそのタッチに相対的に移動しようとしました。

さらに、このキャンバスは制限されている必要があるため、ユーザーは永遠にスクロールできません。ただし、.scale(sx, sy, px, py)メソッドを使用すると、 で設定した境界を超えて物事がプッシュされ.translate()ます。

私は... この時点で、ほとんど何に対してもオープンです。Android 4.0 ギャラリー (単一の画像を表示する場合) に見られるように、この機能を追加できることはわかっています。これを処理するソース コードを追跡しようとしましたが、役に立ちませんでした。

4

2 に答える 2

16

ImageViewこれは、 usingでピンチズームを実装するために使用するコードですScaleGestureDetector。変換マリスを使用してを描画することもできるため、変更をほとんどまたはまったく行わずに、それも使用できるはずですCanvas

@Override
public boolean onScale(ScaleGestureDetector detector) {
    float mScaleFactor = (float) Math.min(
        Math.max(.8f, detector.getScaleFactor()), 1.2);
    float origScale = saveScale;
    saveScale *= mScaleFactor;
    if (saveScale > maxScale) {
        saveScale = maxScale;
        mScaleFactor = maxScale / origScale;
    } else if (saveScale < minScale) {
        saveScale = minScale;
        mScaleFactor = minScale / origScale;
    }
    right = width * saveScale - width
            - (2 * redundantXSpace * saveScale);
    bottom = height * saveScale - height
            - (2 * redundantYSpace * saveScale);
    if (origWidth * saveScale <= width
            || origHeight * saveScale <= height) {
        matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
        if (mScaleFactor < 1) {
            matrix.getValues(m);
            float x = m[Matrix.MTRANS_X];
            float y = m[Matrix.MTRANS_Y];
            if (mScaleFactor < 1) {
                if (Math.round(origWidth * saveScale) < width) {
                    if (y < -bottom)
                        matrix.postTranslate(0, -(y + bottom));
                    else if (y > 0)
                        matrix.postTranslate(0, -y);
                } else {
                    if (x < -right)
                        matrix.postTranslate(-(x + right), 0);
                    else if (x > 0)
                        matrix.postTranslate(-x, 0);
                }
            }
        }
    } else {
        matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
        matrix.getValues(m);
        float x = m[Matrix.MTRANS_X];
        float y = m[Matrix.MTRANS_Y];
        if (mScaleFactor < 1) {
            if (x < -right)
                matrix.postTranslate(-(x + right), 0);
            else if (x > 0)
                matrix.postTranslate(-x, 0);
            if (y < -bottom)
                matrix.postTranslate(0, -(y + bottom));
            else if (y > 0)
                matrix.postTranslate(0, -y);
        }
    }
    return true;
}

私の場合、onMeasure()ビューのメソッドで必要な値を計算しました。これは、他の場所で実行することをお勧めします。SurfaceView

width = MeasureSpec.getSize(widthMeasureSpec); // Change this according to your screen size
height = MeasureSpec.getSize(heightMeasureSpec); // Change this according to your screen size

// Fit to screen.
float scale;
float scaleX = (float) width / (float) bmWidth;
float scaleY = (float) height / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
saveScale = 1f;
scaleMappingRatio = saveScale / scale;

// Center the image
redundantYSpace = (float) height - (scale * (float) bmHeight);
redundantXSpace = (float) width - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;

matrix.postTranslate(redundantXSpace, redundantYSpace);

origWidth = width - 2 * redundantXSpace;
origHeight = height - 2 * redundantYSpace;
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
setImageMatrix(matrix);

少し説明:

saveScaleの現在のスケール比ですBitmap

mScaleFactorスケール比に乗算する必要がある係数です。

maxScaleminScale定数値にすることができます。

widthheightは画面の寸法です。

redundantXSpace画像が画面よりも小さい場合、画像は中央に配置されるため、画像の境界線とredundantYSpace画面の境界線の間は空になります。

origHeightおよびorigWidthはビットマップのサイズです

matrixビットマップの描画に使用される現在の変換行列です

秘訣は、初期化時に画像を最初に拡大縮小して中央に配置したときに、その縮尺比を1に選択し、それをscaleMappingRatio基準にして画像の実際の縮尺値をマッピングすることです。

于 2012-07-05T07:54:09.280 に答える