如何将任意变换矩阵应用于视图?

19
自 Android 3.0 版本起,我们可以使用 setRotationX 和 setRotationY 在视图上设置基本的 3D 变换。
在某种情况下,我需要实现一个复杂的变换,该变换围绕着远离的中心点旋转,然后在不同的中心点上进行缩放。仅使用 setScaleX/Y 和 setRotationX/Y 是不可能实现的,因为它们共享相同的中心点。
是否存在一种简单的方法为视图提供所需的矩阵以进行显示?
目前,我找到了两个可行的解决方法:
1. 设置一个旋转矩阵,然后使用它来映射 {0, 0} 点;将得到的点应用于视图的平移,然后使用 View 的 setScaleX/Y 来设置缩放。 2. 在 View 的 onDraw 中实现变换,并将逻辑添加到反向映射触摸事件中。
由于我正在构建一个 AdapterView,它使用可配置的效果显示其项目,例如,可以将效果切换为将项目排列成圆形或类似于“封面流”,因此这两种方法都不是非常方便的。
3个回答

11

我也在努力想这个问题。这是我能想到的最好的解决方案(非常不专业):

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

8
上述的动画解决方案可行,但会导致对applyTransformation的无限调用,从效率角度来看还有些欠缺。此外,如果终止动画,则应用的矩阵不会永久保留在视图中,因此必须让它一直运行。这里提供一个更好的替代方法,每次调用视图上的invalidate()时仅产生一次对setMatrix的调用:
在父视图的构造函数中:
setStaticTransformationsEnabled(true);

然后在正文中:
    @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;
}

只需在任何时候调用子级上的invalidate()即可更新其矩阵。


这对我有用,但有两个注意事项。每次更新转换矩阵都需要迭代子视图以调用每个子视图的invalidate方法 - parent.invalidate() 不起作用。此外,我必须在底层Activity上禁用硬件加速 - 将父view的层类型设置为Software(在代码/构造函数或XML中)是不够的。然而,在视图层次结构的其他地方成功禁用硬件加速是可能的... - MandisaW
修正之前的评论 - 这是一个二选一的情况。如果硬件加速被禁用,那么parent.invalidate()就可以了。如果启用了硬件加速,则我发现在更新变换后需要调用child.invalidate()。似乎与绘图操作顺序有关,如此处所述:https://developer.android.com/guide/topics/graphics/hardware-accel.html#model - MandisaW

5

xtravar的回答很棒,这里是我的动画实现

    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);
    }
}

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