位图而不是视图上的波纹动画

Posted

技术标签:

【中文标题】位图而不是视图上的波纹动画【英文标题】:Ripple Animation on a Bitmap instead of View 【发布时间】:2019-10-10 17:03:57 【问题描述】:

我一直在使用位图在视图中渲染小的彩色圆圈。现在我想在点击时在这些圆圈周围显示自定义涟漪动画。

在通过 *** 的答案搜索了一段时间后,主要建议将这些位图包装在 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;
    

【问题讨论】:

你有位置和尺寸的圆圈列表吗?如果是,请分享您的代码以检测视图和列表条目类上的触摸事件。 @Ferran 是的,我管理位图数组列表。我已经编辑了这个问题。现在,我将选择作为参数的圆的索引发送给函数 fill() 并在其索引匹配时缩放相同的位图。我还想在点击时在该圆形位图周围显示波纹并在位图缩放完成后触发波纹动画。 【参考方案1】:

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_
    android:layout_
    android:padding="0dp"
    >

    <com.mcblau.pacerblue.components.RippleLayout
        android:id="@+id/container"
        android:layout_
        android:layout_
        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); 将圆半径设置为波纹颜色。

【讨论】:

以上是关于位图而不是视图上的波纹动画的主要内容,如果未能解决你的问题,请参考以下文章

仅对 ImageView 的“src”alpha 属性而不是整个视图进行动画处理

带有波纹动画的Android自定义视图边缘裁剪

imageview 上的波纹效果

非按钮视图上的 Android 波纹效果 + 高程

在没有用户交互的情况下播放波纹动画

阻止其他活动组件的动画视图