Android复杂动画会在视图大小更改时跳过帧

问题描述 投票:0回答:1

我正在尝试创建一个有点复杂的动画,其中涉及同一布局中的多个视图。视图将改变大小,不透明度,并且需要同时播放所有的图像切换器。我已经通过使用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>

目标动画信息显示在布局文本上。要澄清更多:

  1. animationView需要将大小从0dp更改为60dp(从0x0更改为60x60),
  2. progressView需要将大小从0dp更改为50dp(从0x0更改为50x50),
  3. [gamepadBackgroundView需要将大小从28dp更改为45dp(从28x28更改为45x45),其alpha值需要从0.35f更改为1f。
  4. gamepadImageSwitcher需要将大小从14x9更改为24x16,并且其中的图像也需要使用setImageResource进行更改。它的动画是淡出->淡入动画。
  5. 在播放动画时,需要从5dp到0删除gamepadImageSwitcher的marginTop
  6. 由于这些更改,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);
}

是否有任何优化建议?备择方案?感谢您的帮助,非常感谢。

android android-animation
1个回答
0
投票

DragLayer上具有固定的宽度和高度(也称为根视图)解决了大多数问题:显然,当根视图包装内容时,重新布局的成本效益太高。因为我知道目标动画的最大宽度和最大高度,所以固定了根视图的宽度和高度,然后将居中的containerView用作wrap_content似乎已经解决了问题。

© www.soinside.com 2019 - 2024. All rights reserved.