Jak zrobić płynny obrót obrazu w Androidzie?

Używam RotateAnimation do obracania obrazu, który używam jako niestandardowego cyklicznego spinnera w Androidzie. Oto mój plik rotate_indefinitely.xml, który umieściłem w 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" />    

Kiedy zastosuję to do mojego ImageView za pomocą AndroidUtils.loadAnimation(), działa świetnie!

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

Jedyny problem polega na tym, że obrót obrazu wydaje się wstrzymywać na początku każdego cyklu.

Innymi słowy, obraz obraca się o 360 stopni, zatrzymuje się na krótko, a następnie obraca się o 360 stopni, itd.

Podejrzewam, że problem polega na tym, że animacja używa domyślnego interpolatora jak android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator), Ale Nie wiem, jak powiedzieć, żeby nie interpolować animacji.

Jak mogę wyłączyć interpolację (jeśli rzeczywiście jest to problem), aby mój cykl animacji przebiegał płynnie?

Author: emmby, 2009-10-28

14 answers

Masz rację co do AccelerateInterpolator; powinieneś zamiast tego użyć LinearInterpolator.

Możesz użyć wbudowanego {[2] } z pliku XML animacji za pomocą android:interpolator="@android:anim/linear_interpolator".

Lub możesz utworzyć własny plik interpolacji XML w swoim projekcie, np. nazwij go res/anim/linear_interpolator.xml:

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

I dodać do animacji XML:

android:interpolator="@anim/linear_interpolator"

Uwaga specjalna: jeśli animacja obrotu znajduje się wewnątrz zestawu, ustawienie interpolatora wydaje się nie działać. Dokonywanie obracania górnego elementu naprawia to. (pozwoli to zaoszczędzić Twój czas.)

 182
Author: Bakhtiyor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-28 11:45:24

Miałem również ten problem i próbowałem ustawić interpolator liniowy w xml bez powodzenia. Rozwiązaniem, które zadziałało dla mnie było stworzenie animacji w postaci Rotateanimacji w kodzie.

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);
 55
Author: Rabs G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 13:09:09

To działa dobrze

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

Aby odwrócić obrót:

<?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" />
 30
Author: Luis E. Fernandez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 09:37:35

Spróbuj użyć toDegrees="359" ponieważ 360° i 0° są takie same.

 26
Author: Fernando Gallego,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 01:03:56

Może coś takiego pomoże:

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

Przy okazji, możesz obracać o więcej niż 360 jak:

imageView.animate().rotationBy(10000)...
 24
Author: Vitaly Zinchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-07 20:12:18

Przycinanie <set>-elementu, który zawija <rotate>-Element rozwiązuje problem!

Dzięki Shalafi!

Więc Twoja Rotacja_ccw.xml powinien wyglądać tak:

<?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"
    />
 5
Author: Christopher Stock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-27 01:01:45
ObjectAnimatior.ofFloat(view,"rotation",0,360).start();
Spróbuj tego.
 4
Author: Ashraf Rahman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-26 10:56:30

Bez względu na to, co próbowałem, nie mogłem tego zrobić, używając kodu (i setrotacji) do płynnej animacji rotacji. Skończyło się na tym, że zmiany stopnia były tak małe, że małe pauzy były niezauważalne. Jeśli nie musisz wykonywać zbyt wielu obrotów, czas wykonania tej pętli jest znikomy. Efekt jest płynnym obrotem:

        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
Author: ununiform,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-25 19:36:33

Jak hanry wspomniał powyżej umieszczenie linera iterpolator jest w porządku. Ale jeśli rotacja jest wewnątrz zestawu, musisz umieścić android: shareInterpolator= "false", aby było gładkie.

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

Jeśli Sharedinterpolator nie jest false, powyższy kod daje usterki.

 3
Author: Rahul Agrawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-13 20:09:28

Jeśli używasz animacji zestawu jak ja, powinieneś dodać interpolację wewnątrz znacznika zestawu:

<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>
To mi pomogło.
 2
Author: Kokusho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-26 18:30:45

Czy to możliwe, że ponieważ przechodzisz od 0 do 360, spędzasz trochę więcej czasu na 0/360, niż się spodziewasz? Być może Ustaw na 359 lub 358.

 1
Author: William Rose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-28 00:10:33

W systemie Android, jeśli chcesz animować obiekt i przenieść go z lokalizacji1 do lokalizacji2, interfejs API animacji oblicza pośrednie lokalizacje (animacje), a następnie ustawia w kolejce do głównego wątku odpowiednie operacje przenoszenia w odpowiednim czasie za pomocą timera. Działa to dobrze, z wyjątkiem tego, że główny wątek jest zwykle używany do wielu innych rzeczy - malowania, otwierania plików, odpowiadania na wejścia użytkownika itp. Zegar w kolejce często może być opóźniony. Dobrze napisane programy zawsze będą spróbuj wykonać jak najwięcej operacji w tle (nie głównym) wątki jednak nie zawsze można uniknąć korzystania z głównego wątku. Operacje wymagające operowania na obiekcie UI zawsze muszą być wykonywane w głównym wątku. Ponadto wiele interfejsów API będzie przekierowywać operacje z powrotem do głównego wątku jako forma bezpieczeństwa wątku.

Widoki są rysowane na tym samym wątku GUI, który jest również używany dla wszystkich interakcji użytkownika.

Więc jeśli potrzebujesz szybko zaktualizować GUI lub jeśli rendering zajmuje zbyt dużo czasu i wpływa na wrażenia użytkownika, a następnie użyj SurfaceView.

Przykład rotacji obrazu:

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

Aktywność:

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
Author: phnmnn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-19 06:47:09

Spróbuj użyć więcej niż 360, aby uniknąć ponownego uruchamiania.

Ja uzywam 3600 Z 360 i u mnie dziala dobrze:

<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
Author: fisher3421,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-14 15:08:33

W Kotlinie:

 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
Author: Hitesh Sahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-14 16:16:46