Як зробити плавне обертання зображення в Android?


217

Я використовую a RotateAnimationдля обертання зображення, яке я використовую як користувацький циклічний спінер в Android. Ось мій rotate_indefinitely.xmlфайл, який я розмістив у 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" />    

Коли я застосовую це до свого ImageViewвикористання AndroidUtils.loadAnimation(), він чудово працює!

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

Одна проблема полягає в тому, що обертання зображення, здається, призупиняється вгорі кожного циклу.

Іншими словами, зображення обертається на 360 градусів, короткочасне пауза, потім знову повертається на 360 градусів тощо.

Я підозрюю, що проблема полягає в тому, що анімація використовує інтерполятор за замовчуванням на зразок android:iterpolator="@android:anim/accelerate_interpolator"( AccelerateInterpolator), але я не знаю, як сказати, щоб не інтерполювати анімацію.

Як я можу вимкнути інтерполяцію (якщо це справді проблема), щоб мій цикл анімації був плавним?

Відповіді:


199

Ви маєте рацію щодо AccelerateInterpolator; ви повинні використовувати LinearInterpolator замість цього.

Ви можете використовувати вбудований android.R.anim.linear_interpolatorфайл XML-анімації за допомогою android:interpolator="@android:anim/linear_interpolator".

Або ви можете створити власний файл інтерполяції XML у своєму проекті, наприклад, назвіть його res/anim/linear_interpolator.xml:

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

І додайте до анімації XML:

android:interpolator="@anim/linear_interpolator"

Спеціальна примітка: Якщо ваша анімація обертання знаходиться всередині набору, то, здається, встановлення інтерполятора не працює. Здійснення обертання верхній елемент фіксує це. (це заощадить ваш час.)


1
Це тому, що інтерполятор був написаний неправильно (немає "n"). Вам не потрібно робити власні
Kingpin

25
Я спробував кожен доступний інтерполятор, включаючи лінійний, і я все ще отримую цю маленьку «зачіпку» на початку кожного циклу.
Адам Рабунг

11
Якщо ваша анімація обертання знаходиться всередині набору, здається, що інтерполятор не працює. Здійснення обертання верхній елемент фіксує це
шалафі

Гей, що робити, якщо ви насправді хочете використовувати accelerate_decelerate_interpolator без невеликої "паузи" між кожною анімацією?
агоніст_

90

У мене була і ця проблема, і я без спроби намагався встановити лінійний інтерполятор у xml. Для мене справою було створення анімації як коду RotateAnimation.

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
якщо ви хочете, щоб анімація залишалася в орієнтації в кінці, додайтеrotate.setFillAfter(true);
Fonix

4
якщо ви хочете, щоб анімація постійно залишалася без кінця, додайтеrotate.setRepeatCount(Animation.INFINITE);
ahmednabil88

38

Це чудово працює

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

Для зворотного обертання:

<?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
Для зворотного зв’язку достатньо мати повторний рахунок 2 та встановити android: repeMode = "reverse" - не потрібно мати два різних XML-файли.
RoundSparrow hilltx

3
чому 358, а не 359 чи 360?
бехеліт

Де додати цей файл до ресурсів? Чи анімація для цього окремий пакет?
abggcv

30

Можливо, щось подібне допоможе:

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

До речі, ви можете обертатись більш ніж на 360 так:

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

Працює бездоганно, чи очистить також програму imageView.clearAnimation ()?
XcodeNOOB

Його добре працює для запуску анімації, але після запуску її не очищають imageView.clearAnimation (), я використовував у події слухача на дотик
Abhijit Jagtap

Не можна самостійно зупиняти ці бігуни
Штани

@ Штани ви можете зателефонувати за допомогоюEndAction (це) за певної умови. Якщо умова помилкова, не буде викликано функцію EndAction (це), і анімація повинна припинитися
Віталій Зінченко



8

Об'єкт обертання програмно.

// обертання за годинниковою стрілкою:

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

// Обертання проти годинникової стрілки:

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

view є об'єктом вашого ImageView або інших віджетів.

rotate.setRepeatCount (10); використовувати для повторення обертання.

500 - ваша тривалість анімації.


6

Обрізка <set>-Element, який загорнув <rotate>-Element вирішує проблему!

Дякую Шалафі!

Отже, ваш Rotation_ccw.xml повинен виглядати так:

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

Незалежно від того, що я намагався, я не міг змусити це правильно працювати, використовуючи код (і setRotation) для анімації плавного повороту. Що я в кінцевому підсумку робив - робив зміни ступеня настільки невеликими, що невеликі паузи непомітні. Якщо вам не потрібно робити занадто багато обертань, час на виконання цього циклу незначний. Ефект - плавне обертання:

        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

Як згадував Ханрі вище, розміщувати лайнер ітерполятор добре. Але якщо обертання всередині набору, ви повинні поставити android: shareInterpolator = "false", щоб зробити його плавним.

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

Якщо Sharedinterpolator не відповідає дійсності, наведений вище код дає проблеми.


3

Якщо ви використовуєте такий набір Анімація, як я, вам слід додати інтерполяцію всередині тегу 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>

Це працювало для мене.


3

У Котліні:

 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

Чи можливо, що, оскільки ви переходите від 0 до 360, ви витрачаєте трохи більше часу на 0/360, ніж ви очікували? Можливо, встановити градуси на 359 або 358.


1
Чудова теорія. Я майже впевнений, що це не так, тому що прискорення / уповільнення виглядає досить плавно і навмисно. На всякий випадок, хоча я спробував зменшити градуси до 358, і не було помітних змін у поведінці.
emmby

1

Спробуйте використовувати більше 360, щоб уникнути перезавантаження.

Я використовую 3600 з 360, і це прекрасно працює для мене:

<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

В Android, якщо ви хочете анімувати об’єкт і змусити його переміщувати об’єкт з location1 на location2, API анімації визначає проміжні місця (тюнінг), а потім чергає на основну нитку відповідні операції переміщення у відповідний час за допомогою таймера . Це чудово працює, за винятком того, що основний потік зазвичай використовується для багатьох інших речей - малювання, відкриття файлів, відповіді на введення користувачів тощо. Добре написані програми завжди намагатимуться виконати якомога більше операцій у фонових (не головних) потоках, проте ви не завжди зможете уникнути використання основної нитки. Операції, які вимагають, щоб ви працювали над об’єктом інтерфейсу, завжди повинні виконуватися в основному потоці. Крім того, багато API спрямовують операції назад до основного потоку як форму безпеки потоку.

Усі представлення зображуються на одній темі GUI, яка також використовується для взаємодії з користувачами.

Тож якщо вам потрібно швидко оновлювати графічний інтерфейс, або якщо візуалізація займає занадто багато часу і впливає на досвід роботи користувачів, тоді використовуйте SurfaceView.

Приклад зображення обертання:

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

діяльність:

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
Хоча цей код може вирішити питання, краще додати контекст, чому / як він працює. Це може допомогти майбутнім користувачам навчитися та застосувати ці знання до власного коду. Ви також можете мати позитивні відгуки від користувачів у вигляді оновлень, коли пояснюється код.
borchvm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.