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 для повторного просмотра, поведение разворачивается / свертывается.
- Исходное состояние: свернуть
- вызвать метод с дочерним флагом видимости (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)