2010-04-16 232 views
24

我創建了一個類似於SlideDrawer的東西,但大部分定製都基本上正常,但動畫在最後閃爍。Android TranslateAnimation在動畫後重置

爲了進一步解釋,我得到了一個TranslateAnimation那麼這個動畫它返回到原來的位置後,如果我設置setFillAfter然後裏面的按鈕佈局停止工作。如果我聽onAnimationEnd並將其他的佈局設置爲View.GONE佈局fickers。從動畫結束來看,在View.GONE被調用之前,視圖會回到原來的位置。

任何意見將是真棒。謝謝

回答

43

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 
} 

這是這個問題的正確解決方法,提供的功能中過riden視圖 - 而不是附連到動畫的AnimationListener的onAnimationEnd(...)方法>onAnimationEnd(...)方法。

這個工作是否正常,不再有對動畫的結尾任何閃爍。希望這可以幫助

+0

這對我很有幫助,非常感謝。 – 2011-04-21 19:52:55

+0

我很高興Nick。 – Soham 2012-08-14 10:58:48

+0

謝謝,這個作品如上所述,浪費時間才找到你的帖子! – radhoo 2012-09-09 11:36:12

1

擺脫setFillAfter和只使用View.GONEonAnimationEnd()。爲實現使用TranslateAnimation滑動面板來樣定製View顯示here

+0

-1該示例不是對動畫重置的響應。 – 2010-11-12 11:31:23

+0

@ahmet emrah:你的聲明證明是......究竟是什麼? – CommonsWare 2010-11-12 12:00:09

+0

當我們需要類似的連續事件的視圖時,使視圖「消失」不是一種選擇。例如。再次點擊動畫按鈕,再次使用動畫滑塊等。 – radhoo 2012-09-09 11:37:25

3

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); 

目前,不知道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(); 
     } 
    } 
} 

然後回到你的其他代碼(可能是一個活動):

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。

23

從API 11中,您可以使用ObjectAnimator,它實際上更改視圖屬性,即在翻譯的情況下,視圖將保留在它在動畫之後到達的位置。

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

更多here

+0

FYI您可以使用9oldAndroids lib的老版本的android使用相同的api。 http://nineoldandroids.com – Patrick 2012-07-09 23:10:50

+0

@帕特里克這可能很酷,我會試一試 – 2cupsOfTech 2012-07-12 11:40:15

+1

謝謝你,它結束了我掙扎的2天。 – Bhimbim 2014-03-25 11:20:28

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); 
} 
+1

給出java.lang.ClassCastException:android.widget.ImageView無法轉換爲com.animate.FadeableImageView。可能是什麼原因? – Joyson 2013-07-03 11:39:52

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屬性。假設每個包含文本的LinearLayout(總是顯示,隱藏第一行,隱藏第二行)的高度爲20(使總高度爲60)。例如,滑塊的頂部屬性爲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)。

希望這一切都合情合理 - 我花了一段時間讓一分錢掉落,但它工作得很好,沒有閃爍,也沒有重置到原來的位置(它總是有大聲笑)。希望Java代碼也適用於這個C#示例。