Пульсация анимации на растровом изображении вместо просмотра - PullRequest
3 голосов
/ 24 мая 2019

Я использовал растровое изображение для рендеринга маленьких цветных кругов внутри вида.Теперь я хочу показывать пользовательскую анимацию Ripples вокруг этих кругов при нажатии.

После поиска в ответах StackOverflow в основном предлагается обернуть эти растровые изображения внутри VIEW и применить анимацию к этим представлениям с помощью инфраструктуры анимации Android.,

Но проблема в том, что я уже рисую эти растровые изображения внутри представления, используя холст этого представления с помощью функции

canvas.drawBitmap()

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

protected void fill(ILineDataSet set, boolean drawCircleHole,
        boolean drawTransparentCircleHole, int selectedEntryIndex) {

        int colorCount = set.getCircleColorCount();
        int holeColorCount = set.getCircleHoleColorCount();
        circleRadius = set.getCircleRadius();
        circleHoleRadius = set.getCircleHoleRadius();

        for (int i = 0; i < (colorCount > holeColorCount ? colorCount: holeColorCount); i++) {

            Bitmap.Config conf = Bitmap.Config.ARGB_4444;
            Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1),
                (int) (circleRadius * 2.1), conf);

            Canvas canvas = new Canvas(circleBitmap);
            circleBitmaps[i] = circleBitmap;

            //fill colors in the values' circles
            mRenderPaint.setColor(set.getCircleColor(i < colorCount ? i : 0));
            mCirclePaintInner.setColor(set.getCircleHoleColor(i < holeColorCount ? i : 0));

            if (drawTransparentCircleHole) {
                // Begin path for circle with hole
                mCirclePathBuffer.reset();

                mCirclePathBuffer.addCircle(
                        circleRadius,
                        circleRadius,
                        circleRadius,
                        Path.Direction.CW);

                // Cut hole in path
                mCirclePathBuffer.addCircle(
                        circleRadius,
                        circleRadius,
                        circleHoleRadius,
                        Path.Direction.CCW);

                // Fill in-between
                canvas.drawPath(mCirclePathBuffer, mRenderPaint);
            } else {

                canvas.drawCircle(
                        circleRadius,
                        circleRadius,
                        circleRadius,
                        mRenderPaint);

                if (drawCircleHole) {
                    canvas.drawCircle(
                            circleRadius,
                            circleRadius,
                            circleHoleRadius,
                            mCirclePaintInner);
                }
            }

            if (i == selectedEntryIndex) {
                circleBitmaps[i] = getScaledUpBitmap(circleBitmaps[i]);
            }
        }
    }

    private Bitmap getScaledUpBitmap(Bitmap bm) {
        //scale bitmap as twice its size
        int width = bm.getWidth();
        int height = bm.getHeight();

        Matrix matrix = new Matrix();
        matrix.postScale(1.5f, 1.5f);

        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
        bm.recycle();
        return resizedBitmap;
    }

1 Ответ

0 голосов
/ 27 мая 2019

Анимация Android Ripple - это комбинация растущей анимации + анимация затухания. Другими словами, это процесс рисования, и поэтому не рекомендуется делать это непосредственно на растровом изображении.

Я написал класс, который представляет собой макет, который можно использовать в качестве сенсорной панели. Этот макет, называемый RippleLayout, расширен с RelativeLayout. Вы можете использовать этот макет, чтобы рисовать круги и управлять событием onTouchListener, чтобы показать анимацию пульсации в заданной точке.

RippleLayout.java

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import java.util.ArrayList;
import java.util.List;

public class RippleLayout extends RelativeLayout {

    private AnimatorSet mAnimatorSet;
    private Paint mPaint;
    private boolean mStarted;
    private float mX, mY;
    private int mCount = 1;
    private int mDuration = 300;
    private float mRadius = 300;
    private IntRippleView rippleView;

    public RippleLayout(Context context) {
        this(context, null, 0);
    }

    public RippleLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void build() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.GRAY);
        List<Animator> animators = new ArrayList<>();
        rippleView = new IntRippleView(getContext());
        rippleView.setCircleRadius(0);
        rippleView.setCircleAlpha(1);
        addView(rippleView);

        ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "CircleRadius", 0f, 1f);
        scaleXAnimator.setRepeatCount(0);
        animators.add(scaleXAnimator);

        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "CircleAlpha", 1f, 0f);
        alphaAnimator.setRepeatCount(0);
        animators.add(alphaAnimator);

        mAnimatorSet = new AnimatorSet();
        mAnimatorSet.setInterpolator(new LinearInterpolator());
        mAnimatorSet.playTogether(animators);
        mAnimatorSet.setDuration(mDuration);
        mAnimatorSet.addListener(mAnimatorListener);
    }

    public void doRipple(float x, float y) {
        doRipple(x, y, mRadius, mPaint.getColor());
    }

    public synchronized void doRipple(float x, float y, float radius, int acolor) {
        if (mStarted)
            mAnimatorSet.end();
        mX = x;
        mY = y;
        mRadius = radius;
        rippleView.bringToFront();
        mPaint.setColor(acolor);
        mAnimatorSet.start();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        build();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mAnimatorSet != null) {
            mAnimatorSet.cancel();
            mAnimatorSet = null;
        }
        mPaint = null;
    }

    private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {

        @Override
        public void onAnimationStart(Animator animator) {
            mStarted = true;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            mStarted = false;
        }

        @Override
        public void onAnimationCancel(Animator animator) {
            mStarted = false;
        }

        @Override
        public void onAnimationRepeat(Animator animator) {
        }

    };

    private class IntRippleView extends View {

        public IntRippleView(Context context) {
            super(context);
        }

        private float CircleRadius = 0;

        public void setCircleRadius(float value) {
            CircleRadius = value;
            invalidate();
        }

        public float getCircleRadius() {
            return CircleRadius;
        }

        private float CircleAlpha = 1;

        public void setCircleAlpha(float value) {
            CircleAlpha = value;
            invalidate();
        }

        public float getCircleAlpha() {
            return CircleAlpha;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            int aColor = Color.RED;
            mPaint.setAlpha((int)(255 * CircleAlpha));
            canvas.drawCircle(mX, mY, mRadius * CircleRadius, mPaint);
        }

    }

}

Как пользоваться

В формате XML

<android.support.constraint.ConstraintLayout
    android:id="@+id/mainlayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="0dp"
    >

    <com.mcblau.pacerblue.components.RippleLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
    >

    </com.mcblau.pacerblue.components.RippleLayout>


</android.support.constraint.ConstraintLayout>

В вашей деятельности

    container = findViewById(R.id.container);
    container.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // detect here which circle was touch
            // get the center of the circle => mCenterX, mCenterY
            container.doRipple(mCenterX, mCenterY); // ripple animation 
            return true;
        }
    });

Вы можете нарисовать свои круги непосредственно в RippleLayout или в другом. Если вы поместите RippleLayout точно поверх своего растрового макета, вы можете использовать его в качестве сенсорной панели. В этом случае используйте setOnTouchListener макета вашего круга.

Используйте doRipple(x, y); или doRipple(x, y, radius, acolor);, чтобы задать радиус круга в виде цвета пульсации.

...