Есть ли способ прослушать конец анимации в AnimatedVectorDrawables

Я создал AnimatedVectorDrawable, он работает очень хорошо, теперь я ищу способ изменить анимацию или скрыть представление после его завершения. Я надеялся, что есть слушатель, но это не похоже на то, что есть. Может кто-нибудь помочь?

ИЗМЕНИТЬ

Итак, я нашел обходной путь, но не очень элегантный способ. То, что я сделал, это создать поток и опрос, если анимация запущена.

new Runnable() {
    public void run() {
        while(mLoop) {
            if(mAnimatedVectorDrawable.isRunning()) {
                Thread.sleep(mPollingInterval);
            } else {
                mLoop = false;
                // TODO what ever
            }
        }
    }
};

Если кто-то найдет лучшее решение, поделитесь им.

Ответы

Ответ 1

Мой первый инстинкт заключался в том, чтобы взять исходный код, добавить некоторые обратные вызовы и создать из него пользовательский чертеж. Конечно, это означало бы отсутствие поддержки xml.

Оказывается, что AnimatedVectorDrawable использует VectorDrawable's private method (s). Таким образом, этот подход не будет работать.

Мы могли бы создать простой класс-оболочку вокруг AnimatedVectorDrawable и добавить обратные вызовы:

public class AVDWrapper {

    private Handler mHandler;
    private Animatable mDrawable;
    private Callback mCallback;
    private Runnable mAnimationDoneRunnable = new Runnable() {

        @Override
        public void run() {
            if (mCallback != null)
                mCallback.onAnimationDone();
        }
    };

    public interface Callback {
        public void onAnimationDone();
        public void onAnimationStopped();
    }

    public AVDWrapper(Animatable drawable, 
                            Handler handler, Callback callback) {
        mDrawable = drawable;
        mHandler = handler;
        mCallback = callback;
    }

    // Duration of the animation
    public void start(long duration) {
        mDrawable.start();
        mHandler.postDelayed(mAnimationDoneRunnable, duration);
    }

    public void stop() {
        mDrawable.stop();
        mHandler.removeCallbacks(mAnimationDoneRunnable);

        if (mCallback != null)
            mCallback.onAnimationStopped();
    }
}

Ваш код будет выглядеть так:

final Drawable drawable = circle.getDrawable();
final Animatable animatable = (Animatable) drawable;

AVDWrapper.Callback callback = new AVDWrapper.Callback() {
        @Override
        public void onAnimationDone() {
            tick.setAlpha(1f);
        }

        @Override
        public void onAnimationStopped() {
          // Okay
        }
    };

AVDWrapper avdw = new AVDWrapper(animatable, mHandler, callback);
//animatable.start();
avdw.start(2000L);

tick.setAlpha(0f);
//tick.animate().alpha(1f).setStartDelay(2000).setDuration(1).start();

// One wrapper is sufficient if the duration is same
final Drawable drawable2 = tick.getDrawable();
final Animatable animatable2 = (Animatable) drawable2;
animatable2.start();

Но это именно то, что вы делаете с setStartDelay. Поэтому я не знаю, насколько это полезно.

Изменить: все это также можно реализовать в расширенном AnimatedVectorDrawable. Но вы полностью потеряете поддержку xml.

Ответ 2

Не пробовал это, но Android 6.0 имеет registerAnimationCallback метод из интерфейса Animatable2 для этого

Изменить: да, это работает в эмуляторе Android 6.0:

mAnimatedVectorDrawable.registerAnimationCallback (new Animatable2.AnimationCallback(){
    public void onAnimationEnd(Drawable drawable){
        //Do something
    }
}

Edit2: похоже, что они не добавили поддержку этого в AnimatedVectorDrawableCompat из поддержки lib 23.2+: (

Edit3: похоже, что это добавлено в поддержку lib 25.3.0

Спасибо Карсон!

Ответ 3

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

int longestAnimationTime = 500; //miliseconds, defined in XML possibly?
drawable.start();
drawableView.postDelayed(new Runnable() {

        @Override
        public void run() {
            drawableView.setVisibility(View.GONE);
        }
}, longestAnimationTime);

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

Ответ 4

Я нашел ту же проблему. Я пытаюсь оживить кружок, нарисованный, и галочку внутри.

Лучшее, что я могу сделать, это играть с продолжительностью. Что-то вроде этого:

final Drawable drawable = circle.getDrawable();
final Animatable animatable = (Animatable) drawable;
animatable.start();

tick.setAlpha(0f);
tick.animate().alpha(1f).setStartDelay(2000).setDuration(1).start();
final Drawable drawable2 = tick.getDrawable();
final Animatable animatable2 = (Animatable) drawable2;
animatable2.start();

Было бы здорово иметь слушателя