【问题标题】:Single Animation - Multiple Views单个动画 - 多个视图
【发布时间】:2012-02-09 19:21:10
【问题描述】:

有没有办法同时为多个视图设置动画?

我想做的是翻译动画:

我有 5 个 TextViews 和 4 个彩色条带(带有背景的普通 RelativeLayouts)。在动画开始时,stip 与 TextView 堆叠在水平行中。最后,我希望所有 TextViews 都堆叠在条带之间:

这是一个非常简单的绘图,但它展示了我想要做什么。有没有办法用动画做到这一点,或者我必须使用画布动画。

【问题讨论】:

    标签: android animation views


    【解决方案1】:

    您可以使用 ObjectAnimator 为多个视图设置动画,如下所示:

    ArrayList<ObjectAnimator> arrayListObjectAnimators = new ArrayList<ObjectAnimator>(); //ArrayList of ObjectAnimators
    
    ObjectAnimator animY = ObjectAnimator.ofFloat(view, "y", 100f);
    arrayListObjectAnimators.add(animY);
    
    ObjectAnimator animX = ObjectAnimator.ofFloat(view, "x", 0f);
    arrayListObjectAnimators.add(animX);
    ...
    ObjectAnimator[] objectAnimators = arrayListObjectAnimators.toArray(new ObjectAnimator[arrayListObjectAnimators.size()]);
    AnimatorSet animSetXY = new AnimatorSet();
    animSetXY.playTogether(objectAnimators);
    animSetXY.setDuration(1000);//1sec
    animSetXY.start();
    

    【讨论】:

    • 唯一的缺点是从版本 11 (honeycomb) 开始是新的
    • 使用 Nineoldandroids 将 API 一路带回 1.0 (nineoldandroids.com)
    • 这不是一次为多个对象设置动画,而是在同一个对象上同时运行多个动画。
    • 我下载了 Tushar 提到的库 NineOldAndroids,并准备使用它,但后来发现它的自述文件通知了该库已弃用,所以我想我应该在这里分享:“NineOldAndroids 已弃用。不会进行新的开发。现有版本(当然)将继续运行。新应用程序应使用可以访问平台动画 API 的minSdkVersion="14" 或更高版本。"
    • 只是为了添加..你也可以避免使用 ArralyList 而只使用 playTogher(animY, animY1) 例如。
    【解决方案2】:

    创建您的动画对象,然后在所有视图上同时使用startAnimation。所以它会是这样的:

    TranslateAnimation anim1;
    TranslateAnimation anim2;
    TranslateAnimation anim3;
    
    // Setup the animation objects
    
    public void startAnimations()
    {
       //... collect view objects
       view1.startAnimation(anim1);
       view2.startAnimation(anim2);
       view3.startAnimation(anim3);
    }
    

    请注意,您一次播放的动画越多,播放的速度就越慢。

    【讨论】:

    • 这不会导致它们不同步(这里不是必需的),尤其是稍后启动的那些?
    • 并非如此。基本上发生的情况是当您在视图上使用startAnimation 时,它开始使自己失效,直到它到达所需的位置。当您一次在所有视图上调用它时,它们都会对自己调用invalidate(),然后在下一个绘图过程中,每个视图都将在它们的下一帧中绘制。由于您无论如何都在 UI 线程上调用所有这些,因此在您从该方法返回之前,不会启动任何动画。注意:使用 pre-Honeycomb 动画框架只会移动视图的视觉部分。用户将无法点击它。
    • 我用 anim1.setFillAfter(true);然后使用布局参数实际移动视图? (我应该尝试一下,但已经晚了:)
    • 一般是这样的,是的。虽然它可能很麻烦。就我个人而言,我从来不需要在动画之后实际移动视图。我相信虽然使用平移动画,但您会根据动画调整边距,所以如果您要向左移动 30dp,您可以使用任何 marginRight ,然后添加 30dp。
    • 由于您在主线程上启动所有这些动画,并且动画也在主线程的循环中执行,因此所有动画将同时启动。
    【解决方案3】:

    你可以使用AnimationSet

    AnimatorSet decSet2 = new AnimatorSet();
            decSet2.playTogether(
                    ObjectAnimator.ofFloat(view, "x",dX),
                    ObjectAnimator.ofFloat(view, "y",dY),
                    ObjectAnimator.ofFloat(mTextCancel, "x",dX),
                    ObjectAnimator.ofFloat(mTextCancel, "y", dY),
                    ObjectAnimator.ofArgb(mBtnOne, "visibility", View.VISIBLE, View.GONE),
                    );
            decSet2.setDuration(0);
            decSet2.start();
    

    【讨论】:

      【解决方案4】:

      我已经设法在多个视图之间共享一个动画实例。至少有一个 AlphaAnimation。我有一个 ListView 和一个动画,应该应用于所有列表项视图的特定子项。在我的情况下,视图应该可以随时“加入”和“离开”共享动画,并且它不应该以任何方式影响其他动画视图或干扰已经运行的动画。 为了实现这一点,我必须制作一个经过调整的 android 库存 AlphaAnimation 副本。 我的用例相当特殊,但让它放在这里以防万一有人不得不用 ListView 处理类似的目标。

      /**
       * This class is a copy of android's stock AlphaAnimation with two adjustments:
       * - fromAlpha, toAlpha and duration are settable at any time.
       * - reset() method can be blocked. Reason: view.setAnimation() calls animation's reset() method
       * which is not intended in our use case.
       * For every new list item view we call setAnimation once for it's life time
       * and animation should not be reset because it is shared by all list item views and may be in progress. 
       */
      public class MutableAlphaAnimation extends Animation {
          private float mFromAlpha;
          private float mToAlpha;
          private boolean resetBlocked;
      
          public MutableAlphaAnimation() {
          }
      
          public void start(float fromAlpha, float toAlpha, long duration) {
              mFromAlpha = fromAlpha;
              mToAlpha = toAlpha;
              setDuration(duration);
              setStartTime(START_ON_FIRST_FRAME);
          }
      
          public void setResetBlocked(boolean resetBlocked) {
              this.resetBlocked = resetBlocked;
          }
      
          @Override
          public void reset() {
              if (! resetBlocked) super.reset();
          }
      
          @Override
          protected void applyTransformation(float interpolatedTime, Transformation t) {
              final float alpha = mFromAlpha;
              t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
          }
      
          @Override
          public boolean willChangeTransformationMatrix() {
              return false;
          }
      
          @Override
          public boolean willChangeBounds() {
              return false;
          }
      }
      

      将此动画设置为视图:

                  animation.setResetBlocked(true);
                  view.setAnimation(animation);
                  animation.setResetBlocked(false);
      

      要启动动画(之前由 setAnimation 设置),必须做两件事:

              animation.start(0.0f, 1.0f, FADE_IN_DURATION);
      

      之后,您必须在每个受动画影响的视图上手动调用 invalidate()。

      通常的 startAnimation() 会为您执行 invalidate(),但 setAnimation 不会。 (阅读 android 源代码中对 View.setAnimation() 方法的评论)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多