Question

j'utilise un RotateAnimation pour faire pivoter une image que j'utilise comme spinner cyclique personnalisé dans Android.Voici mon rotate_indefinitely.xml fichier que j'ai placé dans res/anim/:

<?xml version="1.0" encoding="UTF-8"?>
<rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:duration="1200" />    

Quand j'applique cela à mon ImageView en utilisant AndroidUtils.loadAnimation(), ça marche super bien !

spinner.startAnimation( 
    AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely) );

Le seul problème est que la rotation de l’image semble s’arrêter au début de chaque cycle.

En d’autres termes, l’image pivote de 360 ​​degrés, s’arrête brièvement, puis pivote à nouveau de 360 ​​degrés, etc.

Je soupçonne que le problème est que l'animation utilise un interpolateur par défaut comme android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator), mais je ne sais pas comment lui dire de ne pas interpoler l'animation.

Comment puis-je désactiver l'interpolation (si c'est effectivement le problème) pour que mon cycle d'animation soit fluide ?

Était-ce utile?

La solution

Vous avez raison en ce qui concerne AccelerateInterpolator; vous devez utiliser LinearInterpolator à la place.

Vous pouvez utiliser le android.R.anim.linear_interpolator intégré de votre fichier XML d'animation avec android:interpolator="@android:anim/linear_interpolator".

Vous pouvez également créer votre propre fichier d'interpolation XML dans votre projet, par exemple. nommez-le res/anim/linear_interpolator.xml:

<?xml version="1.0" encoding="utf-8"?>
<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />

Et ajoutez à votre animation XML:

android:interpolator="@anim/linear_interpolator"

Remarque spéciale: si votre animation de rotation se trouve à l'intérieur d'un ensemble, le paramétrage de l'interpolateur ne semble pas fonctionner. Faire pivoter l'élément supérieur le corrige. (cela vous fera gagner du temps.)

Autres conseils

J'ai également eu ce problème et j'ai essayé de régler l'interpolateur linéaire en xml sans succès. La solution qui a fonctionné pour moi a été de créer l’animation sous forme de RotateAnimation dans le code.

RotateAnimation rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(5000);
rotate.setInterpolator(new LinearInterpolator());

ImageView image= (ImageView) findViewById(R.id.imageView);

image.startAnimation(rotate);

Cela fonctionne bien

<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1600"
    android:fromDegrees="0"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:toDegrees="358" />

Pour effectuer une rotation inverse:

<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1600"
    android:fromDegrees="358"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:toDegrees="0" />

Peut-être que quelque chose comme ceci aidera :

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        imageView.animate().rotationBy(360).withEndAction(this).setDuration(3000).setInterpolator(new LinearInterpolator()).start();
    }
};

imageView.animate().rotationBy(360).withEndAction(runnable).setDuration(3000).setInterpolator(new LinearInterpolator()).start();

À propos, vous pouvez effectuer une rotation de plus de 360 ​​​​comme :

imageView.animate().rotationBy(10000)...

Essayez d’utiliser toDegrees="359" depuis 360 & # 176; et 0 & # 176; sont les mêmes.

L'élagage de l'élément <set> qui a enveloppé l'élément <rotate> - résout le problème!

Merci à Shalafi!

Votre fichier Rotation_ccw.xml doit donc ressembler à ceci:

<?xml version="1.0" encoding="utf-8"?>

<rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="-360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="2000"
    android:fillAfter="false"
    android:startOffset="0"
    android:repeatCount="infinite"
    android:interpolator="@android:anim/linear_interpolator"
    />
ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();

Essayez ceci.

Peu importe ce que j'ai essayé, je ne pouvais pas que cela fonctionne correctement avec du code (et setRotation) pour une animation en rotation fluide. Ce que j’ai finalement fait, c’est que les changements de degré ont été si petits que les petites pauses sont imperceptibles. Si vous n'avez pas besoin de faire trop de rotations, le temps nécessaire pour exécuter cette boucle est négligeable. L'effet est une rotation en douceur:

        float lastDegree = 0.0f;
        float increment = 4.0f;
        long moveDuration = 10;
        for(int a = 0; a < 150; a++)
        {
            rAnim = new RotateAnimation(lastDegree, (increment * (float)a),  Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rAnim.setDuration(moveDuration);
            rAnim.setStartOffset(moveDuration * a);
            lastDegree = (increment * (float)a);
            ((AnimationSet) animation).addAnimation(rAnim);
        }

Comme hanry l’a mentionné plus haut, mettre liner iterpolator est correct. Mais si la rotation est à l'intérieur d'un ensemble, vous devez mettre android: shareInterpolator = & "False &"; pour le rendre lisse.

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
**android:shareInterpolator="false"**
>
<rotate
    android:interpolator="@android:anim/linear_interpolator"
    android:duration="300"
    android:fillAfter="true"
    android:repeatCount="10"
    android:repeatMode="restart"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%" />
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator"
    android:duration="3000"
    android:fillAfter="true"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fromXScale="1.0"
    android:fromYScale="1.0"
    android:toXScale="0"
    android:toYScale="0" />
</set>

Si Sharedinterpolator n’est pas false, le code ci-dessus donne des problèmes.

Objet de rotation par programmation.

// rotation dans le sens des aiguilles d'une montre:

    public void rorate_Clockwise(View view) {
        ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 180f, 0f);
//        rotate.setRepeatCount(10);
        rotate.setDuration(500);
        rotate.start();
    }

// Rotation anti-horaire:

 public void rorate_AntiClockwise(View view) {
        ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 180f);
//        rotate.setRepeatCount(10);
        rotate.setDuration(500);
        rotate.start();
    } 

La vue est un objet de votre ImageView ou d’autres widgets.

rotate.setRepeatCount (10); permet de répéter votre rotation.

500 correspond à la durée de votre animation.

Si vous utilisez un ensemble d'animation comme moi, vous devez ajouter l'interpolation à l'intérieur de la balise d'ensemble:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator">

 <rotate
    android:duration="5000"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:startOffset="0"
    android:toDegrees="360" />

 <alpha
    android:duration="200"
    android:fromAlpha="0.7"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
    android:toAlpha="1.0" />

</set>

Cela a fonctionné pour moi.

Est-il possible que, parce que vous passez de 0 à 360, vous passez un peu plus de temps à 0/360 que prévu? Peut-être définir toDegrees à 359 ou 358.

Sous Android, si vous souhaitez animer un objet et le déplacer d'un emplacement à un autre, l'API d'animation détermine les emplacements intermédiaires (interpolation), puis met en file d'attente sur le fil principal les opérations de déplacement appropriées aux moments appropriés. en utilisant une minuterie. Cela fonctionne très bien, sauf que le thread principal est généralement utilisé pour beaucoup d'autres choses & # 8212; peindre, ouvrir des fichiers, répondre aux entrées de l'utilisateur, etc. Un minuteur en file d'attente peut souvent être retardé. Les programmes bien écrits essaieront toujours de faire autant d'opérations que possible dans les threads d'arrière-plan (non principaux), mais vous ne pourrez & # 8217; ne pas toujours éviter d'utiliser le thread principal. Les opérations qui nécessitent que vous agissiez sur un objet d'interface utilisateur doivent toujours être effectuées sur le thread principal. En outre, de nombreuses API redirigent les opérations vers le thread principal sous forme de thread-safety.

Les vues sont toutes dessinées sur le même fil d'interface graphique, qui est également utilisé pour toutes les interactions de l'utilisateur.

Donc, si vous devez mettre à jour l'interface graphique rapidement ou si le rendu prend trop de temps et affecte l'expérience utilisateur, utilisez SurfaceView.

Exemple d'image de rotation:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    private DrawThread drawThread;

    public MySurfaceView(Context context) {
        super(context);
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {   
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        drawThread = new DrawThread(getHolder(), getResources());
        drawThread.setRunning(true);
        drawThread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        drawThread.setRunning(false);
        while (retry) {
            try {
                drawThread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }
}


class DrawThread extends Thread{
    private boolean runFlag = false;
    private SurfaceHolder surfaceHolder;
    private Bitmap picture;
    private Matrix matrix;
    private long prevTime;

    public DrawThread(SurfaceHolder surfaceHolder, Resources resources){
        this.surfaceHolder = surfaceHolder;

        picture = BitmapFactory.decodeResource(resources, R.drawable.icon);

        matrix = new Matrix();
        matrix.postScale(3.0f, 3.0f);
        matrix.postTranslate(100.0f, 100.0f);

        prevTime = System.currentTimeMillis();
    }

    public void setRunning(boolean run) {
        runFlag = run;
    }

    @Override
    public void run() {
        Canvas canvas;
        while (runFlag) {
            long now = System.currentTimeMillis();
            long elapsedTime = now - prevTime;
            if (elapsedTime > 30){

                prevTime = now;
                matrix.preRotate(2.0f, picture.getWidth() / 2, picture.getHeight() / 2);
            }
            canvas = null;
            try {
                canvas = surfaceHolder.lockCanvas(null);
                synchronized (surfaceHolder) {
                    canvas.drawColor(Color.BLACK);
                    canvas.drawBitmap(picture, matrix, null);
                }
            } 
            finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }
}

activité:

public class SurfaceViewActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MySurfaceView(this));
    }
}

Essayez d'en utiliser plus de 360 ​​pour éviter de redémarrer.

J'utilise 3600 au lieu de 360 ​​et cela fonctionne bien pour moi :

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="3600"
    android:interpolator="@android:anim/linear_interpolator"
    android:repeatCount="infinite"
    android:duration="8000"
    android:pivotX="50%"
    android:pivotY="50%" />

Dans Kotlin:

 ivBall.setOnClickListener(View.OnClickListener {

            //Animate using XML
            // val rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely)

            //OR using Code
            val rotateAnimation = RotateAnimation(
                    0f, 359f,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f

            )
            rotateAnimation.duration = 300
            rotateAnimation.repeatCount = 2

            //Either way you can add Listener like this
            rotateAnimation.setAnimationListener(object : Animation.AnimationListener {

                override fun onAnimationStart(animation: Animation?) {
                }

                override fun onAnimationRepeat(animation: Animation?) {
                }

                override fun onAnimationEnd(animation: Animation?) {

                    val rand = Random()
                    val ballHit = rand.nextInt(50) + 1
                    Toast.makeText(context, "ballHit : " + ballHit, Toast.LENGTH_SHORT).show()
                }
            })

            ivBall.startAnimation(rotateAnimation)
        })
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top