I am trying to create a zoomable container and I am targeting API 14+
In my onScale (i am using the ScaleGestureDetector to detect pinch-zoom) I am doing something like this:
public boolean onScale (ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
setScaleX(getScaleX() * scaleFactor);
setScaleY(getScaleY() * scaleFactor);
return true;
};
It works but the zoom is not smooth. In fact it noticeably flickers.
I also tried it with hardware layer thinking that the scaling would happen on the GPU once the texture was uploaded and thus would be super fast. But it made no difference - the zoom is not smooth and flickers weirdly sometimes.
What am I doing wrong?
Does the flicker look like the view flipping back and forth between zoomed and not-zoomed? That's caused by having the ScaleGestureDetector
handling motion events from the same view that you're scaling. When you setScaleX()
that changes the coordinates of the touch, which triggers a new touch event interpreted as reversing the zoom you just applied.
Put the content of your zoomable view inside a single child FrameLayout
and set the scale on that view instead.
I've posted a working pinch-zoom layout here: https://gist.github.com/anorth/9845602
According with your question, the methods setScaleX
and setScaleY
flicker because they perform inside the "drawing to a View" context (setScaleX
and setScaleY
belong to View
). Documentation says:
Option "a," drawing to a View, is your best choice when you want to draw simple graphics that do not need to change dynamically and are not part of a performance-intensive game. For example, you should draw your graphics into a View when you want to display a static graphic or predefined animation, within an otherwise static application.
On the other hand, if you follow the @g00dy answer, you will be performing inside the "drawing to a Canvas" context (scale()
belongs to Canvas
). Documentation says:
Option "b," drawing to a Canvas, is better when your application needs to regularly re-draw itself. Applications such as video games should be drawing to the Canvas on its own.
A pinch gesture is intensive, so it needs to be performed in Option b... so you should use the @g00dy solution or any approach like that.
Here is the documentation cited.
I hope it helps you.
I faced the same problem. The task was:
- create canvas (in subclass of View or SurfaceView, doesn't matter)
- draw on it some picture and graphic primitives (with methods drawBitmap(), drawLine()...)
- make canvas scrollable and scallable
I suppose, no need to show all the class code here.
The solution of the problem with flickering during scalling gesture was very simple.
Just put the scaling procedure into the onScaleEnd() method.
private class MyScaleGestureListener implements OnScaleGestureListener
{
public boolean onScale(ScaleGestureDetector detector)
{
scaleFactor *= detector.getScaleFactor(); // class variable of type float
if (scaleFactor > 5) scaleFactor = 5; // some limitations
if (scaleFactor < 1) scaleFactor = 1;
return true;
}
public boolean onScaleBegin(ScaleGestureDetector detector)
{ return true;}
public void onScaleEnd(ScaleGestureDetector detector)
{
setScaleX(scaleFactor); setScaleY(scaleFactor);
invalidate(); // it seems to me - no effect
}
}
Tested on real device Samsung GALAXY S III mini.