Android TranslateAnimation在动画后重置

时间:2010-04-16 03:10:54

标签: android animation

我正在创建像SlideDrawer这样的东西,但是大多数自定义,基本上这个东西都在工作,但最后动画闪烁。

为了进一步解释,我得到了一个TranslateAnimation然后在这个动画之后它返回到原始位置,如果我设置了setFillAfter,那么布局中的按钮就会停止工作。如果我听onAnimationEnd并将其他布局设置为View.GONE布局fickers。从它的判断来看,在动画结束时,视图会在调用View.GONE之前返回到原始位置。

任何建议都很棒。感谢

6 个答案:

答案 0 :(得分:45)

Here是与此问题相关的实际错误

这基本上表明当AnimationListener附加到动画时onAnimationEnd(...)方法不能很好地工作

解决方法是在您将动画应用到的视图中侦听动画事件。例如,如果您最初将动画侦听器附加到此类动画

mAnimation.setAnimationListener(new AnimationListener() {
    @Override
    public void onAnimationEnd(Animation arg0) {
                       //Functionality here
    }

然后将动画应用到ImageView这样的

mImageView.startAnimation(mAnimation);

要解决此问题,您现在必须创建自定义ImageView

public Class myImageView extends ImageView {

然后覆盖View类的onAnimationEnd方法并提供其中的所有功能

@Override
protected void onAnimationEnd() {
    super.onAnimationEnd();
    //Functionality here
}

这是此问题的正确解决方法,在over-riden View中提供功能 - > onAnimationEnd(...)方法,而不是附加到动画的AnimationListener的onAnimationEnd(...)方法。

这可以正常工作,并且在动画结束时不再有任何闪烁。希望这有帮助

答案 1 :(得分:24)

从API 11开始,您可以使用实际更改视图属性的ObjectAnimator,即在转换的情况下,视图将保留在动画后到达的位置。

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

更多here

答案 2 :(得分:4)

Soham上面的回答对我有用,虽然值得指出(因为在我第一次阅读这个帖子时并不是很明显),你仍然可以通过设置一个单独的监听器来获得与动画监听器几乎相同的行为视图将在视图onAnimationStart()onAnimationEnd()的末尾运行。

例如,如果您的代码需要在动画期间禁用按钮:

Animation a = getAnimation(/* your code */);
a.setDuration(1000);
a.setAnimationListener(new AnimationListener() {
   @Override
   public void onAnimationStart(Animation arg0) {
     myButton.setEnabled(false);
   }

   @Override
   public void onAnimationEnd(Animation arg0) {
     myButton.setEnabled(true);
   }
});
someView.startAnimation(a);

目前,someView不了解myButton,我希望保持这种状态。您可以在自定义视图类上创建一些以相同方式调用的侦听器:

public final class SomeView extends View {
    // other code

    public interface RealAnimationListener {
      public void onAnimationStart();
      public void onAnimationEnd();
    }

    private RealAnimationListener mRealAnimationListener;

    public void setRealAnimationListener(final RealAnimationListener listener) {
      mRealAnimationListener = listener;
    }

    @Override
    protected void onAnimationStart() {
      super.onAnimationStart();
      if (mRealAnimationListener != null) {
         mRealAnimationListener.onAnimationStart();
      }
    }

    @Override
    protected void onAnimationEnd() {
      super.onAnimationEnd();
      if (mRealAnimationListener != null) {
         mRealAnimationListener.onAnimationEnd();
      }
    }
}

然后回到你的其他代码(可能是一个Activity):

Animation a = getAnimation(/* your code */);
a.setDuration(1000);
someView.setRealAnimationListener(new RealAnimationListener() {
   @Override
   public void onAnimationStart() {
     myButton.setEnabled(false);
   }

   @Override
   public void onAnimationEnd() {
     myButton.setEnabled(true);
   }
});
someView.startAnimation(a);

通过这种方式,您可以使组件保持干净,同时仍然可以使AnimationListener正常工作。

答案 3 :(得分:2)

使用Soham的答案,这里是一个专门用于淡化动画的ImageView:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

/*
 * Custom view to prevent flickering on animation end
 * 
 * http://stackoverflow.com/questions/2650351/android-translateanimation-resets-after-animation
 */
public class FadeableImageView extends ImageView {

public FadeableImageView(Context context) {
    super(context);
}

public FadeableImageView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public FadeableImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

@Override
protected void onAnimationEnd() {
    super.onAnimationEnd();
    this.setVisibility(View.GONE);
}
}

这是我的动画代码:

protected void startSplash() {
    final FadeableImageView splash = (FadeableImageView) findViewById(R.id.splash);

    Animation fadeOut = new AlphaAnimation(1, 0);
    fadeOut.setDuration(2000);
    splash.startAnimation(fadeOut);
}

答案 4 :(得分:1)

摆脱setFillAfter并在View.GONE中使用onAnimationEnd()。有关使用TranslateAnimation实现滑动面板的示例自定义视图,请参阅here

答案 5 :(得分:0)

所以,我正在为我的Xamarin项目寻找答案,但我想它也应该适用于Java。我的实现是,动画的LinearLayout始终具有相同的位置(例如,它在x = 100,y == 100),并且您的动画应该与此位置相对。 ObjectAnimator绝对是最佳选择,这是我的解决方案:

首先,一个简单的布局,顶部有一些文字,下面的LinearLayout是动画的目标......

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:p1="http://schemas.android.com/apk/res/android"
    p1:minWidth="25px"
    p1:minHeight="25px"
    p1:layout_width="match_parent"
    p1:layout_height="match_parent"
    p1:id="@+id/frameLayout1">
    <TextView
        p1:text="Some text at the top"
        p1:textAppearance="?android:attr/textAppearanceLarge"
        p1:id="@+id/txtSomeTextAtTheTop"
        p1:layout_width="wrap_content"
        p1:layout_height="wrap_content"
        p1:layout_gravity="center_horizontal" />
    <LinearLayout
        p1:orientation="vertical"
        p1:minWidth="25px"
        p1:minHeight="25px"
        p1:layout_width="wrap_content"
        p1:layout_height="wrap_content"
        p1:id="@+id/linMySlider"
        p1:layout_gravity="center_horizontal|bottom">
        <LinearLayout
            p1:orientation="horizontal"
            p1:minWidth="25px"
            p1:minHeight="25px"
            p1:layout_width="match_parent"
            p1:layout_height="wrap_content"
            p1:id="@+id/linAlwaysDisplay"
            p1:layout_marginBottom="10px">
            <TextView
                p1:text="ALWAYS ON DISPLAY"
                p1:textAppearance="?android:attr/textAppearanceLarge"
                p1:id="@+id/txtAlwaysDisplay"
                p1:layout_width="wrap_content"
                p1:layout_height="wrap_content"
                p1:layout_gravity="center_horizontal" />
        </LinearLayout>
        <LinearLayout
            p1:orientation="horizontal"
            p1:minWidth="25px"
            p1:minHeight="25px"
            p1:layout_width="match_parent"
            p1:layout_height="wrap_content"
            p1:id="@+id/linToHideLineOne">
            <TextView
                p1:text="To Hide Line One"
                p1:textAppearance="?android:attr/textAppearanceLarge"
                p1:id="@+id/txtHideLineOne"
                p1:layout_width="wrap_content"
                p1:layout_height="wrap_content"
                p1:layout_gravity="center_horizontal" />
        </LinearLayout>
        <LinearLayout
            p1:orientation="horizontal"
            p1:minWidth="25px"
            p1:minHeight="25px"
            p1:layout_width="match_parent"
            p1:layout_height="wrap_content"
            p1:id="@+id/linHideLineTwo">
            <TextView
                p1:text="To Hide Line Two"
                p1:textAppearance="?android:attr/textAppearanceLarge"
                p1:id="@+id/txtHideLineTwo"
                p1:layout_width="wrap_content"
                p1:layout_height="match_parent" />
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

然后,我的活动如下所示:

using System;

using Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android.Animation;
using Android.Views.Animations;
using Android.Util;

namespace MyNamespace
{
    [Activity(Label = "testActivity")]
    public class testActivity : Activity
    {
        public static string TAG = "M:testActivity";


        //by default we want the slider to be closed, which is why
        // _sliderOpen has been set to true and we animate it into position when
        //the window gets first focus
        private bool _sliderOpen = true;

        private ViewGroup _linMySlider;
        private LinearLayout _linAlwaysDisplays;

        private int _distanceToTravel;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.testLayout);

            _linMySlider = FindViewById<ViewGroup>(Resource.Id.linMySlider);
            _linAlwaysDisplays = FindViewById<LinearLayout>(Resource.Id.linAlwaysDisplay);

            TextView alwaysDisplayText = FindViewById<TextView>(Resource.Id.txtAlwaysDisplay);
            alwaysDisplayText.Click += AlwaysDisplayText_Click;
        }

        private void AlwaysDisplayText_Click(object sender, EventArgs e)
        {
            DoAnimation(500);
        }

        public override void OnWindowFocusChanged(bool hasFocus)
        {
            base.OnWindowFocusChanged(hasFocus);

            if (hasFocus)
            {
                if (_sliderOpen)
                {
                    //we store this one time as it remains constant throught our sliding animations
                    _distanceToTravel = _linMySlider.Height - _linAlwaysDisplays.Height;
                    DoAnimation(1);
                }
            }
        }

        private void DoAnimation(long duration)
        {
            ObjectAnimator slideMe = null;

            try
            {
                switch (_sliderOpen)
                {
                    case true:
                        slideMe = ObjectAnimator.OfFloat(_linMySlider, "translationY", 0, _distanceToTravel);
                        _sliderOpen = false;
                        break;
                    case false:
                        slideMe = ObjectAnimator.OfFloat(_linMySlider, "translationY", _distanceToTravel, 0);
                        _sliderOpen = true;
                        break;
                }
                slideMe.SetInterpolator(new OvershootInterpolator());
                slideMe.SetDuration(duration);
                slideMe.Start();
            }
            catch (Exception e)
            {
                Log.Error(TAG, "DoAnimation: Exception - " + e.Message);
            }
        }
    }
}

最重要的一点是_distanceToTravel(在这种情况下,在Y轴上进行转换)是相对于我们动画的LinearLayout的Top属性。假设保持文本的每个LinearLayouts(总是显示,隐藏第一行,隐藏第二行)的高度为20(使总高度为60)。例如,滑块的Top属性为2100.因为它位于底部,隐藏两条线意味着我们必须将LinearLayout linMySlider向下移动40以隐藏两条线,只留下第一条可见线。如果你认为LinearLayout总是为2100,那么在滑下来我们就会加40(好吧,不是我们,Animator为我们做了这个),这在第一个OfFloat线中很明显,其中起始Y位置为0(即相对于2100为0,因此等于2100),其结束Y位置为_distanceToTravel(为40,但相对等于,实际上为2140)。在相反的方向上,我们从Y的_distanceToTravel开始(再次是40,但实际上是2140),我们以0结束(你猜测它距离2100,因此,2100)。

希望这一切都有意义 - 我花了一点时间让便士下降,但它工作得很好,没有闪烁,也没有重置回原来的位置(它总是有大声笑)。希望这与C#示例中的Java代码相同。