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

Я использую определенную 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);
        }
    });

Так что в основном я раздуваю взгляд под ним. Затем я оживляю вид сверху слева. Как только он выходит за пределы экрана и анимация заканчивается, он сбрасывает свою позицию назад.

10 ответов

Решение

Анимации работают как положено. То, что вы что-то анимировали, не означает, что вы действительно это перенесли Анимация влияет только на нарисованные пиксели во время самой анимации, а не на конфигурацию виджетов.

Вам нужно добавить AnimationListenerи в onAnimationEnd() метод, сделайте что-то, что сделает ваш ход постоянным (например, удаляет представление сверху от его родителя, помечает представление сверху как имеющее видимость GONE).

Наконец-то есть способ обойти, правильный способ сделать это 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>

Вы можете видеть, что я определил filterAfter="true" в set тег, если вы пытаетесь определить его в 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 botton)

он перемещает ваш макет в новую позицию. Первый аргумент определяет левое значение, которое у нас равно 150, потому что анимация перевода перевела наше представление на 150-ось X, верхняя часть равна 0, потому что у нас нет анимированной оси Y, теперь справа мы сделали someView.getWidth() + 150 в основном мы получаем ширину нашего вида и добавляем 150, потому что теперь мы слева переместимся на 150 осей X, чтобы сделать ширину вида оригинальной, а дно равно высоте нашего вида.

Я надеюсь, что вы, люди, теперь поняли концепцию перевода, и, тем не менее, у вас есть вопросы, которые вы можете задать сразу в разделе комментариев, я рад помочь:)

РЕДАКТИРОВАТЬ Не использовать layout() метод, который может быть вызван платформой, когда когда-либо представление недействительно, и ваши изменения не будут сохраняться, используйте LayoutParams установить параметры макета в соответствии с вашими требованиями

Я могу немного опоздать с ответом, но у меня есть решение для этого:

Просто добавь android:fillAfter="true"в вашем xml

Ты можешь использовать

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 с Project -> properties -> Android, чем import android.animation.ObjectAnimator;

С уважением Айк

Вам нужно добавить эту команду:

final Animation animSlideLeft = new TranslateAnimation(0, -80, 0,-350, 0, 0, 0, 0);
animSlideLeft.setFillAfter(true);

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)

}

Я пошел на тот же вопрос и решил его, установив marginLeft в OnAnimationEnd()..

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

Эта проблема беспокоила больше недели. И ответ Мухаммеда указал мне верный способ решить его.

Я использовал разметку для реализации боковых меню с анимацией. "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();
    }
}

Используйте вспомогательные методы ниже, чтобы легко оживить ваш вид. Затем вы можете анимировать и сбросить после завершения, как это:

// 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();
        }
      }  
    }
  });
}

Ничего из ответов у меня не сработало; хотя ответ @Muhammad Babar вдохновил меня на поиск решения:

Он упомянул, что мы можем разместить представление в новой позиции по окончании анимации:

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

Хотя у меня это не сработало; Мне пришлось сделать это в обработчике с отложенным запуском, используя значение задержки, которое немного меньше длительности анимации.

Итак, в моем случае продолжительность анимации составляет 500 мс, и я использовал обработчик на 450 мс, поэтому он запускается непосредственно перед тем, как анимация заканчивается на 50 мс.

      Handler().postDelayed({
    someView.layout(150, 0, someView.width + 150, someView.hight)
    someView.requestLayout()
    someView.forceLayout()
}, 450)
Другие вопросы по тегам