Как сделать плавное вращение изображения в Android?
Я использую 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
), но я не знаю, как сказать ему, чтобы не интерполировать анимацию.
как я могу отключить интерполяцию (если это действительно проблема), чтобы сделать мой цикл анимации гладко?
14 ответов:
вы правы насчет 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"
Примечание: если ваша анимация поворота находится внутри набора, установка интерполятора не кажется работать. Выполнение поворота верхнего элемента фиксирует его. (это сэкономит ваше время.)
У меня тоже была эта проблема, и я попытался установить линейный интерполятор в 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);
это хорошо
<?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" />
может, что-то вроде этого поможет:
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)...
подрезка
<set>
-элемент, который обернул<rotate>
-элемент решает проблему!спасибо Shalafi!
Так что ваш 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" />
независимо от того, что я пытался, я не мог заставить это работать правильно, используя код (и 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); }
Как уже упоминал хэнри выше, установка линейного итерполятора прекрасна. Но если вращение находится внутри набора, вы должны поставить 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 не является ложным, приведенный выше код дает сбои.
Если вы используете анимацию набора, как я, вы должны добавить интерполяцию внутри тега набора:
<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>
это сработало для меня.
возможно, потому что вы идете от 0 до 360, вы тратите немного больше времени на 0/360, чем вы ожидали? Возможно, установите toDegrees в 359 или 358.
В Android, Если вы хотите анимировать объект и заставить его переместить объект из location1 в location2, API анимации вычисляет промежуточные местоположения (tweening), а затем ставит в очередь на основной поток соответствующие операции перемещения в соответствующее время с помощью таймера. Это прекрасно работает, за исключением того, что основной поток обычно используется для многих других вещей - рисования, открытия файлов, ответа на пользовательские входы и т. д. Таймер в очереди часто может быть отложен. Хорошо написанные программы будут всегда попробуйте выполнить как можно больше операций в фоновых (не основных) потоках, однако вы не всегда можете избежать использования основного потока. Операции, требующие работы с объектом пользовательского интерфейса, всегда должны выполняться в основном потоке. Кроме того, многие 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)); } }
попробуйте использовать более 360, чтобы избежать перезагрузки.
Я использую 3600 insted 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%" />
В Котлин:
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) })