1. 程式人生 > >自定義九宮格手勢解鎖

自定義九宮格手勢解鎖

專案中用到手勢解鎖,然而沒有在GitHub上找到想要的樣式= =,只好自己來定義了,下面來看程式碼~~

基本上很多應用的手勢解鎖全都是九宮格的,內部內就是九個小圈圈而已。那麼我們就先來自定義這個小圈圈吧~

圈圈的顏色選擇狀態有大致有三種狀態,所以我定義了一個列舉來區分

package com.juzisang.com.library;

/**
 * Created by 橘子桑 on 2016/3/27.
 */
public enum LockState {
    SELECT_STATE,//選中
    ERRER_STATE, //錯誤
    DEFAULT_COLOR //預設
}

圈圈分為邊框,內部填充色,還有內部圓。所以我定義了三個畫筆來區分。

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當然會有自定義屬性,所以有這麼多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"/>

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);
    }
}

以上註釋都寫的很清楚了,下面講一下遇到的一些問題。

1.畫出來的線被上面的圈圈覆蓋了。

通過百度,知道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;
        }

2.怎麼設定觸控的區域?

    /**
     * 獲取給定座標點的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,得到可觸控區域距離邊框的距的距離。
光看程式碼看著有點圓,畫個圖看一下吧
這裡寫圖片描述

畫個圖是不是清晰很多,只要用getLeft+邊距,和getRight-邊距,就能得到可觸控區域在x軸上的範圍了,Y軸同理,不懂的同學自己用筆畫一下吧~

差不多就上面兩個問題了
這裡寫圖片描述

下載地址