美文网首页
打造兼容的Android揭面水波动画库

打造兼容的Android揭面水波动画库

作者: apkcore | 来源:发表于2017-05-04 16:42 被阅读118次

    今天看到了@Anderson大码渣写的文章,对5.0以上默认使用ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, endRadius);来实现渐变,

    预览 ,写得非常好,这一块的封装就跳过了,可以直接看他的原文。

    4.0以上兼容

    要想在低版本中也能正常使用,我们可以直接自定义一个viewGroup,挂载在decorview上,然后使用canvas.clipPath裁剪一个圆,这样达到覆盖的目的。

    
    public class CircularRevealLayout extends FrameLayout {
    
        private static final String TAG = "CircularRevealLayout";
    
        private float revealRadius;
        private int centerX;
        private int centerY;
        private float startRadius;
        private float endRadius;
    
        private View childRevealView;
        private boolean isRunning = false;//动画是否正在执行
        private Path path;//绘制路径
    
    
    
        public CircularRevealLayout(Context context) {
            this(context, null, 0);
        }
    
        public CircularRevealLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CircularRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
    
        }
    
        private void init() {
            path = new Path();
            setFocusable(false);
        }
    
        /**
         * 设置要揭示的子view
         * @param childRevealView
         */
        public void setChildRevealView(View childRevealView) {
            this.childRevealView = childRevealView;
        }
    
        /**
         * 设置要揭示的子view的下标
         * @param index
         */
        public void setChildRevealViewIndex(int index) {
            if(getChildCount()>index){
                this.childRevealView=getChildAt(index);
            }
    
        }
    
        /**
         * 设置揭示半径
         * @param revealRadius
         */
        private void setRevealRadius(float revealRadius) {
            this.revealRadius = revealRadius;
            Log.e(TAG, "revealRadius=" + revealRadius);
            invalidate();
        }
    
        /**
         * 设置揭示的中心点x坐标
         * @param centerX
         */
        public void setCenterX(int centerX) {
            this.centerX = centerX;
        }
    
        /**
         * 设置揭示的中心点y坐标
         * @param centerY
         */
    
        public void setCenterY(int centerY) {
            this.centerY = centerY;
        }
    
        /**
         * 设置揭示的开始半径
         * @param startRadius
         */
        public void setStartRadius(float startRadius) {
            this.startRadius = startRadius;
    
        }
    
        /**
         * 设置揭示的结束半径
         * @param endRadius
         */
        public void setEndRadius(float endRadius) {
            this.endRadius = endRadius;
        }
    
    
        public Animator getAnimator() {
    
            ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "revealRadius", startRadius, endRadius);
            objectAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    isRunning = true;
                }
    
                @Override
                public void onAnimationEnd(Animator animator) {
                    isRunning = false;
                }
    
                @Override
                public void onAnimationCancel(Animator animator) {
                    isRunning = false;
                }
    
                @Override
                public void onAnimationRepeat(Animator animator) {
    
                }
            });
            return objectAnimator;
        }
    
        @Override
        protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
            if (isRunning && child == childRevealView) {
                final int state = canvas.save();
    
                path.reset();
                path.addCircle(centerX, centerY, revealRadius, Path.Direction.CW);//Pat.Direction.CW:顺时针
    
                canvas.clipPath(path);//裁剪
    
                boolean isInvalided = super.drawChild(canvas, child, drawingTime);
    
                canvas.restoreToCount(state);
    
                return isInvalided;
            }
    
            return super.drawChild(canvas, child, drawingTime);
        }
    
    }
    

    再实现兼容的ViewAnimationCompatUtils

    
    public class ViewAnimationCompatUtils {
        private static final String TAG = "ViewAnimationCompatUtil";
    
        public static Animator createCircularReveal(@NonNull View view, int centerX, int centerY, float startRadius, float endRadius) {
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, endRadius);
            }
    
            //如果父view已经是CircularRevealLayout,则设置参数后直接返回animator
            if (view.getParent() != null && view.getParent() instanceof CircularRevealLayout) {
                Log.e(TAG, "parent is CircularRevealLayout");
                CircularRevealLayout circularRevealLayout = (CircularRevealLayout) view.getParent();
                circularRevealLayout.setCenterX(centerX);
                circularRevealLayout.setCenterY(centerY);
                circularRevealLayout.setStartRadius(startRadius);
                circularRevealLayout.setEndRadius(endRadius);
                circularRevealLayout.setChildRevealView(view);
                return circularRevealLayout.getAnimator();
            }
    
            Log.e(TAG, "parent is not CircularRevealLayout");
    
            //如果父view不是CircularRevealLayout,则先为view添加父view CircularRevealLayout
            //之后将CircularRevealLayout替换掉原来的view
            CircularRevealLayout circularRevealLayout = new CircularRevealLayout(view.getContext());
    
            //开启硬件加速
            circularRevealLayout.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    
            circularRevealLayout.setCenterX(centerX);
            circularRevealLayout.setCenterY(centerY);
            circularRevealLayout.setStartRadius(startRadius);
            circularRevealLayout.setEndRadius(endRadius);
            circularRevealLayout.setChildRevealView(view);
    
            ViewGroup.LayoutParams params = view.getLayoutParams();
            ViewGroup parent = (ViewGroup) view.getParent();
    
            int index = 0;
            if (parent != null) {
                index = parent.indexOfChild(view);//记录view在原先父view的下标
                Log.e(TAG, "index=" + index);
                parent.removeView(view);
                circularRevealLayout.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
                parent.addView(circularRevealLayout, index, params);
            }
            return circularRevealLayout.getAnimator();
        }
    }
    
    

    写到这儿,已经能正常显示了,不过在@Anderson大码渣](http://www.jianshu.com/p/d4b421795154)的基础上我们最好再继续做一定的修改,上文在AnimationHelper的startActivityForResult中写了个延迟任务,

     view.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Animator anim = ViewAnimationCompatUtils.createCircularReveal(imageview, xCenter, yCenter, finalRadius, 0);
                            anim.setDuration(durationMills);
                            anim.addListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {
                                    super.onAnimationEnd(animation);
                                    try {
                                        decorView.removeView(imageview);
                                        imageview.setVisibility(View.GONE);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            });
                            anim.start();
                        }
                    }, 1000);
    

    在4.0的系统上退回到前一个activity的时候动画有时候会显示不出来,所以最在在baseactivity的onresume中写一个回调,当回调时调用此动画执行。

    BaseActivity...
    
        @Override
        protected void onResume() {
            super.onResume();
            if (resume != null) {
                resume.setResume();
            }
        }
    
        public void setResume(IonResume resume) {
            this.resume = resume;
        }
    
        private IonResume resume;
        
        public interface IonResume {
            void setResume();
        }
    
    ...
    thisActivity.setResume(new BaseActivity.IonResume() {
                        @Override
                        public void setResume() {
                            if (imageview.getVisibility()==View.VISIBLE){
                                Animator anim = ViewAnimationCompatUtils.createCircularReveal(imageview, xCenter, yCenter, finalRadius, 0);
                                anim.setDuration(800);
                                anim.addListener(new AnimatorListenerAdapter() {
                                    @Override
                                    public void onAnimationEnd(Animator animation) {
                                        super.onAnimationEnd(animation);
                                        try {
                                            decorView.removeView(imageview);
                                            imageview.setVisibility(View.GONE);
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                });
                                anim.start();
                            }
                        }
                    });
    

    Github代码下载
    也欢迎大家关注我的简书CSDN

    相关文章

      网友评论

          本文标题:打造兼容的Android揭面水波动画库

          本文链接:https://www.haomeiwen.com/subject/rqhdtxtx.html