Android点击Button水波纹效果

先上图,看看接下来我要向大家介绍的是个什么东西,例如以下图:
这里写图片描写叙述
接下来要介绍的就是怎样实现上述图中的波纹效果。这样的效果假设大家没有体验过的话,能够看看百度手机卫士或者360手机卫士,里面的按钮点击效果都是这样的,另外Android 5.0以上的版本号也出现了这样的效果。

不多说,以下聊聊详细的怎么实现。

首先大家看到的是三个button,水波纹的出现给我们的错觉是直接将波纹绘制在button上面的,可是这样能做到吗?首先button自己有background和src,假设把半透明的水波纹当作background或者src绘制到button上面,肯定是会损失button原有的样式的。可能有朋友猜想那就把水波纹绘制在屏幕上呗,恭喜这位朋友答对了。至少我是这么干的,详细思路就是,我们自己实现一个layout,在layout中捕捉事件,并对事件进行对应的处理,在down事件中寻找当前用户点击的是哪个view,找出view所在的矩形区域,将一个透明的圆环绘制到这个矩形区域,在up事件中,延时分发view的onclick事件。


1、自己实现一个layout:
2、重写layout的dispatchTouchEvent方法,在down事件中找出被点击的view。

    public View findTargetView(float x, float y, View anchorView) {
        ArrayList<View> touchablesView =  anchorView.getTouchables();
        View targetView = null;
        for (View child : touchablesView) {
            RectF rectF = getViewRectF(child);
            if (rectF.contains(x, y) && child.isClickable()) {
                // 这说明被点击的view找到了
                targetView = child;
                break;
            }
        }
        return targetView;
    }

接着找出view所在的矩形区域,由于要将波纹绘制到该区域:

    public RectF getViewRectF(View view) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int childLeft = location[0];
        int childTop = location[1];
        int childRight = childLeft + view.getMeasuredWidth();
        int childBottom = childTop + view.getMeasuredHeight();
        return new RectF(childLeft, childTop, childRight, childBottom);
    }

矩形区域找到之后,这个区域就是我们要绘制的博波纹所在地。上面也说过了,波纹事实上就是圆环,绘制圆的画是须要知道圆心坐标和圆的半径,圆心坐标肯定就是down时候的x和y了。可是半径怎么计算合适?大家看到上面的图知道假设view的宽度大于高度,点击view的左下角或者右下角,那么半径基本上就是等于view的宽度。点击view的上部或者下部分,半径就是在0和view的高度之间,详细的计算方式看下图:
这里写图片描写叙述
那么依据上图。半径的计算方式就应该是:

    float left = circleCenterX - targetTouchRectF.left;
    float right = targetTouchRectF.right - circleCenterX;
    float top = circleCenterY - targetTouchRectF.top;
    float bottom = targetTouchRectF.bottom - circleCenterY;
    // 计算出最大的值则为半径
    rawRadius = Math.max(bottom, Math.max(Math.max(left, right), top));

半径算出来了。虽说圆心就是down时的x和y,可是有个地方还是须要注意的,在绘制圆环的时候提供的圆心坐标的x和y是在本文中是相对于layout的,所以在计算y的时候是须要进行一定处理的:

    /**
     * 获取圆环的中心坐标
     */
    public float[] getCircleCenterPostion(float x,float y){
        int[] location = new int[2];
        float[] mDownPositon = new float[2];
        getLocationOnScreen(location );
        mDownPositon[0] = x;
        mDownPositon[1] = y -location[1];
        return mDownPositon;
    }

圆心坐标和半径都计算好了,记下来就能够绘制圆形波纹了。那么在哪里绘制这个波纹比較合适呢?有朋友立刻就说肯定是在onDraw方法里面绘制了,那么恭喜你在我看来你是答错了,我们的layout中是非常有非常多childview的,而layout是个viewGroup,viewGroup在绘制的时候,是先绘制自身的背景。再绘制自身,再绘制childview,假设在onDraw中绘制波纹。也就意味者后面绘制出来的childView会将我们的波纹遮盖,所以我们就应该等到childview绘制完成后再来绘制波纹,这样能够保证childview在最顶层。
重写dispatchDraw方法:

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        /**
         * 绘制完子元素后開始绘制波纹
         */
        if (mTargetTouchView != null) {
            RectF clipRectF = clipRectF(mTargetTouchView);
            canvas.save();
            // 为了不让绘制的圆环超出所要绘制的范围
            canvas.clipRect(clipRectF);
            if(drawedRadius < rawRadius){
                drawedRadius += rawRadius / drawingRadiusDegrees;
                canvas.drawCircle(mDownPositon[0], mDownPositon[1], drawedRadius, mHalfTransPaint);
                postInvalidateDelayed(INVALID_DURATION);
            }else{
                canvas.drawCircle(mDownPositon[0], mDownPositon[1], rawRadius, mTransPaint);
                post(delayedRunnable);
            }
            canvas.restore();
        }
    }

在分发绘制事件中大家能够看到,波纹是一段一段的绘制。形例如以下图:
这里写图片描写叙述
而这一段段的波纹正是通过绘制一个个的圆环实现的,所以在没绘制完成一个圆环的时候。都须要延时又一次绘制下一个圆环。


通过上面波纹效果基本上完成了,可是按钮是有点击事件的,像360手机卫士或者百度手机卫士等都是等波纹效果播放完成后才会响应点击事件,所以我们这里也要对这个点击事件进行延时响应。


在up事件中,记录此次事件的event,而且返回true,表示消费此次的事件,然后再圆环绘制完成后。再利用找到的view去分发这个event:

    if (ev.getAction() == MotionEvent.ACTION_UP) {
            // 须要让波纹绘制完成后再运行在up中运行的方法
//          if(drawedRadius==0){
//              return false;
//          }
//          long totalTime = (long) (INVALID_DURATION * (drawingRadiusDegrees+5));
//          // 离波纹结束的时间
//          long time = (long) (totalTime - drawedRadius*totalTime / rawRadius);
            delayedRunnable.event = ev;
            return true;
    }

    class postUpEventDelayed implements Runnable{
        private MotionEvent event;
        @Override
        public void run() {
            if(mTargetTouchView!=null && mTargetTouchView.isClickable()
                    && event!=null){
                mTargetTouchView.dispatchTouchEvent(event);// 分发
            }
        }
    }

在dispatchDraw方法中。推断假设绘制完成就post(delayedRunnable);运行childView的事件延时分发。

本文就写到这里了。本文案例的实现同一时候也參考了http://m.blog.csdn.net/blog/singwhatiwanna/42614953。在此对博主表示感谢。

源代码下载连接:Android button点击水波纹效果

posted @ 2017-07-19 09:13  mfmdaoyou  阅读(4382)  评论(0编辑  收藏  举报