it-swarm.com.de

Wie mache ich eine reibungslose Bildrotation in Android?

Ich verwende RotateAnimation, um ein Bild zu drehen, das ich als benutzerdefinierten zyklischen Spinner in Android verwende. Hier ist meine rotate_indefinitely.xml-Datei, die ich in res/anim/ abgelegt habe:

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

Wenn ich dies auf ImageView mit AndroidUtils.loadAnimation() anwende, funktioniert es hervorragend! 

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

Das einzige Problem ist, dass die Bildrotation an jedem Zyklus ganz oben zu stehen scheint.

Mit anderen Worten, das Bild dreht sich um 360 Grad, hält kurz an und dreht sich dann wieder um 360 Grad usw.

Ich vermute, dass das Problem darin besteht, dass die Animation einen Standardinterpolator wie Android:iterpolator="@Android:anim/accelerate_interpolator" (AccelerateInterpolator) verwendet, aber ich weiß nicht, wie ich sie anweisen soll, die Animation nicht zu interpolieren.

Wie kann ich die Interpolation ausschalten (wenn dies tatsächlich das Problem ist), um den Animationszyklus reibungslos zu gestalten?

185
emmby

Sie haben recht mit AccelerateInterpolator; Sie sollten stattdessen LinearInterpolator verwenden.

Sie können den integrierten Android.R.anim.linear_interpolator aus Ihrer Animations-XML-Datei mit Android:interpolator="@Android:anim/linear_interpolator" verwenden.

Oder Sie können in Ihrem Projekt eine eigene XML-Interpolationsdatei erstellen, z. Nennen Sie es res/anim/linear_interpolator.xml:

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

Und fügen Sie Ihrer Animations-XML hinzu:

Android:interpolator="@anim/linear_interpolator"

Besonderer Hinweis: Wenn sich die Drehanimation innerhalb einer Gruppe befindet, scheint die Einstellung des Interpolators nicht zu funktionieren. Durch das Drehen des oberen Elements wird es fixiert. (Dies wird Ihre Zeit sparen.)

185
Bakhtiyor

Ich hatte auch dieses Problem und versuchte, den linearen Interpolator in XML ohne Erfolg zu setzen. Die Lösung, die für mich funktioniert hat, war die Animation als RotateAnimation in Code zu erstellen.

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);
63
Rabs G

Das funktioniert gut

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

Zum Rückwärtsdrehen:

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

Verwenden Sie toDegrees="359", da 360 ° und 0 ° gleich sind.

27

Vielleicht hilft so etwas:

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

Übrigens können Sie sich um mehr als 360 Grad drehen:

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

Das Beschneiden des <set>- Elements, das das <rotate>- Element umhüllt, löst das Problem!

Danke an Shalafi!

Ihre Rotation_ccw.xml sollte also so aussehen:

<?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"
    />
6
ObjectAnimatior.ofFloat(view,"rotation",0,360).start();

Versuche dies.

4
Ashraf Rahman

Wie von Hanry bereits erwähnt, ist der Putter-Iterpolator in Ordnung. Wenn sich die Rotation jedoch innerhalb eines Satzes befindet, müssen Sie Android setzen: shareInterpolator = "false", um eine glatte Oberfläche zu erhalten.

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

Wenn der Sharedinterpolator nicht falsch ist, führt der obige Code zu Störungen.

3
Rahul Agrawal

Egal, was ich ausprobiert habe, ich konnte nicht mit Code (und SetRotation) eine reibungslose Rotationsanimation erstellen. Am Ende habe ich die Gradänderungen so klein gemacht, dass die kleinen Pausen nicht wahrgenommen werden. Wenn Sie nicht zu viele Umdrehungen durchführen müssen, ist die Zeit zum Ausführen dieser Schleife zu vernachlässigen. Der Effekt ist eine sanfte Rotation:

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

Wenn Sie eine Set-Animation wie mich verwenden, sollten Sie die Interpolation innerhalb des Set-Tags hinzufügen:

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

Das hat für mich funktioniert. 

2
Kokusho

Ist es möglich, dass Sie bei 0/360 etwas mehr Zeit verbringen, als Sie erwarten, weil Sie von 0 auf 360 gehen? Stellen Sie vielleichtDegrees auf 359 oder 358 ein.

1
William Rose

Rotationsobjekt programmgesteuert.

// Drehung im Uhrzeigersinn: 

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

// Drehung gegen den Uhrzeigersinn:

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

view ist ein Objekt Ihrer ImageView oder anderer Widgets.

rotate.setRepeatCount (10); verwenden Sie, um Ihre Rotation zu wiederholen.

500 ist die Dauer Ihrer Animation.

0
Geet Thakur

In 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)
        })
0
Hitesh Sahu

Wenn Sie in Android ein Objekt animieren und veranlassen möchten, ein Objekt von location1 nach location2 zu verschieben, ermittelt die Animations-API die Zwischenpositionen (Tweening) und reiht dann die entsprechenden Verschiebungsoperationen zu geeigneten Zeitpunkten mit einem Zeitgeber in den Haupt-Thread ein . Dies funktioniert gut, mit der Ausnahme, dass der Haupt-Thread normalerweise für viele andere Dinge verwendet wird - zum Malen, Öffnen von Dateien, Antworten auf Benutzereingaben usw. Ein in der Warteschlange befindlicher Timer kann oft verzögert werden. Gut geschriebene Programme versuchen immer, so viele Operationen wie möglich in Hintergrundthreads (nicht-Hauptthreads) auszuführen. Sie können jedoch nicht immer den Hauptthread verwenden. Vorgänge, für die Sie ein UI-Objekt bearbeiten müssen, müssen immer im Haupt-Thread ausgeführt werden. Außerdem werden viele APIs Operationen als eine Form von Threadsicherheit zum Hauptthread zurückführen.

Ansichten werden alle in demselben GUI-Thread gezeichnet, der auch für alle Benutzerinteraktionen verwendet wird.

Wenn Sie die GUI schnell aktualisieren müssen oder das Rendern zu lange dauert und die Benutzererfahrung beeinträchtigt, verwenden Sie SurfaceView.

Beispiel für ein Rotationsbild:

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

aktivität:

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

Versuchen Sie, mehr als 360 zu verwenden, um einen Neustart zu vermeiden. 

Ich benutze 3600 anstelle von 360 und das funktioniert gut für mich:

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