Создайте анимацию загрузки "растущей ряби" - OutOfMemoryException - PullRequest
0 голосов
/ 07 октября 2018

Я хочу отобразить эту анимацию в приложении для Android:

enter image description here

Итак, я создал <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>

Затем я устанавливаю это как background для ImageView и запускаю анимацию, используя

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 для определения другого shape в том же XML-файле, но с другим размером, полагая, что это приведет к уменьшению круга, но они оба заняли 100% обзора.

Кроме того, я не уверен, будет ли AnimationDrawable фактически использовать меньше памяти, если я определю кадры в XML, а не серию png изображений?Возможно, я все еще столкнусь с той же проблемой использования памяти?

Какие у меня есть альтернативы?Если бы время не было проблемой, я бы попробовал создать собственный вид, но, похоже, для этой анимации много работы.

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Решение ScaleAnimation, предоставленное Ves , будет работать, но я подумал, что мог бы также погрузиться в него и сделать правильный (и, я думаю, более эффективный) кастом View.Особенно, когда речь идет о нескольких «рябях».

enter image description here

Это весь класс 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);
        }
    }
}

Просто позвоните .startAnimation() на RippleView, чтобы запустить анимацию.

RippleView r = findViewById(R.id.rippleView);
r.startAnimation();
0 голосов
/ 07 октября 2018

Вы можете использовать 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.

...