Android AnimationDrawable и знание, когда анимация заканчивается

Я хочу сделать анимацию с несколькими файлами изображений, и для этого AnimationDrawable работает очень хорошо. Тем не менее, мне нужно знать, когда начинается анимация и когда она заканчивается (например, добавить слушателя, как Animation.AnimationListener). После поиска ответов, у меня плохое ощущение, что AnimationDrawable не поддерживает слушателей.

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

После некоторого чтения я придумал это решение. Я все еще удивлен, что нет слушателя как часть объекта AnimationDrawable , но я не хотел передавать обратные вызовы назад и вперед, поэтому вместо этого я создал абстрактный class, который вызывает метод onAnimationFinish() . Я надеюсь, что это помогает кому-то.

Пользовательский class анимации:

 public abstract class CustomAnimationDrawableNew extends AnimationDrawable { /** Handles the animation callback. */ Handler mAnimationHandler; public CustomAnimationDrawableNew(AnimationDrawable aniDrawable) { /* Add each frame to our animation drawable */ for (int i = 0; i < aniDrawable.getNumberOfFrames(); i++) { this.addFrame(aniDrawable.getFrame(i), aniDrawable.getDuration(i)); } } @Override public void start() { super.start(); /* * Call super.start() to call the base class start animation method. * Then add a handler to call onAnimationFinish() when the total * duration for the animation has passed */ mAnimationHandler = new Handler(); mAnimationHandler.post(new Runnable() { @Override public void run() { onAnimationStart(); } }; mAnimationHandler.postDelayed(new Runnable() { @Override public void run() { onAnimationFinish(); } }, getTotalDuration()); } /** * Gets the total duration of all frames. * * @return The total duration. */ public int getTotalDuration() { int iDuration = 0; for (int i = 0; i < this.getNumberOfFrames(); i++) { iDuration += this.getDuration(i); } return iDuration; } /** * Called when the animation finishes. */ public abstract void onAnimationFinish(); /** * Called when the animation starts. */ public abstract void onAnimationStart(); } 

Чтобы использовать этот class:

  ImageView iv = (ImageView) findViewById(R.id.iv_testing_testani); iv.setOnClickListener(new OnClickListener() { public void onClick(final View v) { // Pass our animation drawable to our custom drawable class CustomAnimationDrawableNew cad = new CustomAnimationDrawableNew( (AnimationDrawable) getResources().getDrawable( R.drawable.anim_test)) { @Override void onAnimationStart() { // Animation has started... } @Override void onAnimationFinish() { // Animation has finished... } }; // Set the views drawable to our custom drawable v.setBackgroundDrawable(cad); // Start the animation cad.start(); } }); 

Мне нужно было знать, когда завершится мой однозарядный AnimationDrawable, без необходимости подclassа AnimationDrawable, так как я должен установить список анимации в XML. Я написал этот class и протестировал его на Gingerbread и ICS. Его можно легко расширить, чтобы обеспечить обратный вызов для каждого кадра.

 /** * Provides a callback when a non-looping {@link AnimationDrawable} completes its animation sequence. More precisely, * {@link #onAnimationComplete()} is triggered when {@link View#invalidateDrawable(Drawable)} has been called on the * last frame. * * @author Benedict Lau */ public abstract class AnimationDrawableCallback implements Callback { /** * The last frame of {@link Drawable} in the {@link AnimationDrawable}. */ private Drawable mLastFrame; /** * The client's {@link Callback} implementation. All calls are proxied to this wrapped {@link Callback} * implementation after intercepting the events we need. */ private Callback mWrappedCallback; /** * Flag to ensure that {@link #onAnimationComplete()} is called only once, since * {@link #invalidateDrawable(Drawable)} may be called multiple times. */ private boolean mIsCallbackTriggered = false; /** * * @param animationDrawable * the {@link AnimationDrawable}. * @param callback * the client's {@link Callback} implementation. This is usually the {@link View} the has the * {@link AnimationDrawable} as background. */ public AnimationDrawableCallback(AnimationDrawable animationDrawable, Callback callback) { mLastFrame = animationDrawable.getFrame(animationDrawable.getNumberOfFrames() - 1); mWrappedCallback = callback; } @Override public void invalidateDrawable(Drawable who) { if (mWrappedCallback != null) { mWrappedCallback.invalidateDrawable(who); } if (!mIsCallbackTriggered && mLastFrame != null && mLastFrame.equals(who.getCurrent())) { mIsCallbackTriggered = true; onAnimationComplete(); } } @Override public void scheduleDrawable(Drawable who, Runnable what, long when) { if (mWrappedCallback != null) { mWrappedCallback.scheduleDrawable(who, what, when); } } @Override public void unscheduleDrawable(Drawable who, Runnable what) { if (mWrappedCallback != null) { mWrappedCallback.unscheduleDrawable(who, what); } } // // Public methods. // /** * Callback triggered when {@link View#invalidateDrawable(Drawable)} has been called on the last frame, which marks * the end of a non-looping animation sequence. */ public abstract void onAnimationComplete(); } 

Вот как его использовать.

 AnimationDrawable countdownAnimation = (AnimationDrawable) mStartButton.getBackground(); countdownAnimation.setCallback(new AnimationDrawableCallback(countdownAnimation, mStartButton) { @Override public void onAnimationComplete() { // TODO Do something. } }); countdownAnimation.start(); 

Окончание анимации можно легко отследить, переопределив метод selectDrawable в classе AnimationDrawable. Полный код:

 public class AnimationDrawable2 extends AnimationDrawable { public interface IAnimationFinishListener { void onAnimationFinished(); } private boolean finished = false; private IAnimationFinishListener animationFinishListener; public IAnimationFinishListener getAnimationFinishListener() { return animationFinishListener; } public void setAnimationFinishListener(IAnimationFinishListener animationFinishListener) { this.animationFinishListener = animationFinishListener; } @Override public boolean selectDrawable(int idx) { boolean ret = super.selectDrawable(idx); if ((idx != 0) && (idx == getNumberOfFrames() - 1)) { if (!finished) { finished = true; if (animationFinishListener != null) animationFinishListener.onAnimationFinished(); } } return ret; } } 

Я использовал рекурсивную функцию, которая проверяет, является ли текущий кадр последним кадром каждый раз в течение нескольких миллисекунд.

 private void checkIfAnimationDone(AnimationDrawable anim){ final AnimationDrawable a = anim; int timeBetweenChecks = 300; Handler h = new Handler(); h.postDelayed(new Runnable(){ public void run(){ if (a.getCurrent() != a.getFrame(a.getNumberOfFrames() - 1)){ checkIfAnimationDone(a); } else{ Toast.makeText(getApplicationContext(), "ANIMATION DONE!", Toast.LENGTH_SHORT).show(); } } }, timeBetweenChecks); } 

Таймер – это плохой выбор для этого, потому что вы застрянете в попытке выполнить в не-UI-streamе, например, HowsItStack. Для простых задач вы можете просто использовать обработчик для вызова метода с определенным интервалом. Как это:

 handler.postDelayed(runnable, duration of your animation); //Put this where you start your animation private Handler handler = new Handler(); private Runnable runnable = new Runnable() { public void run() { handler.removeCallbacks(runnable) DoSomethingWhenAnimationEnds(); } }; 

removeCallbacks гарантирует, что это выполняется только один раз.

если вы хотите внедрить свою анимацию в адаптер – следует использовать следующий открытый class CustomAnimationDrawable extends AnimationDrawable {

 /** * Handles the animation callback. */ Handler mAnimationHandler; private OnAnimationFinish onAnimationFinish; public void setAnimationDrawable(AnimationDrawable aniDrawable) { for (int i = 0; i < aniDrawable.getNumberOfFrames(); i++) { this.addFrame(aniDrawable.getFrame(i), aniDrawable.getDuration(i)); } } public void setOnFinishListener(OnAnimationFinish onAnimationFinishListener) { onAnimationFinish = onAnimationFinishListener; } @Override public void stop() { super.stop(); } @Override public void start() { super.start(); mAnimationHandler = new Handler(); mAnimationHandler.postDelayed(new Runnable() { public void run() { if (onAnimationFinish != null) onAnimationFinish.onFinish(); } }, getTotalDuration()); } /** * Gets the total duration of all frames. * * @return The total duration. */ public int getTotalDuration() { int iDuration = 0; for (int i = 0; i < this.getNumberOfFrames(); i++) { iDuration += this.getDuration(i); } return iDuration; } /** * Called when the animation finishes. */ public interface OnAnimationFinish { void onFinish(); } 

}

и реализация в адаптере RecycleView

 @Override public void onBindViewHolder(PlayGridAdapter.ViewHolder holder, int position) { final Button mButton = holder.button; mButton.setBackgroundResource(R.drawable.animation_overturn); final CustomAnimationDrawable mOverturnAnimation = new CustomAnimationDrawable(); mOverturnAnimation.setAnimationDrawable((AnimationDrawable) mContext.getResources().getDrawable(R.drawable.animation_overturn)); mOverturnAnimation.setOnFinishListener(new CustomAnimationDrawable.OnAnimationFinish() { @Override public void onFinish() { // your perform } }); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { mOverturnAnimation.start(); } }); } 

Я думаю, ваш код не работает, потому что вы пытаетесь изменить представление из не-UI-Thread. Попробуйте вызвать runOnUiThread (Runnable) из вашей активности. Я использовал его для постепенного исчезновения меню после анимации для этого меню. Этот код работает для меня:

 Animation ani = AnimationUtils.loadAnimation(YourActivityNameHere.this, R.anim.fadeout_animation); menuView.startAnimation(ani); // Use Timer to set visibility to GONE after the animation finishes. TimerTask timerTask = new TimerTask(){ @Override public void run() { YourActivityNameHere.this.runOnUiThread(new Runnable(){ @Override public void run() { menuView.setVisibility(View.GONE); } });}}; timer.schedule(timerTask, ani.getDuration()); 

У меня была такая же проблема, когда мне пришлось реализовать щелчок на кнопке после остановки анимации. Я проверил текущий кадр и конечный кадр анимации, чтобы узнать, когда анимация остановлена. Обратите внимание, что это не слушатель, а просто способ узнать, что анимация остановлена.

 if (spinAnimation.getCurrent().equals( spinAnimation.getFrame(spinAnimation .getNumberOfFrames() - 1))) { Toast.makeText(MainActivity.this, "finished", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "Not finished", Toast.LENGTH_SHORT).show(); } 

Я не знаю обо всех этих других решениях, но это тот, который ближе всего подходит для простого добавления слушателя в class AnimationDrawable.

 class AnimationDrawableListenable extends AnimationDrawable{ static interface AnimationDrawableListener { void selectIndex(int idx, boolean b); } public AnimationDrawableListener animationDrawableListener; public boolean selectDrawable(int idx) { boolean selectDrawable = super.selectDrawable(idx); animationDrawableListener.selectIndex(idx,selectDrawable); return selectDrawable; } public void setAnimationDrawableListener(AnimationDrawableListener animationDrawableListener) { this.animationDrawableListener = animationDrawableListener; } } 

Я предпочитаю не идти на временное решение, как мне кажется, недостаточно надежным.

Я люблю решение Руслана Янчишина: https://stackoverflow.com/a/12314579/72437

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

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

animation_list.xml

      

AnimationDrawableWithCallback.java

 import android.graphics.drawable.AnimationDrawable; /** * Created by yccheok on 24/1/2016. */ public class AnimationDrawableWithCallback extends AnimationDrawable { public AnimationDrawableWithCallback(AnimationDrawable aniDrawable) { /* Add each frame to our animation drawable */ for (int i = 0; i < aniDrawable.getNumberOfFrames(); i++) { this.addFrame(aniDrawable.getFrame(i), aniDrawable.getDuration(i)); } } public interface IAnimationFinishListener { void onAnimationFinished(); } private boolean finished = false; private IAnimationFinishListener animationFinishListener; public void setAnimationFinishListener(IAnimationFinishListener animationFinishListener) { this.animationFinishListener = animationFinishListener; } @Override public boolean selectDrawable(int idx) { if (idx >= (this.getNumberOfFrames()-1)) { if (!finished) { finished = true; if (animationFinishListener != null) animationFinishListener.onAnimationFinished(); } return false; } boolean ret = super.selectDrawable(idx); return ret; } } 

Так мы можем использовать вышеуказанный class.

  AnimationDrawableWithCallback animationDrawable2 = new AnimationDrawableWithCallback(rowLayoutAnimatorList); animationDrawable2.setAnimationFinishListener(new AnimationDrawableWithCallback.IAnimationFinishListener() { @Override public void onAnimationFinished() { ... } }); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { view.setBackground(animationDrawable2); } else { view.setBackgroundDrawable(animationDrawable2); } // https://stackoverflow.com/questions/14297003/animating-all-items-in-animation-list animationDrawable2.setEnterFadeDuration(this.configMediumAnimTime); animationDrawable2.setExitFadeDuration(this.configMediumAnimTime); animationDrawable2.start(); 

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

В моем коде я избавился от finished флага Ruslan, и я также использовал логическое значение, возвращаемое super.selectDrawable() .

Вот мой код:

 class AnimationDrawableWithCallback extends AnimationDrawable { interface IAnimationFinishListener { void onAnimationChanged(int index, boolean finished); } private IAnimationFinishListener animationFinishListener; public IAnimationFinishListener getAnimationFinishListener() { return animationFinishListener; } void setAnimationFinishListener(IAnimationFinishListener animationFinishListener) { this.animationFinishListener = animationFinishListener; } @Override public boolean selectDrawable(int index) { boolean drawableChanged = super.selectDrawable(index); if (drawableChanged && animationFinishListener != null) { boolean animationFinished = (index == getNumberOfFrames() - 1); animationFinishListener.onAnimationChanged(index, animationFinished); } return drawableChanged; } } 

И вот пример того, как его реализовать …

 public class MyFragment extends Fragment implements AnimationDrawableWithCallback.IAnimationFinishListener { @Override public void onAnimationChanged(int index, boolean finished) { // Do whatever you need here } } 

Если вы хотите узнать только, когда завершился первый цикл анимации, вы можете установить булевский флаг в вашем fragmentе / активности.

Я использовал следующий метод, и он действительно работает.

 Animation anim1 = AnimationUtils.loadAnimation( this, R.anim.hori); Animation anim2 = AnimationUtils.loadAnimation( this, R.anim.hori2); ImageSwitcher isw=new ImageSwitcher(this); isw.setInAnimation(anim1); isw.setOutAnimation(anim2); 

Я надеюсь, что это решит вашу проблему.

  • API Google Maps V2 'не удалось загрузить карту. Не удалось связаться с серверами Google Servers,
  • Как проверить текущие запущенные приложения на Android?
  • Обновление базы данных push-уведомлений Firebase
  • Доступ к файлам с карты genymotion sd
  • Получить метаданные изображения - Android
  • Уведомления Android с кнопками на нем
  • Волейбольная сеть для Android
  • Экран мигает при использовании веб-просмотра со вспышкой
  • Обнаружить сенсорное нажатие против длинного нажатия против движения?
  • Проблемы с рендерингом в Android Studio v 1.1 / 1.2
  • Альтернативы PreferenceFragment с поддержкой android-v4
  • Interesting Posts

    Можно ли использовать учетную запись администратора для повседневного использования, если UAC включен?

    ASP.NET Web API – PUT & DELETE Глаголы не разрешены – IIS 8

    Как составить график данных банковского счета в Excel с таблицей изменений баланса?

    Могут ли на жесткий диск (ноутбук) воздействовать магниты, например, наушники или сотовый телефон?

    Почему std :: strstream не рекомендуется?

    Функции, возвращающие функцию

    Доверенные сертификаты Root регулярно исчезают в Windows 7

    Будет ли отсоединение USB-ключа без безопасного его устранения, вызовет проблемы?

    Как я могу запустить (16-разрядный) .COM исполняемый файл, который был переименован в .COS?

    Получение запроса полезной нагрузки из запроса POST в Java-сервлете

    Как мне управлять зависимостями между проектами в рабочей области Xcode?

    Создание PDF-документов

    Что является самым легким проигрывателем FLV для встраивания видео на веб-сайт?

    Известны ли временные метки временных файлов Windows?

    Sshfs с пересылкой x?

    Давайте будем гением компьютера.