Как установить произвольную матрицу преобразования в представление?

Начиная с Android 3.0, у нас есть возможность установить базовые 3D-преобразования в Views с помощью setRotationX и setRotationY, например.

В конкретном случае, я должен был бы достичь сложного преобразования, который вращается вокруг удаленной точки поворота, то масштабирования на другой точке поворота. Это невозможно с помощью setScaleX/Y и setRotationX/Y, поскольку они имеют одну и ту же точку поворота.

Существует ли какой-либо простой способ предоставить желаемую матрицу для представления для отображения?

В настоящее время я нашел два возможных решения:

  • Настройте матрицу вращения, затем используйте ее для сопоставления точки {0,0}; примените результирующую точку к представлению View, затем используйте View setScaleX/Y, чтобы установить масштабирование

  • Взломайте View onDraw и примените преобразование на холсте. Добавьте логику и к событиям переадресации карты.

Оба не очень удобны, так как я создаю AdapterView, который отображает его элементы с помощью настраиваемого эффекта; например, может быть возможно переключить эффект, чтобы элементы были помещены в круг или как "поток обложек".

Ответы

Ответ 1

Я тоже обманываю свой мозг. Это лучшее, что я придумал (супер хаки):

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

Ответ 2

Анимационное решение выше работает, но оно приводит к бесконечным вызовам applyTransformation, что оставляет желать лучшего с точки зрения эффективности. Кроме того, если вы убиваете анимацию, применяемая матрица не является постоянной для представления, поэтому вы вынуждены позволять ей работать вечно. Здесь альтернативный менее хакерский подход, который вызывает только один вызов setMatrix каждый раз, когда invalidate() на вид вызывается:

В конструкторе представлений родителей:

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() для дочернего элемента в любое время, когда вы хотите обновить его.

Ответ 3

xtravar ответ велик, и реализация MyAnimation здесь

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