您的位置:首页 > 移动开发 > Android开发

Android 5.0水波纹效果

2015-07-29 17:32 183 查看
前言

上篇博客已经是很久以前了,这段时间的确是忙了很多,技术相关的也做的少了。近期一个项目中,需要在4.4上实现一些5.0上的特效,例如Button之类的控件点击时的水波纹效果。在blog里找到了任教主的一篇博文,受到了启发,将他的Demo改动了一些就实现了。具体在应用到自己项目中时遇到了些需求变化和其他问题,这里就相当于做一个记录。

效果演示

AS还是不怎么会用,gif的图就没有了,这里给个静态的





这里说明一下,这个截图是一个拨号盘,在拨号的时候会有水波纹效果。

[code]对比Demo中的效果
![这里写图片描述](http://img.blog.csdn.net/20150729171157159)


从效果图中可以看出与其他水波纹Demo的差异之处在于这个水波纹可以突破控件自身的大小。

思路

重载dispatchTouchEvent获取相应的坐标以及绘制对象,

重载dispatchDraw,根据点击事件来绘制一个逐渐变大的Circle。

代码及说明

Java代码

[code] public class CustomTableLayout extends TableLayout{

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Paint mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    /**目标控件宽度*/
    private int mTargetWidth;

    /**目标控件高度*/
    private int mTargetHeight;

    /**最小高宽*/
    private int mMinBetweenWidthAndHeight;
    private int mMaxBetweenWidthAndHeight;

    /**水波纹最大半径*/
    private int mMaxRevealRadius;

    /**水波纹半径增量*/
    private int mRevealRadiusGap;

    /**水波纹半径*/
    private int mRevealRadius = 0;

    /**布局处于屏幕的坐标*/
    private int[] mLocationInScreen = new int[2];

    /**是否绘制水波纹*/
    private boolean mShouldDoAnimation = false;

    /**点击状态*/
    private boolean mIsPressed = false;

    /**绘制间隔*/
    private int INVALIDATE_DURATION = 40;

    /**点击的View*/
    private View mTouchTarget;

    public CustomTableLayout(Context context) {
        super(context);
        init();
    }

    public CustomTableLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setWillNotDraw(false);
        mPaint.setColor(getResources().getColor(R.color.reveal_color_2));
        mBgPaint.setColor(getResources().getColor(R.color.reveal_color_2));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        this.getLocationOnScreen(mLocationInScreen);
    }

    /**
     * @Title: initParametersForChildNew   
     * @Description: 初始化各个参数   
     * @param: @param view
     * @return: void      
     * @throws
     */
    private void initParametersForChildNew(View view){
        mTargetWidth = view.getMeasuredWidth();
        mTargetHeight = view.getMeasuredHeight();
        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
        mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);
        mRevealRadius = 0;
        mShouldDoAnimation = true;
        mIsPressed = true;
        mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;

        int[] location = new int[2];
        view.getLocationOnScreen(location);

        //突破控件大小的关键之一
        mMaxRevealRadius = mMaxBetweenWidthAndHeight/4 +2*mRevealRadiusGap;
    }

    /**
     * @Title: getTouchTarget   
     * @Description: 获取点击的View   
     * @param: @param view
     * @param: @param x
     * @param: @param y
     * @param: @return
     * @return: View      
     * @throws
     */
    private View getTouchTarget(View view, int x, int y) {
        View target = null;
        ArrayList<View> TouchableViews = view.getTouchables();
        for (View child : TouchableViews) {
            if (isTouchPointInView(child, x, y)) {
                target = child;
                break;
            }
        }
        return target;
    }

    /**
     * @Title: isTouchPointInView   
     * @Description: 判断point是否是处于View中  
     * @param: @param view
     * @param: @param x
     * @param: @param y
     * @param: @return
     * @return: boolean      
     * @throws
     */
    private boolean isTouchPointInView(View view, int x, int y) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0];
        int top = location[1];
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        if (view.isClickable() && y >= top && y <= bottom
                && x >= left && x <= right) {
            return true;
        }
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
         int x = (int) event.getRawX();
            int y = (int) event.getRawY();
            int action = event.getAction();
            if (action == MotionEvent.ACTION_DOWN) {
                View touchTarget = getTouchTarget(this, x, y);
                if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled()) {
                    mTouchTarget = touchTarget;
                    initParametersForChildNew(touchTarget);
                    postInvalidateDelayed(INVALIDATE_DURATION);
                }
            } else if (action == MotionEvent.ACTION_UP) {
                mIsPressed = false;
                postInvalidateDelayed(INVALIDATE_DURATION*2);
            } else if (action == MotionEvent.ACTION_CANCEL) {
                mIsPressed = false;
                postInvalidateDelayed(INVALIDATE_DURATION);
            }

        return super.dispatchTouchEvent(event);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

         if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) {
                return;
            }

            if (mRevealRadius > mMaxRevealRadius) {

            } else {
                mRevealRadius += mRevealRadiusGap;
            }
            this.getLocationOnScreen(mLocationInScreen);
            int[] location = new int[2];
            mTouchTarget.getLocationOnScreen(location);
            int left = location[0] - mLocationInScreen[0];
            int top = location[1] - mLocationInScreen[1];
            int right = left + mTouchTarget.getMeasuredWidth();
            int bottom = top + mTouchTarget.getMeasuredHeight();
            canvas.save();
            canvas.clipRect(left, top-mRevealRadius, right, bottom+mRevealRadius);
            canvas.drawCircle(left+mTargetWidth/2, top+mTargetHeight/2, mMaxRevealRadius, mBgPaint);
            canvas.drawCircle(left+mTargetWidth/2, top+mTargetHeight/2, mRevealRadius, mPaint);
            canvas.restore();

            if (mRevealRadius <= mMaxRevealRadius) {
                postInvalidateDelayed(INVALIDATE_DURATION, left, top-mMaxRevealRadius, right, bottom+mMaxRevealRadius);
            } else if (!mIsPressed) {
                mShouldDoAnimation = false;
                postInvalidateDelayed(INVALIDATE_DURATION, left, top-mMaxRevealRadius, right, bottom+mMaxRevealRadius);
            }
    }
}


我这继承的是一个TabLayout,当然也可以继承其他的Layout实现相同效果。

由于是要做一个扩散出来的水波纹,这里的circle就的圆心就取的是死的,没有根据event来获取,需求不一致就需要重新计算canvas.drawCircle(left+mTargetWidth/2, top+mTargetHeight/2, mRevealRadius, mPaint);和postInvalidateDelayed(INVALIDATE_DURATION, left, top-mMaxRevealRadius, right, bottom+mMaxRevealRadius);

最后有几个需要提醒的地方是,dispatchTouchEvent中最好只return super.dispatchTouchEvent(event);

否则可能会出现长按事件混淆的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: