Bagaimana cara membuat rotasi gambar yang halus di Android?


217

Saya menggunakan a RotateAnimationuntuk memutar gambar yang saya gunakan sebagai pemintal siklus khusus di Android. Ini rotate_indefinitely.xmlfile saya , yang saya tempatkan di 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" />    

Ketika saya menerapkan ini pada ImageViewpenggunaan saya AndroidUtils.loadAnimation(), itu berfungsi dengan baik!

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

Masalahnya adalah rotasi gambar tampaknya berhenti di bagian atas setiap siklus.

Dengan kata lain, gambar berputar 360 derajat, berhenti sebentar, lalu berputar 360 derajat lagi, dll.

Saya menduga bahwa masalahnya adalah bahwa animasi menggunakan interpolator default seperti android:iterpolator="@android:anim/accelerate_interpolator"( AccelerateInterpolator), tapi saya tidak tahu bagaimana mengatakannya untuk tidak menginterpolasi animasi.

Bagaimana saya bisa mematikan interpolasi (jika memang itu masalahnya) untuk membuat siklus animasi saya lancar?

Jawaban:


199

Anda benar tentang AccelerateInterpolator; Anda harus menggunakan LinearInterpolator sebagai gantinya.

Anda dapat menggunakan built-in android.R.anim.linear_interpolatordari file XML animasi dengan android:interpolator="@android:anim/linear_interpolator".

Atau Anda dapat membuat file interpolasi XML Anda sendiri di proyek Anda, mis. Beri nama res/anim/linear_interpolator.xml:

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

Dan tambahkan ke XML animasi Anda:

android:interpolator="@anim/linear_interpolator"

Catatan Khusus: Jika animasi rotate Anda ada di dalam set, pengaturan interpolator tampaknya tidak berfungsi. Membuat memutar elemen atas memperbaikinya. (ini akan menghemat waktu Anda.)


1
Ini karena interpolator salah dieja (no "n"). Anda tidak perlu membuatnya sendiri
Kingpin

25
Saya sudah mencoba setiap interpolator yang tersedia, termasuk linier, dan saya masih mendapatkan "halangan" kecil ini di awal setiap siklus.
Adam Rabung

11
Jika animasi rotate Anda ada di dalam set, pengaturan interpolator tampaknya tidak berfungsi. Membuat memutar elemen atas memperbaikinya
shalafi

Hai, bagaimana jika Anda benar-benar ingin menggunakan accelerate_decelerate_interpolator tanpa "jeda" kecil di antara setiap animasi?
agonist_

90

Saya punya masalah ini juga, dan mencoba mengatur interpolator linier dalam xml tanpa hasil. Solusi yang berhasil bagi saya adalah membuat animasi sebagai RotateAnimation dalam kode.

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

9
jika Anda ingin animasi tetap pada orientasi di akhir, tambahkanrotate.setFillAfter(true);
Fonix

4
jika Anda ingin animasi tetap permanen tanpa akhir, tambahkanrotate.setRepeatCount(Animation.INFINITE);
ahmednabil88

38

Ini berfungsi dengan baik

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

Untuk membalikkan rotasi:

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

5
Untuk membalikkan, cukup lakukan repeatcount 2 dan atur android: repeatMode = "reverse" - tidak perlu memiliki dua file XML yang berbeda.
RoundSparrow hilltx

3
mengapa 358 dan bukan 359 atau 360?
behelit

Di mana menambahkan file ini ke dalam sumber daya? Apakah animasi paket terpisah untuk ini?
abggcv

30

Mungkin, sesuatu seperti ini akan membantu:

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

Omong-omong, Anda dapat memutar lebih dari 360 seperti:

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

Bekerja dengan sempurna, apakah imageView.clearAnimation () akan membersihkan runnable juga?
XcodeNOOB

Ini berfungsi dengan baik untuk memulai animasi, tetapi setelah mulai dijalankan tidak dapat dibersihkan oleh imageView.clearAnimation (), saya digunakan dalam acara pendengar sentuh
Abhijit Jagtap

Tidak dapat menghentikan runnables ini secara independen
Pants

@Pants Anda dapat menelepon denganEndAction (ini) pada kondisi tertentu. Jika kondisinya salah, withEndAction (ini) tidak akan dipanggil, dan animasi harus berhenti
Vitaly Zinchenko


19
ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();

Coba ini.


8

Rotasi Obyek secara terprogram.

// rotasi searah jarum jam :

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

// Rotasi berlawanan arah jarum jam:

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

lihat adalah objek ImageView Anda atau widget lainnya.

rotate.setRepeatCount (10); gunakan untuk mengulang rotasi Anda.

500 adalah durasi waktu animasi Anda.


6

Pemangkasan <set>-Element yang membungkus<rotate> -Element memecahkan masalah!

Terima kasih untuk Shalafi!

Jadi Rotation_ccw.xml Anda akan terlihat seperti ini:

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

Tidak peduli apa yang saya coba, saya tidak bisa mendapatkan ini berfungsi dengan benar menggunakan kode (dan setRotation) untuk animasi rotasi halus. Apa yang akhirnya saya lakukan adalah membuat perubahan derajat sangat kecil, sehingga jeda kecil tidak terlihat. Jika Anda tidak perlu melakukan terlalu banyak rotasi, waktu untuk mengeksekusi loop ini dapat diabaikan. Efeknya adalah rotasi yang halus:

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

di mana Anda "animasi" deklarasi variabel?
Rishabh Saxena

3

Seperti Hanny telah disebutkan di atas menempatkan liner iterpolator baik-baik saja. Tetapi jika rotasi ada di dalam set Anda harus meletakkan android: shareInterpolator = "false" untuk membuatnya lancar.

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

Jika Sharedinterpolator tidak salah, kode di atas memberikan gangguan.


3

Jika Anda menggunakan animasi set seperti saya, Anda harus menambahkan interpolasi di dalam tag set:

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

Itu berhasil bagi saya.


3

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

1

Apakah mungkin karena Anda beralih dari 0 ke 360, Anda menghabiskan sedikit lebih banyak waktu pada 0/360 daripada yang Anda harapkan? Mungkin diatur ke Derajat ke 359 atau 358.


1
Teori hebat. Saya cukup yakin itu bukan karena speedup / perlambatan terlihat cukup halus dan disengaja. Untuk berjaga-jaga meskipun saya mencoba menurunkan derajat ke 358 dan tidak ada perubahan perilaku yang jelas.
emmby

1

Cobalah untuk menggunakan lebih dari 360 untuk menghindari memulai kembali.

Saya menggunakan 3600 insted of 360 dan ini berfungsi dengan baik untuk saya:

<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

Di Android, jika Anda ingin menghidupkan objek dan membuatnya memindahkan objek dari location1 ke location2, API animasi menentukan lokasi perantara (tweening) dan kemudian mengantri ke utas utama operasi pemindahan yang tepat pada waktu yang tepat menggunakan penghitung waktu . Ini berfungsi dengan baik kecuali bahwa utas utama biasanya digunakan untuk banyak hal lain - melukis, membuka file, menanggapi input pengguna dll. Timer yang antri sering dapat ditunda. Program yang ditulis dengan baik akan selalu mencoba melakukan sebanyak mungkin operasi di utas latar (non utama) namun Anda tidak dapat selalu menghindari menggunakan utas utama. Operasi yang mengharuskan Anda beroperasi pada objek UI selalu harus dilakukan di utas utama. Juga, banyak API akan menyalurkan operasi kembali ke utas utama sebagai bentuk keselamatan utas.

Tampilan semuanya digambar pada utas GUI yang sama yang juga digunakan untuk semua interaksi pengguna.

Jadi jika Anda perlu memperbarui GUI dengan cepat atau jika rendering terlalu banyak waktu dan memengaruhi pengalaman pengguna, gunakan SurfaceView.

Contoh gambar rotasi:

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

aktivitas:

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
private fun rotateTheView(view: View?, startAngle: Float, endAngle: Float) {
    val rotate = ObjectAnimator.ofFloat(view, "rotation", startAngle, endAngle)
    //rotate.setRepeatCount(10);
    rotate.duration = 400
    rotate.start()
}

1
Meskipun kode ini dapat memberikan solusi untuk pertanyaan, lebih baik menambahkan konteks mengapa / cara kerjanya. Ini dapat membantu pengguna di masa depan belajar, dan menerapkan pengetahuan itu ke kode mereka sendiri. Anda juga cenderung mendapat umpan balik positif dari pengguna dalam bentuk upvotes, ketika kode dijelaskan.
borchvm
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.