為了加強APP的安全性,很多應用都會給自己添加手勢密碼的功能。
今天使用View實作了一個簡單手勢密碼的控件。
不多說有圖有真相:
View Java:
public class GesturesPasswordView extends View {
/**
* 監聽 繪制密碼完成
*/
public interface OnGesturesFinish {
public void onFinish(String keys);
}
public GesturesPasswordView(Context context) {
this(context, null);
}
public GesturesPasswordView(Context context, AttributeSet attrs) {
this(context, attrs, );
}
public GesturesPasswordView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void setOnGesturesFinish(OnGesturesFinish l) {
mOnGesturesFinish = l;
}
OnGesturesFinish mOnGesturesFinish;
// 是否繪制觸摸線
boolean isTouchLine = false;
// 是否啟用繪制功能
boolean isEnabled = true;
//滑動狀态
boolean isTouching = false;
//繪圖區域
Rect R_Image;
//點資料
PointData[] mPointData;
Paint Paint_BG;
//儲存繪制出來的密碼
List<Object> Keys = new LinkedList<Object>();
//最新選中的點
PointData CurrentPoint;
//目前觸摸位置
float Touch_X, Touch_Y;
void init() {
}
//計算點坐标
void calculate() {
Paint_BG = new Paint();
Paint_BG.setAntiAlias(true);
Paint_BG.setStrokeWidth();
int min = Math.min(getWidth(), getHeight());
R_Image = new Rect(, , min, min);
mPointData = new PointData[];
float Period = min / f;
float offWith = getWidth() > min ? (getWidth() - min) / : ;
for (int y = ; y < ; y++) {
for (int x = ; x < ; x++) {
int index = y * + x;
int B_Y = y == ? : y == ? : ;
int B_X = x == ? : x == ? : ;
mPointData[index] = new PointData(Period * B_X + offWith, Period * B_Y, Period, mPointObservable, String.valueOf(index));
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (Paint_BG == null) calculate();
//畫圓
for (int i = ; i < ; i++) {
mPointData[i].onDraw(canvas);
}
//畫延伸線
if (isEnabled && CurrentPoint != null) {
Paint_BG.setColor(PointData.Color_PointSelected);
Paint_BG.setStyle(Paint.Style.FILL);
canvas.drawLine(CurrentPoint.X, CurrentPoint.Y, Touch_X, Touch_Y, Paint_BG);
}
}
//重置View
public void reset() {
for (int i = ; i < mPointData.length; i++)
mPointData[i].isSelected = false;
CurrentPoint = null;
StringBuffer str = new StringBuffer();
for (Object o : Keys)
str.append(o.toString());
Keys.clear();
invalidate();
isEnabled = true;
if (mOnGesturesFinish != null) mOnGesturesFinish.onFinish(str.toString());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (isEnabled) {
Touch_X = event.getX();
Touch_Y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
if (isTouching) {
isEnabled = false;
isTouchLine = false;
invalidate();
if (Keys.size() != )
mHandler.sendMessageDelayed(mHandler.obtainMessage(), );
else isEnabled = true;
}
break;
case MotionEvent.ACTION_DOWN:
isTouching = false;
for (int i = ; i < mPointData.length; i++) {
if (mPointData[i].isOverlap(Touch_X, Touch_Y)) {
isTouchLine = true;
isTouching = true;
mPointData[i].onTouchEvent(event);
break;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (isTouching) {
for (int i = ; i < mPointData.length; i++) {
mPointData[i].onTouchEvent(event);
}
}
break;
}
invalidate();
}
return true;
}
//實作延時重置功能
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
reset();
}
};
/**
* 觀察選中點
*/
PointObservable mPointObservable = new PointObservable() {
@Override
public void onSelected(PointData tag) {
tag.setLastPoint(CurrentPoint == null ? tag : CurrentPoint);
CurrentPoint = tag;
Keys.add(CurrentPoint.Tag);
}
};
/**
* 觸摸點
*/
public static class PointData {
public float X, Y, Radius;
public Object Tag;
public boolean isSelected = false;
RectF mRect;
PointObservable mPointObservable;
Paint paint;
//上一個點
PointData LastPoint;
public PointData(float x, float y, float radius, PointObservable l) {
this(x, y, radius, l, null);
}
public void setLastPoint(PointData last) {
LastPoint = last;
}
public PointData(float x, float y, float radius, PointObservable l, Object tag) {
X = x;
Y = y;
Radius = radius;
mPointObservable = l;
Tag = tag;
paint = new Paint();
paint.setColor();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth();
mRect = new RectF(X - radius, Y - radius, X + radius, Y + radius);
}
/**
* 預設點的顔色
*/
public static final int Color_PointDefualt = ;
/**
* 選中點的顔色
*/
public static final int Color_PointSelected = ;
public void onDraw(Canvas canvas) {
canvas.save();
if (!isSelected) {//未選中狀态
paint.setColor(Color_PointDefualt);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(X, Y, Radius, paint);
} else {//0xffEF0601 //紅色
paint.setColor(Color_PointSelected);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(X, Y, Radius, paint);
//選中狀态
paint.setStyle(Paint.Style.FILL);
paint.setColor();
canvas.drawCircle(X, Y, Radius / , paint);
paint.setColor(Color_PointSelected);
canvas.drawCircle(X, Y, Radius / , paint);
//繪制點的連線
if (LastPoint != null && !LastPoint.equals(this)) {
canvas.drawLine(LastPoint.X, LastPoint.Y, X, Y, paint);
}
}
canvas.restore();
}
public void onTouchEvent(MotionEvent e) {
if (!isSelected) { //未選中的時候
if (isOverlap(e.getX(), e.getY())) {
isSelected = true;
if (mPointObservable != null) mPointObservable.onSelected(this);
// }
}
}
}
/**
* 獲得點到點的距離
*/
static float getPointToPoint(float x1, float y1, float x2, float y2) {
float x = x2 - x1;
float y = y2 - y1;
double num = Math.sqrt(x * x + y * y);
return Double.isNaN(num) ? - : (float) num;
}
/**
* 是否重疊
*/
public boolean isOverlap(float x, float y) {
float distance = getPointToPoint(x, y, X, Y);
return distance != - && distance < Radius;
}
}
private interface PointObservable {
/**
* 當
*/
public void onSelected(PointData point);
}
}
XML 引用:
<packagename.View.GesturesPasswordView
android:id="@+id/view_GesturesPassword"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
最後監聽手勢密碼:
OnGesturesFinish mOnGesturesFinish = new GesturesPasswordView.OnGesturesFinish() {
String KeyOld = "";
@Override
public void onFinish(String keys) {
if (!isSetPassword) {
if (keys.length() < minkey) {
KeyOld = "";
TextInfo.setText("設定密碼\n至少連接配接3個點");
return;
}
if (TextUtils.isEmpty(KeyOld)) { //首次設定密碼
TextInfo.setText("請再輸入密碼");
KeyOld = keys;
} else {
if (!keys.equals(KeyOld)) {
KeyOld = "";
TextInfo.setText("兩次密碼不相同,請重新輸入");
} else {//成功設定密碼
setPassword(getActivity(), keys); //儲存密碼
startAPP();
}
}
} else {
if (!KeyOld.equals(keys)) {
TextInfo.setText("請重試");
} else { //密碼驗證成功
startAPP();
}
}
}
};
設定監聽:
mGesturesPasswordView = (GesturesPasswordView) findViewById(R.id.view_GesturesPassword);
mGesturesPasswordView.setOnGesturesFinish(mOnGesturesFinish);