Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set an arbitrary transform matrix to a View?

Since Android 3.0, we have the possibility to set basic 3D transformations to Views using setRotationX and setRotationY, for instance.

In a specific case, I would need to achieve a complex transformation that is rotating around a distant pivot point then scaling on a different pivot point. This is not possible simply by using setScaleX/Y and setRotationX/Y since they share the same pivot point.

Does any simple way exist to provide the desired matrix to the view for display?

For now, I have found two possible workarounds:

  • Set up a rotation matrix then use it to map a {0,0} point; apply the resulting point to the View's translation then use the View's setScaleX/Y to set the scaling

  • Hack the View's onDraw and apply the transformation on the canvas. Add the logic to reverse map touch events, too.

Both are not really convenient since I am building an AdapterView which displays its items using a configurable effect; for example, it can be possible to switch the effect to have the items arrranged in a circle or like a "cover flow".

like image 348
DavGin Avatar asked Aug 02 '13 15:08

DavGin


People also ask

What is a 4x4 rotation matrix?

The 4 by 4 transformation matrix uses homogeneous coordinates, which allow to distinguish between points and vectors. Vectors have a direction and magnitude whereas points are positions specified by 3 coordinates with respect to the origin and three base vectors i, j and k that are stored in the first three columns.

How does transformation matrix work?

Transformation Matrix is a matrix that transforms one vector into another vector by the process of matrix multiplication. The transformation matrix alters the cartesian system and maps the coordinates of the vector to the new coordinates.


3 Answers

I, too, am wracking my brain over this. This is the best I've come up with (super hacky):

 MyAnimation animation = new MyAnimation(matrix);
 animation.setDuration(0);
 animation.setFillAfter(true);
 view.setAnimation(animation);
like image 55
xtravar Avatar answered Oct 03 '22 14:10

xtravar


The animation solution above works but it results in infinite calls to applyTransformation which leaves a little to be desired from an efficiency standpoint. In addition if you kill the animation the matrix applied isn't permanent to the view so you're forced into letting it run forever. Here's an alternative less hacky approach that results in only a single call to setMatrix each time invalidate() on the view is called:

In the parents view constructor:

setStaticTransformationsEnabled(true);

Then in the body:

    @Override
    protected boolean getChildStaticTransformation(View child, Transformation t) {

    // apply transform to child view - child triggers this call by call to `invalidate()`
    t.getMatrix().set(someMatrix);
    return true;
}

Simply call invalidate() on the child anytime you want to update it's matrix.

like image 37
Travis Avatar answered Oct 03 '22 13:10

Travis


xtravar's answer is great, and MyAnimation implementation is here

    private class MyAnimation extends Animation {
    private Matrix matrix;

    public MyAnimation(Matrix matrix) {
        this.matrix = matrix;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        t.getMatrix().set(matrix);
    }
}
like image 28
mes Avatar answered Oct 03 '22 13:10

mes