看过极客学院自定义图案解锁控件教程,自己尝试写了一个。
效果图:
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;
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);
/** * 判断点的状态并绘制到屏幕上 * @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); } } }
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; } }
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; } } }
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; } }); } }