Анимация по массиву точек

Я уверен, что есть простой способ сделать это, но я застрял. Скажем, у меня есть список точек:

Point[] list = {pointA, pointB, pointC, ...}

Я хотел бы оживить ImageView через каждую точку Поэтому я попробовал это:

id = 0;
AnimatorListenerAdapter animEnd = new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        id++;
        if(id != list.length) {
            iv.animate()
                .translationX(list[id].getX())
                .translationY(list[id].getY())
                .setDuration(200)
                .setListener(this);
        }
    }
};

iv.animate()
    .translationX(list[id].getX()).translationY(list[id].getY())
    .setDuration(200).setListener(animEnd);

Это работает, но есть небольшая задержка между каждой анимацией.

Любая идея? Спасибо!

Ответ 1

Вероятно, вы получаете задержки между шагами анимации, потому что вы всегда начинаете новую анимацию при каждом переходе от шага к шагу. Чтобы преодолеть эту ситуацию, у вас есть несколько вариантов.

Keyframes

Здесь вы можете найти технику под названием Keyframe Animation, которая является очень распространенной технологией анимации и, вероятно, именно то, что вы хотите.

Объект Keyframe состоит из пары time/value, которая позволяет вам определить определенное состояние в определенное время анимации. Каждый ключевой кадр может также иметь свой собственный интерполятор для управления поведением анимации в интервале между предыдущим временем ключевого кадра и временем этого ключевого кадра.

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);

В вашем случае вы можете сопоставить точки в list в списке экземпляров Keyframe и...

Point[] list = {pointA, pointB, pointC, ...}
List<Keyframe> kfs = new ArrayList<Keyframe>();
foreach (Point p : points) {
  Keyframe kf = new Keyframe.ofFloat(p.x); // or what ever
  kfs.add(kf);
}

... позже передайте эти ключевые кадры некоторому методу factory для создания некоторого PropertyValuesHolder, например ofFloat, который имеет следующее подпись:

public static PropertyValuesHolder ofFloat (
    Property<?, Float> property, 
    float... values
)

Второй параметр - это список переменных аргументов, который также принимает массивы как входы. Таким образом, должно быть возможно передать ваш kfs методу в качестве второго аргумента. Как-то.

В вашем случае я разработал бы следующие методы, так как вы разрабатываете для dalvik VM, вы не можете использовать java8 лямбда-выражения:

// maps points to X/Y float values
List<Float> toArrayX(Point[] points) { ... }
List<Float> toArrayY(Point[] points) { ... }
// maps float values to Keyframes
List<Keyframe> toKeyframes(List<Float> floats) { ... }
void createAnimation(Point[] points) {
  List<Keyframe> xs = toKeyframes(toArrayX(points));
  PropertyValuesHolder phvX = PropertyValuesHolder
    .ofKeyframe("translationX", xs);

  List<Keyframe> ys = toKeyframes(toArrayY(points));
  PropertyValuesHolder phvY = PropertyValuesHolder
    .ofKeyframe("translationY", ys);

  linkPropertyValuesHolder(phvX);
  linkPropertyValuesHolder(phvY);
}
void linkPropertyValuesHolder(PropertyValuesHolder phv) {
  // setup target
  ObjectAnimator anim = ObjectAnimator
    .ofPropertyValuesHolder(target, phv)
  anim.setDuration(5000ms);
}

Интерполяторы

В качестве альтернативы вы можете указать переходы, заданные точками, через экземпляр Interpolator.

Интерполятор определяет скорость изменения анимации. Это позволяет ускорить основные эффекты анимации (альфа, масштаб, перевод, поворот), замедлить, повторить и т.д.

Интерполятор отображает дробное поплавок между 0.0 и 1.0 другим дробным поплавком между 0.0 и 1.0. Как и следующие три; LinearInterpolator, AccelerateDecelerateInterpolator и BounceInterpolator:

kgzvR.png5wAf0.pngBxfoO.png

Изображения из здесь

Интерполятор пути

С PathInterpolator можно рисовать условные интерполяторы с использованием экземпляров Path. Нарисованный путь будет использоваться для управления анимацией. В вашем случае вы можете создать два пути: один для x и другой для перевода y.

Однако будьте осторожны при построении интерполяторов из путей, потому что

... Путь должен соответствовать функции y = f (x).

Путь не должен иметь пробелов в направлении x и не должен зацикливаться на нем, так что две точки могут иметь одну и ту же координату x. Хорошо, что в вертикальном направлении есть непересекающаяся линия:

Итак, взгляните на следующий фрагмент кода, который берется здесь, создает допустимый путь и может использоваться как вход для конструктора PathInterpolator.

Path path = new Path();
path.lineTo(0.25f, 0.25f);
path.moveTo(0.25f, 0.5f);
path.lineTo(1f, 1f);

Пользовательский интерполятор

Если заданные интерполяторы недостаточно гибки, вы можете просто реализовать интерфейс Interpolator и впоследствии создать новый экземпляр. Интерфейс очень узкий, и он предоставляет только один метод под названием getInterpolation со следующей подписью.

public abstract float getInterpolation (float input)    

Код и XML

Еще один последний вариант - перенести всю конфигурацию анимации в XML вместо того, чтобы выпекать эти данные непосредственно в двоичном дистрибутиве кода. Тем не менее, для каждого из указанных параметров потребуется, по возможности, другая настройка для управления через XML.

Надеюсь, что это поможет, но это всего лишь псевдокод. Я не тестировал код... поэтому никаких гарантий для правильности.