九宫格安全手势锁
2015-07-20 10:08
344 查看
这些东西是从网上搜罗的,自己整理了然后以后自己用着方便,东西可以直接用,demo下载
效果图就不传了,直接运行就能看效果,最好还是下载下来看一下,博客上可能有漏说的,开发工具eclipse
直接上代码,代码中有解释
效果图就不传了,直接运行就能看效果,最好还是下载下来看一下,博客上可能有漏说的,开发工具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方法中传入的就是用户选择的宫格,在这里对其进行逻辑上的操作。
相关文章推荐
- executeFind()方法和execute()方法区别
- Java程序员面试题收集(6)
- 【阅读】《head first html5》第一章——认识html5
- 国内域名商.wang总量TOP14统计报告(7月17日)
- 正则表达式
- JUnit五分钟掌握
- cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element
- 【sqlserver】阻止更新要求重建表的解决办法
- JUnit五分钟掌握
- 【iOS发展-28】制造业UITabBarController标记控制器、定制UITabBarItem文字图像6途径和More评论
- BASH中用到的通配符和特殊符号
- mysql 下载地址
- Algorithms—138.Copy List with Random Pointer
- 动态切换数据源(spring3.0+hibernate3.0)
- Xcode6模拟器app和文件在哪儿?
- Obj-C中的@class
- JDK的组成:Client Hotspot VM 与Server Hotspot VM 的区别 ???
- POJ 1486 (2分匹配 必须变判断)
- 【memcache/redis】memcache常见问题汇总
- poj 2352