android - voltea - programa para rotar imagenes
Cómo hacer una rotación de imagen suave en Android? (13)
¿Es posible que porque pasas de 0 a 360, pases un poco más de tiempo en 0/360 de lo que estás esperando? Tal vez establecido en Grados a 359 o 358.
Estoy usando un RotateAnimation
para rotar una imagen que estoy usando como un spinner cíclico personalizado en Android. Aquí está mi archivo rotate_indefinitely.xml
, que coloqué 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 aplico esto a mi ImageView
usando AndroidUtils.loadAnimation()
, ¡funciona genial!
spinner.startAnimation(
AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely) );
El único problema es que la rotación de la imagen parece detenerse en la parte superior de cada ciclo.
En otras palabras, la imagen gira 360 grados, se detiene brevemente, luego gira 360 grados nuevamente, etc.
Sospecho que el problema es que la animación está usando un interpolador predeterminado como android:iterpolator="@android:anim/accelerate_interpolator"
( AccelerateInterpolator
), pero no sé cómo decirle que no interpole la animación.
¿Cómo puedo desactivar la interpolación (si ese es el problema) para hacer que mi ciclo de animación sea fluido?
Como Hanry ha mencionado anteriormente, poner liner iterpolator está 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 proporciona fallas.
En Android, si desea animar un objeto y hacer que mueva un objeto de location1 a location2, la API de animación determina las ubicaciones intermedias (interpolación) y luego pone en cola en el hilo principal las operaciones de movimiento apropiadas en el momento apropiado utilizando 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.
Las vistas se dibujan en el mismo hilo 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 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));
}
}
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" />
Intenta usar más de 360 para evitar reiniciar.
Uso 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%" />
Intente usar toDegrees="359"
ya que 360 ° y 0 ° son iguales.
No importa lo que intenté, no pude hacer que esto funcione bien usando el código (y setRotation) para una animación de 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);
}
Podando el <set>
Elemento que envolvió el <rotate>
Element resuelve el problema!
Gracias a Shalafi!
Por lo tanto, su Rotation_ccw.xml debería funcionar de la siguiente manera:
<?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"
/>
Si está utilizando una animación configurada como yo, debe agregar la interpolación dentro de la etiqueta establecida:
<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í.
Tal vez, algo como esto ayudará:
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, puedes rotar más de 360 como:
imageView.animate().rotationBy(10000)...
También tuve este problema e intenté establecer 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);
Tiene razón acerca de 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, por ejemplo, nombre 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 la rotación del elemento superior lo soluciona. (Esto le ahorrará tiempo)
ObjectAnimatior.ofFloat(view,"rotation",0,360).start();
Prueba esto.