900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 九宫格图案解锁 支付宝解锁 微信钱包解锁

九宫格图案解锁 支付宝解锁 微信钱包解锁

时间:2022-09-30 12:41:44

相关推荐

九宫格图案解锁 支付宝解锁 微信钱包解锁

先来看看效果:

1.九宫格界面实现了密码的绘制,包括:绘制中、绘制不符合要求、绘制成功3个状态

2.绘制过程的监听:绘制错误、绘制成功,以及密码点所对应的密码

3.附上源码(编码:UTF-8,可导入到Android Studio中):点击下载(/detail/fang_guiliang/8449057)

再来看看代码的实现:

第1步:新建自定义属性,在values文件夹下面新建资源文件,命名为:attrs.xml,用于自定义属性,其中的代码如下:

deblcokingMargin:是密码点之间的间距

<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="DeblockingDot"><attr name="deblcokingMargin" format="dimension"></attr></declare-styleable></resources>

第2步:新建DeblockingDot类继承View,并且实现相应的构造方法,并获取上面自定义属性的值,事实上密码错误成功需要显示的图片也可以按照自定义属性的方法加载到DeblockingDot类中,此处直接加载资源文件:

public DeblockingDot(Context context) {this(context, null);}public DeblockingDot(Context context, AttributeSet attrs) {this(context, attrs, 0);}public DeblockingDot(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DeblockingDot);final int length = array.getIndexCount();for (int i = 0; i < length; i++) {int attr = array.getIndex(i);switch (attr) {case R.styleable.DeblockingDot_deblcokingMargin:margin = array.getDimension(attr, TypedValue.applyDimension(PLEX_UNIT_SP, 0,getResources().getDisplayMetrics()));break;}}array.recycle();//回收资源initData();//初始化数据}

intitData()方法中的代码:获取图片资源,创建对象等:

private Bitmap mDefaultBitmap;//默认状态下的图private Bitmap mErrorBitmap;//错误状态下的图private Bitmap mPressedBitmap;//选中状态下的图private Bitmap mPressedLine;//选中的线private Bitmap mErrorLine;//错误的线private Paint mPaint;//画笔,用于画线和密码状态的private Matrix mMatrix;//矩阵,用于缩放线private ArrayList<Point> pointList;//密码点的集合

private void initData() {mDefaultBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_default);mErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_error);mPressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pressed);mPressedLine = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);mErrorLine = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);mBitmapR = mDefaultBitmap.getHeight() / 2;mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setAntiAlias(true);//间距必须大于图标的半径margin = margin < mBitmapR ? mBitmapR : margin;pointList = new ArrayList<Point>();mMatrix = new Matrix();}

第3步:需要创建密码点的类:Point,用于保存密码点对应的状态、坐标、所代表的密码,其中还包含几个工具方法,用于计算点和点的距离,手指接触屏幕的点到密码点的距离等,具体代码如下:

public static class Point {//默认状态public static int STATUS_DEFAULT = 0;//错误状态public static int STATUS_ERROR = 1;//选中状态public static int STATUS_PRESSED = 2;//x轴位置public float x;//y轴位置public float y;//状态public int status;//密码public int password;public Point(float x, float y) {this.x = x;this.y = y;}/*** 判断两个点之间的距离是否小于r** @param pointX 点的x轴* @param pointY 点的y轴* @param mouseX 鼠标的x轴* @param mouseY 鼠标的y轴* @param r* @return*/public static boolean checkPointDistance(float pointX, float pointY, float mouseX, float mouseY, float r) {return Math.sqrt((pointX - mouseX) * (pointX - mouseX) + (pointY - mouseY) * (pointY - mouseY)) < r;}/*** @param a 点a* @param b 点b* @return 两点之间的距离*/public static double distacne(Point a, Point b) {return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));}}

第4步:重写onDraw方法进行绘制,由于需要用到控件的宽高,所以密码点需要在onDraw初始化

private boolean isInit;//是否初始化过点@Overrideprotected void onDraw(Canvas canvas) {if (!isInit) {initPoint();isInit = true;}points2Canvas(canvas);line2Canvas(canvas);}private Point[][] points;//二维数组,存放3行3列的密码点,一共9个值/*** 初始化密码点*/private void initPoint() {float width = getWidth();float height = getHeight();float offsetsX = 0f;//x轴偏移量float offsetsY = 0f;//Y轴偏移量if (height > width) {offsetsY = (height - width) / 2;height = width;} else {offsetsX = (width - height) / 2;width = height;}points = new Point[3][3];float oneX = offsetsX + margin;//第一竖x轴float twoX = offsetsX + width / 2;//第二竖x轴float threeX = offsetsX + width - margin;//第三竖x轴float oneY = offsetsY + margin;//第一行y轴float twoY = offsetsY + height / 2;//第二行y轴float threeY = offsetsY + width - margin;//第三行y轴//第一行points[0][0] = new Point(oneX, oneY);points[0][1] = new Point(twoX, oneY);points[0][2] = new Point(threeX, oneY);//第二行points[1][0] = new Point(oneX, twoY);points[1][1] = new Point(twoX, twoY);points[1][2] = new Point(threeX, twoY);//第三行points[2][0] = new Point(oneX, threeY);points[2][1] = new Point(twoX, threeY);points[2][2] = new Point(threeX, threeY);//为每个点设置对应的密码int password = 0;for (Point[] points : this.points) {for (Point point : points) {point.password = ++password;}}}/*** 画点** @param canvas*/private void points2Canvas(Canvas canvas) {Point point;for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[i].length; j++) {point = points[i][j];if (point.status == Point.STATUS_DEFAULT) {canvas.drawBitmap(mDefaultBitmap, point.x - mBitmapR, point.y - mBitmapR, mPaint);} else if (point.status == Point.STATUS_PRESSED) {canvas.drawBitmap(mPressedBitmap, point.x - mBitmapR, point.y - mBitmapR, mPaint);} else if (point.status == Point.STATUS_ERROR) {canvas.drawBitmap(mErrorBitmap, point.x - mBitmapR, point.y - mBitmapR, mPaint);}}}}

第5步:重写onTouchEvent方法,监听屏幕,包括手指按下,拖动和离开,在此过程中判断手指经过的位置,同时绘制图案

@Overridepublic boolean onTouchEvent(MotionEvent event) {mouseX = event.getX();//手指位置xmouseY = event.getY();//手指位置YisFinish = false;isNeedCanvas = false;Point point = null;switch (event.getAction()) {case MotionEvent.ACTION_DOWN://手指按下resetPoint();point = checkSelectPoint();if (point != null) {isSelected = true;if (listener != null)listener.onActionDown(true);}break;case MotionEvent.ACTION_MOVE:if (isSelected) {//当第一个点确认后才开始检查其它点point = checkSelectPoint();if (point == null) {//不是目标的点isNeedCanvas = true;}} else {point = checkSelectPoint();if (point != null) {isSelected = true;if (listener != null)listener.onActionDown(true);}}break;case MotionEvent.ACTION_UP:isSelected = false;isFinish = true;break;}//防止重复选中if (!isFinish && isSelected && point != null) {//在绘制过程中检测pointif (!pointList.contains(point)) {point.status = Point.STATUS_PRESSED;pointList.add(point);} else {isNeedCanvas = true;}}// 绘制结束,检测密码是否符合规则if (isFinish) {/*if (pointList.size() == 1) {resetPoint();}*/if (pointList.size() < MIN_POINT_COUNT && pointList.size() > 0) {errorPoint();if (listener != null) {listener.onPointChange(null);}} else {String password = "";for (int i = 0; i < pointList.size(); i++) {password = password + pointList.get(i).password;}if (listener != null) {listener.onPointChange(password);}}}postInvalidate();//重新绘制return true;}/*** 重置点的状态*/public void resetPoint() {for (int i = 0; i < pointList.size(); i++) {pointList.get(i).status = Point.STATUS_DEFAULT;}pointList.clear();}/*** 绘制错误的点*/private void errorPoint() {for (int i = 0; i < pointList.size(); i++) {pointList.get(i).status = Point.STATUS_ERROR;}}/*** 检测鼠标按下是否接近某个点*/private Point checkSelectPoint() {Point point = null;Point tmpPoint = null;for (int i = 0; i < points.length; i++) {for (int j = 0; j < points[i].length; j++) {tmpPoint = points[i][j];if (Point.checkPointDistance(tmpPoint.x, tmpPoint.y, mouseX, mouseY, mBitmapR)) {point = tmpPoint;}}}return point;}

第6步:画线,其中线的方向即角度是难点,理解好思路就够了,其中线的图标很小,在绘制中需要用到矩阵对其进行平铺拉伸

/*** 画线** @param canvas*/private void line2Canvas(Canvas canvas) {if (pointList.size() > 0) {Point a = pointList.get(0);Point b;for (int i = 1; i < pointList.size(); i++) {//从第二个点开始,赋给第一个点b = pointList.get(i);line2Canvas(canvas, a, b);a = b;}if (isNeedCanvas) {//需要继续画线条line2Canvas(canvas, a, new Point(mouseX, mouseY));}}}private void line2Canvas(Canvas canvas, Point a, Point b) {float lineLength = (float) Point.distacne(a, b);//需要画的线的长度float degree = getDegree(a, b);canvas.rotate(degree, a.x, a.y);//旋转画布,原点发发生了变化if (a.status == Point.STATUS_PRESSED) {mMatrix.setScale(lineLength / mPressedLine.getWidth(), 1);//设置X轴缩放比例mMatrix.postTranslate(a.x - mErrorLine.getWidth() / 2, a.y - mErrorLine.getHeight() / 2);//矩阵平铺canvas.drawBitmap(mPressedLine, mMatrix, mPaint);} else {mMatrix.setScale(lineLength / mErrorLine.getWidth(), 1);//设置X轴缩放比例mMatrix.postTranslate(a.x - mErrorLine.getWidth() / 2, a.y - mErrorLine.getHeight() / 2);//矩阵平铺canvas.drawBitmap(mErrorLine, mMatrix, mPaint);}canvas.rotate(-degree, a.x, a.y);//恢复画布,恢复原点}/*** 获取线的角度** @param a 点a* @param b 点b* @return 角度*/private float getDegree(Point a, Point b) {float ax = a.x;float ay = a.y;float bx = b.x;float by = b.y;float degress = 0f;if (ax == bx) {//x轴相等,角度为270或者90度if (ay > by) {degress = 270;} else if (ay < by) {degress = 90;}} else if (ay == by) {//y轴相等,角度为0或者180;if (ax > bx) {degress = 180;} else if (ax < bx) {degress = 0;}} else if (ax > bx) {//b在a的左边,90度到270度if (ay < by) {//b在a下,90度到180度degress = 90 + switchDegress(Math.abs(bx - ax), Math.abs(by - ay));} else if (ay > by) {//b在a的下边,180度到270度degress = 270 - switchDegress(Math.abs(bx - ax), Math.abs(by - ay));}} else if (ax < bx) {//b在a的右边,270到90度if (ay > by) {//b在a下边,360到0degress = 270 + switchDegress(Math.abs(bx - ax), Math.abs(by - ay));} else if (ay < by) {//b在a上边,0到90degress = 90 - switchDegress(Math.abs(bx - ax), Math.abs(by - ay));}}return degress;}

/*** 强制转换为角度** @param x x轴* @param y y轴* @return 角度*/private float switchDegress(float x, float y) {return (float) Math.toDegrees(Math.atan2(x, y));}

以上描述了,整个自定义九宫格图案的完成思路,代码也注释清楚了,角度这块可能难理解,暂且这样吧,可以下载源码Demo整体看下,里面实现了密码的监听等,可以直接使用到项目中,有疑问的请留言一同讨论。

附上源码:点击下载(/detail/fang_guiliang/8449057)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。