天天看點

自定義九宮格手勢解鎖

項目中用到手勢解鎖,然而沒有在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, );
    }

    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 /  - Math.abs(getPaddingLeft()) - mFrameLineWidth / ;
    }

    @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 / , mContentWidth / , mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / , mContentWidth / , mContentRadius - mFrameLineWidth / , mNodeFullPaint);
                //中心圓
                if (mInsideNodeShow)
                    canvas.drawCircle(mContentWidth / , mContentWidth / , mNodeRadius, mNodeCirclePaint);
                break;
            case ERRER_STATE://錯誤
                mNodeFramePaint.setColor(mErrerColor);
                mNodeFullPaint.setColor(mErrerFullColor);
                mNodeCirclePaint.setColor(mErrerNodeColor);
                //外圓
                canvas.drawCircle(mContentWidth / , mContentWidth / , mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / , mContentWidth / , mContentRadius - mFrameLineWidth / , mNodeFullPaint);
                //中心圓
                canvas.drawCircle(mContentWidth / , mContentWidth / , mNodeRadius, mNodeCirclePaint);
                break;
            case SELECT_STATE://選中
                mNodeFramePaint.setColor(mSelectColor);
                mNodeFullPaint.setColor(mFrameSelectFullColor);
                mNodeCirclePaint.setColor(mNodeSelectColor);
                //外圓
                canvas.drawCircle(mContentWidth / , mContentWidth / , mContentRadius, mNodeFramePaint);
                //填充色
                canvas.drawCircle(mContentWidth / , mContentWidth / , mContentRadius - mFrameLineWidth / , mNodeFullPaint);
                //中心圓
                canvas.drawCircle(mContentWidth / , mContentWidth / , 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()) / ;
    }
    //中心點Y
    public int getCenterY() {
        return (getTop() + getBottom()) / ;
    }
    //設定圈圈在手勢鎖當中的位置
    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, );
    }

    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, F));
        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, F));
        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, F));
        int mErrerNodeColor = r.getColor(R.styleable.LockView_errorNodeColor, mErrerColor);
        //圓框變的寬度
        int mFrameLineWidth = (int) r.getDimension(R.styleable.LockView_frameLineWidth, DensityUtils.dip2px(context, ));
        //内圓的直徑
        int mNodeRadius = (int) r.getDimension(R.styleable.LockView_nodeRadius, DensityUtils.dip2px(context, ));
        //内邊距
        int mNodePadding = (int) r.getDimension(R.styleable.LockView_nodePadding, DensityUtils.dip2px(context, ));
        //觸摸有效區域
        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, ));
        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 = ; i < ; i++) {
            MarkerView view = new MarkerView(context, mDefaultColor, mDefailtFullColor, mNodeDefaultColor, mSelectColor, mFrameSelectFullColor, mNodeSelectColor,
                    mErrerColor, mErrerFullColor, mErrerNodeColor, mFrameLineWidth, mNodeRadius, mNodePadding, insideNodeShow);
            view.setNum(i + );
            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 = ; 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() * ) / ;
            for (int n = ; n < ; n++) {
                MarkerView node = (MarkerView) getChildAt(n);
                // 擷取3*3宮格内坐标
                int row = n / ;
                int col = n % ;
                //加上内間距
                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();
                        for (MarkerView markerView : mNodeViews) {
                            pawBuilder.append(markerView.getNum());
                        }
                        mOnLockCallback.onProgress(pawBuilder.toString(), nodeView.getNum());
                    }
                }
                if (mNodeViews.size() > ) {
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                LogUtils.i("手指擡起了");
                isTouch = false;
                pawBuilder.setLength();
                if (mNodeViews.size() <= ) return true;
                pawBuilder.delete(, 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() > ) {
                MarkerView lastNode = mNodeViews.get(mNodeViews.size() - );
                canvas.drawLine(lastNode.getCenterX(), lastNode.getCenterY(), x, y, mLinePaint);
            }
        } else {
            //如果手指離開螢幕,并且設定了手指離開立即重繪
            onDrawNodeViewLock(canvas);
        }


    }

    private void onDrawNodeViewLock(Canvas canvas) {
        //從第一個和最後一個的連接配接線
        for (int i = ; i < mNodeViews.size(); i++) {
            MarkerView frontNode = mNodeViews.get(i - );
            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 = ; n < getChildCount(); n++) {
            MarkerView node = (MarkerView) getChildAt(n);
            //計算觸摸區域以外的距離
            float ratioPadding = (node.getWidth() - (node.getWidth() * mTouchRatio)) / ;
            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 = ; 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) != ;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != ;
        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 = ; n < getChildCount(); n++) {
            MarkerView node = (MarkerView) getChildAt(n);
            //計算觸摸區域以外的距離
            float ratioPadding = (node.getWidth() - (node.getWidth() * mTouchRatio)) / ;
            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軸同理,不懂的同學自己用筆畫一下吧~

差不多就上面兩個問題了

自定義九宮格手勢解鎖

下載下傳位址

繼續閱讀