【发布时间】:2020-03-02 19:41:38
【问题描述】:
我正在尝试创建一个有点复杂的动画,其中涉及同一布局中的多个视图。视图将改变大小、不透明度,并且有一个需要同时播放的图像切换器。我已经设法通过使用ValueAnimator 来执行必要的动画,但是动画跳过了太多需要以某种方式优化的帧(可能是 25 帧)。
为了展示一些关于动画的细节,这里是我的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<com.example.android.views.DragLayer xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/containerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- Target width is 60dp for animation view. -->
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animationView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:visibility="gone"
app:lottie_rawRes="@raw/bubble_loop_animation"
app:lottie_loop="true"
app:lottie_autoPlay="false" />
<!-- Target width is 50dp for progress view. -->
<com.example.android.views.ProgressView
android:id="@+id/progressView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
android:visibility="gone"
app:useDefaultProgressColors="false"
app:strokeWidth="10dp"
app:strokeGradientStart="#FFC928"
app:strokeGradientEnd="#E53935" />
<!-- Width transition: from 28dp to 45dp
Height transition: from 28dp to 45dp
Alpha transition: from 0.35 to 1 -->
<View
android:id="@+id/gamepadBackgroundView"
android:layout_width="28dp"
android:layout_height="28dp"
android:background="@drawable/gamepad_background"
android:layout_gravity="center"
android:alpha="0.35" />
<!-- Width transition: from 19dp to 29dp
Height transition: from 14dp to 20dp -->
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/fireImageView"
android:layout_width="19dp"
android:layout_height="14dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_fire" />
<!-- Width transition: from 14dp to 24dp
Height transition: from 9dp to 16dp
Remove marginTop on scale up and add on scale down -->
<ImageSwitcher
android:id="@+id/gamepadImageSwitcher"
android:layout_width="14.1dp"
android:layout_height="9.3dp"
android:layout_gravity="center"
android:layout_marginTop="5dp" />
</FrameLayout>
</com.burakgon.gamebooster3.views.DragLayer>
目标动画信息显示在布局文本上。进一步澄清:
- animationView 需要将尺寸从 0dp 更改为 60dp(0x0 到 60x60),
- progressView 需要将尺寸从 0dp 更改为 50dp(0x0 到 50x50),
- gamepadBackgroundView 需要将大小从 28dp 更改为 45dp(28x28 到 45x45),并且其 alpha 需要从 0.35f 更改为 1f。
- gamepadImageSwitcher 需要将尺寸从 14x9 更改为 24x16,并且内部图像也需要更改为
setImageResource。它的动画是淡出 --> 淡入动画。 - gamepadImageSwitcher 的
marginTop需要在动画播放时移除,从 5dp 到 0。 - 由于这些更改,containerView 和
com.example.android.views.DragLayer视图的大小也会发生变化。
这些动画需要有一个反转版本,我也成功实现了它,但是它也会跳过帧。我说的是大块,有时我什至看不到有动画。我在播放动画时使用View.LAYER_TYPE_HARDWARE,但它们似乎也没有帮助。由于所有视图大小的变化,这些操作都很繁重,但我不确定我还能做些什么。
这是我尝试实现动画的方法。
// Required constants for animations.
static
{
ANIM_VIEW_SIZE_SMALL = 0;
ANIM_VIEW_SIZE_BIG = (int) dpToPx(60);
PROGRESS_VIEW_SIZE_SMALL = 0;
PROGRESS_VIEW_SIZE_BIG = (int) dpToPx(50);
GAMEPAD_BACKGROUND_SIZE_SMALL = (int) dpToPx(28);
GAMEPAD_BACKGROUND_SIZE_BIG = (int) dpToPx(45);
FIRE_ICON_WIDTH = (int) dpToPx(18);
FIRE_ICON_HEIGHT = (int) dpToPx(22);
IMAGE_SWITCHER_WIDTH_SMALL = (int) dpToPx(14);
IMAGE_SWITCHER_WIDTH_BIG = (int) dpToPx(24);
IMAGE_SWITCHER_HEIGHT_SMALL = (int) dpToPx(9);
IMAGE_SWITCHER_HEIGHT_BIG = (int) dpToPx(16);
IMAGE_SWITCHER_MARGIN_TOP = (int) dpToPx(5);
}
private void startAnimation()
{
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(ANIM_LONG);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float value = animation.getAnimatedValue() != null ? (float) animation.getAnimatedValue() : 0f;
arrangeViewsToBiggerTransition(value);
updateTranslationValues();
}
});
animator.addListener(new AnimatorListenerAdapter()
{
@Override
public void onAnimationStart(Animator animation)
{
super.onAnimationStart(animation);
makeViewsVisible();
setLayerTypeHardware(progressView, gamepadBackgroundView,
gamepadImageSwitcher, fireImageView);
gamepadImageSwitcher.setImageResource(R.drawable.ic_game);
}
@Override
public void onAnimationEnd(Animator animation)
{
super.onAnimationEnd(animation);
resetLayerType(progressView, gamepadBackgroundView,
gamepadImageSwitcher, fireImageView);
animationView.playAnimation();
}
});
animator.addListener(getDeleteAnimatorListener());
animator.start();
}
private void setLayerTypeHardware(View... views)
{
for (View view : views)
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
private void resetLayerType(View... views)
{
for (View view : views)
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
/**
* Arranges the views defined by percent. Changes from 0 to 1, not 0 to 100.
*
* @param percent is the value between 0 and 1.
*/
private void arrangeViewsToBiggerTransition(float percent)
{
float gamepadBackgroundAlpha = getValueByPercent(GAMEPAD_BACKGROUND_ALPHA, 1f, percent);
int animViewSize = (int) getValueByPercent(ANIM_VIEW_SIZE_SMALL, ANIM_VIEW_SIZE_BIG, percent);
int progressViewSize = (int) getValueByPercent(PROGRESS_VIEW_SIZE_SMALL, PROGRESS_VIEW_SIZE_BIG, percent);
int gamepadBackgroundViewSize = (int) getValueByPercent(GAMEPAD_BACKGROUND_SIZE_SMALL, GAMEPAD_BACKGROUND_SIZE_BIG, percent);
int imageSwitcherWidth = (int) getValueByPercent(IMAGE_SWITCHER_WIDTH_SMALL, IMAGE_SWITCHER_WIDTH_BIG, percent);
int imageSwitcherHeight = (int) getValueByPercent(IMAGE_SWITCHER_HEIGHT_SMALL, IMAGE_SWITCHER_HEIGHT_BIG, percent);
int imageSwitcherMarginTop = (int) getValueByPercent(IMAGE_SWITCHER_MARGIN_TOP, 0f, percent);
editViewSize(animationView, animViewSize, animViewSize);
editViewSize(progressView, progressViewSize, progressViewSize);
editViewSize(gamepadBackgroundView, gamepadBackgroundViewSize, gamepadBackgroundViewSize);
editViewSize(gamepadImageSwitcher, imageSwitcherWidth, imageSwitcherHeight, false);
editViewMarginTop(gamepadImageSwitcher, imageSwitcherMarginTop);
gamepadBackgroundView.setAlpha(gamepadBackgroundAlpha);
fireImageView.setAlpha(1f - percent);
}
/**
* Edits the view size and requests layout.
*/
private void editViewSize(View view, int width, int height)
{
editViewSize(view, width, height, true);
}
/**
* Edits the view size and requests layout.
*/
private void editViewSize(View view, int width, int height, boolean requestLayout)
{
if (view.getLayoutParams() != null)
{
view.getLayoutParams().width = width;
view.getLayoutParams().height = height;
if (requestLayout)
view.requestLayout();
}
}
/**
* Edits the view's margin top attribute and requests layout.
*/
private void editViewMarginTop(View view, int marginTop)
{
if (view.getLayoutParams() instanceof MarginLayoutParams)
{
((MarginLayoutParams) view.getLayoutParams()).topMargin = marginTop;
view.requestLayout();
}
}
/**
* Returns the calculated percentage value for start and end. Percent
* should be between 0 and 1.
*
* @param start is the start value.
* @param end is the end value.
* @param percent is between 0 and 1.
*
* @return (end - start) / percent
*/
private float getValueByPercent(float start, float end, float percent)
{
return start + ((end - start) * percent);
}
有什么优化建议吗?备择方案?任何帮助表示赞赏,非常感谢。
【问题讨论】: