前言
九宮格手勢解鎖已經(jīng)是非常常見的手機解鎖方式,支付寶等一些軟件也都曾經(jīng)使用過,感覺還是很高大上的。在github已經(jīng)有很好的開源控件,大家可以去自己搜索,我自己寫了一個自定義的九宮格控件,作為練習(xí)作業(yè)。
效果圖##
我不會做動圖,就湊合看吧......
這里寫圖片描述
先貼自定義屬性代碼<declare-styleable name="DrawPointLineUnlockView"> <attr name="columns" format="integer"/> <attr name="rows" format="integer"/> <attr name="lineWidth" format="dimension"/> <attr name="pointRadius" format="dimension"/> <attr name="pointColor" format="color" /> </declare-styleable>
我把自定義屬性的連線寬度,作為了九宮格的實心圓的半徑和外面的空心圓的邊框?qū)挾取?/p>
貼代碼
package lzp.com.interestlibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import lzp.com.interestlibrary.R;
import lzp.com.interestlibrary.view.bean.DrawPoint;
/**
* Created by li.zhipeng on 16/11/17.
* <p>
* 九宮格解鎖View
* <p>
* 可以定制顏色和九宮格的數(shù)量
*/
public class DrawPointLineUnlockView extends View {
/**
* 手勢按下時的x坐標(biāo)
*/
private float xDown = -1;
/**
* 手勢按下時的y坐標(biāo)
*/
private float yDown = -1;
/**
* 手勢y移動時的x坐標(biāo)
*/
private float xMove = -1;
/**
* 手勢移動時的y坐標(biāo)
*/
private float yMove = -1;
/**
* 畫筆的寬度
*/
private int lineWidth = 10;
/**
* 每一個九宮格的大小
*/
private int pointRadius = 30;
/**
* 九宮格的橫向個數(shù)
*/
private int columeCount = 3;
/**
* 九宮格的行數(shù)
*/
private int rowCount = 3;
/**
* 九宮格的顏色
*/
private int pointColor = Color.parseColor("#000000");
/**
* 畫筆
*/
private Paint paint;
/**
* 保存所有的九宮格的點的數(shù)組
*/
private ArrayList<DrawPoint> pointsList;
/**
* 已經(jīng)繪制的九宮格的點
*/
private ArrayList<DrawPoint> drawPoints;
/**
* 進行判斷的點
* <p>
* 只創(chuàng)建一個,不返回的創(chuàng)建對象,節(jié)省內(nèi)存
*/
private DrawPoint tempPoint;
/**
* 是否要進行繪制
*/
private boolean needDraw;
/**
* 密碼
* */
private String passward;
/**
* 解鎖解鎖回調(diào)
* */
private OnDrawPointUnlockResultListener listener;
public void setPassward(String passward){
this.passward = passward;
}
public DrawPointLineUnlockView(Context context) {
this(context, null);
}
public DrawPointLineUnlockView(Context context, AttributeSet attrs) {
super(context, attrs);
// 獲取自定義屬性的值
TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.DrawPointLineUnlockView);
// 畫筆的寬度
lineWidth = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_lineWidth, 10);
// 每一個九宮格的大小
pointRadius = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_pointRadius, 30);
// 九宮格的橫向個數(shù)
columeCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_columns, 3);
// 九宮格的列數(shù)
rowCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_rows, 3);
// 九宮格的行數(shù)
pointColor = typedArray.getColor(R.styleable.DrawPointLineUnlockView_pointColor, Color.parseColor("#000000"));
typedArray.recycle();
// 防止View不進行繪制,執(zhí)行onDraw()方法
setWillNotDraw(false);
pointsList = new ArrayList<>();
drawPoints = new ArrayList<>();
tempPoint = new DrawPoint(-1, pointRadius, lineWidth);
// 初始化畫筆
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(pointColor);
paint.setStrokeWidth(lineWidth);
paint.setPathEffect(new CornerPathEffect(5));
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
xDown = event.getX();
yDown = event.getY();
// 判斷是否這個點是九宮格的某一個點,否則不進行繪制
tempPoint.x = xDown;
tempPoint.y = yDown;
int index = pointsList.indexOf(tempPoint);
if (index != -1) {
needDraw = true;
DrawPoint point = pointsList.get(index);
drawPoints.add(point);
xDown = point.x;
yDown = point.y;
} else {
needDraw = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (needDraw) {
xMove = event.getX();
yMove = event.getY();
// 判斷是否這個點是九宮格的某一個點,如果是的話,連接這個點
tempPoint.x = xMove;
tempPoint.y = yMove;
int moveIndex = pointsList.indexOf(tempPoint);
// 判斷這個點是否是九宮格的點 且 這個點并沒有被連接過
if (moveIndex != -1 && !drawPoints.contains(tempPoint)) {
DrawPoint point = pointsList.get(moveIndex);
drawPoints.add(point);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (needDraw) {
xDown = -1;
yDown = -1;
xMove = -1;
yMove = -1;
// 監(jiān)聽回調(diào)
if (listener != null){
String unlockResult = returnPwd();
listener.onUnlock(unlockResult, unlockResult.equals(passward));
}
// 清除所有的連接點
drawPoints.clear();
// 重繪
invalidate();
}
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
// 畫出所有的九宮格的點
for (DrawPoint point : pointsList) {
point.draw(canvas, paint);
}
// 如果正在解鎖且已經(jīng)劃過了某些九宮格,畫出他們之間的連線
int size = drawPoints.size() - 1;
if (needDraw && size >= 0) {
// 只有一個起點,畫出第一個點和手指位置的連線
if (size == 0) {
DrawPoint point = drawPoints.get(0);
canvas.drawLine(point.x, point.y, xMove, yMove, paint);
}
// 多個點,畫出點和點之間的連線
else {
for (int i = 0; i < size; i++) {
DrawPoint point = drawPoints.get(i);
DrawPoint nextPoint = drawPoints.get(i + 1);
canvas.drawLine(point.x, point.y, nextPoint.x, nextPoint.y, paint);
}
// 畫出最后一個點和當(dāng)前手指位置之間的連線
DrawPoint point = drawPoints.get(size);
canvas.drawLine(point.x, point.y, xMove, yMove, paint);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 測量當(dāng)前的寬高 來繪制指定個數(shù)的九宮格
float width = getWidth() - pointRadius * 2;
float height = getHeight() - pointRadius * 2;
// 獲取padding值
float paddingLeft = getPaddingLeft();
float paddingRight = getPaddingRight();
float paddingTop = getPaddingTop();
float paddingBottom = getPaddingBottom();
// 求出每個點的起始位置
float xDistance = (width - paddingTop - paddingBottom) / (columeCount - 1);
float yDistance = (height - paddingLeft - paddingRight) / (rowCount - 1);
// 計算每一個九宮格的圓心
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columeCount; j++) {
DrawPoint pointF = new DrawPoint(j + rowCount * i, pointRadius, lineWidth);
pointF.y = yDistance * i + pointRadius + paddingTop;
pointF.x = xDistance * j + pointRadius + paddingLeft;
pointsList.add(pointF);
}
}
}
/**
* 返回密碼
* */
private String returnPwd(){
StringBuilder builder = new StringBuilder();
for (DrawPoint point : drawPoints){
builder.append(point.getValue());
}
return builder.toString();
}
/**
* 解鎖回調(diào)
* */
public interface OnDrawPointUnlockResultListener{
/**
* @param passward 解鎖的結(jié)果密碼
* @param success 與設(shè)置的密碼進行匹配的結(jié)果
* */
void onUnlock(String passward, boolean success);
}
public void setOnDrawPointUnlockResultListener(OnDrawPointUnlockResultListener listener){
this.listener = listener;
}
}
注釋寫的都是很清楚,不用做過多的解釋,關(guān)于計算每一個九宮格的中心點,就畫圖說明一下
這里寫圖片描述
圖太難畫了 ,我們首先減去了九宮格的直徑,這樣均分了之后,我們就可以得到每一個九宮格的最左邊的點,然后用最左邊的點加上了半徑就得到了x方向的圓心。在y方向上同理。
貼出DrawPoint的代碼:
package lzp.com.interestlibrary.view.bean;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
/**
* Created by li.zhipeng on 16/11/18.
* <p>
* 九宮格的點
*/
public class DrawPoint extends PointF {
/**
* 外部的空心圓的半徑
*/
private int pointRadius;
/**
* 內(nèi)部的小實心圓的半徑
*/
private int centerPointRadius;
/**
* 這個點的值,用于解鎖密碼的匹配
*/
private int value;
public int getValue(){
return value;
}
public DrawPoint(int value, int pointRadius, int centerPointRadius) {
this.value = value;
this.pointRadius = pointRadius;
this.centerPointRadius = centerPointRadius;
}
public void draw(Canvas canvas, Paint paint) {
// 畫中心的小實心圓
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x, y, centerPointRadius, paint);
// 畫外面的空心圓
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(x, y, pointRadius, paint);
}
/**
* 重寫此方法,判斷是否包含這個點
*/
@Override
public boolean equals(Object o) {
if (o instanceof DrawPoint) {
// 匹配的誤差是 大圓的半徑
DrawPoint point = (DrawPoint) o;
if (Math.abs(this.x - point.x) < pointRadius
&& Math.abs(this.y - point.y) < pointRadius) {
return true;
}
}
return false;
}
}
MainActivity:
DrawPointLineUnlockView drawPointLineUnlockView = (DrawPointLineUnlockView) findViewById(R.id.lock);
drawPointLineUnlockView.setOnDrawPointUnlockResultListener(new DrawPointLineUnlockView.OnDrawPointUnlockResultListener() {
@Override
public void onUnlock(String passward, boolean success) {
Toast.makeText(MainActivity.this, passward, Toast.LENGTH_SHORT).show();
}
});
結(jié)尾##
如果有什么問題和好的建議歡迎大家留言批評指正,尤其是妹紙。