Android 自定义水波纹 可实现全屏效果,指定view效果,用起来非常方便,灵活

Android 5.0 以上引入了控件点击的水波纹效果,为了用户体验和相关需求需要。

以下是个人观点:

我认为系统自带水波纹效果,应用场景较少,而且使用起来不够灵活,通过自定义,可以作用于很多view,根据ui需求,调节成自己需要的,非常方便便利,即使应用这个屏幕都没问题,不多说,直接上代码:

布局里面引入:

    <com.lenovo.spacetextile.RevealLayout
        android:id="@+id/mImg_Select"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:clickable="true" />

然后把这个自定义copy进去就可以了

import android.annotation.SuppressLint;
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.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import java.util.ArrayList;

@SuppressLint("AppCompatCustomView")
public class RevealLayout extends ImageView implements Runnable
{

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 被点击的控件的宽高
    private int mTargetWidth;
    private int mTargetHeight;
    // 在被选中的控件长宽中的最大值和最小值
    private int mMinBetweenWidthAndHeight;
    private int mMaxBetweenWidthAndHeight;

    // mMaxRadius为绘制的水波纹圆圈最大的半径
    private int mMaxRevealRadius;
    // mRevealRadiusGap为每次重新绘制半径增加的值
    private int mRevealRadiusGap;
    // mRevealRadius为初始的数值
    private int mRevealRadius = 0;
    // 用户点击处的坐标
    private float mCenterX;
    private float mCenterY;
    // 获取自定义控件MyRevealLayout 在屏幕上的位置
    private int[] mLocationInScreen = new int[2];
    // 是否执行动画
    private boolean mShouldDoAnimation = false;
    // 是否被按下
    private boolean mIsPressed = false;
    // 重新绘制的时间 单位毫秒
    private int INVALIDATE_DURATION = 100;
    // mTouchTarget指的是用户点击的那个view
    private View mTouchTarget;
    // 松手的事件分发线程
    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable();

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

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

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init();
    }

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

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

    private 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;
        mShouldDoAnimation = true;
        mIsPressed = true;
        mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;

        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0] - mLocationInScreen[0];
        int transformedCenterX = (int) mCenterX - left;
        mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX);
    }

    /**
     * 绘制水波
     */
    protected void dispatchDraw(Canvas canvas)
    {
        super.dispatchDraw(canvas);
        if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null)
        {
            return;
        }

        if (mRevealRadius > mMinBetweenWidthAndHeight / 2)
        {
            mRevealRadius += mRevealRadiusGap * 4;
        } else
        {
            mRevealRadius += mRevealRadiusGap;
        }
        this.getLocationOnScreen(mLocationInScreen);
        int[] location = new int[2];
        mTouchTarget.getLocationOnScreen(location);
        // 获得要绘制View的left, top, right, bottom值
        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, right, bottom);
        canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);
        canvas.restore();

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

    @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;
                initParametersForChild(event, touchTarget);
                // 刷新界面,延迟执行时间
                postInvalidateDelayed(INVALIDATE_DURATION);
            }
        } else if (action == MotionEvent.ACTION_UP)
        {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
            mDispatchUpTouchEventRunnable.event = event;
            postDelayed(mDispatchUpTouchEventRunnable, 40);
            return true;
        } else if (action == MotionEvent.ACTION_CANCEL)
        {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
        }

        return super.dispatchTouchEvent(event);
    }

    /**
     * 遍历view树找到用户所点击的那个view
     *
     * @param view
     * @param x
     * @param y
     * @return
     */
    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;
    }

    /**
     * 判断事件的xy是否落在view的上下左右四个角之内
     *
     * @param view
     * @param x
     * @param y
     * @return
     */
    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 performClick()
    {
        postDelayed(this, 300);
        return true;
    }

    @Override
    public void run()
    {
        super.performClick();
    }

    private class DispatchUpTouchEventRunnable implements Runnable
    {
        public MotionEvent event;

        @Override
        public void run()
        {
            if (mTouchTarget == null || !mTouchTarget.isEnabled())
            {
                return;
            }

            if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY()))
            {
                // 使用代码主动去调用控件的点击事件(模拟人手去触摸控件)
                mTouchTarget.performClick();
            }
        }
    };

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值