虽然现在的应用很少在使用九宫格解锁,不过系统的应用锁还是可以见到的。实现的效果如下:
分析
自定义属性
实现代码
/**
* 创建者: mao
* 功能描述:九宫格解锁View
*/
public class LockPatternView extends View{
private int mNormalColor;
private int mTouchedColor;
private int mErrorColor;
private float mLineStrokeWidth;
private float mCircleStrokeWidth;
private Paint mOuterPaint;
private Paint mInnerPaint;
private Paint mLinePaint;
private float mMotionX;
private float mMotionY;
//按下的时候如果在某个圆内
private boolean isTouched;
//所有被选中的圆的集合
private List mTouchedCells=new ArrayList<>();
private OnPatternListener mOnPatternListener;
public LockPatternView(Context context) {
this(context,null);
}
public LockPatternView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(attrs);
initPaint();//初始化画笔
}
public void setOnPatternListener(OnPatternListener onPatternListener) {
mOnPatternListener = onPatternListener;
}
private void initAttrs( @Nullable AttributeSet attrs) {
TypedArray typedArray=getContext().obtainStyledAttributes(attrs, R.styleable.LockPatternView);
mNormalColor=typedArray.getColor(R.styleable.LockPatternView_normalColor,Color.parseColor("#FFC0CB"));
mTouchedColor=typedArray.getColor(R.styleable.LockPatternView_touchedColor,Color.parseColor("#00BFFF"));
mErrorColor=typedArray.getColor(R.styleable.LockPatternView_errorColor,Color.parseColor("#DC143C"));
mLineStrokeWidth=typedArray.getDimension(R.styleable.LockPatternView_lineStrokeWidth,DensityUtil.dp2px(getContext(),5));
mCircleStrokeWidth=typedArray.getDimension(R.styleable.LockPatternView_circleStrokeWidth,DensityUtil.dp2px(getContext(),5));
typedArray.recycle();
}
private void initPaint() {
//外圆画笔
mOuterPaint=new Paint();
mOuterPaint.setAntiAlias(true);
mOuterPaint.setColor(mNormalColor);
mOuterPaint.setStyle(Paint.Style.STROKE);
mOuterPaint.setStrokeWidth(mCircleStrokeWidth);
//内圆画笔
mInnerPaint=new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setColor(mNormalColor);
mInnerPaint.setStyle(Paint.Style.STROKE);
mOuterPaint.setStrokeWidth(mCircleStrokeWidth);
//线条的画笔
mLinePaint=new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(mNormalColor);
mLinePaint.setStrokeWidth(mLineStrokeWidth);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int minimumWidth = getSuggestedMinimumWidth();
final int minimumHeight = getSuggestedMinimumHeight();
int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
setMeasuredDimension(viewWidth, viewHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int viewWidth = getMeasuredWidth();
int viewHeight = getMeasuredHeight();
//x,y方向到九宫格的偏移
int offsetX;
int offsetY;
//单元格的宽度
int cellWidth;
if (viewHeight>viewWidth){
offsetX =0;
offsetY =(viewHeight - viewWidth)/2;
cellWidth = viewWidth /3;
}else {
offsetX =(viewWidth - viewHeight)/2;
offsetY =0;
cellWidth = viewHeight /3;
}
Cell.initCircleRadius(cellWidth /6, cellWidth /14);
Cell.createCells(offsetX, offsetY, cellWidth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Cell[][] cells=Cell.getsCells();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Cell cell=cells[i][j];
//根据圆的状态设置画笔颜色
if (cell.isNormal()){
mOuterPaint.setColor(mNormalColor);
mInnerPaint.setColor(mNormalColor);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.outerCircleRadius,mOuterPaint);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.innerCircleRadius,mInnerPaint);
}
if (cell.isTouched()){
mOuterPaint.setColor(mTouchedColor);
mInnerPaint.setColor(mTouchedColor);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.outerCircleRadius,mOuterPaint);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.innerCircleRadius,mInnerPaint);
}
if (cell.isError()){
mOuterPaint.setColor(mErrorColor);
mInnerPaint.setColor(mErrorColor);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.outerCircleRadius,mOuterPaint);
canvas.drawCircle(cell.centerX,cell.centerY,Cell.innerCircleRadius,mInnerPaint);
}
}
}
//绘制线
drawLine(canvas);
}
private Cell tempCell=new Cell(-1);
//绘制线条
private void drawLine(Canvas canvas) {
if (mTouchedCells.size()>0){
Cell lastCell=mTouchedCells.get(0);
//遍历绘制出所有被触摸过的圆之间的连线
for (int i=1;i0){
for (Cell touchedCell : mTouchedCells) {
touchedCell.setStatus(Cell.CellState.STATUS_ERROR);
}
mLinePaint.setColor(mErrorColor);
notifyPatternError();
}
}
private void notifyPatternStarted() {
if (mOnPatternListener!=null){
mOnPatternListener.onPatternStart();
}
}
private void notifyPatternEnd() {
if (mOnPatternListener != null) {
StringBuilder builder=new StringBuilder();
for (Cell cell : mTouchedCells) {
builder.append(cell.index);
}
mOnPatternListener.onPatternEnd(builder.toString());
}
}
private void notifyPatternError() {
if (mOnPatternListener!=null){
mOnPatternListener.onPatternError(mTouchedCells);
}
}
//重置状态
public void resetPattern() {
Cell[][] cells =Cell.getsCells();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Cell cell=cells[i][j];
cell.setStatus(Cell.CellState.STATUS_NORMAL);
}
}
mLinePaint.setColor(mNormalColor);
mTouchedCells.clear();
}
private int resolveMeasured(int measureSpec, int desired)
{
int result;
int specSize = MeasureSpec.getSize(measureSpec);
switch (MeasureSpec.getMode(measureSpec)) {
case MeasureSpec.UNSPECIFIED:
result = desired;
break;
case MeasureSpec.AT_MOST:
result = Math.max(specSize, desired);
break;
case MeasureSpec.EXACTLY:
default:
result = specSize;
}
return result;
}
//九宫格——单元
public static final class Cell {
float centerX;
float centerY;
final int index;
private static Cell[][] sCells ;
//外圆的半径
static int outerCircleRadius;
//内圆的半径
static int innerCircleRadius;
int status;
//创建九宫格Cell[3][3]
static void createCells(int offsetX, int offsetY, int cellWidth) {
if (sCells==null){
Cell[][] cells = new Cell[3][3];
int index=0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cells[i][j] = new Cell(offsetX+cellWidth*(j*2+1)/2, offsetY+(i*2+1)*cellWidth/2, index++);
}
}
sCells=cells;
}
}
void setStatus(int status){
this.status=status;
}
public boolean isNormal(){
return status == CellState.STATUS_NORMAL;
}
boolean isTouched(){
return status == CellState.STATUS_TOUCHED;
}
boolean isError(){
return status == CellState.STATUS_ERROR;
}
static Cell[][] getsCells(){
return sCells;
}
private Cell(int index) {
this.index=index;
}
private Cell(float centerX, float centerY, int index) {
this.centerX = centerX;
this.centerY = centerY;
this.index=index;
}
//初始化圆的半径,外圆半径和内圆半径
static void initCircleRadius(int outerCircleRadius, int innerCircleRadius) {
Cell.outerCircleRadius=outerCircleRadius;
Cell.innerCircleRadius=innerCircleRadius;
}
//如果触摸位置 x,y在某个单元格的圆内就返回该单元
private static Cell checkRange(float x, float y) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Cell cell=sCells[i][j];
if (!(((cell.centerX-x)*(cell.centerX-x)+(cell.centerY-y)*(cell.centerY-y))>outerCircleRadius*outerCircleRadius)){
return cell;
}
}
}
return null;
}
//判断当前的触摸位置是否在某个单元中
static boolean isInCircle(float x, float y) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
Cell cell=sCells[i][j];
if (!(((cell.centerX-x)*(cell.centerX-x)+(cell.centerY-y)*(cell.centerY-y))>outerCircleRadius*outerCircleRadius)){
return true;
}
}
}
return false;
}
void setCenter(float motionX, float motionY) {
this.centerX = motionX;
this.centerY = motionY;
}
//单元格的状态
static class CellState {
static int STATUS_NORMAL=0;
static int STATUS_TOUCHED=1;
static int STATUS_ERROR=2;
}
}
public static class MathUtil{
/**
* 计算两点之间的距离(x1,y1)--(x2,y2)
*/
static float distance(float x1, float y1, float x2, float y2){
float d2=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
return (float) Math.sqrt(d2);
}
}
public static class DensityUtil {
private DensityUtil() {
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dp转px
*/
static int dp2px(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
}
/**
* 回调接口
*/
public interface OnPatternListener {
void onPatternStart();
void onPatternEnd(String pattern);
void onPatternError(List cells);
}
}
| |
参考:Touch事件分发 - 九宫格解锁