Android 模仿android5上的Reveal实现

来源:互联网 发布:美国大豆数据 编辑:程序博客网 时间:2024/05/11 05:39

模仿android5上的Reveal实现


          大家应该都接触过安卓5吧,上面的按钮点击的时候会出现波纹的动画,那个是新出来的一个reveal,但是在低版本的系统上不支持,只能想办法自己实现了,同样也是看到了任老师的实现方法,自己分析了一会儿后,加上了点自己的见解,分享给大家,demo的源码我已经上传了.
         
         实现的基本原理就是,自定义一个继承了LinearLayout的MyRevealLayout控件,重写这个控件里面的一些函数,只要是在这个Layout中的控件被点击了,遍历MyRevealLayout中的所有子控件,和点击的位置作比对,确定被点击的子控件后初始化各种参数,随后开始绘制水波.


        
package com.example.dada.testapplication;import java.util.ArrayList;import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Paint;import android.os.Build;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.LinearLayout;public class MyRevealLayout extends LinearLayout implements Runnable {    /**     * 画笔工具     */    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    /**     * 用户点击处的坐标     */    private float mCenterX, mCenterY;    /**     * 获取自定义控件MyRevealLayout     * 在屏幕上的位置     */    private int[] mLocation = new int[2];    /**     * 重新绘制的时间     */    private int INVALIDATE_DURATION = 20;    /**     * 被点击的控件的参数     */    private int mTargetHeight, mTargetWidth;    /**     * mRevealRadius为初始的数值     * mRevealRadiusGap为每次重新绘制半径增加的值     * mMaxRadius为绘制的水波纹圆圈最大的半径     */    private int mRevealRadius = 0, mRevealRadiusGap, mMaxRadius;    /**     * 通过名字就可以看出来了把     * 在被选中的控件长宽中的最大值和最小值     */    private int mMinBetweenWidthAndHeight, mMaxBetweenWidthAndHeight;    /**     * 是否被按下     * 是否需要执行动画     */    private boolean mIsPressed;    private boolean mShouldDoAnimation;    /**     * 这个就是在布局文件中被点击的控件了     */    private View mTargetView;    /**     * 松手的事件分发线程     */    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();    public MyRevealLayout(Context context) {        super(context);        init();    }    public MyRevealLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    public MyRevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public void init() {        setWillNotDraw(false);        mPaint.setColor(getResources().getColor(R.color.reveal_color));    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        this.getLocationOnScreen(mLocation);    }    /**     * 这个就是主要的绘制函数     * 在确定了被点击的控件之后     * 这个方法就会不断的被调用     * 直到水波纹的绘制完成     *     * 这个的原理就是不断的改变绘制的水波纹圆圈半径的大小     * 同时也在判断是否需要继续绘制     * @param canvas     */    @Override    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mTargetView == null || !mShouldDoAnimation || mTargetWidth <= 0)            return;        if (mRevealRadius > mMinBetweenWidthAndHeight / 2)            mRevealRadius += mRevealRadiusGap * 4;        else            mRevealRadius += mRevealRadiusGap;        int[] location = new int[2];        this.getLocationOnScreen(mLocation);        mTargetView.getLocationOnScreen(location);        int top = location[1] - mLocation[1];        int left = location[0] - mLocation[0];        int right = left + mTargetView.getMeasuredWidth();        int bottom = top + mTargetView.getMeasuredHeight();        canvas.save();        canvas.clipRect(left, top, right, bottom);        canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);        canvas.restore();        if (mRevealRadius <= mMaxRadius)            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);        else if (!mIsPressed) {            mShouldDoAnimation = false;            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);        }    }    /**     * 重写事件分发     * 分别对按下等动作做出不同的响应     *     *     * @param event     * @return     */    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        int x = (int) event.getRawX();        int y = (int) event.getRawY();        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                View targetView = getTargetView(this, x, y);                if (targetView != null && targetView.isEnabled()) {                    mTargetView = targetView;                    initParametersForChild(event, targetView);                    postInvalidateDelayed(INVALIDATE_DURATION);                }                break;            case MotionEvent.ACTION_UP:                mIsPressed = false;                postInvalidateDelayed(INVALIDATE_DURATION);                mDispatchUpTouchEventRunnable.event = event;                postDelayed(mDispatchUpTouchEventRunnable, 100);                break;            case MotionEvent.ACTION_CANCEL:                mIsPressed = false;                postInvalidateDelayed(INVALIDATE_DURATION);                break;        }        return super.dispatchTouchEvent(event);    }    /**     * 这个函数的作用正如它的名字一样     * 用于返回被点击的控件是哪一个     *     * 其中调用了另外一个函数isTouchPointInView     * 在后面会注释到     * @param view     * @param x     * @param y     * @return     */    public View getTargetView(View view, int x, int y) {        View target = null;        ArrayList<View> views = view.getTouchables();        for (View child : views)            if (isTouchPointInView(child, x, y)) {                target = child;                break;            }        return target;    }    /**     * 对比用户点击的坐标和传入的控件坐标     * 如果在控件内就返回true     * 否则返回false     *     * @param child     * @param x     * @param y     * @return     */    public boolean isTouchPointInView(View child, int x, int y) {        int[] location = new int[2];        child.getLocationOnScreen(location);        int top = location[1];        int left = location[0];        int right = left + child.getMeasuredWidth();        int bottom = top + child.getMeasuredHeight();        if (child.isClickable() && y >= top && y <= bottom && x >= left && x <= right)            return true;        else            return false;    }    /**     * 初始化被点击控件的参数     * 确定开始绘制水波纹的各项参数     * 比如圆的半径,最大半径,半径增长数值等     *     * 在dispatchTouchEvent()对DOWN事件响应的时候调用     * @param event     * @param view     */    public void initParametersForChild(MotionEvent event, View view) {        mCenterX = event.getX();        mCenterY = event.getY();        mTargetWidth = view.getMeasuredWidth();        mTargetHeight = view.getMeasuredHeight();        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);        mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);        mRevealRadius = 0;        mRevealRadiusGap = mMinBetweenWidthAndHeight / 20;        mIsPressed = true;        mShouldDoAnimation = true;        int[] location = new int[2];        view.getLocationOnScreen(location);        int left = location[0] - mLocation[0];        int mTransformedCenterX = (int) mCenterX - left;        mMaxRadius = Math.max(mTransformedCenterX, mTargetWidth - mTransformedCenterX);        int top = location[1]-mLocation[1];        int transformedCenterY = (int)mCenterY-top;        mMaxRadius = Math.max(mMaxRadius,Math.max(transformedCenterY,mTargetHeight-transformedCenterY));    }    @Override    public void run() {        super.performClick();    }    @Override    public boolean performClick() {        postDelayed(this, 40);        return true;    }    private class DispatchUpTouchEventRunnable implements Runnable {        public MotionEvent event;        @Override        public void run() {            if (mTargetView.isEnabled() && mTargetView.isClickable())                return;            if (isTouchPointInView(mTargetView, (int) event.getRawX(), (int) event.getRawX()))                mTargetView.performClick();        }    }}

         上面的代码就是自定义的控件MyRevealLayout.java,使用的时候只需要将它当作是一个LInearLayout一样的用就可以了,小达自己的理解都写在注释里面了,理解起来应该不是很困难把...~~~
0 0
原创粉丝点击