看過極客學院自定義圖案解鎖控件教程,自己嘗試寫了一個。
效果圖:
1.首先要繪製九個點,先看兩張圖片
通過上面的兩張圖片可以很容易計算出沒個圓的圓心,也就是正方型的頂點座標。並保存這九個點,要保存這九個點必須先創建一個點類。
package com.example.chl.myapplication;
/**
* Created by chl on 16-4-6.
*/
public class TuAnPoint {
public static int STATE_NORMAL = 1;
public static int STATE_PRESS = 2;
public static int STATE_ERROR = 3;
private float x;
private float y;
private int state = STATE_NORMAL;
public TuAnPoint(float x, float y) {
this.x = x;
this.y = y;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
/**
* 重寫equals方法來判斷兩個點對象是否相等
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
boolean bx = x == ((TuAnPoint) o).getX() ? true : false;
boolean by = y == ((TuAnPoint) o).getY() ? true : false;
boolean bb=false;
if (bx && by){
bb=true;
}
return bb;
}
}
把這些座標轉化成我們定義的點對象並保存在List集合中:
float width = getWidth();
float height = getHeight();
float offsetX = 0;
float offsetY = 0;
float offset = 0;
float length = 0;
offset = Math.abs(width - height) / 2f;
if (width > height) {
//橫屏
offsetX = offset;
offsetY = 0;
length = height / 4f;
} else {
//豎屏
offsetX = 0;
offsetY = offset;
length = width / 4f;
}
listPoint.add(new TuAnPoint(offsetX + length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + length, offsetY + 3 * length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 3 * length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 3 * length));
isInit = true;
2.把保存的點繪製到屏幕上
1)把點在不同狀態下的資源圖片加載爲Bimap
private Bitmap normal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
private Bitmap error = BitmapFactory.decodeResource(getResources(), R.drawable.error);
private Bitmap press = BitmapFactory.decodeResource(getResources(), R.drawable.press);
2)繪製圖片
/**
* 判斷點的狀態並繪製到屏幕上
* @param canvas
*/
private void drawPoint(Canvas canvas) {
for (TuAnPoint tuAnPoint : listPoint) {
if (tuAnPoint.getState() == TuAnPoint.STATE_PRESS) {
canvas.drawBitmap(normal, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
} else if (tuAnPoint.getState() ==TuAnPoint.STATE_PRESS) {
canvas.drawBitmap(press, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
} else {
canvas.drawBitmap(error, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
}
}
}
因爲drawBitmap方法參數是圖片左上角的座標,所以要減去一個圖片的半徑。
3.onTouchEvent處理
@Override
public boolean onTouchEvent(MotionEvent event) {
mouseX = event.getX();
mouseY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (pressList.size() > 0) {
for (TuAnPoint p : pressList) {
p.setState(TuAnPoint.STATE_NORMAL);
}
pressList.clear();
}
getSelectPoint();
break;
case MotionEvent.ACTION_MOVE:
getSelectPoint();
break;
case MotionEvent.ACTION_UP:
int pressCount = 0;
for (TuAnPoint tuAnPoint : listPoint) {
if (!pressList.contains(tuAnPoint)) {
if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
pressCount++;
}
}
}
if (pressCount == 0 && pressList.size() > 0) {
mouseX = pressList.get(pressList.size() - 1).getX();
mouseY = pressList.get(pressList.size() - 1).getY();
}
boolean isFinish = false;
if (finishedListener != null) {
isFinish = finishedListener.onDrawFinished(pressList);
if (!isFinish) {
for (TuAnPoint point : pressList) {
point.setState(TuAnPoint.STATE_ERROR);
}
}
}
break;
}
invalidate();
return true;
}
/**
* 判斷當前手指在屏幕上的點是否在九個點的範圍之類,如果是則選中改點
*/
private void getSelectPoint() {
for (TuAnPoint tuAnPoint : listPoint) {
if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
pressPoint = tuAnPoint;
if (pressList.size() > 0) {
TuAnPoint p = pressList.get(pressList.size() - 1);
for (TuAnPoint isPress : listPoint) {
boolean b = isLine(p, isPress, pressPoint);
//Log.d("tuanjiesuo", "bb:" + b);
if (b) {
if (((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX()) && ((isPress.getY() > p.getY()) && (isPress.getY() < pressPoint.getY())))
|| ((isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX()) && ((isPress.getY() < p.getY()) && (isPress.getY() > pressPoint.getY())))) {
//Log.d("tuanjiesuo","jinglai");
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())
|| (isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX())
|| (isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())
|| (isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
// break;
}
}
}
if (!pressList.contains(pressPoint)) {
pressList.add(pressPoint);
}
pressPoint.setState(TuAnPoint.STATE_PRESS);
}
}
}
/**
* 判斷isPress這個點是否在點p點pressPoint的直線上
*
* @param p
* @param isPress
* @param pressPoint
* @return
*/
private boolean isLine(TuAnPoint p, TuAnPoint isPress, TuAnPoint pressPoint) {
boolean b = (p.getY() - isPress.getY()) * (pressPoint.getX() - p.getX()) == (p.getX() - isPress.getX()) * (pressPoint.getY() - p.getY()) ? true : false;
return b;
}
/**
* 計算傳入的座標到手指在屏幕上點的距離
* @param x
* @param y
* @return
*/
private float getPointsDistance(float x, float y) {
float distance = (float) Math.sqrt((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY));
return distance;
}
/**
* 提供給外部接口,當繪製完成後調用
*/
public interface OnDrawFinishedListener {
boolean onDrawFinished(List<TuAnPoint> pressList);
}
/**
* 提供外部傳入接口對象的方法
* @param listener
*/
public void setFinishedListener(OnDrawFinishedListener listener) {
finishedListener = listener;
}
最後是完整的項目代碼,註釋比較完整:
package com.example.chl.myapplication;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Created by chl on 16-4-6.
*/
public class TuAnView extends View {
private List<TuAnPoint> listPoint = new ArrayList<TuAnPoint>();
private List<TuAnPoint> pressList = new ArrayList<TuAnPoint>();//被選中的點保存的集合
private boolean isInit = false;
private Bitmap normal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
private Bitmap error = BitmapFactory.decodeResource(getResources(), R.drawable.error);
private Bitmap press = BitmapFactory.decodeResource(getResources(), R.drawable.press);
private float radius = normal.getWidth() / 2f;//圖片半徑
private float mouseX = 0;
private float mouseY = 0;
private TuAnPoint pressPoint;
private Paint errorPaint;//當密碼錯誤時,畫線的畫筆
private Paint pressPaint;//當密碼正確時,畫線的畫筆
private OnDrawFinishedListener finishedListener;
public TuAnView(Context context) {
super(context);
// init();
}
public TuAnView(Context context, AttributeSet attrs) {
super(context, attrs);
// init();
}
public TuAnView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isInit) {
init();
}
drawPoint(canvas);//畫點
if (pressList.size() > 0) {//判斷當有點被選中時才畫線
TuAnPoint a = pressList.get(0);//把集合中第一個點作爲線的開始點
for (int i = 1; i < pressList.size(); i++) {
drawLine(canvas, a, pressList.get(i));//畫線
a = pressList.get(i);//把當前點作爲下次畫線的起點
}
drawLine(canvas, a, new TuAnPoint(mouseX, mouseY));//當有起點確定之後,手指在屏幕上滑動,但是沒確定終點時畫線
}
}
/**
* 畫線
* @param canvas
* @param a
* @param b
*/
private void drawLine(Canvas canvas, TuAnPoint a, TuAnPoint b) {
if (a.getState() == TuAnPoint.STATE_PRESS) {
canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), pressPaint);
} else {
canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), errorPaint);
}
}
/**
* 判斷點的狀態並繪製到屏幕上
* @param canvas
*/
private void drawPoint(Canvas canvas) {
for (TuAnPoint tuAnPoint : listPoint) {
if (tuAnPoint.getState() == TuAnPoint.STATE_PRESS) {
canvas.drawBitmap(normal, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
} else if (tuAnPoint.getState() ==TuAnPoint.STATE_PRESS) {
canvas.drawBitmap(press, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
} else {
canvas.drawBitmap(error, tuAnPoint.getX() - radius, tuAnPoint.getY() - radius, null);
}
}
}
/**
* 初始化
*/
private void init() {
errorPaint = new Paint();
pressPaint = new Paint();
errorPaint.setColor(Color.RED);
errorPaint.setStrokeWidth(5);
pressPaint.setColor(Color.YELLOW);
pressPaint.setStrokeWidth(5);
float width = getWidth();
float height = getHeight();
float offsetX = 0;
float offsetY = 0;
float offset = 0;
float length = 0;
offset = Math.abs(width - height) / 2f;
if (width > height) {
//橫屏
offsetX = offset;
offsetY = 0;
length = height / 4f;
} else {
//豎屏
offsetX = 0;
offsetY = offset;
length = width / 4f;
}
listPoint.add(new TuAnPoint(offsetX + length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + length));
listPoint.add(new TuAnPoint(offsetX + length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 2 * length));
listPoint.add(new TuAnPoint(offsetX + length, offsetY + 3 * length));
listPoint.add(new TuAnPoint(offsetX + 2 * length, offsetY + 3 * length));
listPoint.add(new TuAnPoint(offsetX + 3 * length, offsetY + 3 * length));
isInit = true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mouseX = event.getX();
mouseY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (pressList.size() > 0) {
for (TuAnPoint p : pressList) {
p.setState(TuAnPoint.STATE_NORMAL);
}
pressList.clear();
}
getSelectPoint();
break;
case MotionEvent.ACTION_MOVE:
getSelectPoint();
break;
case MotionEvent.ACTION_UP:
int pressCount = 0;
for (TuAnPoint tuAnPoint : listPoint) {
if (!pressList.contains(tuAnPoint)) {
if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
pressCount++;
}
}
}
if (pressCount == 0 && pressList.size() > 0) {
mouseX = pressList.get(pressList.size() - 1).getX();
mouseY = pressList.get(pressList.size() - 1).getY();
}
boolean isFinish = false;
if (finishedListener != null) {
isFinish = finishedListener.onDrawFinished(pressList);
if (!isFinish) {
for (TuAnPoint point : pressList) {
point.setState(TuAnPoint.STATE_ERROR);
}
}
}
break;
}
invalidate();
return true;
}
/**
* 判斷當前手指在屏幕上的點是否在九個點的範圍之類,如果是則選中改點
*/
private void getSelectPoint() {
for (TuAnPoint tuAnPoint : listPoint) {
if (getPointsDistance(tuAnPoint.getX(), tuAnPoint.getY()) <= radius) {
pressPoint = tuAnPoint;
if (pressList.size() > 0) {
TuAnPoint p = pressList.get(pressList.size() - 1);//得到此次繪製線時的起始點
for (TuAnPoint isPress : listPoint) {
/**
* 以爲有種情況是,當你選中第一個點後直接繞過第二個點而選中第三個點時,
* 默認第二個點也要選中,所以要做以下判斷
*/
boolean b = isLine(p, isPress, pressPoint);//判斷是否還有其他點在起點和終點的直線上
//Log.d("tuanjiesuo", "bb:" + b);
if (b) {//如果有點在這條直線上,接下來判斷改點是在這兩點的中間還是兩端,是中間的話就要默認選中
if (((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX()) && ((isPress.getY() > p.getY()) && (isPress.getY() < pressPoint.getY())))
|| ((isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX()) && ((isPress.getY() < p.getY()) && (isPress.getY() > pressPoint.getY())))) {
//Log.d("tuanjiesuo","jinglai");
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())
|| (isPress.getX() == p.getX() && isPress.getX() == pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX())
|| (isPress.getY() == p.getY() && isPress.getY() == pressPoint.getY() && isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
if ((isPress.getX() > p.getX() && isPress.getX() < pressPoint.getX() && isPress.getY() < p.getY() && isPress.getY() > pressPoint.getY())
|| (isPress.getX() < p.getX() && isPress.getX() > pressPoint.getX() && isPress.getY() > p.getY() && isPress.getY() < pressPoint.getY())) {
isPress.setState(TuAnPoint.STATE_PRESS);
if (!pressList.contains(isPress)) {
pressList.add(isPress);
}
}
// break;
}
}
}
if (!pressList.contains(pressPoint)) {
pressList.add(pressPoint);
}
pressPoint.setState(TuAnPoint.STATE_PRESS);
}
}
}
/**
* 判斷isPress這個點是否在點p點pressPoint的直線上
*
* @param p
* @param isPress
* @param pressPoint
* @return
*/
private boolean isLine(TuAnPoint p, TuAnPoint isPress, TuAnPoint pressPoint) {
boolean b = (p.getY() - isPress.getY()) * (pressPoint.getX() - p.getX()) == (p.getX() - isPress.getX()) * (pressPoint.getY() - p.getY()) ? true : false;
return b;
}
/**
* 計算傳入的座標到手指在屏幕上點的距離
* @param x
* @param y
* @return
*/
private float getPointsDistance(float x, float y) {
float distance = (float) Math.sqrt((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY));
return distance;
}
/**
* 提供給外部接口,當繪製完成後調用
*/
public interface OnDrawFinishedListener {
boolean onDrawFinished(List<TuAnPoint> pressList);
}
/**
* 提供外部傳入接口對象的方法
* @param listener
*/
public void setFinishedListener(OnDrawFinishedListener listener) {
finishedListener = listener;
}
}
設置密碼的Activity:
package com.example.chl.myapplication;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import java.util.List;
public class SetTuAnActivity extends AppCompatActivity {
private TuAnView tuAnView;
private List<TuAnPoint> pressList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_tu_an);
tuAnView=(TuAnView)findViewById(R.id.tuanjiesuo);
tuAnView.setFinishedListener(new TuAnView.OnDrawFinishedListener() {
@Override
public boolean onDrawFinished(List<TuAnPoint> pressList) {
SetTuAnActivity.this.pressList=pressList;
return true;
}
});
}
public void click(View view){
switch (view.getId()){
case R.id.button1:
((MyAppliction)getApplication()).pressList=pressList;
Toast.makeText(this,"保存成功",Toast.LENGTH_SHORT).show();
finish();
break;
case R.id.button2:
((MyAppliction)getApplication()).pressList=null;
break;
}
}
}
測試密碼的Activity:
public class TwoActivity extends AppCompatActivity {
private TuAnView tuAnView;
private List<TuAnPoint> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
list=((MyAppliction)getApplication()).pressList;
tuAnView=(TuAnView)findViewById(R.id.two_tuan);
tuAnView.setFinishedListener(new TuAnView.OnDrawFinishedListener() {
@Override
public boolean onDrawFinished(List<TuAnPoint> pressList) {
boolean b=true;
if (pressList.size()!=list.size()){
b=false;
}else {
for (int i = 0; i < pressList.size(); i++) {
if (!pressList.get(i).equals(list.get(i))) {
b = false;
}
}
}
if (b){
Toast.makeText(TwoActivity.this,"密碼正確",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(TwoActivity.this,"密碼錯誤",Toast.LENGTH_SHORT).show();
}
return b;
}
});
}
}