首页 > 解决方案 > 在android中制作高效的重复动画

问题描述

我对 Android 很陌生,我一直在尝试在我的主要活动中制作 4 张图像的重复动画(翻译和淡入淡出)背景。我在动画编程之前和并行学习了动画(尤其是动画师),所以我认为我没有找到最优雅,更不用说高效的方法。

我的主要问题是应用程序在设备的几次方向更改后因内存不足错误而崩溃。我当然想找到一种方法来解决这个问题,但也要让整个事情变得优雅和高效。

编辑:

我基本上所做的是使用一个AnimatorSet实例顺序播放一组翻译和过渡(它们本身是AnimatorSet由翻译、翻译+淡出、翻译+淡入等组成的)。最后,我添加了一个监听器,它会在每次动画结束时再次播放动画。

这是我的 MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private AnimatorSet mAnimatorSet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ImageView backgroundOne = findViewById(R.id.background_main1);
        ImageView backgroundTwo = findViewById(R.id.background_main2);
        ImageView backgroundThree = findViewById(R.id.background_main3);
        ImageView backgroundFour = findViewById(R.id.background_main4);

        mAnimatorSet = BackgroundAnimator.set
                (backgroundOne, backgroundTwo, backgroundThree, backgroundFour);

        setImagesWidth(backgroundOne, backgroundTwo, backgroundThree, backgroundFour);


        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                animation.start();
            }
        });

        mAnimatorSet.start();


    }

    private void setImagesWidth(ImageView... imageViews) {

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);

        int width = size.x;
        int height = size.y;

        for (ImageView imageView : imageViews) {
            imageView.getLayoutParams().width = width + 800;
            imageView.getLayoutParams().height = height;
            imageView.requestLayout();
        }


    }

    public void onAddClick(View view) {

        startActivity(new Intent(this, AddItemActivity.class));
    }

    private static class BackgroundAnimator {

        private static AnimatorSet animatorSet;

        enum TranslateOptions {

            INITIAL,
            LEFT_TO_RIGHT,
            RIGHT_TO_LEFT
        }

        /**
         * set the animator
         */
        private static AnimatorSet set(ImageView viewOne, ImageView viewTwo,
                                       ImageView viewThree, ImageView viewFour) {


            ArrayList<Animator> list = new ArrayList<>();


            list.add(translate(viewOne, TranslateOptions.LEFT_TO_RIGHT));
            list.add(transitionLeftEnd(viewOne, viewTwo));
            list.add(translate(viewTwo, TranslateOptions.RIGHT_TO_LEFT));
            list.add(transitionRightEnd(viewTwo, viewThree));
            list.add(translate(viewThree, TranslateOptions.LEFT_TO_RIGHT));
            list.add(transitionLeftEnd(viewThree, viewFour));
            list.add(translate(viewFour, TranslateOptions.RIGHT_TO_LEFT));
            list.add(transitionRightEnd(viewFour, viewOne));


            animatorSet = new AnimatorSet();
            animatorSet.playSequentially(list);
            return animatorSet;

        }


        private static Animator translate(ImageView view, TranslateOptions option) {

            float startValue = 0, endValue = 0;

            switch (option) {

                case INITIAL:
                    startValue = -500;
                    endValue = 300;
                    break;

                case LEFT_TO_RIGHT: {
                    startValue = -300;
                    endValue = 300;
                    break;
                }
                case RIGHT_TO_LEFT: {
                    startValue = 300;
                    endValue = -300;
                    break;
                }

            }

            ObjectAnimator animator = ObjectAnimator.ofFloat
                    (view, "translationX", startValue, endValue);
            animator.setInterpolator(new LinearInterpolator());
            animator.setDuration(9000);

            return animator;
        }

        private static AnimatorSet transitionLeftEnd(ImageView viewOut, ImageView viewIn) {

            return transition(viewOut, viewIn, 300, 500);


        }


        private static AnimatorSet transitionRightEnd(ImageView viewOut, ImageView viewIn) {

            return transition(viewOut, viewIn, -300, -500);


        }

        private static AnimatorSet transition(ImageView viewOut, ImageView viewIn,
                                              float startValue, float endValue) {


            PropertyValuesHolder translateOut =
                    PropertyValuesHolder.ofFloat("translationX", startValue, endValue);
            PropertyValuesHolder fadeOut =
                    PropertyValuesHolder.ofFloat("alpha", 0);

            ObjectAnimator animatorOut = ObjectAnimator.ofPropertyValuesHolder(viewOut, translateOut, fadeOut);
            animatorOut.setInterpolator(new LinearInterpolator());
            animatorOut.setDuration(2250);

            PropertyValuesHolder translateIn =
                    PropertyValuesHolder.ofFloat
                            ("translationX", endValue, startValue);
            PropertyValuesHolder fadeIn =
                    PropertyValuesHolder.ofFloat("alpha", 1);

            ObjectAnimator animatorIn = ObjectAnimator.ofPropertyValuesHolder(viewIn, translateIn, fadeIn);
            animatorIn.setInterpolator(new LinearInterpolator());
            animatorIn.setDuration(2250);

            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.playTogether(animatorIn, animatorOut);


            return animatorSet;
        }
    }
}

这是来自 logcat 的错误报告:

java.lang.OutOfMemoryError: Failed to allocate a 6144012 byte allocation with 2875952 free bytes and 2MB until OOM
        at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
        at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613)
        at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:446)
        at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:1080)
        at android.content.res.Resources.createFromResourceStream(Resources.java:2952)
        at android.content.res.Resources.loadDrawableForCookie(Resources.java:2684)
        at android.content.res.Resources.loadDrawable(Resources.java:2580)
        at android.content.res.MiuiResources.loadDrawable(MiuiResources.java:388)
        at android.content.res.Resources.getDrawable(Resources.java:824)
        at android.content.Context.getDrawable(Context.java:467)
        at android.support.v4.content.ContextCompat.getDrawable(ContextCompat.java:463)
        at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:203)
        at android.support.v7.widget.AppCompatDrawableManager.getDrawable(AppCompatDrawableManager.java:191)
        at android.support.v7.content.res.AppCompatResources.getDrawable(AppCompatResources.java:102)
        at android.support.v7.widget.AppCompatImageHelper.loadFromAttributes(AppCompatImageHelper.java:59)
        at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:78)
        at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:68)
        at android.support.v7.app.AppCompatViewInflater.createImageView(AppCompatViewInflater.java:182)
        at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:106)
        at android.support.v7.app.AppCompatDelegateImpl.createView(AppCompatDelegateImpl.java:1266)
        at android.support.v7.app.AppCompatDelegateImpl.onCreateView(AppCompatDelegateImpl.java:1316)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:750)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:708)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:839)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:802)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:519)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:427)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at android.support.v7.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469)
        at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
        at com.tfreifeld.collectx.MainActivity.onCreate(MainActivity.java:30)
        at android.app.Activity.performCreate(Activity.java:6355)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2440)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2547)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4191)
        at android.app.ActivityThread.access$1200(ActivityThread.java:151)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1406)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:157)
        at android.app.ActivityThread.main(ActivityThread.java:5603)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:774)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:652)

标签: androidanimationanimator

解决方案


您的内存泄漏是由动画引起的。不仅您永远不会停止/取消动画,您还为每个动画设置了一个侦听器,以便在它完成时重播它。

这些动画具有静态引用(无论正在播放它们),并且在您取消它们之前不会被 GC(我会在取消时删除动画侦听器)。动画本身具有对视图的引用,这些视图具有 Activity 上下文等...

此外,BackgroundAnimation'sanimatorSet是静态的,并且会泄漏至少一项活动。

至于你关于动画的一般问题 - 你做错了几件事。AnimatorSet.Builder使用动画合成而不是AnimatorSet它本身可能会更好。此外,还有比使用AlphaAnimationTranslateAnimationObjectAnimator等更简单的方法。而且一般代码中有一些重复,可以提取到方法中。


推荐阅读