简体   繁体   English

在Android中制作高效的重复动画

[英]Make an efficient repeating animation in android

I'm quite new to Android, and I have been trying to make a repeated animation (translation and crossfade) background of 4 images in my main activity. 我刚接触Android,所以我一直尝试在主要活动中重复制作4张图片的动画(翻译和淡入淡出)背景。 I learned about animations (and Animators particularly) before and in parallel to programming the animations, so I assume I did not find the most elegant, let alone efficient, way of doing that. 我在对动画进行编程之前和同时学习了动画(尤其是Animators),所以我想我没有找到最优雅,更有效的方法。

My main issue is that the application crashes with Out of Memory Error after several orientation changes of the device. 我的主要问题是,在多次更改设备方向后,应用程序因内存不足错误而崩溃。 I would like to find a way to fix that of course, but also make the whole thing elegant and memory efficient. 我想找到一种解决方法,当然,还可以使整个过程变得优雅而高效。

EDIT: 编辑:

What I did basically, is using an AnimatorSet instance to play sequentially a set of translations and transitions (which are themselves an AnimatorSet composed of translation, translation + fade out, translation + fade in, etc..). 我所做的基本上是使用AnimatorSet实例顺序播放一组翻译和过渡(它们本身是由翻译,翻译+淡出,翻译+淡入等组成的AnimatorSet )。 Finally, I've added a listener that will play the animation again, every time it ends. 最后,我添加了一个侦听器,它将在每次结束时再次播放动画。

Here is my MainActivity.java: 这是我的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;
        }
    }
}

Here is the error report from the logcat: 这是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)

Your memory leak is caused by the animations. 您的内存泄漏是由动画引起的。 Not only you are never stopping/cancelling the animations, you've set a listener on each animation to replay it when it finish. 不仅您永远都不会停止/取消动画,而且还在每个动画上设置了一个侦听器以在完成动画时对其进行重放。

Those animations have static references (in whatever is playing them) and will not be GC'ed until you cancel them (I would have removed the animation listeners as well when canceling). 这些动画具有静态引用(无论正在播放什么),在取消它们之前将不会被GC处理(取消时,我也会删除动画侦听器)。 The animations themselves have references to the views, which have to the Activity context, etc... 动画本身引用了视图,引用了Activity上下文,等等。

In addition, BackgroundAnimation 's animatorSet is static and will leak at least one activity. 另外, BackgroundAnimationanimatorSet是静态的,并且将泄漏至少一个活动。

As to your general question about animations - you are doing several thing wrong. 关于您有关动画的一般问题-您做错了几件事。 You will probably be better by using AnimatorSet.Builder for animations composition instead of AnimatorSet itself. 通过将AnimatorSet.Builder用于动画合成,而不是AnimatorSet本身,可能会更好。 In addition, there are much simpler ways than using ObjectAnimator such as AlphaAnimation and TranslateAnimation . 另外,还有比使用ObjectAnimator简单得多的方法,例如AlphaAnimationTranslateAnimation And in general there are some repetitions in code, which can be extracted to methods. 通常,代码中有一些重复,可以将其提取为方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM