A animação Android não se repete


85

Estou tentando fazer uma animação simples que se repetiria várias vezes (ou infinitamente).
Parece que android:repeatCountnão funciona!
Aqui está meu recurso de animação de /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>

Primeiro, ele deve dimensionar a imagem de 1,0 a 1,2 em 500 ms.
E, em seguida, reduza para 1,0 em 500 ms.
Aqui está como estou usando:

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

Faz um ciclo e depois termina.
Ele aumenta, diminui e então para.

Como posso fazer isso funcionar conforme o esperado?


O que é imgView aqui em seu código java?
clifgray

Respostas:


63

Atualização: em setembro de 2011, um engenheiro do Android corrigiu esse problema na maior parte. Os atributos que foram ignorados em XML agora funcionam, com exceção de repeatCounte fillEnabledque ainda são ignorados (propositalmente por algum motivo). Isso significa que ainda não é fácil repetir um, AnimationSetinfelizmente.

Para obter detalhes, consulte a visão geral nos documentos atualizados (explica quais atributos são ignorados, quais funcionam e quais são passados ​​para os filhos). E para uma compreensão mais profunda do que fillAfter, fillBeforee fillEnabledrealmente faz, consulte a postagem do blog do engenheiro (Chet Haase) sobre isso aqui .


Resposta Original

Para expandir as respostas de Pavel e outros: é verdade que a <set>etiqueta é ridiculamente cheia de erros. Ele não pode lidar corretamente com repeatCountuma série de outros atributos.

Passei algumas horas descobrindo o que ele pode e não pode resolver e enviei um relatório de bug / problema aqui: Issue 17662

Em resumo (isso diz respeito AnimationSets):

setRepeatCount () / android: repeatCount

Este atributo (bem como repeatMode) não funciona em código ou XML. Isso torna difícil repetir todo um conjunto de animações.

setDuration () / android: duração

Definir isso em um AnimationSet no código WORKS (substitui todas as durações das animações filhas), mas não quando incluído na tag em XML

setFillAfter () / android: fillAfter

Isso funciona em código e XML para a tag. Estranhamente, fiz com que ele também funcionasse sem a necessidade de definir fillEnabled como true.

setFillBefore () / android: fillBefore

Parece não ter efeito / ignorado tanto no código quanto no XML

setFillEnabled () / android: fillEnabled

Parece não ter efeito / ignorado no código e no XML. Ainda posso fazer o fillAfter funcionar, mesmo sem incluir fillEnabled ou definir fillEnabled como false.

setStartOffset () / android: startOffset

Isso funciona apenas em código e não em XML.


48

Descobri que a tag <set> tem uma implementação com erros na classe AnimationSet .
Ele não pode lidar corretamente com repeatCount .
O que podemos fazer - é definir repeatCount diretamente na tag <scale> .

Este recurso XML está funcionando bem:

<?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"
/>

Infelizmente, isso é limitado a apenas uma animação por vez.
Não podemos definir uma sequência de animações desta forma ...


Estou executando 2 animações em um conjunto e eles não estão me dando nenhum problema. por favor me diga sobre qual problema você está falando? qual bug? atualmente trabalhando em 1.6 sdk
AZ_

Declarar repeatCount em xml funciona, mas não no código
onmyway133

39

Você deve incluir o atributo

android:repeatCount="infinite"

Mas em sua animação de "escala" não em "conjunto"


1
mas essas animações esperarão que a anterior seja concluída? obrigado
filthy_wizard

Obrigado, isso funcionou! Configurá-lo programaticamente não funcionou por qualquer motivo.
onda cereja

Obrigado! Isso funcionou. Mas é contínuo. É possível fazer isso acontecer, digamos, a cada 5 segundos?
d34th4ck3r

32

Para obter uma animação repetida, usei o ouvinte de animação e chamei a animação novamente quando ela terminou. Isso faz uma retícula da câmera focalizando como uma animação com colchetes.

Aqui está o layout de animação 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>

Aqui está o código 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
Sim, deve ser a resposta correta. Trabalhando em todos os dispositivos e nível de sistema operacional
Smeet

também me ajudou, mas removi essas duas linhas do método End Animation anim = AnimationUtils.loadAnimation (BuzzFinderActivity.this, R.anim.crosshair_focusing); anim.setAnimationListener (this);
aida

10

Eu também estava enfrentando o mesmo problema .. incluí android: repeatCount = "infinite" no arquivo XMl .. agora está funcionando bem ...

  <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

você pode tentar este código. Em seu código, basta adicionar,

firstAnimation.setRepeatCount(5);

Isso irá repetir a animação por um tempo definido

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

Isso irá repetir a animação indefinidamente.


4
repeatModedeve ser RESTARTouREVERSE
xinthink

isso é exatamente o que eu quero, definido para definir dinamicamente como infinito.
Varun Chaudhary

2
setRepeat não funciona de acordo com code.google.com/p/android/issues/detail?id=17662
ElliotM

4

Tentei usar o código de Daniel para mostrar a animação número exato de vezes e tive um problema: a animação foi mostrada aproximadamente n / 2 vezes, quando n vezes o esperado.

Então, eu modifiquei o código de Daniel:

//...
@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);
            }
        }  
    } 
}
//... 

Usando a variante, mostrada acima, a animação é mostrada exatamente REPEAT_COUNT vezes, porque o método View.post () dá a capacidade de iniciar uma nova animação após terminar todas as ações, relacionadas com a animação anterior.


3

você tem que adicionar apenas uma linha em seu código xml que sugeri abaixo.

<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

Resolvi esse problema usando android:repeatMode="reverse"before no meu projeto.

<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

Com o Android SDK versão 4.0.3:

Nos elementos de animação fornecidos:

android: repeatCount = "- 1"

torna uma animação infinita.


Obrigado! Funciona bem no 4.2 sem qualquer solução alternativa
ruX

2

Adicione a seguinte classe ao seu projeto:

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

Para um loop infinito de sua visualização, faça o seguinte:

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

Se você quiser repetir a animação apenas N vezes, faça o seguinte:

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

N representa o número de repetições.


1

Eu faço a maioria das minhas coisas de forma programática e posso estar atrasado ou ineficiente nisso, mas concluí o objetivo do conjunto de animações de repetição (eu até tenho 2 conjuntos de animação alternados). Tudo o que esse código faz é simplesmente desaparecer em uma imagem, pausar, depois desaparecer, aparecer outra imagem, pausar, desaparecer e trazer de volta a primeira (enxágue e repita). Eu primeiro defini minhas visualizações de imagens:

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

Em seguida, criei dois cronômetros, cronômetros de tarefa e manipuladores para lidar com quando iniciar e parar cada animação:

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

Por fim, em vez de criar conjuntos de animação adicionando animações, apenas animarei os ouvintes para determinar quando iniciar cada animação:

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

        }
    });
}    

A animação limpa e invalida onde apenas as tentativas anteriores e fazer isso funcionar direito. Não sei se são obrigatórios ou não.

Espero que isso ajude alguém.


Ryan


1

eu tenho que ir ... eu estava tentando obter uma visão para girar continuamente em um círculo.

Anteriormente eu estava usando a rotação.setRepeatMode (-1), mas isso não funcionou. mudou para setrepeatcount e funciona. Isso está no Jelly Bean 4.2.2

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

0

Eu enfrentei o mesmo problema, mas não queria fazer nenhuma coisa de temporização em Java por causa do ponto que o thread de interface do usuário pode estar muito ocupado às vezes. O sinalizador INFINITE não funciona para a tag definida. Resolvi o problema com um pequeno trecho de código:

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

com o seguinte 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" />

Onde mIcon é um ImageView do meu layout.


0

Eu resolvi esse problema. Esta é minha versão da correção:

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

Acabei de encontrar esse problema enquanto trabalhava em um aplicativo compatível com versões anteriores. tão frustrante! Acabei desenvolvendo uma classe de solução alternativa que pode ser chamada de onCreate e iniciará qualquer recurso de animação em um loop indefinido.

a classe, AnimationLooper, está disponível aqui: https://gist.github.com/2018678


0

Depois de pesquisar as respostas na internet, encontrei uma solução que funciona perfeitamente para mim. (E sim, repeatCount e repeatMode são extremamente problemáticos quando usados ​​junto com 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>

Em atividade: (Resolva introduzindo um pequeno atraso após o término da animação).

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

Existem muitas classes que você gostaria de pesquisar, mas atualmente estou usando o objectAnimator, que é altamente flexível. Eu não recomendaria usar Animation ou AnimationUtils:

  • Animação
  • AnimationUtils
  • Animador
  • AnimatorInflater
  • AnimatorListener
  • AnimatorListenerAdapter

0

É necessário ouvir a conclusão da primeira animação e reiniciá-la no retorno da chamada onStopAnimation, tente este link


0

Pequenos ajustes na resposta @Danufr para evitar que recursos sejam carregados novamente.

    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

Resolvi esse problema usando thread.

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

está funcionando bem

 GifDrawable gifDrawable = (GifDrawable) gifImageView.getDrawable();
    gifDrawable.setLoopCount(0);

0

Nenhuma das soluções acima funcionou no meu caso. A solução de Danuofr funcionou para o conjunto de animação, mas quando eu estava fazendo testes de unidade, meus testes costumavam ficar presos neste loop infinito. Por fim, específico para o meu caso, precisei repetir essa animação várias vezes. Portanto, adicionei manualmente cópias da minha animação em anim_rot.xml em cascata adicionando o valor de deslocamento . Eu sei que é ruim e não funcionará para muitos, mas foi a única solução para o meu caso.

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>

Fiz isso para repetir a animação 3 vezes. Você pode adicionar mais cópias para repetir vezes específicas, adicionando valores de deslocamento.


-1

Tente adicionar o código a um thread em loop ou a uma instrução while / for


Bem, a única solução que encontrei é evitar o uso de set tag.
Pavel Chernov
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.