自定义九宫格手势解锁

发布 : 2016-04-26 分类 : Android 浏览 :

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

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

一、状态

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

1
2
3
4
5
6
7
8
9
10
package com.juzisang.com.library;

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

二、圆圈 View

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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,不要问我为什么这么多属性,任性= =(其实我还想写更多),自定义属性的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!-- 线的颜色 -->
<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 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
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);
}
}

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

五、遇到的问题

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


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

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

下面是 View 的 draw()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@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;
}

怎么设置触摸的区域?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 获取给定坐标点的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 轴同理,不懂的同学自己用笔画一下吧~

差不多就上面两个问题了

下载地址

本文作者 : Juzisang
原文链接 : https://www.juzisang.com/passages/自定义九宫格手势解锁/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
留下足迹