Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the proper bounds of a view after transformation Matrix

Tags:

android

I have a custom view where I use transformations. So far so good, function like setRotationY(), setScaleX(), setTranslationY() or even getMatrix() work as expected, I’m able manipulate my view and it displays fine. Where I hit the wall is that a number of function behave strangely after that. For example function like getHitRect() return totally weird values! This is not helping my touch events.

I tried to overload the function but it is still far from working especially when using rotation or scaling (Translation working fine through). I think this as something to do with the fact that the matrix is expressed in child coordinate, so how can I get it in parents coordinate?

@Override
    public void getHitRect(Rect outRect){

        RectF rect = new RectF(); 
        rect.top = (float) this.getTop(); 
        rect.bottom = (float) this.getBottom(); 
        rect.left = (float) this.getLeft(); 
        rect.right = (float) this.getRight();      

    this.getMatrix().mapRect(rect);
        rect.round(outRect);
    }

Can I get some straighter value directly from some function? Like the new Height, Width, top or bottom.

like image 873
VeV Avatar asked Nov 11 '11 15:11

VeV


1 Answers

When overriding the “getChildStaticTransformation” method of the ViewGroup or even using transformation function like setRotationY(), setScaleX(), setTranslationY(), getMatrix() (available from API 11) you are only affecting the rendering Matrix. As a result your custom Child View will return Bounds “Rects” far from where your child is getting draw. This is not an issue most of the time but when you start to be willing to click on it.. trouble are starting... Here is how I workaround the issue. I’m sure there might be a better ways but as I haven’t found many things on the subject here it is.

In the ViewGroup Overload:

public interface Itransformable {
    public void setTransformationMatrix(Matrix trsMatrix);
}

@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
    if (child instanceof Itransformable){   
        t.clear();
        t.setTransformationType(Transformation.TYPE_MATRIX);
        ...
        // Do whatever transformation you want here
        ...
        ((Itransformable)child).setTransformationMatrix(t.getMatrix());
        return true;
    } else {
        return false;
    }
}

Here is the Child Custom View: Note that I'm not storing directly the Transformation Matrix in the custom view but instead the transformed Rect. If you want to go for storing the matrix (i.e. for later transformation like point...) you might need to clone it as the matrix will get altered in some strange way like it is recycle or something.

public class MyCustomView extends View implements MyViewGroup.Itransformable{

private Rect mViewRect = null;

public void setTransformationMatrix(Matrix trsMatrix){
    if (trsMatrix!=null){
        RectF rect = new RectF();
        rect.top = 0;
        rect.bottom = (float) this.getHeight(); 
        rect.left = 0; 
        rect.right = (float) this.getWidth();  

        trsMatrix.mapRect(rect);
        rect.offset((float) this.getLeft(), (float) this.getTop());

        if (mViewRect == null) mViewRect = new Rect();
        rect.round(mViewRect);
    }
}

public Rect getTransformatedRect() {
    if (mViewRect!=null){
        // OutOfScreen WorkArround - As the view is not Displayed, mViewRect doesn't get updated.
        if(getRight() < 0 || getLeft() > mParentWidth){
            return new Rect(getLeft(),getTop(),getRight(),getBottom());
        } else {
            return mViewRect;
        }
    } else {
        return new Rect(getLeft(),getTop(),getRight(),getBottom());
    }
}

@Override
public void getHitRect(Rect outRect){

    if (mViewRect == null){
        super.getHitRect(outRect);
    } else {
        outRect.set(getTransformatedRect());
    }
}
like image 180
VeV Avatar answered Oct 21 '22 00:10

VeV