最近跟著視頻學(xué)習(xí)了個(gè)圖案解鎖的功能,效果圖如下:
需求如下:
1.第一次的需要設(shè)置鎖屏密碼
2.繪制密碼的時(shí)候密碼必須是五位或者五位以上的
3.繪制錯(cuò)誤的時(shí)候會(huì)提示,并將點(diǎn)和線的狀態(tài)還原成初始狀態(tài)
4.繪制完成的時(shí)候,如果是五位及以上的,比對(duì)密碼摘完,如果一樣則解鎖成功,不一樣就提示“密碼錯(cuò)誤”
具體操作如下
1.初始化點(diǎn)和線的資源
/**
* 初始化點(diǎn)
*/
private void initPoints() {
//1.獲取布局寬高
width = getWidth();
height = getHeight();
//橫屏和豎屏
if (width > height) {
offsetsX = (width - height) / 2;
width = height;
} else {
offssetsY = (height - width) / 2;
height = width;
}
//圖片資源
pointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.point_normal);
pointPressed = BitmapFactory.decodeResource(getResources(), R.drawable.point_pressed);
pointError = BitmapFactory.decodeResource(getResources(), R.drawable.point_error);
linePressed = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
lineError = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
points[0][0] = new Point((offsetsX + width / 4), (offssetsY + width / 4));
points[0][1] = new Point((offsetsX + width / 2), (offssetsY + width / 4));
points[0][2] = new Point((offsetsX + width - width / 4), (offssetsY + width / 4));
points[1][0] = new Point((offsetsX + width / 4), (offssetsY + width / 2));
points[1][1] = new Point((offsetsX + width / 2), (offssetsY + width / 2));
points[1][2] = new Point((offsetsX + width - width / 4), (offssetsY + width / 2));
points[2][0] = new Point((offsetsX + width / 4), (offssetsY + width - width / 4));
points[2][1] = new Point((offsetsX + width / 2), (offssetsY + width - width / 4));
points[2][2] = new Point((offsetsX + width - width / 4), (offssetsY + width - width / 4));
mPointRadius = pointNormal.getWidth() / 2;
// 設(shè)置密碼
int index = 1;
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
points[i][j].index = index;
index++;
}
}
isInit = true;
}
2.繪制點(diǎn)
/**
* 將點(diǎn)繪制到畫(huà)布
*
* @param canvas
*/
private void points2Canvas(Canvas canvas) {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
Point point = points[i][j];
if (point.state == Point.STATE_NORMAL) {
canvas.drawBitmap(pointNormal, point.x - mPointRadius, point.y - mPointRadius, paint);
} else if (point.state == Point.STATE_PRESSED) {
canvas.drawBitmap(pointPressed, point.x - mPointRadius, point.y - mPointRadius, paint);
} else {
canvas.drawBitmap(pointError, point.x - mPointRadius, point.y - mPointRadius, paint);
}
}
}
}
3.movingX傻谁,movingY記錄觸發(fā)的位置孝治,isFinish記錄繪制圖案是否結(jié)束,isSelected記錄九宮格的點(diǎn)是否被選中审磁,只要我們沒(méi)有觸發(fā)onTouchEvent方法中的 MotionEvent.ACTION_UP谈飒,就沒(méi)有結(jié)束,只有手指抬起的時(shí)候才結(jié)束态蒂。
手指按下的時(shí)候需要判斷當(dāng)前按下的位置杭措,是不是九宮格的點(diǎn),在按下的時(shí)候钾恢,將所有的點(diǎn)和線還原
case MotionEvent.ACTION_DOWN:
//重新繪制
resetPoint();
point = checkSelectPoint();
if (point != null) {
isSelected = true;
}
break;
/**
* 檢查點(diǎn)是否選中
*
* @return
*/
private Point checkSelectPoint() {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
Point point = points[i][j];
if (with(point.x, point.y, mPointRadius, movingX, movingY)) {
return point;
}
}
}
return null;
}
/**
* 是否重合
*
* @param poinX 參考點(diǎn)的X
* @param pointY 參考點(diǎn)的Y
* @param r 圓的半徑
* @param movingX 移動(dòng)點(diǎn)的X
* @param movingY 移動(dòng)點(diǎn)的Y
* @return 是否重合
*/
private static boolean with(float poinX, float pointY, float r, float movingX, float movingY) {
return Math.sqrt((poinX - movingX) * (poinX - movingX) + (pointY - movingY) * (pointY - movingY)) < r;
}
/**
* 重置
*/
public void resetPoint() {
//將點(diǎn)的狀態(tài)還原
for (Point point : pointList) {
point.state = Point.STATE_NORMAL;
}
pointList.clear();
}
在手指移動(dòng)的時(shí)候手素,如果點(diǎn)被選中,得到被選中的九宮格的點(diǎn)
if (isSelected) {
point = checkSelectPoint();
isMoveButNotPoint = true;
}
手指抬起的時(shí)候
isFinish = true;
isSelected = false;
在onTouchEvent的最后瘩蚪,繪制沒(méi)有結(jié)束泉懦,點(diǎn)被選中,判斷這個(gè)點(diǎn)是否在已選中的點(diǎn)的集合內(nèi)疹瘦,不在的話添加進(jìn)去崩哩,在的話,移動(dòng)但不是九宮格的點(diǎn)的標(biāo)志isMoveButNotPoint為true
//選中重復(fù)檢查
if (!isFinish && isSelected && point != null) {
if (checkCrossPoint(point)) { //交叉點(diǎn)
isMoveButNotPoint = true;
} else { //非交叉點(diǎn)(新的點(diǎn))
point.state = Point.STATE_PRESSED;
pointList.add(point);
}
}
繪制結(jié)束,如果只有一個(gè)點(diǎn)則繪制不成立邓嘹,繪制的點(diǎn)的個(gè)數(shù) 在2-5內(nèi)酣栈,繪制錯(cuò)誤,5個(gè)及以上表示成功汹押,但不是真的成功钉嘹,因?yàn)橐椭霸O(shè)置的點(diǎn)進(jìn)行對(duì)比,一樣才表示成功
//繪制結(jié)束
if (isFinish) {
if (pointList.size() == 1) {//繪制不成立
resetPoint();
} else if (pointList.size() < pointSize && pointList.size() >= 2) {//繪制錯(cuò)誤
errPoint();
if (onPatterChangeListener != null) {
onPatterChangeListener.onPatterChange(null);
}
onResultRest();
} else {//繪制成功
if (onPatterChangeListener != null) {
String passwordStr = "";
for (int i = 0; i < pointList.size(); i++) {
passwordStr = passwordStr + pointList.get(i).index;
}
if (!TextUtils.isEmpty(passwordStr)) {
onPatterChangeListener.onPatterChange(passwordStr);
}
}
onResultRest();
}
}
繪制線
if (pointList.size() > 0) {
Point startPoint = pointList.get(0);
//繪制九宮格坐標(biāo)里的點(diǎn)
for (int i = 0; i < pointList.size(); i++) {
Point endPoint = pointList.get(i);
lineToCanvas(canvas, startPoint, endPoint);
startPoint = endPoint;
}
//繪制九宮格坐標(biāo)以外的點(diǎn)
if (isMoveButNotPoint) {
lineToCanvas(canvas, startPoint, new Point(movingX, movingY));
}
}
/**
* 將線繪制到畫(huà)布上
*
* @param canvas 畫(huà)布
* @param startPoint 開(kāi)始的點(diǎn)
* @param endPoint 結(jié)束的點(diǎn)
*/
private void lineToCanvas(Canvas canvas, Point startPoint, Point endPoint) {
float lineLength = (float) twoPointDistance(startPoint, endPoint);
float degree = getDegrees(startPoint, endPoint);
canvas.rotate(degree, startPoint.x, startPoint.y); //旋轉(zhuǎn)
if (startPoint.state == Point.STATE_PRESSED) { //按下的狀態(tài)
//設(shè)置線的縮放比例,在這里線是往一個(gè)方向縮放的,即x軸,我們只需要設(shè)置x軸的縮放比例即可,y軸默認(rèn)為1
matrix.setScale(lineLength / linePressed.getWidth(), 1);
matrix.postTranslate(startPoint.x - linePressed.getWidth() / 2, startPoint.y - linePressed.getHeight() / 2);
canvas.drawBitmap(linePressed, matrix, paint);
} else { //錯(cuò)誤的狀態(tài)
matrix.setScale(lineLength / lineError.getWidth(), 1);
matrix.postTranslate(startPoint.x - lineError.getWidth() / 2, startPoint.y - lineError.getHeight() / 2);
canvas.drawBitmap(lineError, matrix, paint);
}
canvas.rotate(-degree, startPoint.x, startPoint.y); //把旋轉(zhuǎn)的角度轉(zhuǎn)回來(lái)
}
點(diǎn)和線就繪制成功了鲸阻,下面寫(xiě)個(gè)監(jiān)聽(tīng)
/**
* 圖案監(jiān)聽(tīng)器
*/
public interface OnPatterChangeListener {
void onPatterChange(String passwordStr);
/**
* 圖案重新繪制
*
* @param isStart
*/
void onPatterStart(boolean isStart);
}
private OnPatterChangeListener onPatterChangeListener;
public void setOnPatterChangeListener(OnPatterChangeListener onPatterChangeListener) {
this.onPatterChangeListener = onPatterChangeListener;
}
在繪制出錯(cuò)或者繪制成功的時(shí)候調(diào)用onPatterChange方法,在開(kāi)始繪制的時(shí)候調(diào)用onPatterStart方法缨睡。
在繪制成功和繪制出錯(cuò)的時(shí)候?qū)Ⅻc(diǎn)和線的還原成初始狀態(tài)
private void onResultRest() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
resetPoint();
postInvalidate();
}
}, 1000);
}
至此鸟悴,給各位大佬奉上項(xiàng)目地址:
https://github.com/diudiuhf/SatelliteMenu