您的位置:首页 > 其它

九宫格安全手势锁

2015-07-20 10:08 344 查看
这些东西是从网上搜罗的,自己整理了然后以后自己用着方便,东西可以直接用,demo下载

效果图就不传了,直接运行就能看效果,最好还是下载下来看一下,博客上可能有漏说的,开发工具eclipse

直接上代码,代码中有解释

/**
* 九宫格手势锁组件,可以通过xml来设置的有连线的颜色, 每个格子的背景图和选中选错的图片 其他的设置可以通过代码来设置(看代码中的注释)
* 在使用的时候必须要设置setOnPatternListener(OnPatternListener onPatternListener),
* 实现OnPatternListener 的 onPatternDetected方法,其中此方法中传入的参数既是使用者选中的单元格
*
* <pre>
* <com.luckchoudog.ui.LockPatternView
*         xmlns:app="http://schemas.android.com/apk/res-auto"
*         android:id="@+id/gesturepwd_create_lockview"
*         android:layout_width="wrap_content"
*         android:layout_height="wrap_content"
*         android:layout_gravity="center_horizontal"
*         app:Demo_circleNormal="@drawable/circleNormal_bg" >
* </com.luckchoudog.ui.LockPatternView>
* </pre>
*
* 此类有用到attr文件中的相关属性
*
* @author luckchoudog
*/
@SuppressLint("NewApi")
public class LockPatternView extends View {
/**
* 九宫格为3 十六宫格为4 ;这个值直接关系显示出的效果
*/
private static int number = 3;
/**
* 此view的所占区域,区域宽高都取设定宽高的最小值设置View区域(默认)
*/
private static final int ASPECT_SQUARE = 0; //
/**
* 此view的所占区域,以宽为标准,高取设定宽高的最小值设置View区域
*/
private static final int ASPECT_LOCK_WIDTH = 1;
/**
* 此view的所占区域,以高为标准,宽取设定宽高的最小值设置View区域
*/
private static final int ASPECT_LOCK_HEIGHT = 2;
/**
* 动画模式时,两点之间连接的时间(毫秒),此值越小速度越快
*/
private static final int MILLIS_PER_CIRCLE_ANIMATING = 600;
/**
* 使用者自己实现的OnPatternListener设置到此View
*/
private OnPatternListener mOnPatternListener;
/**
* 每次操作中所有已经选中的单元格的list
*/
private ArrayList<Cell> mPattern = new ArrayList<Cell>(number * number);
/**
* 目前正在绘制的图案圆的查找表,例如3x3模式
*/
private boolean[][] mPatternDrawLookup = new boolean[number][number];
private float mInProgressX = -1;
private float mInProgressY = -1;
private long mAnimatingPeriodStart;
/**
* 默认的单元图片模式
*/
private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
/**
* 是否允许输入,是否允许绘制,默认为true允许使用
*/
private boolean mInputEnabled = true;
/**
* 是否显示滑动轨迹 true显示 false不显示
*/
private boolean mInStealthMode = true;
/**
* 是否有触觉反馈 true表示有 false表示没有
*/
private boolean mEnableHapticFeedback = true;
/**
* 线的宽度(设定值范围为1~50)
*/
private float mDiameterFactor = 2;
private final int mStrokeAlpha = 128;
/**
* 默认平常线的颜色
*/
private int lineColor_normal = Color.YELLOW;
/**
* 默认错误线的颜色
*/
private int lineColor_woring = Color.RED;
/**
* 每个单元格的背景图片
*/
private int circle_Normal;
/**
* 单元格被选中的图片
*/
private int circleGreen;
/**
* 单元格选中错误的图片
*/
private int circleRed;

private float mSquareWidth;
private float mSquareHeight;
private Paint mPaint = new Paint();
private Paint mPathPaint = new Paint();
private Bitmap mBitmapCircleDefault;
private Bitmap mBitmapCircleGreen;
private Bitmap mBitmapCircleRed;
private boolean mPatternInProgress = false;

private final Path mCurrentPath = new Path();
private final Rect mInvalidate = new Rect();

private int mBitmapWidth;
private int mBitmapHeight;

private int mAspect;
private final Matrix mCircleMatrix = new Matrix();

/**
* 每个单元格的实例,代表使用者所看到的每个点
*/
public static class Cell {
int row;
int column;
static Cell[][] sCells = new Cell[number][number];
static {
for (int i = 0; i < number; i++) {
for (int j = 0; j < number; j++) {
sCells[i][j] = new Cell(i, j);
}
}
}

/**
* @param row
*            The row of the cell.
* @param column
*            The column of the cell.
*/
private Cell(int row, int column) {
checkRange(row, column);
this.row = row;
this.column = column;
}

public int getRow() {
return row;
}

public int getColumn() {
return column;
}

/**
* @param row
*            The row of the cell.
* @param column
*            The column of the cell.
*/
public static synchronized Cell of(int row, int column) {
checkRange(row, column);
return sCells[row][column];
}

private static void checkRange(int row, int column) {
if (row < 0 || row > number - 1) {
throw new IllegalArgumentException("row must be in range 0-" + (number - 1));
}
if (column < 0 || column > number - 1) {
throw new IllegalArgumentException("column must be in range 0-" + (number - 1));
}
}

public String toString() {
return "(row=" + row + ",clmn=" + column + ")";
}
}

/**
* 显示当前的模式
*/
public enum DisplayMode {

/**
* 正确模式 (i.e draw it in a friendly color)
*/
Correct,

/**
* 动画模式 (for demo, and help).
*/
Animate,

/**
* 错误模式 (i.e draw a foreboding color)
*/
Wrong
}

/**
* 用户输入的检测模式的调用返回接口。使用者需要实现此接口,并设置到此View中
*/
public static interface OnPatternListener {

/**
* 开始新的模式(当手指开始滑动)
*/
void onPatternStart();

/**
* 模式被清除(完成结束模式)
*/
void onPatternCleared();

/**
* 用户每次新增的所选择的单元格调用(正在运行模式完成后)
*
* @param pattern
*            所有选中的单元格
*/
void onPatternCellAdded(List<Cell> pattern);

/**
* 最后完成滑动(一般使用此方法来检测最后选择的单元格)
*
* @param pattern
*            所有选中的单元格
*/
void onPatternDetected(List<Cell> pattern);
}

public LockPatternView(Context context) {
this(context, null);
initRFile(context);
}

private void initRFile(Context context) {
circle_Normal = MResource.getIdByName(context, "drawable", "ui_lockpatternview_pattern_item_bg");
circleGreen = MResource.getIdByName(context, "drawable", "ui_lockpatternview_pattern_selected");
circleRed = MResource.getIdByName(context, "drawable", "ui_lockpatternview_pattern_selected_wrong");
}

public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
initRFile(context);
TypedArray a = context.obtainStyledAttributes(attrs, MResource.getIdsByName(context, "Demo_LockPatternView"));
String aspect = a.getString(MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_aspect"));
if ("Demo_square".equals(aspect)) {
mAspect = ASPECT_SQUARE;
} else if ("Demo_lock_width".equals(aspect)) {
mAspect = ASPECT_LOCK_WIDTH;
} else if ("Demo_lock_height".equals(aspect)) {
mAspect = ASPECT_LOCK_HEIGHT;
} else {
mAspect = ASPECT_SQUARE;
}
lineColor_normal = a.getColor(MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_lineNormal"),
lineColor_normal);
lineColor_woring = a.getColor(MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_lineWrong"),
lineColor_woring);
setClickable(true);
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
mPathPaint.setColor(lineColor_normal);
mPathPaint.setAlpha(mStrokeAlpha);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);

mBitmapCircleDefault = getBitmapFor(a.getResourceId(
MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_circleNormal"), circle_Normal));
mBitmapCircleGreen = getBitmapFor(a.getResourceId(
MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_circleGreen"), circleGreen));
mBitmapCircleRed = getBitmapFor(a.getResourceId(
MResource.getIdByName(context, "styleable", "Demo_LockPatternView_Demo_circleRed"), circleRed));
// bitmaps have the size of the largest bitmap in this group
final Bitmap bitmaps[] = { mBitmapCircleDefault, mBitmapCircleGreen, mBitmapCircleRed };
for (Bitmap bitmap : bitmaps) {
mBitmapWidth = Math.max(mBitmapWidth, bitmap.getWidth());
mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
}
a.recycle();

}

private Bitmap getBitmapFor(int resId) {
return BitmapFactory.decodeResource(getContext().getResources(), resId);
}

/**
* 禁用输入(连续输错好多次时)
*/
public void disableInput() {
mInputEnabled = false;
}

/**
* 启用输入
*/
public void enableInput() {
mInputEnabled = true;
}

/**
* 获取是否显示滑动轨迹
*
* @return true显示 false不显示
*/
public boolean isInStealthMode() {
return mInStealthMode;
}

/**
* 获取是否有触觉反馈
*
* @return true表示有 false表示没有
*/
public boolean isTactileFeedbackEnabled() {
return mEnableHapticFeedback;
}

/**
* 设置是否显示滑动轨迹 true显示 false不显示
*/
public void setInStealthMode(boolean inStealthMode) {
mInStealthMode = inStealthMode;
}

/**
* 设置是否有触觉反馈 true表示有 false表示没有
*/
public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
mEnableHapticFeedback = tactileFeedbackEnabled;
}

/**
* 使用者自己实现的OnPatternListener设置到此View
*/
public void setOnPatternListener(OnPatternListener onPatternListener) {
mOnPatternListener = onPatternListener;
}

/**
* 设置动画演示,传入的是动画过程中的多有单元格,动画顺序和传入的单元格顺序相同
*
* @param pattern
*            动画演示的所有单元格
*/
public void setAnimationPattern(List<Cell> pattern) {
mPattern.clear();
mPattern.addAll(pattern);
clearPatternDrawLookup();
for (Cell cell : pattern) {
mPatternDrawLookup[cell.getRow()][cell.getColumn()] = true;
}
setDisplayMode(DisplayMode.Animate);
}

private void setPattern(DisplayMode displayMode, List<Cell> pattern) {
mPattern.clear();
mPattern.addAll(pattern);
clearPatternDrawLookup();
for (Cell cell : pattern) {
mPatternDrawLookup[cell.getRow()][cell.getColumn()] = true;
}
setDisplayMode(displayMode);
}

/**
* 设置View的所占区域模式
*/
public void setDisplayMode(DisplayMode displayMode) {
mPatternDisplayMode = displayMode;
if (displayMode == DisplayMode.Animate) {
if (mPattern.size() == 0) {
throw new IllegalStateException("you must have a pattern to "
+ "animate if you want to set the display mode to animate");
}
mAnimatingPeriodStart = SystemClock.elapsedRealtime();
final Cell first = mPattern.get(0);
mInProgressX = getCenterXForColumn(first.getColumn());
mInProgressY = getCenterYForRow(first.getRow());
clearPatternDrawLookup();
}
invalidate();
}

private void notifyCellAdded() {
if (mOnPatternListener != null) {
mOnPatternListener.onPatternCellAdded(mPattern);
}
}

private void notifyPatternStarted() {
if (mOnPatternListener != null) {
mOnPatternListener.onPatternStart();
}
}

private void notifyPatternDetected() {
if (mOnPatternListener != null) {
mOnPatternListener.onPatternDetected(mPattern);
}
}

private void notifyPatternCleared() {
if (mOnPatternListener != null) {
mOnPatternListener.onPatternCleared();
}
}

/**
* 清除绘制的图案
*/
public void clearPattern() {
resetPattern();
}

/**
* 重置所有图案状态
*/
private void resetPattern() {
mPattern.clear();
clearPatternDrawLookup();
mPatternDisplayMode = DisplayMode.Correct;
invalidate();
}

/**
* 清除图案列表-图案都标记为未选中
*/
private void clearPatternDrawLookup() {
for (int i = 0; i < number; i++) {
for (int j = 0; j < number; j++) {
mPatternDrawLookup[i][j] = false;
}
}
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
final int width = w - getPaddingLeft() - getPaddingRight();
mSquareWidth = (float) width / number;
final int height = h - getPaddingTop() - getPaddingBottom();
mSquareHeight = (float) height / number;
}

private int resolveMeasured(int measureSpec, int desired) {
int result = 0;
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;
}

@Override
protected int getSuggestedMinimumWidth() {
// View should be large enough to contain 3 side-by-side target bitmaps
return number * mBitmapWidth;
}

@Override
protected int getSuggestedMinimumHeight() {
// View should be large enough to contain 3 side-by-side target bitmaps
return number * mBitmapWidth;
}

@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);

switch (mAspect) {
case ASPECT_SQUARE:
viewWidth = viewHeight = Math.min(viewWidth, viewHeight);
break;
case ASPECT_LOCK_WIDTH:
viewHeight = Math.min(viewWidth, viewHeight);
break;
case ASPECT_LOCK_HEIGHT:
viewWidth = Math.min(viewWidth, viewHeight);
break;
}
setMeasuredDimension(viewWidth, viewHeight);
}

private Cell detectAndAddHit(float x, float y) {
final Cell cell = checkForNewHit(x, y);
if (cell != null) {
// check for gaps in existing pattern
Cell fillInGapCell = null;
final ArrayList<Cell> pattern = mPattern;
if (!pattern.isEmpty()) {
final Cell lastCell = pattern.get(pattern.size() - 1);
int dRow = cell.row - lastCell.row;
int dColumn = cell.column - lastCell.column;

int fillInRow = lastCell.row;
int fillInColumn = lastCell.column;

if (Math.abs(dRow) == (number - 1) && Math.abs(dColumn) != 1) {
fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1);
}

if (Math.abs(dColumn) == (number - 1) && Math.abs(dRow) != 1) {
fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1);
}

fillInGapCell = Cell.of(fillInRow, fillInColumn);
}

if (fillInGapCell != null && !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
addCellToPattern(fillInGapCell);
}
addCellToPattern(cell);
if (mEnableHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
| HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
return cell;
}
return null;
}

private void addCellToPattern(Cell newCell) {
mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
mPattern.add(newCell);
notifyCellAdded();
}

/**
* 判断手指区域是否在某个单元格内
*/
private Cell checkForNewHit(float x, float y) {

final int rowHit = getRowHit(y);
if (rowHit < 0) {
return null;
}
final int columnHit = getColumnHit(x);
if (columnHit < 0) {
return null;
}

if (mPatternDrawLookup[rowHit][columnHit]) {
return null;
}
return Cell.of(rowHit, columnHit);
}

private int getRowHit(float y) {

final float squareHeight = mSquareHeight;
float hitSize = squareHeight * 0.7f;

float offset = getPaddingTop() + (squareHeight - hitSize) / (float) (number - 1);
for (int i = 0; i < number; i++) {

final float hitTop = offset + squareHeight * i;
if (y >= hitTop && y <= hitTop + hitSize) {
return i;
}
}
return -1;
}

private int getColumnHit(float x) {
final float squareWidth = mSquareWidth;
float hitSize = squareWidth * 0.7f;

float offset = getPaddingLeft() + (squareWidth - hitSize) / (float) (number - 1);
for (int i = 0; i < number; i++) {

final float hitLeft = offset + squareWidth * i;
if (x >= hitLeft && x <= hitLeft + hitSize) {
return i;
}
}
return -1;
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mInputEnabled || !isEnabled()) {
return false;
}

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
handleActionDown(event);
return true;
case MotionEvent.ACTION_UP:
handleActionUp(event);
return true;
case MotionEvent.ACTION_MOVE:
handleActionMove(event);
return true;
case MotionEvent.ACTION_CANCEL:
resetPattern();
mPatternInProgress = false;
notifyPatternCleared();
return true;
}
return false;
}

/**
* 划线的算法,有兴趣的看看吧
*/
private void handleActionMove(MotionEvent event) {
final int historySize = event.getHistorySize();
for (int i = 0; i < historySize + 1; i++) {
final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
final int patternSizePreHitDetect = mPattern.size();
Cell hitCell = detectAndAddHit(x, y);
final int patternSize = mPattern.size();
if (hitCell != null && patternSize == 1) {
mPatternInProgress = true;
notifyPatternStarted();
}
// note current x and y for rubber banding of in progress patterns
final float dx = Math.abs(x - mInProgressX);
final float dy = Math.abs(y - mInProgressY);
if (dx + dy > mSquareWidth * 0.01f) {
float oldX = mInProgressX;
float oldY = mInProgressY;
mInProgressX = x;
mInProgressY = y;
if (mPatternInProgress && patternSize > 0) {
final ArrayList<Cell> pattern = mPattern;
final float radius = mSquareWidth * mDiameterFactor * 0.01f;
final Cell lastCell = pattern.get(patternSize - 1);
float startX = getCenterXForColumn(lastCell.column);
float startY = getCenterYForRow(lastCell.row);
float left;
float top;
float right;
float bottom;
final Rect invalidateRect = mInvalidate;
if (startX < x) {
left = startX;
right = x;
} else {
left = x;
right = startX;
}
if (startY < y) {
top = startY;
bottom = y;
} else {
top = y;
bottom = startY;
}
// Invalidate between the pattern's last cell and the
// current location
invalidateRect.set((int) (left - radius), (int) (top - radius), (int) (right + radius),
(int) (bottom + radius));
if (startX < oldX) {
left = startX;
right = oldX;
} else {
left = oldX;
right = startX;
}
if (startY < oldY) {
top = startY;
bottom = oldY;
} else {
top = oldY;
bottom = startY;
}
// Invalidate between the pattern's last cell and the
// previous location
invalidateRect.union((int) (left - radius), (int) (top - radius), (int) (right + radius),
(int) (bottom + radius));
// Invalidate between the pattern's new cell and the
// pattern's previous cell
if (hitCell != null) {
startX = getCenterXForColumn(hitCell.column);
startY = getCenterYForRow(hitCell.row);
if (patternSize >= (number - 1)) {
// (re-using hitcell for old cell)
hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
oldX = getCenterXForColumn(hitCell.column);
oldY = getCenterYForRow(hitCell.row);
if (startX < oldX) {
left = startX;
right = oldX;
} else {
left = oldX;
right = startX;
}
if (startY < oldY) {
top = startY;
bottom = oldY;
} else {
top = oldY;
bottom = startY;
}
} else {
left = right = startX;
top = bottom = startY;
}
final float widthOffset = mSquareWidth / 2f;
final float heightOffset = mSquareHeight / 2f;
invalidateRect.set((int) (left - widthOffset), (int) (top - heightOffset), (int) (right + widthOffset),
(int) (bottom + heightOffset));
}
invalidate(invalidateRect);
} else {
invalidate();
}
}
}
}

private void handleActionUp(MotionEvent event) {
// report pattern detected
if (!mPattern.isEmpty()) {
mPatternInProgress = false;
notifyPatternDetected();
invalidate();
}
}

private void handleActionDown(MotionEvent event) {
resetPattern();
final float x = event.getX();
final float y = event.getY();
final Cell hitCell = detectAndAddHit(x, y);
if (hitCell != null) {
mPatternInProgress = true;
mPatternDisplayMode = DisplayMode.Correct;
notifyPatternStarted();
} else {
mPatternInProgress = false;
notifyPatternCleared();
}
if (hitCell != null) {
final float startX = getCenterXForColumn(hitCell.column);
final float startY = getCenterYForRow(hitCell.row);

final float widthOffset = mSquareWidth / 2f;
final float heightOffset = mSquareHeight / 2f;

invalidate((int) (startX - widthOffset), (int) (startY - heightOffset), (int) (startX + widthOffset),
(int) (startY + heightOffset));
}
mInProgressX = x;
mInProgressY = y;
}

private float getCenterXForColumn(int column) {
return getPaddingLeft() + column * mSquareWidth + mSquareWidth / 2f;
}

private float getCenterYForRow(int row) {
return getPaddingTop() + row * mSquareHeight + mSquareHeight / 2f;
}

@Override
protected void onDraw(Canvas canvas) {
final ArrayList<Cell> pattern = mPattern;
final int count = pattern.size();
final boolean[][] drawLookup = mPatternDrawLookup;
if (mPatternDisplayMode == DisplayMode.Animate) {
// figure out which circles to draw
// + 1 so we pause on complete pattern
final int oneCycle = (count + 1) * MILLIS_PER_CIRCLE_ANIMATING;
final int spotInCycle = (int) (SystemClock.elapsedRealtime() - mAnimatingPeriodStart) % oneCycle;
final int numCircles = spotInCycle / MILLIS_PER_CIRCLE_ANIMATING;
clearPatternDrawLookup();
for (int i = 0; i < numCircles; i++) {
final Cell cell = pattern.get(i);
drawLookup[cell.getRow()][cell.getColumn()] = true;
}
// figure out in progress portion of ghosting line
final boolean needToUpdateInProgressPoint = numCircles > 0 && numCircles < count;
if (needToUpdateInProgressPoint) {
final float percentageOfNextCircle = ((float) (spotInCycle % MILLIS_PER_CIRCLE_ANIMATING))
/ MILLIS_PER_CIRCLE_ANIMATING;
final Cell currentCell = pattern.get(numCircles - 1);
final float centerX = getCenterXForColumn(currentCell.column);
final float centerY = getCenterYForRow(currentCell.row);
final Cell nextCell = pattern.get(numCircles);
final float dx = percentageOfNextCircle * (getCenterXForColumn(nextCell.column) - centerX);
final float dy = percentageOfNextCircle * (getCenterYForRow(nextCell.row) - centerY);
mInProgressX = centerX + dx;
mInProgressY = centerY + dy;
}
invalidate();
}
final float squareWidth = mSquareWidth;
final float squareHeight = mSquareHeight;
float radius = (squareWidth * mDiameterFactor * 0.01f);
mPathPaint.setStrokeWidth(radius);
final Path currentPath = mCurrentPath;
currentPath.rewind();
final boolean drawPath = (mInStealthMode || mPatternDisplayMode != DisplayMode.Correct);
boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
mPaint.setFilterBitmap(true);
if (drawPath) {
boolean anyCircles = false;
for (int i = 0; i < count; i++) {
Cell cell = pattern.get(i);
// only draw the part of the pattern stored in
// the lookup table (this is only different in the case
// of animation).
if (!drawLookup[cell.row][cell.column]) {
break;
}
anyCircles = true;
float centerX = getCenterXForColumn(cell.column);
float centerY = getCenterYForRow(cell.row);
if (i == 0) {
currentPath.moveTo(centerX, centerY);
} else {
currentPath.lineTo(centerX, centerY);
}
}
// add last in progress section
if ((mPatternInProgress || mPatternDisplayMode == DisplayMode.Animate) && anyCircles) {
currentPath.lineTo(mInProgressX, mInProgressY);
}
// chang the line color in different DisplayMode
if (mPatternDisplayMode == DisplayMode.Wrong)
mPathPaint.setColor(lineColor_woring);
else
mPathPaint.setColor(lineColor_normal);
canvas.drawPath(currentPath, mPathPaint);
}

// draw the circles
final int paddingTop = getPaddingTop();
final int paddingLeft = getPaddingLeft();

for (int i = 0; i < number; i++) {
float topY = paddingTop + i * squareHeight;
for (int j = 0; j < number; j++) {
float leftX = paddingLeft + j * squareWidth;
drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]);
}
}
mPaint.setFilterBitmap(oldFlag); // restore default flag
}

/**
* 解密
*/
public List<LockPatternView.Cell> stringToPattern(String string) {
List<LockPatternView.Cell> result = new ArrayList<LockPatternView.Cell>();
final byte[] bytes = string.getBytes();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
result.add(LockPatternView.Cell.of(b / LockPatternView.number, b % LockPatternView.number));
}
return result;
}

/**
* 加密,将选中的单元格转换成自己字符串便于存储
*/
public static String patternToString(List<LockPatternView.Cell> pattern) {
if (pattern == null) {
return "";
}
final int patternSize = pattern.size();

byte[] res = new byte[patternSize];
for (int i = 0; i < patternSize; i++) {
LockPatternView.Cell cell = pattern.get(i);
res[i] = (byte) (cell.getRow() * LockPatternView.number + cell.getColumn());
}
return new String(res);
}

/**
* 画每个单元格
*/
private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPattern) {
Bitmap outerCircle;
Bitmap innerCircle = null;
if (!partOfPattern || (!mInStealthMode && mPatternDisplayMode == DisplayMode.Correct)) {
// unselected circle
outerCircle = mBitmapCircleDefault;
innerCircle = null;
} else if (mPatternInProgress) {
// user is in middle of drawing a pattern
outerCircle = mBitmapCircleDefault;
innerCircle = mBitmapCircleGreen;
} else if (mPatternDisplayMode == DisplayMode.Wrong) {
// the pattern is wrong
outerCircle = mBitmapCircleDefault;
innerCircle = mBitmapCircleRed;
} else if (mPatternDisplayMode == DisplayMode.Correct || mPatternDisplayMode == DisplayMode.Animate) {
// the pattern is correct
outerCircle = mBitmapCircleDefault;
innerCircle = mBitmapCircleGreen;
} else {
throw new IllegalStateException("unknown display mode " + mPatternDisplayMode);
}
final int width = mBitmapWidth;
final int height = mBitmapHeight;
final float squareWidth = mSquareWidth;
final float squareHeight = mSquareHeight;
int offsetX = (int) ((squareWidth - width) / 2f);
int offsetY = (int) ((squareHeight - height) / 2f);
// Allow circles to shrink if the view is too small to hold them.
float sx = Math.min(mSquareWidth / mBitmapWidth, 1.0f);
float sy = Math.min(mSquareHeight / mBitmapHeight, 1.0f);
mCircleMatrix.setTranslate(leftX + offsetX, topY + offsetY);
mCircleMatrix.preTranslate(mBitmapWidth / (number - 1), mBitmapHeight / (number - 1));
mCircleMatrix.preScale(sx, sy);
mCircleMatrix.preTranslate(-mBitmapWidth / (number - 1), -mBitmapHeight / (number - 1));
canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
if (innerCircle != null)
canvas.drawBitmap(innerCircle, mCircleMatrix, mPaint);
}

@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return new SavedState(superState, patternToString(mPattern), mPatternDisplayMode.ordinal(), mInputEnabled,
mInStealthMode, mEnableHapticFeedback);
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
final SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setPattern(DisplayMode.Correct, stringToPattern(ss.getSerializedPattern()));
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
}

/**
* SavedState是Parcelable的子类
*/
private static class SavedState extends BaseSavedState {

private final String mSerializedPattern;
private final int mDisplayMode;
private final boolean mInputEnabled;
private final boolean mInStealthMode;
private final boolean mTactileFeedbackEnabled;

/**
* Constructor called from {@link LockPatternView#onSaveInstanceState()}
*/
private SavedState(Parcelable superState, String serializedPattern, int displayMode, boolean inputEnabled,
boolean inStealthMode, boolean tactileFeedbackEnabled) {
super(superState);
mSerializedPattern = serializedPattern;
mDisplayMode = displayMode;
mInputEnabled = inputEnabled;
mInStealthMode = inStealthMode;
mTactileFeedbackEnabled = tactileFeedbackEnabled;
}

/**
* Constructor called from {@link #CREATOR}
*/
private SavedState(Parcel in) {
super(in);
mSerializedPattern = in.readString();
mDisplayMode = in.readInt();
mInputEnabled = (Boolean) in.readValue(null);
mInStealthMode = (Boolean) in.readValue(null);
mTactileFeedbackEnabled = (Boolean) in.readValue(null);
}

public String getSerializedPattern() {
return mSerializedPattern;
}

public int getDisplayMode() {
return mDisplayMode;
}

public boolean isInputEnabled() {
return mInputEnabled;
}

public boolean isInStealthMode() {
return mInStealthMode;
}

public boolean isTactileFeedbackEnabled() {
return mTactileFeedbackEnabled;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(mSerializedPattern);
dest.writeInt(mDisplayMode);
dest.writeValue(mInputEnabled);
dest.writeValue(mInStealthMode);
dest.writeValue(mTactileFeedbackEnabled);
}
}
}
其中用到的attr value有:

<!-- 九宫格所需的属性 -->
<declare-styleable name="Demo_LockPatternView">
<attr name="Demo_aspect">
<enum name="Demo_square" value="0" />
<enum name="Demo_lockWidth" value="1" />
<enum name="Demo_lockHeight" value="2" />
</attr>
<attr name="Demo_lineNormal" format="color" />
<attr name="Demo_lineWrong" format="color" />
<attr name="Demo_circleNormal" format="reference" />
<attr name="Demo_circleGreen" format="reference" />
<attr name="Demo_circleRed" format="reference" />
</declare-styleable>
此view可以通过xml直接配置使用,必须要强调的,必须要设置设置setOnPatternListener(OnPatternListener onPatternListener) 必须传入自己实现的回调OnPatternListener,在回调中的onPatternDetected方法中传入的就是用户选择的宫格,在这里对其进行逻辑上的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: