14

I found out that setPivotX (also setPivotY) works strange in Android. If you set pivot when view's scale is set to 1.00f nothing happens (just pivot changes). But if the scale isn't equal 1.0f (e.g. setScaleX(0.9f)) and you set the pivot the view moves relatively(?) to the new pivot. Isn't it strange? I know that horizontal and vertical positions (translations) are NOT related to the pivot value, but why the view moves with scale factor other than 1.0f?

Please check this out with and without the scaling part.

public class ScaleView extends View {

private final ScaleGestureDetector mScaleGestureDetector;

public ScaleView(Context context, AttributeSet attrs) {
    super(context, attrs);


    //setScaleX(0.9f);
    //setScaleY(0.9f);

    mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            // does nothing intentionally
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            setPivotX(detector.getFocusX());
            setPivotY(detector.getFocusY());
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            return false;
        }
    });
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    mScaleGestureDetector.onTouchEvent(event);
    return super.onTouchEvent(event);
}
}

How do I set the same position of the view, which was before the pivot changed?

tomrozb
  • 25,773
  • 31
  • 101
  • 122

2 Answers2

10

I solved the problem. Here is the working code:

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
    float newX = detector.getFocusX();
    float newY = detector.getFocusY();
    setTranslationX(getTranslationX() + (getPivotX() - newX) * (1 - getScaleX()));
    setTranslationY(getTranslationY() + (getPivotY() - newY) * (1 - getScaleY()));
    setPivotX(newX);
    setPivotY(newY);
    return true;
}

The main problem is to understand how pivot works on scaled views, then there shouldn't be any problems with strange pivot behaviour.

tomrozb
  • 25,773
  • 31
  • 101
  • 122
  • 5
    This seems like a bug in Android to me. Scaling about a pivot point is done by translating to the pivot, performing the scale, and translating back. The `setScale` operations should be doing this, and the View shouldn't be jumping around. Scaling is an operation that shouldn't be visibly translating Views. – Christopher Perry Nov 05 '13 at 08:44
  • Hi, I've stumbled upon the same problem and was able to fix it with your answer. However, I still wonder, how does pivot work on scaled views. Could you provide some guidelines, or maybe a link? Thanks in advance. – aleien Feb 08 '16 at 17:06
  • @aleien sorry I cannot help you, I don't think there is any guideline about scaling views using pivot points. – tomrozb Feb 09 '16 at 09:29
  • @tomrozb that's a bummer. But still, how have you came up with this translation functions? I can see, why there is `getPivotX() - newX`, but not why `1 - getScaleX()` Also it seems that rotating scaled views is painful too. At least when you need to go from top-left pivot to center and back to top-left again (: I don't really understand why and where does center of view go when you switch pivot from (0, 0) to (width/2, width/2) if it's scaled or rotated and it bothers me a lot. – aleien Feb 09 '16 at 13:15
  • @aleien trial and error - that's the answer. If I remember correctly I've spent 2 hours trying to figure it out back in 2013. – tomrozb Feb 10 '16 at 22:39
  • @tomrozb I see (: Still, thanks for the answer and your time! – aleien Feb 11 '16 at 06:07
3

@aleien, I can answer your question. When setting a new pivot, view's properties will be recalculated. Android rescale the original view with new pivot but the same scale value which leads to new scaled view has an offset between the old scaled view. The offset is just (getPivotX() - newX) * (1 - getScaleX()), in order to counteract this offset, we can use setTranslationX(). Hope my answer can help you!

Mario Nikolaus
  • 2,346
  • 1
  • 21
  • 28
Ryan
  • 221
  • 2
  • 6