Android TranslateAnimation сбрасывается после анимации

Я создаю что-то вроде SlideDrawer, но с большинством настроек, в принципе, это работает, но анимация мерцает в конце.

Для дальнейшего объяснения я получил TranslateAnimation, затем после этой анимации она возвращается в исходное положение, если я установил setFillAfter, то кнопки внутри макета перестают работать. Если я слушаю onAnimationEnd и устанавливаю макет другого в View.GONE, макет перестает работать. Судя по тому, что в конце анимации вид возвращается в исходное положение до вызова View.GONE.

Любой совет будет потрясающим. Спасибо

6 ответов

Решение

Вот фактическая ошибка, связанная с этой проблемой

Это в основном утверждает, что onAnimationEnd(...) метод не очень хорошо работает, когда AnimationListener присоединен к Animation

Обходной путь - прослушивать события анимации в представлении, к которому вы применяли анимацию. Например, если изначально вы подключали слушателя анимации к анимации, как это

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

а затем применить к анимации к ImageView как это

mImageView.startAnimation(mAnimation);

Чтобы обойти эту проблему, вы должны создать ImageView

public Class myImageView extends ImageView {

а затем переопределите метод onAnimationEnd класса View и предоставьте там все функции

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

Это правильный способ решения этой проблемы, предоставьте функциональность в переопределенном представлении -> onAnimationEnd(...) метод в отличие от onAnimationEnd(...) метод AnimationListener, прикрепленный к анимации.

Это работает правильно, и больше нет мерцания в конце анимации. Надеюсь это поможет

Из API 11 вы можете использовать ObjectAnimator, который фактически изменяет свойства вида, то есть в случае перевода вид останется в том положении, в котором он находится после анимации.

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

Больше здесь.

Приведенный выше ответ Сохама работает для меня, хотя стоит отметить (так как я не сразу понял, когда впервые читал эту ветку), что вы все равно можете получить почти то же поведение, что и слушатель анимации, установив отдельного слушателя в представлении. быть запущенным в конце вашего просмотра 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, который работает.

Используя ответ Сохэма, вот 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://stackru.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);
}

Избавляться от setFillAfter и просто использовать View.GONE в onAnimationEnd(), Смотрите здесь для примера пользовательского представления, которое реализует скользящую панель, используя TranslateAnimation,

Итак, я искал ответ на этот вопрос для своего проекта 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) относится к свойству Top объекта LinearLayout, который мы анимируем. Предположим, что каждый из LinearLayouts, который содержит текст (ВСЕГДА НА ОТОБРАЖЕНИИ, Чтобы скрыть строку 1, Чтобы скрыть строку 2), имеет высоту 20 (что делает общую высоту 60). Слайдер, скажем, имеет свойство Top 2100. Поскольку он расположен внизу, чтобы скрыть две линии, нам нужно переместить LinearLayout linMySlider вниз на 40, чтобы скрыть две строки, оставив только первую видимую. Если вы думаете, что LinearLayout ВСЕГДА равен 2100, то имеет смысл, что на слайде мы добавим к нему 40 (ну, не мы, Animator делает это за нас), что видно в первой строке OfFloat, где начальная позиция Y равен 0 (то есть 0 относительно 2100, то есть равно 2100), а его конечная позиция Y равна _distanceToTravel (который равен 40, но опять-таки относительный, так что фактически равен 2140). В обратном направлении мы начинаем с _distanceToTravel для Y (снова 40, но на самом деле 2140) и заканчиваем 0 (вы угадали 0 от 2100 и, следовательно, 2100).

Надеюсь, что все это имеет смысл - мне потребовалось немного времени, чтобы уронить пенни, но она работает очень хорошо, без мерцания и без возврата к исходному положению (которое всегда имело lol). Надеюсь, то же самое относится и к коду Java, как и в этом примере C#.

Другие вопросы по тегам