2011-07-21 12 views
8

私はカスタムSurfaceViewを拡張し、ピンチズームとスクロール機能を持​​たせようとしています。 私のキャンバスの翻訳マトリックスにはどのように制限を設けることができますか?

は私がスクロールする方法:

@Override 
    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
      float distanceX, float distanceY) { 

     // subtract scrolled amount 
     matrix.postTranslate(-distanceX, -distanceY); 

     rebound(); 

     invalidate(); 

     // handled 
     return true; 
    } 

は、私はズーム方法:

@Override 
    public boolean onScale(ScaleGestureDetector detector) { 
     if (detector.isInProgress()) { 
      // get scale 
      float factor = detector.getScaleFactor(); 

      // Don't let the object get too small or too large. 
      if (factor * scale > 1f) { 
       factor = 1f/scale; 
      } else if (factor * scale < minScale) { 
       factor = minScale/scale; 
      } 

      // store local scale 
      scale *= factor; 

      // do the scale 
      matrix.preScale(factor, factor, detector.getFocusX(), detector.getFocusY()); 

      rebound(); 

      invalidate(); 
     } 

     return true; 
    } 

(参考のために、私は、これらの方法の両方とも現在onDraw :)

@Override 
protected void onDraw(Canvas canvas) { 
    super.onDraw(canvas); 

    canvas.save(); 
    canvas.setMatrix(matrix); 
    // [...] stuff drawn with matrix settings 
    canvas.restore(); 
    // [...] stuff drawn without matrix settings, as an overlay 
} 

ためにこのコードを使用します機能します。ズーミングは最小(0f〜1f)と最大(現在は常に1f)のスケール値で正しく停止します。使用

グローバルフィールド:

  • drawWdrawH = float、スケールでキャンバスデータのピクセルサイズ= 1
  • parentW、= floatparentH、可視ビューのピクセルサイズ。
  • matrix = android.graphics.Matrix

問題は、私はそれが自動的にビューに滞在した内容を強制する実装しようとしている「rebound()」(多分あわや、より良い名前を必要とする)方法です。

私は、境界がどこにあるべきか(四角形(0、0、parentW、parentH)内)を計算しようとする試みを試みました。

ここに私が現在持っているものがありますが、それは間違いなく機能しません。それどころか、もっと右に押します。問題は私の考えではなく、私の考えであるように感じます。 誰かが、この実装での試行の問題を解決するために、あまりにも遠すぎたり、問題を修正したりする場合、マトリックスをエッジに変換する、よりシンプルでクリーンなコードを考え出すことができますか?それがトップに表示させるために使用されるチェックが

public void rebound() { 
    // bounds 
    RectF currentBounds = new RectF(0, 0, drawW, drawH); 
    matrix.mapRect(currentBounds); 
    RectF parentBounds = new RectF(0, 0, parentW, parentH/2); 

    PointF diff = new PointF(0, 0); 

    if (currentBounds.left > parentBounds.left) { 
     diff.x += (parentBounds.left - currentBounds.left); 
    } 
    if (currentBounds.top > parentBounds.top) { 
     diff.y += (parentBounds.top - currentBounds.top); 
    } 
    if (currentBounds.width() > parentBounds.width()) { 
     if (currentBounds.right < parentBounds.right) { 
      diff.x += (parentBounds.right - currentBounds.right); 
     } 
     if (currentBounds.bottom < parentBounds.bottom) { 
      diff.y += (parentBounds.bottom - currentBounds.bottom); 
     } 
    } 

    matrix.postTranslate(diff.x, diff.y); 
} 

行列がフィールドだった前に私が書いた以前のバージョンを残した場合ザ・働いている(私はちょうどcanvas.scale()onDrawで、その後canvas.translate()を使用):

public void rebound() { 
    // bounds 
    int boundTop = 0; 
    int boundLeft = 0; 
    int boundRight = (int)(-scale * drawW + parentW); 
    int boundBottom = (int)(-scale * drawH + parentH); 

    if (boundLeft >= boundRight) { 
     mScrollX = Math.min(boundLeft, Math.max(boundRight, mScrollX)); 
    } else { 
     mScrollX = 0; 
    } 
    if (boundTop >= boundBottom) { 
     mScrollY = Math.min(boundTop, Math.max(boundBottom, mScrollY)); 
    } else { 
     mScrollY = 0; 
    } 
} 

私はdetector.getFocusX()detector.getFocusY()を中心に正確にスケールできるように新しい方法を使用しています。

更新日:私は現在の方法に変更しました。これは幾分かしか動作せず、ズーム方向を変えた後はy方向の方向を中心にして境界を定め、間違っています。私はまた、 "preScale"と "postTranslate"を作って(私が理解しているように)それは常にスケールを適用して翻訳し、それらを混合しないようにすべきです。

最終更新:これで動作します。それは私が戻っ限界にそれを翻訳することによりたくないスクロールを否定

public void rebound() { 
    // make a rectangle representing what our current canvas looks like 
    RectF currentBounds = new RectF(0, 0, drawW, drawH); 
    matrix.mapRect(currentBounds); 
    // make a rectangle representing the scroll bounds 
    RectF areaBounds = new RectF((float) getLeft(), 
            (float) getTop(), 
            (float) parentW + (float) getLeft(), 
            (float) parentH + (float) getTop()); 

    // the difference between the current rectangle and the rectangle we want 
    PointF diff = new PointF(0f, 0f); 

    // x-direction 
    if (currentBounds.width() > areaBounds.width()) { 
     // allow scrolling only if the amount of content is too wide at this scale 
     if (currentBounds.left > areaBounds.left) { 
      // stop from scrolling too far left 
      diff.x = (areaBounds.left - currentBounds.left); 
     } 
     if (currentBounds.right < areaBounds.right) { 
      // stop from scrolling too far right 
      diff.x = (areaBounds.right - currentBounds.right); 
     } 
    } else { 
     // negate any scrolling 
     diff.x = (areaBounds.left - currentBounds.left); 
    } 

    // y-direction 
    if (currentBounds.height() > areaBounds.height()) { 
     // allow scrolling only if the amount of content is too tall at this scale 
     if (currentBounds.top > areaBounds.top) { 
      // stop from scrolling too far above 
      diff.y = (areaBounds.top - currentBounds.top); 
     } 
     if (currentBounds.bottom < areaBounds.bottom) { 
      // stop from scrolling too far below 
      diff.y = (areaBounds.bottom - currentBounds.bottom); 
     } 
    } else { 
     // negate any scrolling 
     diff.y = (areaBounds.top - currentBounds.top); 
    } 

    // translate 
    matrix.postTranslate(diff.x, diff.y); 
} 

:ここでコメントして作業rebound方法です。コンテンツが小さすぎる場合はスクロールを完全に無効にし、コンテンツを左上に表示させます。

答えて

0

私は自分のピンチをズームするためにこのような何かを実装しました。あなたのy軸が中心から外れているという問題は、画面全体のサイズではなくビューになっている可能性があります。または、スケールに合わせて座標を変更していない可能性があります。

私の実装では、スケール係数が計算され、次の要素が適用されます。 canvas.scale(pinchZoomScale、pinchZoomScale); 次に、画面の物理サイズをピクセル単位で計算し、それをメートルに変換し、最終的にスケーリング係数を適用して、描画されたすべてのオブジェクトが正しくオフセットされるようにします。

この実装では、画面の中央に何があるべきかを常に知り、ズームレベルに関わらず常にロックします。

+0

あなたはそうです!ビューは画面全体のサイズではありません。キャンバスの視点ではなく、画面に対するマトリックスの変換ですか?私はそれがキャンバスに相対的でスクリーンには相対的に最も理にかなっていると仮定していました... – Ribose

+0

マトリックスはキャンバスに関連していますが、位置0,0でアンカーされるので、それが2.5,2.5になるようにしてください。 場合によっては、表示サイズが混乱し、操作している実際のビューではなく、ガイドとして画面サイズを使用しようとする場合があります。キャンバス上のオブジェクトの位置を調整してキャンバスをスケーリングするのは、多くの試行錯誤がありました。 – ScouseChris

+1

ありがとう!多くの試行錯誤の末、私はそれが最終的に働くようになった。 – Ribose

関連する問題