私はカスタム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)のスケール値で正しく停止します。使用
グローバルフィールド:
drawW
、drawH
=float
、スケールでキャンバスデータのピクセルサイズ= 1parentW
、=float
parentH
、可視ビューのピクセルサイズ。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
方法です。コンテンツが小さすぎる場合はスクロールを完全に無効にし、コンテンツを左上に表示させます。
あなたはそうです!ビューは画面全体のサイズではありません。キャンバスの視点ではなく、画面に対するマトリックスの変換ですか?私はそれがキャンバスに相対的でスクリーンには相対的に最も理にかなっていると仮定していました... – Ribose
マトリックスはキャンバスに関連していますが、位置0,0でアンカーされるので、それが2.5,2.5になるようにしてください。 場合によっては、表示サイズが混乱し、操作している実際のビューではなく、ガイドとして画面サイズを使用しようとする場合があります。キャンバス上のオブジェクトの位置を調整してキャンバスをスケーリングするのは、多くの試行錯誤がありました。 – ScouseChris
ありがとう!多くの試行錯誤の末、私はそれが最終的に働くようになった。 – Ribose