5

I have a very simple test app with a custom component MyView.java - which displays a scrollable and scalable image (representing a board in a Scrabble-like word game):

Nexus screenshot

1) In my scale listener I adjust the scale factor:

public boolean onScale(ScaleGestureDetector detector) {
    mScale *= detector.getScaleFactor();

    // XXX how to adjust mOffsetX and mOffsetY ? XXX

    constrainZoom();
    constrainOffsets();
    invalidate();
    return true;
}

2) And then in the drawing method of my custom component I apply the translation and scale factor:

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

    canvas.translate(mOffsetX, mOffsetY);
    canvas.scale(mScale, mScale);
    gameBoard.setBounds(
        0, 
        0, 
        gameBoard.getIntrinsicWidth(),
        gameBoard.getIntrinsicHeight()
    );
    gameBoard.draw(canvas);  
}

Unfortunately, there seems to be a small bug when scaling with pinch gesture -

I can see, that the scale and boundaries are of correct size after zooming, but the offset of the image is not.

The problem get worse, when the focus point of the pinch gesture is far from 0, 0 point of the screen.

It is a bit difficult to describe the problem in words, but when you check out my test project from GitHub you will see it immediately (and you can always double tap to reset the offset and the scale).

This is probably a common problem with a standard way to solve it, but I haven't been able to find it yet.

The board image is CC BY-SA by Denelson83 and the code is based on the How-to support the fling gesture with a bounce effect blog.

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
  • 1
    instead of canvas.translate/scale/rotate use a Matrix, its a way easier to use one Matrix object which can also be used for mapping between two different systems (Matrix.mapPoints) – pskink Oct 28 '14 at 13:42
  • @pskink why is it better than using the native matrix in the Canvas? https://github.com/android/platform_frameworks_base/blob/3bdbf644d61f46b531838558fabbd5b990fc4913/graphics/java/android/graphics/Canvas.java – Alexander Farber Oct 28 '14 at 16:30
  • 1
    see for example my answer here: http://stackoverflow.com/questions/21633545/android-imageview-scaling-and-translating-issue, on how images can be scaled, rotated and translated, all in one controlled by one Matrix – pskink Oct 28 '14 at 16:51
  • Thank you, but your source code seems to be too advanced for my little problem. I have a feeling, that I just have to adjust the `mOffsetX`and `mOffsetY` in my `onScale` method (based on `scale`? and maybe on the focus point of the gesture?) - and that will be sufficient. – Alexander Farber Oct 30 '14 at 10:22
  • 1
    no no no, it's not too advanced, for example you can get the final bounds of your gameBoard Drawable by calling Matrix.mapRect – pskink Oct 30 '14 at 10:28

1 Answers1

1

You are moving the canvas twice: first with translate, then with scale(sx,sy,px,py);.

You could combine your focuses to your offsets in your onScale and then use scale(sx,sy).

ovikoomikko
  • 577
  • 6
  • 11
  • +1 thanks for this valuable comment, but I am not sure how to combine `offsetX` and `focusX` correctly? – Alexander Farber Oct 28 '14 at 16:33
  • 1
    Probably easiest to have scale, offset and focus (currentGestureFocus) as separate variables. Then track the focus for one gesture duration (from `onScaleBegin` to `onScaleEnd`). At `onScaleEnd` add the focus to offset. During the gesture you should add offset and focus at onDraw. Bear in mind the focus needs to be proportional to the current gesture and center of the screen. Matrix would be easier as it handles chaining better, but enforcing the boundaries is harder. Hope this makes sense. – ovikoomikko Oct 29 '14 at 12:51