Анімація Android не повторюється


85

Я намагаюся зробити просту анімацію, яка повторюватиметься кілька разів (або нескінченно).
Здається, android:repeatCountце не працює!
Ось мій анімаційний ресурс від /res/anim/first_animation.xml:

<?xml version="1.0" encoding="utf-8"?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false"
    android:repeatCount="infinite"
    >
    <scale
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="500"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:toXScale="1.2"
        android:toYScale="1.2"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false" />
    <scale
        android:interpolator="@android:anim/accelerate_interpolator"
        android:startOffset="500"
        android:duration="500"
        android:fromXScale="1.2"
        android:fromYScale="1.2"
        android:toXScale="1.0"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:fillAfter="false" />
</set>

Спочатку він повинен масштабувати зображення від 1,0 до 1,2 за 500 мс.
А потім зменшіть його до 1,0 за 500 мс.
Ось як я його використовую:

Animation firstAnimation = AnimationUtils.loadAnimation(this, R.anim.first_animation);
imgView.startAnimation(firstAnimation);

Він робить один цикл, а потім закінчує.
Він масштабується, потім зменшується і зупиняється.

Як я можу зробити цю роботу за призначенням?


Що таке imgView тут у вашому коді Java?
clifgray

Відповіді:


63

Оновлення: Ще у вересні 2011 року інженер Android здебільшого вирішив цю проблему. Атрибути, проігноровані в XML, тепер працюють, за винятком repeatCountі fillEnabledякі все ще ігноруються (навмисно з якоїсь причини). Це означає, що досі нелегко повторити це, AnimationSetна жаль.

Для отримання детальної інформації дивіться огляд в оновлених документах (пояснює, які атрибути ігноруються, які працюють, а які передаються дочірнім організаціям). А для глибшого розуміння того fillAfter, що fillBefore, і що fillEnabledнасправді робимо, дивіться у блозі інженера (Chet Haase) про це тут .


Оригінальна відповідь

Розширити відповіді Павла та інших: це правда, що <set>тег смішно глючить. Він не може правильно мати справу з repeatCountнизкою інших атрибутів.

Я витратив кілька годин, розгадуючи, з чим це може, а що не можна, і подав тут звіт про помилку / випуск: Випуск 17662

У підсумку (це стосується AnimationSet):

setRepeatCount () / android: repeatCount

Цей атрибут (як і repeatMode) не працює в коді чи XML. Це ускладнює повторення цілого набору анімацій.

setDuration () / android: тривалість

Встановлення цього на AnimationSet у коді WORKS (замінює всі тривалості дочірніх анімацій), але не коли включено до тегу в XML

setFillAfter () / android: fillAfter

Це працює як у коді, так і в XML для тегу. Дивно, що я змусив його також працювати без необхідності встановлювати fillEnabled на true.

setFillBefore () / android: fillBefore

Здається, це не має ефекту / ігнорується як в коді, так і в XML

setFillEnabled () / android: fillEnabled

Здається, це не має ефекту / ігнорується як в коді, так і в XML. Я все ще можу отримати fillAfter для роботи, навіть не включаючи fillEnabled або не встановлюючи fillEnabled як false.

setStartOffset () / android: startOffset

Це працює лише в коді, а не в XML.


48

Я виявив, що тег <set> має реалізацію помилок у класі AnimationSet .
Він не може правильно обробляти функцію repeatCount .
Що ми можемо зробити - це встановити repeatCount безпосередньо в тезі <scale> .

Цей XML-ресурс працює добре:

<?xml version="1.0" encoding="utf-8"?>
<scale
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:duration="200"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.05"
    android:toYScale="1.05"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatMode="reverse"
    android:fillAfter="false"
    android:repeatCount="24"
/>

На жаль, це обмежується лише однією анімацією одночасно.
Ми не можемо визначити послідовність анімацій таким чином ...


Я запускаю 2 анімації в наборі, і вони не створюють мені жодних проблем. будь ласка, скажіть мені про яку проблему ви говорите? яка помилка? в даний час працює 1.6 sdk
AZ_

Оголошення repeatCount у xml працює, але не в коді
onmyway133

39

Ви повинні включити атрибут

android:repeatCount="infinite"

Але у вашій "масштабній" анімації не в "наборі"


1
але чи будуть ці анімації чекати завершення попередньої? спасибі
filthy_wizard

Дякую, це спрацювало! Встановити його програмно не з якоїсь причини.
вишнева хвиля

Дякую! Це спрацювало. Але це безперервно. Чи можна змусити це сказати кожні 5 секунд?
d34th4ck3r

32

Щоб отримати повторювану анімацію, я використав прослуховувач анімації та знову викликав анімацію, коли вона закінчилася. Це робить сітку камери фокусуванням, як анімація за допомогою дужок.

Ось макет анімації xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
    android:fromXScale="1.0"
    android:toXScale=".7"
    android:fromYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toYScale=".7"
    android:duration="1000"/>
<scale 
    android:duration="1000"
    android:fromXScale=".7"
    android:toXScale="1.0"
    android:fromYScale=".7"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toYScale="1.0"
    android:startOffset="1000"/>

</set>

Ось код Java

 public void startAnimation() {

            View brackets = findViewById(R.id.brackets);
            brackets.setVisibility(View.VISIBLE);

            Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
            anim.setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationEnd(Animation arg0) {
                    Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
                    anim.setAnimationListener(this);
                    brackets.startAnimation(anim);

                }

                @Override
                public void onAnimationRepeat(Animation arg0) {
                    // TODO Auto-generated method stub

                }

                @Override
                public void onAnimationStart(Animation arg0) {
                    // TODO Auto-generated method stub

                }

            });


            brackets.startAnimation(anim);
}

2
Я, це повинна бути правильна відповідь. Працює на всіх пристроях та рівнях ОС
Smeet,

це мені теж допомогло, але я видалив ці два рядки з методу End Animation anim = AnimationUtils.loadAnimation (BuzzFinderActivity.this, R.anim.crosshair_focusing); anim.setAnimationListener (це);
аїда

10

Я також стикався з тією ж проблемою .. я включив android: repeatCount = "нескінченний" у файл XMl .. тепер він працює нормально ...

  <translate 
           android:fromXDelta="0"
           android:toXDelta="80"
           android:duration="1000"
           android:repeatCount="infinite"   
           android:repeatMode="reverse" 
           android:pivotX="50%"
           android:pivotY="50%"                             
           android:fillAfter="true"/>


9

Ви можете спробувати цей код. Просто додайте у свій код,

firstAnimation.setRepeatCount(5);

Це повторить анімацію протягом певного часу

firstAnimation.setRepeatCount(Animation.INFINITE);
firstAnimation.setRepeatMode(Animation.INFINITE);

Це повторюватиме анімацію нескінченно довго.


4
repeatModeмає бути або RESTARTабоREVERSE
xinthink

це саме те, що я хочу, з динамічним встановленням на нескінченність.
Варун Чаудхарі

2
setRepeat не працює відповідно до code.google.com/p/android/issues/detail?id=17662
ElliotM

4

Я намагався використати код Даніеля, щоб показати анімацію точну кількість разів, і у мене виникла проблема: анімація відображалася приблизно n / 2 рази, коли очікувалося n разів.

Тож я змінив код Даніеля:

//...
@Override
public void onAnimationEnd(Animation arg0) {
    mCurrentCount++;
    if (mCurrentCount < REPEAT_COUNT) {  
        Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
        anim.setAnimationListener(this);
        brackets.post(new Runnable() {
            @Override
            public void run() {
                brackets.startAnimation(anim);
            }
        }  
    } 
}
//... 

Використовуючи варіант, показаний вище, анімація відображається точно REPEAT_COUNT разів, оскільки метод View.post () дає можливість запускати нову анімацію після завершення всіх дій, пов’язаних із попередньою анімацією.


3

Ви повинні додати лише один рядок у своєму коді xml, який я запропонував нижче.

<scale
    android:duration="500"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.2"
    android:toYScale="1.2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite" // just add this one line 
    android:fillAfter="false"
    />
</set>

3

Я вирішив цю проблему, використовуючи android:repeatMode="reverse"раніше у своєму проекті.

<scale
    android:interpolator="@android:anim/decelerate_interpolator"
    android:duration="500"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="1.2"
    android:toYScale="1.2"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatMode="reverse"
    android:repeatCount="infinite" />

2

З Android SDK версії 4.0.3:

У поданих елементах анімації:

android: repeatCount = "- 1"

робить це нескінченною анімацією.


Дякую! Це чудово працює на 4.2 без будь-яких обхідних шляхів
ruX

2

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

import android.view.View;
import android.view.animation.Animation;

public class AnimationRepeater implements Animation.AnimationListener
{
    private View view;
    private Animation animation;
    private int count;

    public AnimationRepeater(View view, Animation animation)
    {
        this.view = view;
        this.animation = animation;
        this.count = -1;
    }

    public AnimationRepeater(View view, Animation animation, int count)
    {
        this.view = view;
        this.animation = animation;
        this.count = count;
    }

    public void start()
    {
        this.view.startAnimation(this.animation);
        this.animation.setAnimationListener(this);
    }

    @Override
    public void onAnimationStart(Animation animation) { }

    @Override
    public void onAnimationEnd(Animation animation)
    {
        if (this.count == -1)
            this.view.startAnimation(animation);
        else
        {
            if (count - 1 >= 0)
            {
                this.animation.start();
                count --;
            }
        }
    }

    @Override
    public void onAnimationRepeat(Animation animation) { }
}

Для нескінченного циклу вашого зору зробіть наступне:

Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a).start();

Якщо ви хочете повторити анімацію лише N-разів, виконайте наступне:

Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a, int N).start();

N означає кількість повторень.


1

Більшість своїх речей я роблю програмно, і, можливо, я запізнююсь або неефективний у цьому, але це, але я виконав ціль повторення анімації (у мене навіть є 2 чергуються набори анімації). Все, що робить цей код, - просто зникає на одному зображенні, робить паузу, потім зникає, зникає на іншому зображенні, робить паузу, зникає і повертає перше (промивання та повторення). Я вперше визначив свої перегляди зображень:

    final ImageView purple = (ImageView)findViewById(R.id.purp);
    final ImageView yellow = (ImageView)findViewById(R.id.yell);
    purple.setVisibility(View.INVISIBLE);
    yellow.setVisibility(View.INVISIBLE);

Потім я створив два таймери, таймери завдань та обробники, щоб вирішити, коли запускати та зупиняти кожну анімацію:

    Timer p = new Timer();
    TimerTask pu = new TimerTask() {
        public void run() {
                handler1.post(new Runnable() {
                        public void run() 
                        {
                           fadein(purple);
                        }
               });
        }};
        p.schedule(pu, 6000, 12000);

    final Handler handler2 = new Handler();

    Timer y = new Timer();
    TimerTask ye = new TimerTask() {
        public void run() {
                handler2.post(new Runnable() {
                        public void run() 
                        {
                           fadein(yellow);
                        }
               });
        }};

        y.schedule(ye, 0, 12000);

Нарешті, замість того, щоб створювати набори анімації шляхом додавання анімацій, я просто слухачам анімації визначаю, коли починати кожну анімацію:

public void fadein (final ImageView image)
{
    Animation anim = new AlphaAnimation(0, 1);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
            pause(image);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}    
public void pause (final ImageView image)
{
    Animation anim = new AlphaAnimation(1, 1);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
            fadeout(image);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}     
public void fadeout (final ImageView image)
{
    Animation anim = new AlphaAnimation(1,0);

    anim.setDuration(2000);

    image.startAnimation(anim);
    anim.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) 
        {
            image.clearAnimation();
            image.invalidate();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub

        }
    });
}    

Очищення та недійсність попередніх спроб та приведення цієї речі до роботи. Не знаю, потрібні вони чи ні.

Сподіваюся, це комусь допомагає.


Райан


1

я повинен це зробити ... я намагався отримати вигляд, щоб обертатися по колу безперервно.

попередній я використовував rota.setRepeatMode (-1), але це не спрацювало. перейшов на setrepeatcount і це працює. Це стосується желейних зерен 4.2.2

 ObjectAnimator rotation = ObjectAnimator.ofFloat(myview,
                          "rotation", 360).setDuration(2000);
                rotation.setRepeatMode(-1);
          rotation.setRepeatCount(Animation.INFINITE); 
 rotation.start();

0

Я зіткнувся з тією ж проблемою, але не хотів виконувати будь-які терміни в Java через те, що потік інтерфейсу може бути дуже зайнятий іноді. Прапор INFINITE не працює для встановленого тегу. Тож я вирішив проблему невеликим шматочком коду:

mAnimation = (AnimationSet) AnimationUtils.loadAnimation(myContext, R.anim.blink);
mIcon.startAnimation(mAnimation);
mAnimation.setAnimationListener(new AnimationListener() {
    public void onAnimationStart(Animation animation) {}
    public void onAnimationRepeat(Animation animation) {}
    public void onAnimationEnd(Animation animation) {
        mIcon.startAnimation(mAnimation);
    }
});

з таким XML:

<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />

<alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.9"
    android:startOffset="1000"
    android:toAlpha="0.0" />

Де mIcon - це ImageView з мого макета.


0

Я вирішив цю проблему. Це моя версія виправлення:

public class HelloAndroidActivity extends Activity {
private static String TAG = "animTest";
private Animation scaleAnimation;
private int currentCover = 0;
private List<ImageView> imageViews = new ArrayList<ImageView>(3);
private Button btn;
private ImageView img;

/**
 * Called when the activity is first created.
 * @param savedInstanceState If the activity is being re-initialized after 
 * previously being shut down then this Bundle contains the data it most 
 * recently supplied in onSaveInstanceState(Bundle). <b>Note: Otherwise it is null.</b>
 */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i(TAG, "onCreate");
    setContentView(R.layout.test);

    img = (ImageView)findViewById(R.id.testpict);
    imageViews.add(img);
    img = (ImageView)findViewById(R.id.testpictTwo);
    imageViews.add(img);
    img = (ImageView)findViewById(R.id.testpict3);
    imageViews.add(img);

    scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.photo_scale);
    scaleAnimation.setAnimationListener(new CyclicAnimationListener());

    btn = (Button)findViewById(R.id.startBtn);
    btn.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            imageViews.get(0).startAnimation(scaleAnimation);
        }
    });



}

private class CyclicAnimationListener implements AnimationListener{

    @Override
    public void onAnimationEnd(Animation animation) {
        currentCover += 1;
        if(currentCover >= imageViews.size()){
            currentCover = 0;
        }
        img = imageViews.get(currentCover);
        scaleAnimation = AnimationUtils.loadAnimation(HelloAndroidActivity.this, R.anim.photo_scale);
        scaleAnimation.setAnimationListener(new CyclicAnimationListener());
        img.startAnimation(scaleAnimation);
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        Log.d("Animation", "Repeat");
    }

    @Override
    public void onAnimationStart(Animation animation) {

    }

}

}

0

я щойно зіткнувся з цією проблемою під час роботи над зворотно сумісним додатком. так розчаровує! я закінчив кодуванням класу приємного обходу, який можна викликати з onCreate, і будь-який анімаційний ресурс буде розпочато у невизначений цикл.

клас AnimationLooper доступний тут: https://gist.github.com/2018678


0

Дослідивши відповіді з Інтернету, я знайшов рішення, яке мені ідеально підходить. (І так, repeatCount і repeatMode надзвичайно глючні при використанні разом з animationSet).

anim_rotate_fade.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:ordering="together" >

    <objectAnimator
        android:duration="3000"
        android:propertyName="rotation"
        android:repeatCount="1"
        android:valueTo="360"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="3000"
        android:propertyName="alpha"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:valueFrom="0.0"
        android:valueTo="0.3"
        android:valueType="floatType" />

    <objectAnimator
        android:duration="3000"
        android:propertyName="y"
        android:repeatCount="1"
        android:repeatMode="reverse"
        android:valueFrom="380"
        android:valueTo="430"
        android:valueType="floatType" />

</set>

В діяльності: (Вирішіть це, ввівши невелику затримку після закінчення анімації).

ImageView starlightImageView = new ImageView(this);
starlightImageView.setImageResource(R.drawable.starlight);
final AnimatorSet animate = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.anim.anim_rotate_fade);
AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                animate.start();
            }
        }, 1000);
    }
};
animate.setTarget(starlightImageView);
animate.addListener(animatorListener);

Є багато класів, над якими ви хотіли б дослідити, але наразі я використовую objectAnimator, який є дуже гнучким. Я б не рекомендував використовувати Animation або AnimationUtils:

  • Анімація
  • AnimationUtils
  • Аніматор
  • AnimatorInflater
  • AnimatorListener
  • AnimatorListenerAdapter

0

Потрібно прослухати завершення першої анімації, а потім перезапустити анімацію в зворотному дзвінку onStopAnimation, спробувати це посилання


0

Невелике налаштування відповіді @Danufr, щоб заощадити ресурси від повторного завантаження.

    operator = (ImageView) findViewById(R.id.operator_loading);
  final  Animation ani = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.finding_operator);


    ani.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {

            operator.startAnimation(ani);

        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });

    operator.setAnimation(ani);

0

Я вирішив цю проблему за допомогою потоку.

Button btn = (Button) findViewById(R.id.buttonpush);
    final TextView textview = (TextView) findViewById(R.id.hello);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            textview.setText("...................");
            final Animation animationtest = AnimationUtils.loadAnimation(MainActivity.this, android.R.anim.slide_in_left);
            animationtest.setDuration(1000);

            final Handler handler = new Handler();
            Runnable runnable = new Runnable() {
                public void run() {
                    handler.postDelayed(this, 1500);
                    textview.startAnimation(animationtest);
                }
            };
            handler.postDelayed(runnable, 500); // start
            handler.removeCallbacks(runnable); //STOP Timer

        }
    });


0

Жодне з наведених вище рішень не спрацювало у моєму випадку. Рішення Danuofr справді працювало для набору анімації, але коли я проводив модульне тестування, мої тести застрягли в цьому нескінченному циклі. Нарешті, конкретно для мого випадку, мені потрібно було повторити цю анімацію певну кількість разів. Отже, я вручну додав копії своєї анімації в anim_rot.xml каскадно, додаючи значення зміщення . Я знаю, що це погано, і для багатьох це не спрацює, але це було єдиним способом вирішення моєї справи.

anim_rot.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="50%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="53%"
        android:startOffset="2000"
        android:toDegrees="20" />
    <rotate
        android:startOffset="4000"
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="56%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="59%"
        android:startOffset="6000"
        android:toDegrees="20" />
    <rotate
        android:startOffset="8000"
        android:duration="2000"
        android:fromDegrees="20"
        android:pivotX="29%"
        android:pivotY="62%"
        android:toDegrees="-20" />
    <rotate
        android:duration="2000"
        android:fromDegrees="-20"
        android:pivotX="29%"
        android:pivotY="65%"
        android:startOffset="10000"
        android:toDegrees="20" />
</set>

Я зробив це, щоб повторити анімацію 3 рази. Ви можете додати більше копій, щоб повторити це певний раз, додавши значення зміщення.


-1

Спробуйте додати код до циклу циклу або оператора while / for


Ну, єдине рішення, яке я знайшов, - це уникати використання тегу set.
Павло Чернов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.