Анимация макета не работает при первом запуске
У меня есть действие со списком элементов, и когда вы нажимаете на элемент, я хочу, чтобы элементы управления воспроизведением этого элемента скользили вверх из нижней части экрана и становились видимыми. Я определил набор анимации для слайда и слайда, и они работают. Я настроил свой animationListener в своей деятельности и начал мой слайд в анимации при щелчке элемента. Моя проблема в том, что при первом запуске приложения, когда я нажимаю на элемент, выполняется обратный вызов onClick, но анимация не происходит. Во второй раз, когда я нажимаю, происходит слайд в анимации, но не слайд. В третий и последующий раз все работает как положено. Вот мои наборы анимации.
vm_slide_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:fromYDelta="800"
android:toYDelta="0"
android:duration="600" />
</set>
vm_slide_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:fromYDelta="0"
android:toYDelta="800"
android:duration="600" />
</set>
Вот мой макет деятельности
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/AppBG">
<RelativeLayout
style="@style/LogoBar"
android:id="@+id/logo_bar"
android:layout_alignParentTop="true">
<include layout="@layout/logobar"></include>
</RelativeLayout>
<RelativeLayout
style="@style/TitleBar"
android:id="@+id/title_bar"
android:layout_below="@+id/logo_bar">
<include layout="@layout/titlebar"></include>"
<Button style="@style/TitleBarButton"
android:id="@+id/vm_speaker_btn"
android:text="@string/vm_speaker_btn_label"
android:layout_alignParentLeft="true"
android:layout_margin="4dp">
</Button>
<Button style="@style/TitleBarButton"
android:id="@+id/vm_edit_btn"
android:text="@string/vm_edit_btn_label"
android:layout_alignParentRight="true"
android:layout_margin="4dp">
</Button>
</RelativeLayout>
<ListView
android:id="@+id/@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title_bar"/>
<RelativeLayout
android:id="@+id/vm_control_panel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/footer"
android:visibility="gone">
<include layout="@layout/vm_control"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<include layout="@layout/taskbar"/>
</RelativeLayout>
</RelativeLayout>
Вот макет для включенного элемента управления
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:id="@+id/vm_control"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/dark_grey">
<TextView
android:id="@+id/vm_ctl_branding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:text="@string/branding"
android:textColor="@color/white">
</TextView>
<TextView style="@style/TimeMark"
android:id="@+id/vm_timestamp"
android:layout_toLeftOf="@+id/vm_progress"
android:layout_below="@+id/vm_ctl_branding"
android:layout_marginRight="3dp"
android:text="0:00">
</TextView>
<SeekBar
android:id="@+id/vm_progress"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_below="@+id/vm_ctl_branding"
android:layout_centerHorizontal="true">
</SeekBar>
<TextView style="@style/TimeMark"
android:id="@+id/vm_timeleft"
android:layout_toRightOf="@+id/vm_progress"
android:layout_below="@+id/vm_ctl_branding"
android:layout_marginLeft="3dp"
android:text="-0:00">
</TextView>
<LinearLayout
android:id="@+id/vm_action_button_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/vm_timestamp"
android:layout_alignRight="@+id/vm_timeleft"
android:orientation="horizontal"
android:layout_below="@+id/vm_progress">
<TextView style="@style/Vm_Action_Button"
android:background="@drawable/vm_action_btn_call"
android:id="@+id/vm_callback_btn"
android:layout_marginRight="5dp"
android:text="@string/vm_action_btn_callback">
</TextView>
<TextView style="@style/Vm_Action_Button"
android:background="@drawable/vm_action_btn_delete"
android:id="@+id/vm_delete_btn"
android:layout_marginLeft="5dp"
android:text="@string/vm_action_btn_delete">
</TextView>
</LinearLayout>
</RelativeLayout>
</merge>
Из моего метода onCreate...
// Handle list item selections
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Voicemail vmItem = (Voicemail) vmla.getItem(position);
Toast.makeText(ctx, "Name: " + vmItem.getCallerName() + " Position: " + position, Toast.LENGTH_SHORT)
.show();
vVm_controls.startAnimation(mSlideIn);
}
});
И мои анимационные обратные вызовы...
@Override
public void onAnimationStart(Animation animation) {
vm_controls_in = !vm_controls_in;
if (vm_controls_in) {
vVm_controls.setVisibility(View.GONE);
} else {
vVm_controls.setVisibility(View.VISIBLE);
// vVm_controls.bringToFront();
}
}
@Override
public void onAnimationEnd(Animation animation) {
if (!vm_controls_in) {
try {
Thread.sleep(1000);
vVm_controls.startAnimation(mSlideOut);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
11 ответов
Как вы обнаружили, проблема заключается в видимости. Когда представление изначально установлено как пропавшее в файле xml, Android не будет отображать макет, пока видимость не будет изменена на видимую или невидимую. Если вы попытаетесь анимировать представление, которое еще не было отрисовано, анимация будет происходить в представлении без макета. После того, как анимация завершена, она будет визуализироваться, и внезапно вид появится без анимации. Это работает в последующие времена, потому что представление имеет макет, даже если установлено, что ушел.
Ключ заключается в том, чтобы установить видимость невидимым, а не помещенным в XML-файл, чтобы он отображался, но был скрыт. Когда анимация происходит в первый раз, вид появляется и будет двигаться как ожидалось.
Конечно, не через 20 минут после публикации этого вопроса я разобрался в своей проблеме. Так как я устанавливал видимость моего макета на "GONE", когда я пытался запустить анимацию, он не запускался с первого раза. Не уверен, почему это происходит во 2-й и последующий раз, но если я установил видимость "VISIBLE" прямо перед тем, как запустить анимацию, это решило проблему. Вот код, где я его изменил...
// Handle list item selections
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Voicemail vmItem = (Voicemail) vmla.getItem(position);
Toast.makeText(ctx, "Name: " + vmItem.getCallerName() + " Position: " + position, Toast.LENGTH_SHORT)
.show();
vVm_controls.setVisibility(View.VISIBLE);
vVm_controls.startAnimation(mSlideIn);
}
});
Я не знаю, является ли это все еще актуальным, но да, это проблема с видимостью. Поэтому я установил видимость в xml-файле на невидимый и вызывал в том месте, где инициализируется ваше представление:
view.post(new Runnable() {
@Override
public void run() {
view.animate().translationY(view.getHeight());
}
};
Теперь звонок
view.animate().translationY(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
view.setVisibility(View.VISIBLE);
}
}
работает и в первый раз.
Если проблема в видимости, вам нужно только отложить анимацию после рендеринга с помощью чего-то вроде:
vVm_controls.setVisibility(View.INVISIBLE);
vVm_controls.post(() -> vVm_controls.startAnimation(mSlideIn));
Многие из текущих ответов не работают для меня, и единственный, который работал, - использовать
onFocusWindowChange()
, но мне не нравится это решение.
Мой способ решения этой проблемы: сначала установите ток, который вы пытаетесь анимировать, как
invisible
в вашем макете xml.
Далее в
onCreate
добавить прослушиватель в макет, чтобы уведомить вас, когда
view
закончен рисунок:
mLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mLayout.setVisibility(View.GONE);
mLayout.animate()
.translationX(-mLayout.getWidth());
mLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
Он перемещает его за пределы экрана после завершения рендеринга.
У меня была та же самая проблема, хотя я устанавливал видимость в начале анимации. Я последовал совету Брокколи и сделал его видимым, прежде чем запустить анимацию, и это сработало как шарм.
ДО:
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_left);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
view.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) { }
@Override
public void onAnimationRepeat(Animation animation) { }
);
view.startAnimation(animation);
ПОСЛЕ:
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.slide_left);
view.setVisibility(View.VISIBLE);
view.startAnimation(animation);
У меня была похожая проблема в Android 4.4.3. Я попробовал большую часть предложенного решения, но только одно решение работало хорошо для меня. Вы должны запустить анимацию после того, как окно завершит рендеринг, и лучший способ - запустить ее внутри onWindowFocusChanged. Не используйте задержку при запуске, так как это повлияет на производительность вашего приложения на высокоскоростных телефонах.
@Override
public void onWindowFocusChanged (boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus)
yourView.startAnimation(anim);
}
дополнительную информацию можно найти в этом посте: https://damianflannery.wordpress.com/2011/06/08/start-animation-in-oncreate-or-onresume-on-android/
Если вы используете RecyclerView и загружаете элементы списка после настройки адаптера, используйте это
recyclerView.scheduleLayoutAnimation();
после
adapter.notifyDataSetChanged();
Это работа по моему делу
У меня была точно такая же проблема, но с помощью ViewPropertyAnimator
, Например, вы объявляете GridView
называется mGridView
, Все, что вам нужно сделать сейчас, это позвонить mGridView.animate()
с вами желаемых изменений.
Анимация моего GridView
был вызван, но из-за неизвестных обстоятельств фактической анимации не было видно. Я тоже все поменял View.GONE
в View.VISIBLE
, но это ничего не изменило. В моем случае я понял, что мне просто нужно было удалить android:requiresFadingEdge="vertical"
в моем XML этого конкретного GridView
, Может быть невозможно использовать Fading Edge в комбинации с ViewPropertyAnimator
,
Ни один из ответов в целом SO не сработал для меня, даже без использования Visibility.GONE вообще.
В итоге я получил пластырь и когда-нибудь изучу его.
Мое временное решение устанавливало высоту на 1 пиксель (вместо 0dp, что делает невозможной работу в моем случае) и видимость на НЕВИДИМЫЙ. В итоге я теряю 1 пиксель, но это довольно сложно заметить.
Код:
ResizeAnimation.kt
class ResizeAnimation(private val view: View, private val newHeight: Int) : Animation() {
private val initialHeight: Int = view.layoutParams.height
private val deltaHeight: Int
init {
deltaHeight = newHeight - initialHeight
}
override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
view.layoutParams.height = (initialHeight + deltaHeight * interpolatedTime).toInt()
view.requestLayout()
}
override fun willChangeBounds(): Boolean {
return true
}
}
Применение:
private fun displayProgressBar() {
progressBar.visibility = View.VISIBLE
val animation = ResizeAnimation(progressBar, 78).apply {
duration = inAnimationDuration
}
progressBar.startAnimation(animation)
}
private fun removeProgressBar() {
val animation = ResizeAnimation(progressBar, 1).apply {
duration = outAnimationDuration
}
progressBar.startAnimation(animation)
progressBar.visibility = View.INVISIBLE
}
Пожалуйста, не принимайте это как окончательное решение, относитесь к этому как к пластырю.
Я знаю, что этот вопрос старый, но мой ответ может помочь другим. Когда нам нужно анимировать представление, но по умолчанию его видимость исчезла, тогда, пожалуйста, установите все свойства скрыть код в xml. В моем случае я использовал translationY и альфа в java файле. Я сделал простые вещи, чтобы решить проблему при первом запуске. Затем добавлены все атрибуты в XML, которые используются в коде.