【问题标题】:Create "growing ripples" loading animation - OutOfMemoryException创建“不断增长的涟漪”加载动画 - OutOfMemoryException
【发布时间】:2018-10-06 22:32:38
【问题描述】:

我想在 Android 应用中显示此动画:

所以我为此创建了一个<animation-list>。它使用 46 帧,每帧由 200x200px png:

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android">
            <item android:drawable="@drawable/anim_loading_ripple_frame_0" android:duration="45" />
            <item android:drawable="@drawable/anim_loading_ripple_frame_1" android:duration="45" />
            <item android:drawable="@drawable/anim_loading_ripple_frame_2" android:duration="45" />
            <item android:drawable="@drawable/anim_loading_ripple_frame_3" android:duration="45" />
            <item android:drawable="@drawable/anim_loading_ripple_frame_4" android:duration="45" />
            <item android:drawable="@drawable/anim_loading_ripple_frame_5" android:duration="45" />
               ...
            <item android:drawable="@drawable/anim_loading_ripple_frame_45" android:duration="45" />
    </animation-list>

然后我将其设置为ImageViewbackground 并使用

开始动画
AnimationDrawable loadingAnimation = 
    (AnimationDrawable) loadingAnimationView.getBackground();
loadingAnimation.start();

一切都很完美,除了应用程序有时会因为OutOfMemoryException而崩溃!似乎这种动画对内存的要求很高,因为我读过其他人遇到同样的问题。

然后我想我可能只是将每个帧变成 XML shape 可绘制,所以我尝试了试验,但我不知道如何使圆圈改变大小 - 它们总是占据视图的整个宽度.

我试着做这样的事情

<item>
    <shape android:shape="oval">
        <padding android:top="30dp" android:right="30dp" android:bottom="30dp" android:left="30dp" />
        <size
            android:width="100dp"
            android:height="100dp" />
        <stroke
            android:color="#0F0"
            android:width="4dp"/>
    </shape>
</item>

并且还使用layer-list 在同一个 XML 文件中定义另一个 shape,但大小不同,认为这会导致圆圈变小,但它们最终都占据了 100% 的视图。

另外,如果我在 XML 中定义帧而不是一系列 png 图像,我不确定 AnimationDrawable 是否真的会使用更少的内存?也许我仍然会遇到同样的内存使用问题?

我有什么选择?如果时间不是问题,我会尝试制作一个自定义视图,但似乎只是为了这个动画需要做很多工作。

【问题讨论】:

  • 如何使用带有矢量图像的ImageView 并在ImageView 本身上为成长圈使用缩放动画?在一个动画结束时,换入备用矢量图像并运行从小到大的缩放动画。

标签: android out-of-memory android-animation animationdrawable


【解决方案1】:

ScaleAnimation solution provided by Ves 可以工作,但我想我不妨深入研究它并制作一个适当的(而且我想更有效)自定义View。尤其是在处理多个“涟漪”时。

这是整个 View 类,可以在 init() 中对波纹进行调整,以更改颜色、波纹数量等。

RippleView.java

public class RippleView extends View implements ValueAnimator.AnimatorUpdateListener {
    public static final String TAG = "RippleView";


    private class Ripple {
        AnimatorSet mAnimatorSet;
        ValueAnimator mRadiusAnimator;
        ValueAnimator mAlphaAnimator;
        Paint mPaint;

        Ripple(float startRadiusFraction, float stopRadiusFraction, float startAlpha, float stopAlpha, int color, long delay, long duration, float strokeWidth, ValueAnimator.AnimatorUpdateListener updateListener){
            mRadiusAnimator = ValueAnimator.ofFloat(startRadiusFraction, stopRadiusFraction);
            mRadiusAnimator.setDuration(duration);
            mRadiusAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mRadiusAnimator.addUpdateListener(updateListener);
            mRadiusAnimator.setInterpolator(new DecelerateInterpolator());

            mAlphaAnimator = ValueAnimator.ofFloat(startAlpha, stopAlpha);
            mAlphaAnimator.setDuration(duration);
            mAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mAlphaAnimator.addUpdateListener(updateListener);
            mAlphaAnimator.setInterpolator(new DecelerateInterpolator());

            mAnimatorSet = new AnimatorSet();
            mAnimatorSet.playTogether(mRadiusAnimator, mAlphaAnimator);
            mAnimatorSet.setStartDelay(delay);

            mPaint = new Paint();
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(color);
            mPaint.setAlpha((int)(255*startAlpha));
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(strokeWidth);
        }

        void draw(Canvas canvas, int centerX, int centerY, float radiusMultiplicator){
            mPaint.setAlpha( (int)(255*(float)mAlphaAnimator.getAnimatedValue()) );
            canvas.drawCircle(centerX, centerY, (float)mRadiusAnimator.getAnimatedValue()*radiusMultiplicator, mPaint);
        }

        void startAnimation(){
            mAnimatorSet.start();
        }

        void stopAnimation(){
            mAnimatorSet.cancel();
        }
    }

    private List<Ripple> mRipples = new ArrayList<>();



    public RippleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public RippleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    public RippleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        if( isInEditMode() )
            return;

        /*
        Tweak your ripples here!
        */
        mRipples = new ArrayList<>();
        mRipples.add(new Ripple(0.0f, 1.0f, 1.0f, 0.0f, Color.RED, 0, 2000, 4, this));
        mRipples.add(new Ripple(0.0f, 1.0f, 1.0f, 0.0f, Color.WHITE, 500, 2000, 4, this));
    }


    public void startAnimation() {
        setVisibility(View.VISIBLE);

        for (Ripple ripple : mRipples) {
            ripple.startAnimation();
        }
    }

    public void stopAnimation() {
        for (Ripple ripple : mRipples) {
            ripple.stopAnimation();
        }

        setVisibility(View.GONE);
    }


    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int centerX = getWidth()/2;
        int centerY = getHeight()/2;
        int radiusMultiplicator = getWidth()/2;

        for (Ripple ripple : mRipples) {
            ripple.draw(canvas, centerX, centerY, radiusMultiplicator);
        }
    }
}

只需在RippleView 上调用.startAnimation() 即可开始动画。

RippleView r = findViewById(R.id.rippleView);
r.startAnimation();

【讨论】:

    【解决方案2】:

    您可以使用 ScaleAnimation 来更改您的尺寸。我创建了一个 ImageView 并使用了您在上面定义的形状:

    <ImageView
        android:id="@+id/circle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/circle"/>
    

    它与您想要的略有不同,因为我没有费心找到中心点来设置动画。它从左上角缩放。 (0,0)

      ImageView iv = findViewById(R.id.circle);
      float startScale = 0.5f;
      float endScale = 3.0f;
      Animation scaleAnim = new ScaleAnimation( startScale, endScale, 
                                  startScale, endScale,
                                  Animation.RELATIVE_TO_SELF, 0,
                                  Animation.RELATIVE_TO_SELF, 0);
      scaleAnim.setRepeatCount(Animation.INFINITE);
      scaleAnim.setRepeatMode(Animation.REPEAT);
      scaleAnim.setDuration(2000);
      iv.setAnimation(scaleAnim);
      scaleAnim.start();
    

    您可以类似地使用 ValueAnimation 为颜色设置动画。

    【讨论】:

      猜你喜欢
      • 2016-04-25
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 2014-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-10
      相关资源
      最近更新 更多