安卓:动画完成后位置重置

73

我正在使用XML定义的动画来将一个视图滑出屏幕。问题是,动画一旦完成就会重置回原始位置。我需要知道如何修复这个问题。以下是XML代码:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
   <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/></set>

以下是我用于调用它的 Java 代码:

    homeScrn = (View)findViewById(R.id.homescreen);
    slideLeftOut = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);

    //Set Click Listeners For Menu
    btnHelp.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            LayoutInflater.from(getApplicationContext()).inflate(R.layout.help, (ViewGroup)findViewById(R.id.subpage), true);
            homeScrn.startAnimation(slideLeftOut);
        }
    });

基本上我的做法是在一个视图下方放置另一个视图,然后将上方的视图向左移动进行动画。一旦它移出屏幕并完成动画,它就会重新回到原来的位置。


这是这个问题的答案:https://dev59.com/QGw05IYBdhLWcg3wXQsg#73496792 - John T
10个回答

123

最终找到了解决方法,正确的做法是setFillAfter(true)

如果你想在xml中定义动画,那么你应该像这样做

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/decelerate_interpolator"
     android:fillAfter="true">

    <translate 
        android:fromXDelta="0%"
        android:toXDelta="-100%"
        android:duration="1000"/>

</set>

您可以看到我在set标签中定义了filterAfter="true",如果您尝试在translate标签中定义它,则不起作用,这可能是框架中的一个错误!!

然后在代码中......

Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_out);
someView.startAnimation(anim);

或者

TranslateAnimation animation = new TranslateAnimation(-90, 150, 0, 0);

animation.setFillAfter(true);

animation.setDuration(1800);

someView.startAnimation(animation);

那么它肯定会起作用!

现在这有点棘手,似乎视图实际上移动到了新位置,但实际上是视图的像素被移动了,也就是说,您的视图实际上处于初始位置但不可见。如果您的视图中有一些按钮或可点击的视图(在我的情况下是布局),您可以测试一下。要解决这个问题,您需要手动将您的视图/布局移动到新位置。

public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)

new TranslateAnimation(-90, 150, 0, 0);

现在我们可以看到,我们的动画将从-90 x轴开始到150 x轴结束。

因此,我们要做的是设置

someView.setAnimationListener(this);

而且在

public void onAnimationEnd(Animation animation)
{
   someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}

现在让我解释一下public void layout(int left, int top, int right, int bottom)

它会将您的布局移动到新位置,第一个参数定义了左边界,我们设为150,因为平移动画已将视图移动到 150 x轴,top是0 ,因为我们没有沿y轴进行动画。现在,在右侧,我们执行someView.getWidth()+150基本上是获取我们视图的宽度并加上 150 ,因为我们的左侧现在移动到 150 x轴 ,以使视图的宽度恢复到原始状态,底部等于我们视图的高度。

希望您现在已经理解了平移的概念,如果还有任何问题,请立即在评论部分提问,我很乐意帮助:)

编辑:不要使用layout()方法,因为每当视图无效时,框架都可能调用它,并且您的更改不会持久保存。请使用LayoutParams根据需要设置布局参数。


谢谢您的解释,我也在使用这种方法,唯一的问题是当动画结束时,动画视图会闪烁。 - user65721
1
@user65721 你是否正在将你的视图移动到新位置??并且你尝试过 setFillEnabled() 吗?? - Muhammad Babar
2
我正在做@CommonsWare告诉我的事情,但是出现了闪烁的问题。设置setFillAfter(true)就解决了问题,谢谢! - Sulfkain

91
动画效果能够正常工作。仅仅因为你添加了动画并不意味着它已经移动了。动画只影响其绘制像素,而不影响小部件的配置。
你需要添加一个AnimationListener,在onAnimationEnd()方法中执行一些使你的移动成为永久性的操作(例如,将顶部视图从其父级中移除,将顶部视图标记为GONE以设置其可见性)。

4
如果必须要进行滑入操作,那该怎么办呢?由于它实际上并没有移动,那我怎么能够将已经滑出的对象再次滑入呢? - Ads
即使您在onAnimationEnd()中永久移动了View,它仍会闪烁。请参考:https://dev59.com/8HE85IYBdhLWcg3wtVxT - Saro Taşciyan
7
slideLeftOut.setFillAfter(true)? - Deepscorn

33

我可能有点晚回复,但我有解决方案:

只需在您的xml中添加android:fillAfter="true"


17
无法用于翻译动画。仅适用于缩放动画。 - Ads
1
@NicolasTyler,请确保在您的动画xml中的根元素中添加fillAfterfillEnabled - Gagan
使用XML顺序加载动画并启动会导致此问题,而使用fillafter为真的一个动画却完美运行。 - Ucdemir
@Ads 这对于我的my_scale_and_rotate_animation.xml动画起作用。也许如果你在translate_only_animation.xml动画中添加一个虚拟的<scale.../>元素(实际上不改变任何内容),然后向父级的<set.../>元素添加android:fillAfter="true",这对你来说也可能有效果? - ban-geoengineering
这个对我有效,我使用了带有透明度和平移动画的AnimationSet,将它们添加到根元素(AnimationSet)中就可以工作。 - Phong Nguyen

9
您可以使用

标签


fillAfter=true
fillEnabled=true

您的视图不会重置到原始位置,但如果您的视图中有一些按钮或其他东西,则它们的位置将不会改变。

您必须使用ObjectAnimator,它适用于API 11级以上。它可以自动更改视图位置。

以下是示例:

ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mContent_container, "translationX", startX, endX);
objectAnimator.setDuration(1000);
objectAnimator.start();

感谢JUL提供的答案

如果您的应用程序找不到object animator,请将API级别从项目 -> 属性 -> Android更改,然后import android.animation.ObjectAnimator;

敬礼 Hayk


7
你需要添加这个命令:
final Animation animSlideLeft = new TranslateAnimation(0, -80, 0,-350, 0, 0, 0, 0);
animSlideLeft.setFillAfter(true);

1
这个问题困扰了我一个多星期。 Muhammad的答案给了我一个简单直接的解决方法。
我使用了几种布局来实现带动画的侧边菜单。 “view.layout不会持久。您应该使用LayoutParams,它将是持久的。”
以下是我的解决方案: (使用哪种LayoutParameter取决于您自己的父布局):
@Override
public void onAnimationEnd(Animation animation) {
FrameLayout.LayoutParams layoutParams=new FrameLayout.LayoutParams(screenWidth, screenHeight);
layoutParams.setMargins((int) -(screenWidth * RATE), 0,
            (int) (screenWidth - screenWidth * RATE), screenHeight);
    for(View v:views) {

        v.setLayoutParams(layoutParams);
        //v.layout((int) -(screenWidth * RATE), 0, (int) (screenWidth - screenWidth * RATE), screenHeight);
        v.clearAnimation();
    }
}

1

我遇到了同样的问题,并通过在OnAnimationEnd()中设置marginLeft来解决它。

MarginLayoutParams params = (MarginLayoutParams) this.getLayoutParams(); params.setMargins(this.getWidth()*position, 0,0,0); this.setLayoutParams(params);


1

fillAfter = true会将视图转换为新的动画位置。

rotateAnimation.fillAfter = true;

如果有人对将arrow_up和arrow_down动画移动到回收视图的展开/折叠行为感兴趣。

  1. 初始状态:折叠
  2. 调用带有子元素可见性标志(View.VISIBLE / View.GONE)的方法。

以下是动画代码。

fun setArrowImage(imageView: ImageView, childVisibility: Int) {

    val angleRange = if (childVisibility == View.VISIBLE) Pair(0F, 180F) else 
    Pair(100f, 0F)

    val rotateAnimation = RotateAnimation(
        angleRange.first, angleRange.second,
        Animation.RELATIVE_TO_SELF, 0.5f,
        Animation.RELATIVE_TO_SELF, 0.5f
    )
    rotateAnimation.duration = 300
    rotateAnimation.fillAfter = true;

    imageView.startAnimation(rotateAnimation)
}

0

所有的答案都对我没用,尽管@Muhammad Babar的回答启发了我找到了解决方案:

他提到我们可以在动画结束时将视图布局到新位置:

public void onAnimationEnd(Animation animation) {
   someView.layout(150, 0, someView.getWidth() + 150, someView.getHeight());
}

虽然这种方法对我不起作用,但我必须使用延迟的Runnable在Handler中执行,延迟值略小于动画持续时间。

因此,在我的情况下,动画持续时间为500毫秒,我使用了450毫秒的处理程序,因此它会在动画结束前50毫秒触发。

Handler().postDelayed({
    someView.layout(150, 0, someView.width + 150, someView.hight)
    someView.requestLayout()
    someView.forceLayout()
}, 450)

0
使用下面的辅助方法轻松地为您的视图添加动画效果。然后,您可以像这样在完成后进行动画和重置

// Animate the view:

fly(
  view1,
  (float) 0,
  (float) 0,
  (float) 0,
  (float) 1000,
  250,
  null,
  null,
  () -> {

    // Return the view to initial position on animation end:  

    fly(
      view1,
      (float) 0,
      (float) 0,
      (float) 0,
      (float) 0,
      0);

    return null;
  });

辅助方法:

/**
 * Translation of a view.
 */
public static void fly(
  View view,
  float fromXDelta,
  float toXDelta,
  float fromYDelta,
  float toYDelta,
  int duration) {

  fly(
    view,
    fromXDelta,
    toXDelta,
    fromYDelta,
    toYDelta,
    duration,
    null,
    null,
    null);
}

/**
 * Translation of a view with handlers.
 *
 * @param view       A view to animate.
 * @param fromXDelta Amount to shift by X axis for start position.
 * @param toXDelta   Amount to shift by X axis for end position.
 * @param fromYDelta Amount to shift by Y axis for start position.
 * @param toYDelta   Amount to shift by Y axis for end position.
 * @param duration   Animation duration.
 * @param start      On animation start. Otherwise NULL.
 * @param repeat     On animation repeat. Otherwise NULL.
 * @param end        On animation end. Otherwise NULL.
 */
public static void fly(
  View view,
  float fromXDelta,
  float toXDelta,
  float fromYDelta,
  float toYDelta,
  int duration,
  Callable start,
  Callable repeat,
  Callable end) {

  Animation animation;

  animation =
    new TranslateAnimation(
      fromXDelta,
      toXDelta,
      fromYDelta,
      toYDelta);

  animation.setDuration(
    duration);

  animation.setInterpolator(
    new DecelerateInterpolator());

  animation.setFillAfter(true);

  view.startAnimation(
    animation);

  animation.setAnimationListener(new AnimationListener() {

    @Override
    public void onAnimationStart(Animation animation) {

      if (start != null) {
        try {

          start.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }

    }

    @Override
    public void onAnimationEnd(Animation animation) {

      if (end != null) {
        try {

          end.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }

    @Override
    public void onAnimationRepeat(
      Animation animation) {

      if (repeat != null) {
        try {

          repeat.call();

        } catch (Exception e) {
          e.printStackTrace();
        }
      }  
    }
  });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接