【问题标题】:How to make a smooth image rotation in Android?如何在 Android 中进行平滑的图像旋转?
【发布时间】:2010-12-10 16:38:13
【问题描述】:

我正在使用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" />    

当我使用AndroidUtils.loadAnimation() 将它应用到我的ImageView 时,效果很好!

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

一个问题是图像旋转似乎在每个周期的顶部暂停。

换句话说,图像旋转 360 度,短暂暂停,然后再次旋转 360 度,等等。

我怀疑问题在于动画使用了默认插值器,例如 android:iterpolator="@android:anim/accelerate_interpolator" (AccelerateInterpolator),但我不知道如何告诉它不要插值动画。

如何关闭插值(如果这确实是问题)以使我的动画循环顺畅?

【问题讨论】:

    标签: android animation view


    【解决方案1】:

    是否有可能因为您从 0 到 360,您在 0/360 上花费的时间比您预期的要多一些?也许将 toDegrees 设置为 359 或 358。

    【讨论】:

    • 伟大的理论。我很确定这不是因为加速/减速非常平滑且经过深思熟虑。以防万一,尽管我尝试将度数降低到 358,但行为没有明显变化。
    【解决方案2】:

    关于 AccelerateInterpolator 是对的;您应该改用 LinearInterpolator。

    您可以将动画 XML 文件中的内置 android.R.anim.linear_interpolatorandroid: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"
    

    特别说明:如果您的旋转动画在集合内,则设置插值器似乎不起作用。使旋转顶部元素修复它。 (这将节省您的时间。)

    【讨论】:

    • 这是因为插值器拼写错误(没有“n”)。你不需要自己制作
    • 我已经尝试了所有可用的插值器,包括线性插值器,但在每个周期开始时我仍然会遇到这个小“障碍”。
    • 如果您的旋转动画在一个集合内,则设置插值器似乎不起作用。使旋转顶部元素修复它
    • 嘿,如果你真的想在每个动画之间没有小“暂停”的情况下使用Accelerate_decelerate_interpolator呢?
    【解决方案3】:

    尝试使用toDegrees="359",因为 360° 和 0° 相同。

    【讨论】:

      【解决方案4】:

      无论我如何尝试,我都无法使用代码(和 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);
              }
      

      【讨论】:

      • 你的“动画”变量声明在哪里?
      【解决方案5】:

      这很好用

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

      【讨论】:

      • 对于反向,只需要重复计数 2 并设置 android:repeatMode="reverse" - 不需要有两个不同的 XML 文件。
      • 为什么是 358 而不是 359 或 360?
      • 在资源中把这个文件添加到哪里?动画是一个单独的包吗?
      【解决方案6】:

      修剪包裹&lt;rotate&gt;-Element 的&lt;set&gt;-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"
          />
      

      【讨论】:

        【解决方案7】:

        正如 hanry 上面提到的那样,放置 liner iterpolator 很好。但如果旋转在一个集合内,你必须设置 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 不为 false,则上面的代码会出现故障。

        【讨论】:

          【解决方案8】:

          我也遇到了这个问题,尝试在 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);
          

          【讨论】:

          • 如果想让动画保持在最后的方向,加rotate.setFillAfter(true);
          • 如果你想让动画永久保持没有结束,请添加rotate.setRepeatCount(Animation.INFINITE);
          • 如果你想让物体无限旋转,你需要设置从180到360的度数。另外,需要Fonix提到的setFillAfter
          【解决方案9】:

          在 Android 中,如果您想为对象设置动画并使其将对象从位置 1 移动到位置 2,动画 API 会计算出中间位置(补间),然后在适当的时间将适当的移动操作排队到主线程中使用计时器。这很好用,除了主线程通常用于许多其他事情——绘画、打开文件、响应用户输入等。排队的计时器通常会被延迟。写得好的程序总是会尝试在后台(非主)线程中执行尽可能多的操作,但是你不能总是避免使用主线程。需要您对 UI 对象进行操作的操作始终必须在主线程上完成。此外,许多 API 会将操作返回到主线程,作为线程安全的一种形式。

          视图都绘制在同一个 GUI 线程上,该线程也用于所有用户交互。

          因此,如果您需要快速更新 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));
              }
          }
          

          【讨论】:

            【解决方案10】:

            如果你像我一样使用 set Animation,你应该在 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>
            

            这对我有用。

            【讨论】:

              【解决方案11】:

              也许,这样的事情会有所帮助:

              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() 是否也会清理可运行对象?
              • 它在开始动画时工作正常,但在开始运行后它不能被 imageView.clearAnimation() 清理,我在触摸监听事件中使用了
              • 无法独立停止这些可运行对象
              • @Pants 您可以在某些情况下调用 withEndAction(this)。如果条件为假, withEndAction(this) 将不会被调用,动画应该停止
              【解决方案12】:
              ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();
              

              试试这个。

              【讨论】:

                【解决方案13】:

                尝试使用超过 360 以避免重新启动。

                我使用 3600 insted of 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%" />
                

                【讨论】:

                  【解决方案14】:

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

                  【讨论】:

                    【解决方案15】:

                    以编程方式旋转对象。

                    //顺时针旋转:

                        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 是您的动画持续时间。

                    【讨论】:

                      【解决方案16】:
                      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()
                      }
                      

                      【讨论】:

                      • 虽然此代码可能会为问题提供解决方案,但最好添加有关其工作原理/方式的上下文。这可以帮助未来的用户学习并将这些知识应用到他们自己的代码中。在解释代码时,您也可能会以赞成票的形式从用户那里获得积极的反馈。
                      【解决方案17】:

                      这是对我来说很好用的代码 sn-p:

                          RotateAnimation rotate = new RotateAnimation(
                                  0, 359,
                                  Animation.RELATIVE_TO_SELF, 0.5f,
                                  Animation.RELATIVE_TO_SELF, 0.5f
                          );
                      
                          rotate.setDuration(1500);
                          rotate.setRepeatCount(Animation.INFINITE);
                          yourView.startAnimation(rotate);
                      

                      在构造函数中检查它不是 360 而是 359,因为 0 和 360 在同一点。

                      【讨论】:

                        【解决方案18】:

                        您可以使用此代码:

                        view.animate().rotation(360.0f).setDuration(1000);
                        

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2011-05-19
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-11-30
                          • 2011-07-20
                          • 2012-11-04
                          • 2012-11-17
                          相关资源
                          最近更新 更多