So, I am expanding a custom SurfaceView and trying to make it available for scaling and scrolling.
How do I scroll:
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
How to zoom in:
@Override public boolean onScale(ScaleGestureDetector detector) { if (detector.isInProgress()) {
(for reference, I use this code for onDraw :)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); canvas.setMatrix(matrix);
Both of these methods are currently functioning well. Scaling stops at least (between 0f and 1f) and the maximum (currently always 1f) scale value is correct.
Used global fields:
drawW , drawH = float , the size in pixels of the canvas data in scale = 1.parentW , parentH = float , the size in pixels of the visible view.matrix = android.graphics.Matrix .
The problem is that the " rebound() " method (maybe needs a better name, heh), which I am trying to implement, which will automatically force the content to remain in view.
I tried various methods to try to figure out where the borders should be (inside the rectangle (0, 0, parentW, parentH)) and translate the matrix "backwards" when it goes too far.
Here is what I now have that definitely does not work, instead pushing it further to the right. I feel that the problem is my math, not my idea. Can someone please come up with simpler or cleaner code that takes the matrix to the edge if it is too far and / or fixes the problems with my attempt at this implementation? . If the checks used so that they appear in the upper left
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); }
The previous version that I wrote before the matrix was a field (I just used canvas.scale() and then canvas.translate() in onDraw ), which worked:
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; } }
I am using a new way so that I can correctly center-scale detector.getFocusX() , detector.getFocusY() .
UPDATE I changed the method to what it is now. It only works a few, still limiting the y-direction off-center and incorrectly after changing zoom levels. I also did this "preScale" and "postTranslate" so that (as I understand it) it should always apply scale and then translate, and not mix them.
FINAL UPDATE : it now works. Here's the working rebound method with comments:
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); }
This negates any scrolling that I don't want, moving it back to the borders. This completely negates scrolling if the content is too small, forcing the content to be in the upper left.