#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
ПОЧЕМУ ЭТО СТРАННО?
- вы видите две прокомментированные строки, которые очищают задачу и запускают действие сверху? если я этого не сделаю, все в порядке
- Если я запускаю его как MainActivity -> HomeActivity и выполняю finish (вместо очистки задач), он работает так же хорошо.
Что происходит?
Заранее спасибо.
Комментарии:
1. Что такое объект radar?
2. это представление, в данном случае ImageView
3. Вы запускаете действие, которое содержит фрагмент. Тогда
Fragment
хочет анимировать представление, это правильно?4. да, правильно. Я начинаю анимировать
onResume
фрагмент
Ответ №1:
Когда вы слышите заявления типа: «ИНОГДА это работает плохо«, не стоит недооценивать это. Это в основном означает, что, вероятно, задействован временной компонент. Поскольку порядок выполнения многопоточных операций не обязательно известен, thinksможет произойти за несколько миллисекунд до или после и / или во время разных нагрузок CPU / GPU. В то же время, на несколько миллисекунд раньше может означать, что что-то еще не закончено, что-то, что нам, возможно, нужно закончить, или что это может быть «интенсивным».
С жизненным циклом активности / фрагмента Android мысли «более или менее» гарантированно происходят в определенном порядке.
Если вы добавляете иногда больше или меньше, у вас есть рецепт неизвестности…
Я не пробовал ваш конкретный сценарий, но я бы рекомендовал следующие шаги:
- Не анимируйте, если ваш макет не был выложен. Вы должны подождать.
- Не анимируйте, если ваш вид не был создан. Вы просто не можете: 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 и анимирую ее после добавления представления в контейнер, она должна работать.