Ошибка с анимацией на Android?

#android #android-fragments #android-animation

#Android #android-фрагменты #android-анимация

Вопрос:

это довольно сложно объяснить, но мы начинаем!

Хорошо, переход до тех пор, пока я не получу фрагмент, в котором размещена анимация, будет:

MainActivity (расширяет активность) -> LoginActivity (расширяет активность) -> теперь я вызываю:

 Intent homeIntent = new Intent(loginActivity.this, HomeActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("risposta",(Serializable)risposta);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // read below (StackOverFlow)
homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // read below (StackOverFlow)
homeIntent.putExtras(bundle);
startActivity(homeIntent);
  

который запускает HomeActivity, который расширяет FragmentActivity, и в конце onCreateView() я добавляю свой фрагмент CircleFragment, который @Override включает это onResume() :

     RotateAnimation rotateAnimation1 = new RotateAnimation(0, 360,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
    rotateAnimation1.setInterpolator(new LinearInterpolator());
    rotateAnimation1.setDuration(4000);
    rotateAnimation1.setRepeatCount(Animation.INFINITE);
    radar.startAnimation(rotateAnimation1);
  

или XML (пробовал оба):

 <?xml version="1.0" encoding="utf-8"?>
<rotate
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:fromDegrees="0"
  android:interpolator="@android:anim/linear_interpolator"
  android:toDegrees="360"
  android:pivotX="50%"
  android:pivotY="25%"
  android:duration="4000"
  android:startOffset="0"
/>
  

ЧТО ПРОИСХОДИТ?

ИНОГДА это работает плохо (иногда нормально!): он запускается примерно ~ (50%, 25%) (или 0,5, 0,25), не точно (вычисляется), но если я позволю экрану погаснуть и возобновлю его, все снова заработает: O

ПОЧЕМУ ЭТО СТРАННО?

  1. вы видите две прокомментированные строки, которые очищают задачу и запускают действие сверху? если я этого не сделаю, все в порядке
  2. Если я запускаю его как MainActivity -> HomeActivity и выполняю finish (вместо очистки задач), он работает так же хорошо.

Что происходит?

Заранее спасибо.

Комментарии:

1. Что такое объект radar?

2. это представление, в данном случае ImageView

3. Вы запускаете действие, которое содержит фрагмент. Тогда Fragment хочет анимировать представление, это правильно?

4. да, правильно. Я начинаю анимировать onResume фрагмент

Ответ №1:

Когда вы слышите заявления типа: «ИНОГДА это работает плохо«, не стоит недооценивать это. Это в основном означает, что, вероятно, задействован временной компонент. Поскольку порядок выполнения многопоточных операций не обязательно известен, thinksможет произойти за несколько миллисекунд до или после и / или во время разных нагрузок CPU / GPU. В то же время, на несколько миллисекунд раньше может означать, что что-то еще не закончено, что-то, что нам, возможно, нужно закончить, или что это может быть «интенсивным».

С жизненным циклом активности / фрагмента Android мысли «более или менее» гарантированно происходят в определенном порядке.

Если вы добавляете иногда больше или меньше, у вас есть рецепт неизвестности…

Я не пробовал ваш конкретный сценарий, но я бы рекомендовал следующие шаги:

  1. Не анимируйте, если ваш макет не был выложен. Вы должны подождать.
  2. Не анимируйте, если ваш вид не был создан. Вы просто не можете: D

Так что, даже если «иногда работает нормально», я подозреваю, что ваше «иногда» связано с тем, что вы анимируете слишком рано, когда просмотр не завершен. Не путайте и без того запутанное название метода onCreatedView с «Да, представление создано и выложено». Создано != выложено.

Итак, когда будет выложено представление?

Когда Android заканчивает выполнять много измерений и рисования.

Откуда мы знаем?

Используя самый уродливый фрагмент кода, который вы увидите сегодня…

Пример (в вашем фрагменте):

 private volatile boolean mRootLayoutComplete = false; //just a global flag to let the Fragment know when we've been through this.

@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
    final View rootView = inflater.inflate(R.layout.your_fragment_layout, container, false);
    // add a GlobalLayoutListener to hear back from the Layout engine.
    rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // This beauty can be thanked to Android for changing the name of the method.
            // In any case, you must unsusbcribe to this immediately.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
                rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }
            // Mark this on a volatile global variable in case you want to use it elsewhere.
            mRootLayoutComplete = true;

            // Now you can ask: Is my view Created and not yet animated? 
            if ( mViewHasBeenCreated amp;amp; !mRadarHasBeenAnimated ) {
                animate(mRadar); // call a method that will do the animation
            }
        };
    } // some {} may be wrong, I did it in this text editor :D
  

Теперь вам нужно отметить, что mViewHasBeenCreated… или вы могли бы проверить, если mRadar != null вместо того, чтобы просто использовать семафор.

Чтобы продолжить мой пример…

 // Define this flag too, because things are asynchronous we don't know who's gonna happen first!
private volatile boolean mViewHasBeenCreated = false;
  

В вашем:

 @Override
public void onViewCreated(final View view, final Bundle savedInstanceState) {
    mViewHasBeenCreated = true;
    super.onViewCreated(view, savedInstanceState);
}
  

В вашем onResume:

 boolean mRadarHasBeenAnimated = false; //flag to avoid multiple animations.

@Override
public void onResume() {
    super.onResume();
    if ( mRootLayoutComplete amp;amp; mViewHasBeenCreated amp;amp; !mRadarHasBeenAnimated ) {
        animate(mRadar); // could also check for null here
    }   
}
  

И анимировать:

 void animate(final View view) {
    if ( view == null ) {
       return;
    }
    // your animation code
    view.animate(youranimation);
    mRadarHasBeenAnimated = true;
}
  

Наконец, если вы хотите анимировать «радар» каждый раз, когда вы выполняете onResume, вы должны сбросить это…

 @Override
public void onPause() {
    super.onPause();
    mRadarHasBeenAnimated = false;
}
  

Это должно сработать.

По сути, вы анимируете свой вид только после выполнения определенных условий (в частности, mRootLayoutComplete), и все флаги предназначены для того, чтобы не запускать его несколько раз, особенно во время создания. Поскольку вы не знаете, что произойдет первым, вам нужно «подождать» и делать это только тогда, когда вы уверены, что сможете.

Удачи!

p.d.: Я не тестировал приведенный выше код как есть, но я использовал этот трюк во многих приложениях, и он всегда работал.

Комментарии:

1. Привет, спасибо, это был мудрый ответ. Я пробовал это, и это не сработало : Но из вашего ответа я подумал о том, чтобы не устанавливать ImageView, и я создал, я установил его как параметры: MATCH_PARENT, MATCH_PARENT, а затем анимировал его. Ни то, ни другое не сработало. Это не имеет смысла, если я установлю анимацию на 50% pivot и анимирую ее после добавления представления в контейнер, она должна работать.