自定义九宫格手势解锁
in Android with 0 comment

自定义九宫格手势解锁

in Android with 0 comment

项目中用到手势解锁,然而没有在 GitHub 上找到想要的样式= =,只好自己来定义了,下面来看代码~~

基本上很多应用的手势解锁全都是九宫格的,内部内就是九个小圈圈而已。那么我们就先来自定义这个小圈圈吧~

一、状态

圈圈的颜色选择状态有大致有三种状态,所以我定义了一个枚举来区分

package com.juzisang.com.library;

/**
 * Created by 橘子桑 on 2016/3/27.
 */
public enum LockState {
    SELECT_STATE,//选中
    ERRER_STATE, //错误
    DEFAULT_COLOR //默认
}

二、圆圈 View

圈圈分为边框,内部填充色,还有内部圆。所以我定义了三个画笔来区分。

package com.juzisang.com.library;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by 橘子桑 on 2016/3/27.
 */
public class MarkerView extends View {
    //是否显示内部的圈圈
    private boolean mInsideNodeShow;
    //宽度
    protected int mContentWidth;
    //宽度
    protected int mContentRadius;
    //选中状态
    protected LockState mCurrentState = LockState.DEFAULT_COLOR;
    //画边框画圆的的画笔
    private Paint mNodeFramePaint;
    private Paint mNodeCirclePaint;
    private Paint mNodeFullPaint;
    //默认的颜色
    private int mDefaultColor = Color.parseColor("#757575");
    private int mDefailtFullColor = Color.parseColor("#64757575");
    private int mNodeDefaultColor = Color.parseColor("#757575");
    //选中的颜色
    private int mSelectColor = Color.parseColor("#7ECEF4");
    private int mFrameSelectFullColor = Color.parseColor("#647ECEF4");
    private int mNodeSelectColor = Color.parseColor("#7ECEF4");
    //错误时候的颜色
    private int mErrerColor = Color.parseColor("#EC6941");
    private int mErrerFullColor = Color.parseColor("#64EC6941");
    private int mErrerNodeColor = Color.parseColor("#EC6941");
    //边框的宽度
    private int mFrameLineWidth;
    private int mNodeRadius;
    //每个圈圈的内边距
    private int mNodePadding;
    //触摸有效的范围
    private float mTouchRatio;
    //当前标记的位置
    private int mNum;

    public MarkerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context, attrs, 0);
    }

    public MarkerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs, defStyleAttr);
    }
    //以后外部布局传来的参数
    public MarkerView(Context context, int mDefaultColor, int mDefailtFullColor, int mNodeDefaultColor,
                      int mSelectColor, int mFrameSelectFullColor, int mNodeSelectColor,
                      int mErrerColor, int mErrerFullColor, int mErrerNodeColor,
                      int mFrameLineWidth, int mNodeRadius, int mNodePadding, boolean insideNodeShow) {
        super(context);
        this.mInsideNodeShow = insideNodeShow;
        this.mDefaultColor = mDefaultColor;
        this.mDefailtFullColor = mDefailtFullColor;
        this.mNodeDefaultColor = mNodeDefaultColor;
        this.mSelectColor = mSelectColor;
        this.mFrameSelectFullColor = mFrameSelectFullColor;
        this.mNodeSelectColor = mNodeSelectColor;
        this.mErrerColor = mErrerColor;
        this.mErrerFullColor = mErrerFullColor;
        this.mErrerNodeColor = mErrerNodeColor;
        this.mFrameLineWidth = mFrameLineWidth;
        this.mNodeRadius = mNodeRadius;
        this.mNodePadding = mNodePadding;
        //内边距
        setPadding(mNodePadding, mNodePadding, mNodePadding, mNodePadding);
        //外部圆
        mNodeFramePaint = new Paint();
        mNodeFramePaint.setColor(mDefaultColor);
        mNodeFramePaint.setAntiAlias(true);
        mNodeFramePaint.setStrokeWidth(mFrameLineWidth);
        mNodeFramePaint.setStyle(Paint.Style.STROKE);//只画出边框

        //内部填充色
        mNodeFullPaint = new Paint();
        mNodeFullPaint.setColor(mDefailtFullColor);
        mNodeFullPaint.setStyle(Paint.Style.FILL);
        mNodeFullPaint.setAntiAlias(true);

        //内部圆
        mNodeCirclePaint = new Paint();
        mNodeCirclePaint.setColor(mNodeDefaultColor);
        mNodeCirclePaint.setStyle(Paint.Style.FILL);//填充
        mNodeCirclePaint.setAntiAlias(true);
    }

    //取当前透明度的百分比
    public int getFullAlpha(int color, float ratio) {
        return Color.argb((int) (Color.alpha(color) * ratio), Color.red(color), Color.green(color), Color.blue(color));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mContentWidth = getWidth();
        mContentRadius = mContentWidth / 2 - Math.abs(getPaddingLeft()) - mFrameLineWidth / 2;
    }

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


        switch (mCurrentState) {
            case DEFAULT_COLOR: //默认
                mNodeFramePaint.setColor(mDefaultColor);
                mNodeFullPaint.setColor(mDefailtFullColor);
                mNodeCirclePaint.setColor(mNodeDefaultColor);
                //外圆
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius - mFrameLineWidth / 2, mNodeFullPaint);
                //中心圆
                if (mInsideNodeShow)
                    canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mNodeRadius, mNodeCirclePaint);
                break;
            case ERRER_STATE://错误
                mNodeFramePaint.setColor(mErrerColor);
                mNodeFullPaint.setColor(mErrerFullColor);
                mNodeCirclePaint.setColor(mErrerNodeColor);
                //外圆
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius - mFrameLineWidth / 2, mNodeFullPaint);
                //中心圆
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mNodeRadius, mNodeCirclePaint);
                break;
            case SELECT_STATE://选中
                mNodeFramePaint.setColor(mSelectColor);
                mNodeFullPaint.setColor(mFrameSelectFullColor);
                mNodeCirclePaint.setColor(mNodeSelectColor);
                //外圆
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mContentRadius - mFrameLineWidth / 2, mNodeFullPaint);
                //中心圆
                canvas.drawCircle(mContentWidth / 2, mContentWidth / 2, mNodeRadius, mNodeCirclePaint);
                break;
        }

    }
    //设置状态,并且重绘
    public void setState(LockState CurrentState) {
        mCurrentState = CurrentState;
        invalidate();
    }
    //是否选中
    public boolean isHighLighted() {
        if (mCurrentState == LockState.SELECT_STATE || mCurrentState == LockState.ERRER_STATE) {
            return true;
        }
        return false;
    }
    //中心点X
    public int getCenterX() {
        return (getLeft() + getRight()) / 2;
    }
    //中心点Y
    public int getCenterY() {
        return (getTop() + getBottom()) / 2;
    }
    //设置圈圈在手势锁当中的位置
    protected void setNum(int num) {
        mNum = num;
    }

    protected int getNum() {
        return mNum;
    }
}

以上就是一个简单的圆了

三、自定义九宫格 View 属性

那么,自定义 View 当然会有自定义属性,所以有这么多 T0T,不要问我为什么这么多属性,任性= =(其实我还想写更多),自定义属性的方法

 <!-- 线的颜色 -->
    <attr name="lineColor" format="color" />
    <!-- 线的宽度 -->
    <attr name="lineWidth" format="dimension" />
    <!--默认颜色 -->
    <attr name="defaultColor" format="color" />
    <!--默认时的填充色-->
    <attr name="defaultFullColor" format="color" />
    <!--默认内部圆颜色-->
    <attr name="defaultNodeColor" format="color" />
    <!-- ======================================================= -->
    <!-- 边框选中时边框的颜色 -->
    <attr name="selectColor" format="color" />
    <!-- 边框选中时内部的填充色 -->
    <attr name="selectFrameFullColor" format="color" />
    <!--内部圆圈选中时的颜色-->
    <attr name="selectNodeColor" format="color" />
    <!-- ======================================================= -->
    <!-- 错误的颜色 -->
    <attr name="errorColor" format="color" />
    <!--错误时内部的填充色-->
    <attr name="errorFullColor" format="color" />
    <!-- 错误时的颜色 -->
    <attr name="errorNodeColor" format="color" />
    <!-- ======================================================= -->
    <!--边框的的宽度-->
    <attr name="frameLineWidth" format="dimension" />
    <!-- 内部圆圈的宽度 -->
    <attr name="nodeRadius" format="dimension" />
    <!--内边距-->
    <attr name="nodePadding" format="dimension" />
    <!--触摸有效的比例-->
    <attr name="touchRatio" format="float" />
    <!-- 是否显示内部的圆圈 -->
    <attr name="insideNodeShow" format="boolean"/>

四、框框 View

LockView 的代码

package com.juzisang.com.library;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;

import java.util.ArrayList;

/**
 * Created by 橘子桑 on 2016/3/27.
 */
public class LockView extends ViewGroup {
    //画连接线的画笔
    private Paint mLinePaint;
    //可以触摸的区域百分比
    private float mTouchRatio;
    //线的颜色
    protected int mLineColor;
    //先的宽度
    protected float mLineWidth;
    //已经选中了的View
    ArrayList<MarkerView> mNodeViews = new ArrayList<>();
    //存储密码
    protected StringBuilder pawBuilder = new StringBuilder();
    //当前手指触摸的x坐标
    protected float x;
    //当前手指触摸的y坐标
    protected float y;
    //回调
    private onLockCallback mOnLockCallback;

    protected int mDefaultColor;

    protected int mSelectColor;

    protected int mErrerColor;

    //禁用手势锁
    private boolean mLockScreen;

    private boolean isTouch;

    //是否把连接线绘制在子View的上面
    private boolean mLineTop = false;
    //手指离开立即重绘
    private boolean mFingerLeaveRedraw = true;

    public LockView(Context context) {
        this(context, null);
    }

    public LockView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LockView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs, defStyleAttr);
    }

    protected void initView(Context context, AttributeSet attrs, int defStyleAttr) {

        TypedArray r = context.obtainStyledAttributes(attrs, R.styleable.MarkerView);

        boolean insideNodeShow = r.getBoolean(R.styleable.LockView_insideNodeShow, false);
        //默认的颜色
        mDefaultColor = r.getColor(R.styleable.LockView_defaultColor, context.getResources().getColor(android.R.color.holo_blue_dark));
        int mDefailtFullColor = r.getColor(R.styleable.LockView_defaultFullColor, getFullAlpha(mDefaultColor, 0.3F));
        int mNodeDefaultColor = (int) r.getColor(R.styleable.LockView_defaultNodeColor, mDefaultColor);
        //选中的颜色
        mSelectColor = (int) r.getColor(R.styleable.LockView_selectColor, context.getResources().getColor(android.R.color.holo_blue_light));
        int mFrameSelectFullColor = r.getColor(R.styleable.LockView_selectFrameFullColor, getFullAlpha(mSelectColor, 0.3F));
        int mNodeSelectColor = r.getColor(R.styleable.LockView_selectNodeColor, mSelectColor);
        //错误时候的颜色
        mErrerColor = r.getColor(R.styleable.LockView_errorColor, context.getResources().getColor(android.R.color.holo_red_light));
        int mErrerFullColor = r.getColor(R.styleable.LockView_errorFullColor, getFullAlpha(mErrerColor, 0.3F));
        int mErrerNodeColor = r.getColor(R.styleable.LockView_errorNodeColor, mErrerColor);
        //圆框变的宽度
        int mFrameLineWidth = (int) r.getDimension(R.styleable.LockView_frameLineWidth, DensityUtils.dip2px(context, 5));
        //内圆的直径
        int mNodeRadius = (int) r.getDimension(R.styleable.LockView_nodeRadius, DensityUtils.dip2px(context, 5));
        //内边距
        int mNodePadding = (int) r.getDimension(R.styleable.LockView_nodePadding, DensityUtils.dip2px(context, 10));
        //触摸有效区域
        mTouchRatio = r.getFloat(R.styleable.LockView_touchRatio, mTouchRatio);
        mLineColor = r.getColor(R.styleable.LockView_lineColor, mDefaultColor);
        mLineWidth = r.getDimension(R.styleable.LockView_lineWidth, DensityUtils.dip2px(context, 5));
        r.recycle();
        //设置线的颜色
        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mLinePaint.setColor(mLineColor);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setStrokeWidth(mLineWidth);
        mLinePaint.setStrokeCap(Paint.Cap.ROUND);
        mLinePaint.setStrokeJoin(Paint.Join.ROUND);

        for (int i = 0; i < 9; i++) {
            MarkerView view = new MarkerView(context, mDefaultColor, mDefailtFullColor, mNodeDefaultColor, mSelectColor, mFrameSelectFullColor, mNodeSelectColor,
                    mErrerColor, mErrerFullColor, mErrerNodeColor, mFrameLineWidth, mNodeRadius, mNodePadding, insideNodeShow);
            view.setNum(i + 1);
            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            view.setLayoutParams(params);
            addView(view);
        }

        // 清除FLAG,否则 onDraw() 不会调用,原因是 ViewGroup 默认透明背景不需要调用 onDraw()
        setWillNotDraw(false);

    }

    public int getFullAlpha(int color, float ratio) {
        return Color.argb((int) (Color.alpha(color) * ratio), Color.red(color), Color.green(color), Color.blue(color));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int size = Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)); // 测量宽度
        setMeasuredDimension(size, size);
        for (int i = 0; i < getChildCount(); i++) {
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            float areaWidth = (r - l - getPaddingLeft() * 2) / 3;
            for (int n = 0; n < 9; n++) {
                MarkerView node = (MarkerView) getChildAt(n);
                // 获取3*3宫格内坐标
                int row = n / 3;
                int col = n % 3;
                //加上内间距
                int left = (int) (getPaddingLeft() + col * areaWidth);
                int top = (int) (getPaddingTop() + row * areaWidth);
                int right = (int) (left + areaWidth);
                int bottom = (int) (top + areaWidth);
                node.layout(left, top, right, bottom);
            }
        }
    }

    /**
     * 设置连接线是否绘制在子View的上面
     * true 绘制在子View的上面
     * false 绘制在子View的下面
     *
     * @param isLineTop 设置连接线是否绘制在子View的上面
     */
    public void setLineTop(boolean isLineTop) {
        mLineTop = isLineTop;
        invalidate();
    }

    /**
     * 设置连接线是否绘制在子View的上面
     * true 绘制在子View的上面
     * false 绘制在子View的下面
     */
    public boolean getLineTop() {
        return mLineTop;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (getLockScreen()) {
            invalidate();
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //恢复默认
                resetDefault();
                x = event.getX();
                y = event.getY();
                isTouch = true;
                break;
            case MotionEvent.ACTION_MOVE:
                x = event.getX(); // 这里要实时记录手指的坐标
                y = event.getY();
                MarkerView nodeView = getNodeAt(x, y);
                //没有选中
                if (nodeView != null && !nodeView.isHighLighted()) {
                    nodeView.setState(LockState.SELECT_STATE);
                    mNodeViews.add(nodeView);
                    //进度
                    if (mOnLockCallback != null) {
                        pawBuilder.setLength(0);
                        for (MarkerView markerView : mNodeViews) {
                            pawBuilder.append(markerView.getNum());
                        }
                        mOnLockCallback.onProgress(pawBuilder.toString(), nodeView.getNum());
                    }
                }
                if (mNodeViews.size() > 0) {
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                LogUtils.i("手指抬起了");
                isTouch = false;
                pawBuilder.setLength(0);
                if (mNodeViews.size() <= 0) return true;
                pawBuilder.delete(0, pawBuilder.length());
                if (mOnLockCallback != null) {
                    for (MarkerView markerView : mNodeViews) {
                        pawBuilder.append(markerView.getNum());
                    }
                    mOnLockCallback.onFinish(pawBuilder.toString());
                }
                if (mFingerLeaveRedraw) {
                    resetDefault();
                } else {
                    invalidate();
                }
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //线画在子view的下面
        if (!mLineTop) onDrawLock(canvas);
    }

    //画子View的地方
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //放在这里的原因是,线会被子View挡到
        if (mLineTop) onDrawLock(canvas);
    }

    /**
     * 画图的方法
     */
    private void onDrawLock(Canvas canvas) {
        //屏幕锁住了,只画起点到终点的
        if (getLockScreen()) {
            onDrawNodeViewLock(canvas);
            return;
        }
        if (isTouch || mFingerLeaveRedraw) {
            //从第一个和最后一个的连接线
            onDrawNodeViewLock(canvas);
            //最后一个点,到手指之间的线
            if (mNodeViews.size() > 0) {
                MarkerView lastNode = mNodeViews.get(mNodeViews.size() - 1);
                canvas.drawLine(lastNode.getCenterX(), lastNode.getCenterY(), x, y, mLinePaint);
            }
        } else {
            //如果手指离开屏幕,并且设置了手指离开立即重绘
            onDrawNodeViewLock(canvas);
        }


    }

    private void onDrawNodeViewLock(Canvas canvas) {
        //从第一个和最后一个的连接线
        for (int i = 1; i < mNodeViews.size(); i++) {
            MarkerView frontNode = mNodeViews.get(i - 1);
            MarkerView backNode = mNodeViews.get(i);
            canvas.drawLine(frontNode.getCenterX(), frontNode.getCenterY(), backNode.getCenterX(), backNode.getCenterY(), mLinePaint);
        }
    }

    /**
     * 获取给定坐标点的Node,返回null表示当前手指在两个Node之间
     */
    private MarkerView getNodeAt(float x, float y) {
        for (int n = 0; n < getChildCount(); n++) {
            MarkerView node = (MarkerView) getChildAt(n);
            //计算触摸区域以外的距离
            float ratioPadding = (node.getWidth() - (node.getWidth() * mTouchRatio)) / 2;
            if (!(x >= node.getLeft() + ratioPadding && x < node.getRight() - ratioPadding)) {
                continue;
            }
            if (!(y >= node.getTop() + ratioPadding && y < node.getBottom() - ratioPadding)) {
                continue;
            }
            return node;
        }
        return null;
    }

    /**
     * 设置连接线的颜色
     *
     * @param color 颜色值
     */
    public void setLineColor(int color) {
        mLinePaint.setColor(color);
    }

    /**
     * 手指离开立即重绘
     */
    public void setfingerLeaveRedraw(boolean mFingerLeaveRedraw) {
        this.mFingerLeaveRedraw = mFingerLeaveRedraw;
    }

    public boolean getfingerLeaveRedraw() {
        return this.mFingerLeaveRedraw;
    }

    /**
     * 重置状态 为默认状态
     */
    public void resetDefault() {
        setState(LockState.DEFAULT_COLOR);
        mNodeViews.clear();
    }

    /**
     * 重置状态错误状态
     */
    public void resetErrer() {
        setState(LockState.ERRER_STATE);
    }

    /**
     * 重置为选中状态
     */
    public void resetSelect() {
        setState(LockState.SELECT_STATE);
    }

    /**
     * 锁屏,不允许触摸
     */
    public void LockScreen(boolean isScreen) {
        mLockScreen = isScreen;
    }

    public boolean getLockScreen() {
        return mLockScreen;
    }

    public void setState(LockState state) {
        switch (state) {
            case DEFAULT_COLOR:
            case SELECT_STATE:
                setLineColor(mSelectColor);
                break;
            case ERRER_STATE:
                setLineColor(mErrerColor);
                break;
        }
        int size = mNodeViews.size();
        for (int i = 0; i < size; i++) {
            mNodeViews.get(i).setState(state);
        }
        invalidate();
    }

    public void setLockCallback(onLockCallback lockCallback) {
        mOnLockCallback = lockCallback;
    }
    //回调
    public interface onLockCallback {

        void onProgress(String paw, int current);

        void onFinish(String paw);
    }
}

以上注释都写的很清楚了,下面讲一下遇到的一些问题。

五、遇到的问题

画出来的线被上面的圈圈覆盖了

20160404155949303.png
通过百度,知道 ViewGroup 的 onDraw 是画布局中的内容的,画子 View 的的方法在这个方法的后面执行,所以 ViewGroup 的内容会被子 View 覆盖,那么怎么才能把连接线画在子 View 的上面呢,很简单
只要在画子 View 的方法中执行就好了

//画子View的地方
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //放在这里的原因是,线会被子View挡到
        if (mLineTop) onDrawLock(canvas);
    }

下面是 View 的 draw()方法

 @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            //这里就是画子View的方法了
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // we're done...
            return;
        }

怎么设置触摸的区域?

    /**
     * 获取给定坐标点的Node,返回null表示当前手指在两个Node之间
     */
    private MarkerView getNodeAt(float x, float y) {
        for (int n = 0; n < getChildCount(); n++) {
            MarkerView node = (MarkerView) getChildAt(n);
            //计算触摸区域以外的距离
            float ratioPadding = (node.getWidth() - (node.getWidth() * mTouchRatio)) / 2;
            if (!(x >= node.getLeft() + ratioPadding && x < node.getRight() - ratioPadding)) {
                continue;
            }
            if (!(y >= node.getTop() + ratioPadding && y < node.getBottom() - ratioPadding)) {
                continue;
            }
            return node;
        }
        return null;
    }

看上面代码,
根据圆圈的宽度减去可触摸区域的长度除 2,得到可触摸区域距离边框的距的距离。
光看代码看着有点圆,画个图看一下吧
20160404162710907.png

画个图是不是清晰很多,只要用 getLeft+边距,和 getRight-边距,就能得到可触摸区域在 x 轴上的范围了,Y 轴同理,不懂的同学自己用笔画一下吧~

差不多就上面两个问题了
20160404164051803.gif

下载地址

Responses