Pregunta

Estoy usando un RotateAnimation para girar una imagen que estoy usando como una costumbre cíclico spinner en Android.Aquí está mi rotate_indefinitely.xml el archivo que he puesto en 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" />    

Cuando voy a aplicar esto a mi ImageView el uso de AndroidUtils.loadAnimation(), funciona muy bien!

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

El único problema es que la rotación de la imagen parece pausa en la parte superior de cada ciclo.

En otras palabras, la imagen gira 360 grados, se detiene brevemente, a continuación, gira 360 grados, de nuevo, etc.

Sospecho que el problema es que la animación es el uso de un defecto interpolador como android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator), pero no sé cómo decirle que no a interpolar la animación.

¿Cómo puedo desactivar la interpolación (si ese es el problema) para hacer mis animaciones ciclo sin problemas?

¿Fue útil?

Solución

Tienes razón sobre AccelerateInterpolator; deberías usar LinearInterpolator en su lugar.

Puede usar el android.R.anim.linear_interpolator incorporado de su archivo XML de animación con android:interpolator="@android:anim/linear_interpolator".

O puede crear su propio archivo de interpolación XML en su proyecto, p. nómbrelo res/anim/linear_interpolator.xml:

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

Y agregue a su animación XML:

android:interpolator="@anim/linear_interpolator"

Nota especial: si su animación de rotación está dentro de un conjunto, la configuración del interpolador no parece funcionar. Hacer girar el elemento superior lo arregla. (esto te ahorrará tiempo)

Otros consejos

También tuve este problema e intenté configurar el interpolador lineal en xml sin éxito. La solución que funcionó para mí fue crear la animación como RotateAnimation en código.

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

Esto funciona 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" />

Para invertir la rotación:

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

Tal vez, algo parecido a esto le ayudará a:

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

Por cierto, puede girar por más de 360 como:

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

Trate de usar toDegrees="359" desde 360° y 0° son los mismos.

¡Podar el <set> - Elemento que envolvió el <rotate> - Elemento resuelve el problema!

¡Gracias a Shalafi!

Entonces su Rotation_ccw.xml debería verse así:

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

Prueba esto.

No importa lo que intenté, no pude hacer que esto funcionara correctamente usando código (y setRotation) para una animación de rotación suave. Lo que terminé haciendo fue hacer que los cambios de grado fueran tan pequeños que las pequeñas pausas no se noten. Si no necesita hacer demasiadas rotaciones, el tiempo para ejecutar este ciclo es insignificante. El efecto es una rotación suave:

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

Como Hanry ha mencionado anteriormente, poner iterpolator de liner está bien. Pero si la rotación está dentro de un conjunto, debe poner android: shareInterpolator = & Quot; false & Quot; para que sea suave.

<?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 no es falso, el código anterior da fallas.

Objeto de rotación mediante programación.

// rotación en sentido horario:

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

// Rotación antihoraria:

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

vista es objeto de su ImageView u otros widgets.

rotate.setRepeatCount (10); utiliza para repetir tu rotación.

500 es la duración del tiempo de animación.

Si está utilizando una animación de conjunto como yo, debe agregar la interpolación dentro de la etiqueta del conjunto:

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

Eso funcionó para mí.

¿Es posible que debido a que pasa de 0 a 360, pase un poco más de tiempo en 0/360 de lo que esperaba? Quizás establezca Grados en 359 o 358.

En Android, si desea animar un objeto y hacer que mueva un objeto de ubicación1 a ubicación2, la API de animación calcula las ubicaciones intermedias (interpolación) y luego pone en cola en el hilo principal las operaciones de movimiento apropiadas en los momentos apropiados usando un temporizador. Esto funciona bien, excepto que el hilo principal generalmente se usa para muchas otras cosas & # 8212; pintar, abrir archivos, responder a las entradas del usuario, etc. Un temporizador en cola a menudo puede retrasarse. Los programas bien escritos siempre intentarán realizar tantas operaciones como sea posible en subprocesos en segundo plano (no principales); sin embargo, no puede & # 8217; no siempre evite usar el subproceso principal. Las operaciones que requieren que opere en un objeto de IU siempre deben realizarse en el hilo principal. Además, muchas API canalizarán las operaciones al hilo principal como una forma de seguridad de hilo.

Las vistas se dibujan en el mismo hilo GUI que también se usa para toda la interacción del usuario.

Entonces, si necesita actualizar la GUI rápidamente o si el renderizado toma demasiado tiempo y afecta la experiencia del usuario, use SurfaceView.

Ejemplo de imagen de rotación:

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

actividad:

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

Trate de usar más de 360 para evitar tener que reiniciar.

Yo uso 3600 en lugar de 360 y esto funciona muy bien para mí:

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

En 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)
        })
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top