2009-10-27 16 views
167

estoy usando un RotateAnimation para girar una imagen que estoy usando como una ruleta personalizada cíclica en Android. Aquí está mi archivo rotate_indefinitely.xml, que coloqué en res/anim/:Como hacer un giro de la imagen suave en Android?

<?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 aplico esto a mi ImageView usando 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 pausarse en la parte superior de cada ciclo.

En otras palabras, la imagen gira 360 grados, se detiene brevemente, y luego gira 360 grados de nuevo, etc.

Sospecho que el problema es que la animación está utilizando una interpolación por defecto como android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator), pero No sé cómo decirle que no interpole la animación.

Cómo puedo apagar la interpolación (si es ése el problema) para que mi ciclo de animación sin problemas?

Respuesta

178

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

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

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

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

Y añadir a su XML animación:

android:interpolator="@anim/linear_interpolator" 

Nota especial: Si la animación de rotación está dentro de un conjunto, estableciendo el interpolador no parece funcionar. Hacer la rotación del elemento superior lo soluciona. (Esto ahorrará su tiempo.)

+1

Esto se debe a que el interpolador se deletreó incorrectamente (no "n"). No necesita hacer su propio – Kingpin

+22

. He probado todos los interpoladores disponibles, incluido el lineal, y todavía tengo este pequeño "enganche" al comienzo de cada ciclo. –

+11

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

1

¿Es posible que debido a que vaya de 0 a 360, que gasta un poco más tiempo en 0/360 de lo que está esperando? Tal conjunto toDegrees a 359 o 358.

+1

gran teoría. Estoy bastante seguro de que no es así porque la aceleración/desaceleración es bastante suave y deliberada. Por si acaso, aunque traté de disminuir los grados a 358 y no hubo un cambio discernible en el comportamiento. – emmby

26

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

3

No importa lo que traté, no pude conseguir que esto funcione derecha usando el código (y setRotation) para la animación rotación suave. Lo que terminé haciendo fue hacer los cambios de grado tan pequeños, que las pequeñas pausas son imperceptibles. 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); 
     } 
28

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 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" /> 
+4

Para reversa, simplemente tenga repeatcount 2 y configure android: repeatMode = "reverse" - no necesita tener dos archivos XML diferentes. –

+0

¿por qué 358 y no 359 o 360? – behelit

+0

¿Dónde agregar este archivo a los recursos? ¿Es la animación un paquete separado para esto? – abggcv

5

La poda de la <set> -elemento que envuelve el <rotate> -elemento resuelve el problema!

Gracias a Shalafi!

Así que su Rotation_ccw.xml debe loook 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" 
    /> 
3

Como se ha mencionado anteriormente Hanry poner iterpolator revestimiento está muy bien. Pero si la rotación está dentro de un conjunto, debes poner android: shareInterpolator = "false" 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 fallos.

40

Tuve este problema también y traté de configurar el interpolador lineal en xml sin éxito. La solución que funcionó para mí fue crear la animación como un RotateAnimation en el 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); 
0

En Android, si se desea animar un objeto y hacer que se mueva un objeto de ubicación1 a Location2, las cifras del API de animación fuera de los lugares intermedios (interpolación) y luego se pone en cola en el hilo principal de las operaciones de movimiento apropiados en los momentos apropiados usando un temporizador. Esto funciona bien, excepto que el hilo principal generalmente se utiliza para muchas otras cosas: 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 hacer tantas operaciones como sea posible en los hilos de fondo (no principales), sin embargo, no siempre se puede evitar el uso del hilo principal. Las operaciones que requieren que opere en un objeto UI siempre tienen que hacerse en el hilo principal. Además, muchas API canalizarán las operaciones al hilo principal como una forma de seguridad de hilos.

Todas las vistas se dibujan en el mismo subproceso de GUI que también se usa para todas las interacciones del usuario.

Por lo tanto, si necesita actualizar la GUI rápidamente o si la renderización requiere demasiado tiempo y afecta la experiencia del usuario, utilice SurfaceView.

Ejemplo imagen 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)); 
    } 
} 
1

Si está utilizando una animación de establecer como yo debería añadir la interpolación dentro de la etiqueta 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í.

15

Tal vez, algo como esto 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 en más de un 360 como:

imageView.animate().rotationBy(10000)... 
+0

Funcionando perfectamente, ¿imageView.clearAnimation() también limpiará el ejecutable? – XcodeNOOB

+0

Está funcionando bien para la animación de inicio, pero después de iniciar ejecutable no es limpiado por imageView.clearAnimation(), lo usé en evento de escucha táctil –

3
ObjectAnimatior.ofFloat(view,"rotation",0,360).start(); 

probar esto.

0

Trate de usar más de 360 ​​para evitar reiniciar.

utilizo 3600 insted de 360 ​​y esto funciona 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%" /> 
Cuestiones relacionadas