Android TranslateAnimation сбрасывается после анимации - PullRequest
27 голосов
/ 16 апреля 2010

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

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

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

Ответы [ 6 ]

45 голосов
/ 25 февраля 2011

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

В основном это означает, что метод 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
}

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

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

24 голосов
/ 23 марта 2012

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

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

Подробнее здесь .

4 голосов
/ 15 ноября 2011

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

2 голосов
/ 07 ноября 2012

Используя ответ Сохэма, вот 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
 * 
 * /1867343/android-translateanimation-sbrasyvaetsya-posle-animatsii
 */
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 голос
/ 16 апреля 2010

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

0 голосов
/ 12 июля 2016

Итак, я искал ответ на этот вопрос для своего проекта 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 #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...