【发布时间】:2011-10-10 01:04:26
【问题描述】:
所以我正在扩展一个自定义 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,画布数据在比例 = 1 时的大小(以像素为单位)。 -
parentW,parentH=float,可见视图的大小(以像素为单位)。 -
matrix=android.graphics.Matrix。
问题是我正在尝试实现的“rebound()”(也许需要一个更好的名称,呵呵)方法会自动强制内容保持在视图中。
我尝试了各种方法来尝试计算边界应该在哪里(在矩形 (0, 0, parentW, parentH) 内)并在矩阵过远时将矩阵“向后”平移。
这是我目前所拥有的,这绝对是行不通的,而是把它往右边推了很多。我觉得问题是我的数学,而不是我的想法。 有人可以想出更简单或更简洁的代码,如果它太远,可以将矩阵转换为边缘和/或解决我在此实现中尝试的问题吗?用于使其出现的 if 检查左上角
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);
}
我在矩阵之前编写的先前版本是一个有效的字段(我只是在onDraw 中使用了canvas.scale() 然后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”,这样(据我所知)它应该总是先应用比例然后再翻译而不是混合它们。
最终更新:现在可以使用了。这是一个使用 cmets 的工作 rebound 方法:
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);
}
它通过将其转换回边界来消除我不想要的任何滚动。如果内容太小,它会完全取消滚动,强制内容位于左上角。
【问题讨论】:
标签: java android matrix android-canvas