Android: 对视图应用任意变换

4

我希望将一个任意的矩阵应用到android.views.View上。

我找到的唯一可靠的方法是这个黑科技:

 MyAnimation animation = new MyAnimation(matrix);
 animation.setDuration(0);
 animation.setFillAfter(true);
 view.setAnimation(animation);

有更好的方法吗?我尝试利用getChildStaticTransformation并将其放在父级中,但那样做不起作用(也许是我做错了?)

1个回答

5

最终,我基于AbsoluteLayout创建了自己的布局,给我的LayoutParams添加了一个Matrix,并利用getChildStaticTransformation方法,在旋转时让我的子元素响应正确的边界。这比我预期的要困难得多。

public class UIViewLayout extends ViewGroup {

@Override
protected boolean getChildStaticTransformation(View child, Transformation t) {
    if(child instanceof UIViewLayout) {
        t.getMatrix().reset();
        UIViewLayout.LayoutParams params = (UIViewLayout.LayoutParams)child.getLayoutParams();
        t.setTransformationType(Transformation.TYPE_MATRIX);
        t.getMatrix().set(params.matrix);
    }
    return true;
}

public UIViewLayout(android.content.Context context) {
    super(context);
    this.setClipChildren(false);
    this.setClipToPadding(false);
    this.setChildrenDrawingOrderEnabled(true);
    this.setStaticTransformationsEnabled(true);
}

public UIViewLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public UIViewLayout(Context context, AttributeSet attrs,
                      int defStyle) {
    super(context, attrs, defStyle);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();

    int maxHeight = 0;
    int maxWidth = 0;

    // Find out how big everyone wants to be
    measureChildren(widthMeasureSpec, heightMeasureSpec);

    // Find rightmost and bottom-most child
    for (int i = 0; i < count; i++) {
        View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            int childRight;
            int childBottom;

            UIViewLayout.LayoutParams lp
                    = (UIViewLayout.LayoutParams) child.getLayoutParams();

            childRight = lp.x + child.getMeasuredWidth();
            childBottom = lp.y + child.getMeasuredHeight();

            maxWidth = Math.max(maxWidth, childRight);
            maxHeight = Math.max(maxHeight, childBottom);
        }
    }

    // Account for padding too
    //maxWidth += mPaddingLeft + mPaddingRight;
    //maxHeight += mPaddingTop + mPaddingBottom;

    // Check against minimum height and width
    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
            resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}

@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0);
}

@Override
protected void onLayout(boolean changed, int l, int t,
                        int r, int b) {
    int count = getChildCount();

    for (int i = 0; i < count; i++) {
        View child = getChildAt(i);
        if (child.getVisibility() != GONE) {

            UIViewLayout.LayoutParams lp =
                    (UIViewLayout.LayoutParams) child.getLayoutParams();

            int childLeft = lp.x;
            int childTop = lp.y;
            child.layout(childLeft, childTop,
                    childLeft + child.getMeasuredWidth(),
                    childTop + child.getMeasuredHeight());

        }
    }
}

@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new UIViewLayout.LayoutParams(getContext(), attrs);
}

// Override to allow type-checking of LayoutParams.
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof UIViewLayout.LayoutParams;
}

@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    return new LayoutParams(p);
}

@Override
public boolean shouldDelayChildPressedState() {
    return false;
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    for(int i = 0; i < this.getChildCount(); i++) {
        View child = getChildAt(i);
        if(child instanceof UIViewLayout) {
            UIViewLayout.LayoutParams params = (UIViewLayout.LayoutParams)child.getLayoutParams();
            if(!params.matrix.isIdentity()) {
                MotionEvent ev2 = MotionEvent.obtain(ev);

                ev2.setLocation(ev2.getX() - params.x, ev2.getY() - params.y);

                Matrix m = new Matrix();
                params.matrix.invert(m);
                ev2.transform(m);
                if(child.dispatchTouchEvent(ev2)) {
                    return true;
                }
                ev2.recycle();
            }
        }
    }
    return super.dispatchTouchEvent(ev);
}

public static class LayoutParams extends ViewGroup.LayoutParams {
    public int x;
    public int y;
    public Matrix matrix;

    public LayoutParams(int width, int height, int x, int y) {
        super(width, height);
        this.x = x;
        this.y = y;
        this.matrix = new Matrix();
    }

    public LayoutParams(Context c, AttributeSet attrs) {
        super(c, attrs);
    }

    public LayoutParams(ViewGroup.LayoutParams source) {
        super(source);
    }
}

}

我发现你的布局有几个问题:1. 我已经删除了一些检查,例如if(child instanceof UIViewLayout),因为我的子视图不是UIViewLayoyt(它们应该吗?);2. onMeasure()返回无效值(由于子视图的初始位置,但它们可以移动和缩放...),在某些版本的Android中(例如4.4),如果返回值太小,则会剪切子视图(我没有解决这个第二个问题)。 - atrebbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接