Search code examples
androidsvgmaterial-designanimatedvectordrawable

Continuous ripple animation - Android


Is it possible to add a continuous ripple effect behind an icon which can be show/hide programmatically?

The icon is set as an end icon drawable on TextInpuLayout.

Something like this:

enter image description here


Solution

  • Please check below code,

    public class RippleBackground extends RelativeLayout{
    
        private static final int DEFAULT_RIPPLE_COUNT=6;
        private static final int DEFAULT_DURATION_TIME=3000;
        private static final float DEFAULT_SCALE=6.0f;
        private static final int DEFAULT_FILL_TYPE=0;
    
        private int rippleColor;
        private float rippleStrokeWidth;
        private float rippleRadius;
        private int rippleDurationTime;
        private int rippleAmount;
        private int rippleDelay;
        private float rippleScale;
        private int rippleType;
        private Paint paint;
        private boolean animationRunning=false;
        private AnimatorSet animatorSet;
        private ArrayList<Animator> animatorList;
        private LayoutParams rippleParams;
        private ArrayList<RippleView> rippleViewList=new ArrayList<RippleView>();
    
        public RippleBackground(Context context) {
            super(context);
        }
    
        public RippleBackground(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context, attrs);
        }
    
        public RippleBackground(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context, attrs);
        }
    
        private void init(final Context context, final AttributeSet attrs) {
            if (isInEditMode())
                return;
    
            if (null == attrs) {
                throw new IllegalArgumentException("Attributes should be provided to this view,");
            }
    
            final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleBackground);
            rippleColor=typedArray.getColor(R.styleable.RippleBackground_rb_color, getResources().getColor(R.color.rippelColor));
            rippleStrokeWidth=typedArray.getDimension(R.styleable.RippleBackground_rb_strokeWidth, getResources().getDimension(R.dimen.rippleStrokeWidth));
            rippleRadius=typedArray.getDimension(R.styleable.RippleBackground_rb_radius,getResources().getDimension(R.dimen.rippleRadius));
            rippleDurationTime=typedArray.getInt(R.styleable.RippleBackground_rb_duration,DEFAULT_DURATION_TIME);
            rippleAmount=typedArray.getInt(R.styleable.RippleBackground_rb_rippleAmount,DEFAULT_RIPPLE_COUNT);
            rippleScale=typedArray.getFloat(R.styleable.RippleBackground_rb_scale,DEFAULT_SCALE);
            rippleType=typedArray.getInt(R.styleable.RippleBackground_rb_type,DEFAULT_FILL_TYPE);
            typedArray.recycle();
    
            rippleDelay=rippleDurationTime/rippleAmount;
    
            paint = new Paint();
            paint.setAntiAlias(true);
            if(rippleType==DEFAULT_FILL_TYPE){
                rippleStrokeWidth=0;
                paint.setStyle(Paint.Style.FILL);
            }else
                paint.setStyle(Paint.Style.STROKE);
            paint.setColor(rippleColor);
    
            rippleParams=new LayoutParams((int)(2*(rippleRadius+rippleStrokeWidth)),(int)(2*(rippleRadius+rippleStrokeWidth)));
            rippleParams.addRule(CENTER_IN_PARENT, TRUE);
    
            animatorSet = new AnimatorSet();
            animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
            animatorList=new ArrayList<Animator>();
    
            for(int i=0;i<rippleAmount;i++){
                RippleView rippleView=new RippleView(getContext());
                addView(rippleView,rippleParams);
                rippleViewList.add(rippleView);
                 final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleX", 1.0f, rippleScale);
                scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);
                scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
                scaleXAnimator.setStartDelay(i * rippleDelay);
                scaleXAnimator.setDuration(rippleDurationTime);
                animatorList.add(scaleXAnimator);
                final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleY", 1.0f, rippleScale);
                scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
                scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
                scaleYAnimator.setStartDelay(i * rippleDelay);
                scaleYAnimator.setDuration(rippleDurationTime);
                animatorList.add(scaleYAnimator);
                final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "Alpha", 1.0f, 0f);
                alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);
                alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);
                alphaAnimator.setStartDelay(i * rippleDelay);
                alphaAnimator.setDuration(rippleDurationTime);
                animatorList.add(alphaAnimator);
            }
    
            animatorSet.playTogether(animatorList);
        }
    
        private class RippleView extends View{
    
            public RippleView(Context context) {
                super(context);
                this.setVisibility(View.INVISIBLE);
            }
    
            @Override
            protected void onDraw(Canvas canvas) {
                int radius=(Math.min(getWidth(),getHeight()))/2;
                canvas.drawCircle(radius,radius,radius-rippleStrokeWidth,paint);
            }
        }
    
        public void startRippleAnimation(){
            if(!isRippleAnimationRunning()){
                for(RippleView rippleView:rippleViewList){
                    rippleView.setVisibility(VISIBLE);
                }
                animatorSet.start();
                animationRunning=true;
            }
        }
    
        public void stopRippleAnimation(){
            if(isRippleAnimationRunning()){
                animatorSet.end();
                animationRunning=false;
            }
        }
    
        public boolean isRippleAnimationRunning(){
            return animationRunning;
        }
    }
    

    in attrs.xml file add this ,

    <declare-styleable name="RippleBackground">
            <attr name="rb_color" format="color" />
            <attr name="rb_strokeWidth" format="dimension"/>
            <attr name="rb_radius" format="dimension"/>
            <attr name="rb_duration" format="integer"/>
            <attr name="rb_rippleAmount" format="integer"/>
            <attr name="rb_scale" format="float"/>
            <attr name="rb_type" format="enum">
                <enum name="fillRipple" value="0"/>
                <enum name="strokeRipple" value="1"/>
            </attr>
        </declare-styleable>
    

    add color in color.xml file

    <color name="rippelColor">#0099CC</color>
    

    add dimension in dimen.xml file

    <dimen name="rippleStrokeWidth">2dp</dimen>
        <dimen name="rippleRadius">64dp</dimen>
    

    After adding above code just start animation like this in your activity

    rippleBackground.startRippleAnimation();