使用Android自定义控件实现滑动解锁九宫格
程序员文章站
2023-12-04 19:50:10
本文概述:
滑动解锁九宫格的分析:
1、需要自定义控件;
2、需要重写事件ontouchevent();
3、需要给九个点设置序号和坐标,这里用m...
本文概述:
滑动解锁九宫格的分析:
1、需要自定义控件;
2、需要重写事件ontouchevent();
3、需要给九个点设置序号和坐标,这里用map类就行;
4、需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用list类就行;
滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看。
我的是这样的:
demo
首先,自定义一个view
/** * 九宫格 */ public class ninegridview extends view { private int width;//该控件的宽 private int height;//该控件的高 private paint mpaintbigcircle;//用于画外圆 private paint mpaintsmallcircle;//用于画内圆 private paint mpaintline;//用于画线 private paint mpainttext;//用于画文本 private path path;//手势划线时需要用到它 private map<integer, float[]> pointcontainer;//存储九个点的坐标 private list<integer> pointerslipped;//存储得到的九宫格密码 public list<integer> getpointerslipped() { return pointerslipped; } public void setpointerslipped(list<integer> pointerslipped) { this.pointerslipped = pointerslipped; } public ninegridview(context context) { super(context); } public ninegridview(context context, attributeset attrs) { super(context, attrs); mpaintbigcircle = new paint(); mpaintbigcircle.setcolor(color.blue); mpaintbigcircle.setstyle(paint.style.stroke);//不充满 mpaintbigcircle.setantialias(true);//抗锯齿打开 mpaintsmallcircle = new paint(); mpaintsmallcircle.setcolor(color.green); mpaintsmallcircle.setstyle(paint.style.fill);//充满,即画的几何体为实心 mpaintsmallcircle.setantialias(true); mpaintline = new paint(); mpaintline.setcolor(color.green); mpaintline.setstyle(paint.style.stroke); mpaintline.setstrokewidth(20); mpaintline.setantialias(true); mpainttext = new paint(); mpainttext.setcolor(color.white); mpainttext.settextalign(paint.align.center);//向*对齐 mpainttext.settextsize(50); mpainttext.setantialias(true); path = new path(); pointcontainer = new hashmap<>(); pointerslipped = new arraylist<>(); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); width = getdefaultsize(getsuggestedminimumwidth(), widthmeasurespec); height = getdefaultsize(getsuggestedminimumheight(), heightmeasurespec); } private float pivotx;//触屏得到的x坐标 private float pivoty;//触屏得到的y坐标 private float selectedx;//当前选中的圆点的x坐标 private float selectedy;//当前选中的圆点的y坐标 private float selectedxold;//从前选中的圆点的x坐标 private float selectedyold;//从前选中的圆点的y坐标 private boolean ishasmoved = false;//用于判断path是否调用过moveto()方法 @override public boolean ontouchevent(motionevent event) { switch (event.getaction()) { case motionevent.action_down: pivotx = event.getx(); pivoty = event.gety(); //每次触屏时需要清空一下pointerslipped,即重置密码 pointerslipped.clear(); log.d("pointtouched", pivotx + "," + pivoty); getselectedpointindex(pivotx, pivoty); invalidate();//重绘 break; case motionevent.action_move: pivotx = event.getx(); pivoty = event.gety(); getselectedpointindex(pivotx, pivoty); invalidate(); break; case motionevent.action_up: /** * 当手指离开屏幕时,重置path */ path.reset(); ishasmoved = false; string indexsequence = ""; //打印出上一次手势密码的值 for(int index:pointerslipped){ indexsequence += "/"+index; } log.d("index",indexsequence); break; } invalidate(); return true; } /** * 得到并存储经过的圆点的序号 * @param pivotx * @param pivoty */ private void getselectedpointindex(float pivotx, float pivoty) { int index = 0; if (pivotx > patternmargin && pivotx < patternmargin + bigcircleradius * 2) { if (pivoty > height / 2 && pivoty < height / 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(1)[0]; selectedy = pointcontainer.get(1)[1]; index = 1; log.d("selectedpoint", selectedx + "," + selectedy); } else if (pivoty > height / 2 + added && pivoty < height / 2 + added + bigcircleradius * 2) { selectedx = pointcontainer.get(4)[0]; selectedy = pointcontainer.get(4)[1]; index = 4; } else if (pivoty > height / 2 + added * 2 && pivoty < height / 2 + added * 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(7)[0]; selectedy = pointcontainer.get(7)[1]; index = 7; } } else if (pivotx > patternmargin + added && pivotx < patternmargin + added + bigcircleradius * 2) { if (pivoty > height / 2 && pivoty < height / 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(2)[0]; selectedy = pointcontainer.get(2)[1]; index = 2; } else if (pivoty > height / 2 + added && pivoty < height / 2 + added + bigcircleradius * 2) { selectedx = pointcontainer.get(5)[0]; selectedy = pointcontainer.get(5)[1]; index = 5; } else if (pivoty > height / 2 + added * 2 && pivoty <height / 2 + added * 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(8)[0]; selectedy = pointcontainer.get(8)[1]; index = 8; } } else if (pivotx > patternmargin + added * 2 && pivotx < patternmargin + added * 2 + bigcircleradius * 2) { if (pivoty > height / 2 && pivoty < height / 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(3)[0]; selectedy = pointcontainer.get(3)[1]; index = 3; } else if (pivoty > height / 2 + added && pivoty < height / 2 + added + bigcircleradius * 2) { selectedx = pointcontainer.get(6)[0]; selectedy = pointcontainer.get(6)[1]; index = 6; } else if (pivoty > height / 2 + added * 2 && pivoty < height / 2 + added * 2 + bigcircleradius * 2) { selectedx = pointcontainer.get(9)[0]; selectedy = pointcontainer.get(9)[1]; index = 9; } } if (selectedx!=selectedxold||selectedy!=selectedyold){ //当这次的坐标与上次的坐标不同时存储这次点序号 pointerslipped.add(index); selectedxold = selectedx; selectedyold = selectedy; if (!ishasmoved){ //当第一次触碰到九个点之一时,path调用moveto; path.moveto(selectedx,selectedy); ishasmoved = true; }else{ //path移动至当前圆点坐标 path.lineto(selectedx,selectedy); } } } private string text = "请绘制解锁图案"; private float x;//绘制的圆形的x坐标 private float y;//绘制圆形的纵坐标 private float added;//水平竖直方向每个圆点中心间距 private float patternmargin = 100;//九宫格距离边界距离 private float bigcircleradius = 90;//外圆半径 private float smallcircleradius = 25;//内圆半径 private int index;//圆点的序号 @override protected void ondraw(canvas canvas) { super.ondraw(canvas); added = (width - patternmargin * 2) / 3; x = patternmargin + added / 2; y = added / 2 + height / 2; index = 1; canvas.drawcolor(color.black); canvas.drawtext(text, width / 2, height / 4, mpainttext); /** * 绘制九个圆点图案 */ for (int column = 0; column < 3; column++) { for (int row = 0; row < 3; row++) { canvas.drawcircle(x, y, bigcircleradius, mpaintbigcircle); canvas.drawcircle(x, y, smallcircleradius, mpaintsmallcircle); pointcontainer.put(index, new float[]{x, y}); index++; x += added; } y += added; x = patternmargin + added / 2; } x = patternmargin + added / 2; y = added / 2 + height / 2; canvas.drawpath(path, mpaintline); } }
为什么要规避重复?
因为在触屏时,会调用很多次ontouchevent()方法,这样存储的手势密码肯定会不准确,我在以上代码中作出了处理,已经避免了重复,看打印信息:
这里写图片描述
显然,密码没有相邻数重复,当然还有一种情况就是手指在两个点之间来回等问题,这种状况也需要避免,这里没有作处理。当然,我做得还不够。。。
自定义view中用到的dp和px互相转换的工具类:
public class sizeconvert { /** * 将dp转换为sp */ public static int dip2px(context context, float dipvalue){ final float scale = context.getresources().getdisplaymetrics().density; return (int)(dipvalue * scale + 0.5f); } /** * sp转dp */ public static int px2dip(context context, float pxvalue){ final float scale = context.getresources().getdisplaymetrics().density; return (int)(pxvalue / scale + 0.5f); } }
主活动:
public class ninegridactivity extends baseactivity{ @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.view_nine_grid); } }
layout中的布局文件view_nine_grid:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <com.test.shiweiwei.myproject.selfish_view.ninegridview android:layout_width="match_parent" android:layout_height="match_parent"/> </linearlayout>
总结
我写的只是最基本的九宫格滑动解密项目,实际用的九宫格解密比这个要复杂,有许多特效和其他更严谨的处理,事件的处理也不是这样草草了事,如果想写得漂亮,还得多花工夫。