Android 仿小米锁屏实现九宫格解锁功能(无需图片资源)
程序员文章站
2024-02-26 21:19:04
最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现。
尊重每个辛苦的博主,在http:/...
最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现。
尊重每个辛苦的博主,在http://blog.csdn.net/mu399/article/details/38734449的基础上进行修改
效果图:
关键代码类:
mathutil.java
/** * @author soban * @create 2016/12/5 15:52. */ public class mathutil { public static double distance(double x1, double y1, double x2, double y2) { return math.sqrt(math.abs(x1 - x2) * math.abs(x1 - x2) + math.abs(y1 - y2) * math.abs(y1 - y2)); } public static double pointtotodegrees(double x, double y) { return math.todegrees(math.atan2(x, y)); } public static boolean checkinround(float sx, float sy, float r, float x, float y) { return math.sqrt((sx - x) * (sx - x) + (sy - y) * (sy - y)) < r; } }
point.java
/** * @author soban * @create 2016/12/5 15:51. */ public class point { public static int state_normal = 0; public static int state_check = 1; // public static int state_check_error = 2; // public float x; public float y; public int state = 0; public int index = 0;// public point() { } public point(float x, float y, int value) { this.x = x; this.y = y; index = value; } public int getcolnum() { return (index - 1) % 3; } public int getrownum() { return (index - 1) / 3; } }
locuspasswordview.java
import android.content.context; import android.graphics.canvas; import android.graphics.paint; import android.graphics.paint.style; import android.text.textutils; import android.util.attributeset; import android.util.log; import android.view.motionevent; import android.view.view; import java.util.arraylist; import java.util.list; import java.util.timer; import java.util.timertask; /** * @author soban * @create 2016/12/5 15:49. */ public class locuspasswordview extends view { /** * 控件的宽高 */ private float width = 0; private float height = 0; private boolean iscache = false; //缓存pwdmaxlen个点 private paint mpaint = new paint(paint.anti_alias_flag); private point[][] mpoints = new point[3][3]; private float dotradius = 0; //选择>pwdminlen的点 private list<point> spoints = new arraylist<point>(); private boolean checking = false; private long clear_time = 1000; private int pwdmaxlen = 9; private int pwdminlen = 4; private boolean istouch = true; private paint linepaint; private paint normalpaint; private paint selectedpaint; private paint errorpaint; private int normaldotcolor = 0xff929292; private int selectedcolor = 0xffc3c3c3; private int selectedlinecolor = 0xffededed; private int errorcolor = 0xfff34b2a; private int errorlinecolor = 0xffeebfb6; public locuspasswordview(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); } public locuspasswordview(context context, attributeset attrs) { super(context, attrs); } public locuspasswordview(context context) { super(context); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); int width = measuredimension(200, widthmeasurespec); int height = measuredimension(200, heightmeasurespec); if (width > height) { setmeasureddimension(height, height); } else { setmeasureddimension(width, width); } } public int measuredimension(int defaultsize, int measurespec) { int result; int specmode = measurespec.getmode(measurespec); int specsize = measurespec.getsize(measurespec); if (specmode == measurespec.exactly) { result = specsize; } else { result = defaultsize; //unspecified if (specmode == measurespec.at_most) { result = math.min(result, specsize); } } return result; } @override public void ondraw(canvas canvas) { if (!iscache) { initcache(); } drawtocanvas(canvas); } private void drawtocanvas(canvas canvas) { boolean inerrorstate = false; float radiu = dotradius / 16; //圆的半径 //画点 for (int i = 0; i < mpoints.length; i++) { for (int j = 0; j < mpoints[i].length; j++) { point p = mpoints[i][j]; if (p.state == point.state_check) { selectedpaint.setcolor(selectedcolor); canvas.drawcircle(p.x, p.y, radiu, selectedpaint); } else if (p.state == point.state_check_error) { inerrorstate = true; errorpaint.setcolor(errorcolor); canvas.drawcircle(p.x, p.y, radiu, errorpaint); } else { normalpaint.setcolor(normaldotcolor); canvas.drawcircle(p.x, p.y, radiu, normalpaint); } } } if (inerrorstate) { linepaint.setcolor(errorlinecolor); } else { linepaint.setcolor(selectedlinecolor); } //画线 if (spoints.size() > 0) { int tmpalpha = mpaint.getalpha(); point tp = spoints.get(0); for (int i = 1; i < spoints.size(); i++) { point p = spoints.get(i); drawline(tp, p, canvas, linepaint); tp = p; } if (this.movingnopoint) { drawline(tp, new point(moveingx, moveingy, -1), canvas, linepaint); } mpaint.setalpha(tmpalpha); } } /** * 画线 * @param start * @param end * @param canvas * @param paint */ private void drawline(point start, point end, canvas canvas, paint paint) { float radiu = dotradius / 16; //圆的半径 double d = mathutil.distance(start.x, start.y, end.x, end.y); float rx = (float) ((end.x - start.x) * radiu / d); float ry = (float) ((end.y - start.y) * radiu / d); canvas.drawline(start.x + rx, start.y + ry, end.x - rx, end.y - ry, paint); } /** * 缓存控件宽高跟点个位置 */ private void initcache() { width = this.getwidth(); height = this.getheight(); float x = 0; float y = 0; if (width > height) { x = (width - height) / 2; width = height; } else { y = (height - width) / 2; height = width; } int leftpadding = 15; float dotpadding = width / 3 - leftpadding; float middlex = width / 2; float middley = height / 2; mpoints[0][0] = new point(x + middlex - dotpadding, y + middley - dotpadding, 1); mpoints[0][1] = new point(x + middlex, y + middley - dotpadding, 2); mpoints[0][2] = new point(x + middlex + dotpadding, y + middley - dotpadding, 3); mpoints[1][0] = new point(x + middlex - dotpadding, y + middley, 4); mpoints[1][1] = new point(x + middlex, y + middley, 5); mpoints[1][2] = new point(x + middlex + dotpadding, y + middley, 6); mpoints[2][0] = new point(x + middlex - dotpadding, y + middley + dotpadding, 7); mpoints[2][1] = new point(x + middlex, y + middley + dotpadding, 8); mpoints[2][2] = new point(x + middlex + dotpadding, y + middley + dotpadding, 9); log.d("jerome", "canvas width:" + width); dotradius = width / 10; iscache = true; initpaints(); } private void initpaints() { linepaint = new paint(); linepaint.setcolor(selectedcolor); linepaint.setstyle(style.fill); linepaint.setantialias(true); linepaint.setstrokewidth(dotradius / 9); selectedpaint = new paint(); selectedpaint.setstyle(style.fill); selectedpaint.setantialias(true); selectedpaint.setstrokewidth(dotradius / 6); errorpaint = new paint(); errorpaint.setstyle(style.fill); errorpaint.setantialias(true); errorpaint.setstrokewidth(dotradius / 6); normalpaint = new paint(); normalpaint.setstyle(style.fill); normalpaint.setantialias(true); normalpaint.setstrokewidth(dotradius / 9); } /** * 检查 * * @param x * @param y * @return */ private point checkselectpoint(float x, float y) { for (int i = 0; i < mpoints.length; i++) { for (int j = 0; j < mpoints[i].length; j++) { point p = mpoints[i][j]; if (mathutil.checkinround(p.x, p.y, dotradius, (int) x, (int) y)) { return p; } } } return null; } /** * 重置 */ private void reset() { for (point p : spoints) { p.state = point.state_normal; } spoints.clear(); this.enabletouch(); } /** * 判断点是否有交叉 返回 0,新点 ,1 与上一点重叠 2,与非最后一点重叠 * * @param p * @return */ private int crosspoint(point p) { // 重叠的不最后一个则 reset if (spoints.contains(p)) { if (spoints.size() > 2) { // 与非最后一点重叠 if (spoints.get(spoints.size() - 1).index != p.index) { return 2; } } return 1; // 与最后一点重叠 } else { return 0; // 新点 } } /** * 添加一个点 * * @param point */ private void addpoint(point point) { if (spoints.size() > 0) { point lastpoint = spoints.get(spoints.size() - 1); int dx = math.abs(lastpoint.getcolnum() - point.getcolnum()); int dy = math.abs(lastpoint.getrownum() - point.getrownum()); if ((dx > 1 || dy > 1) && (dx == 0 || dy == 0 || dx == dy)) { // if ((dx > 1 || dy > 1) && (dx != 2 * dy) && (dy != 2 * dx)) { int middleindex = (point.index + lastpoint.index) / 2 - 1; point middlepoint = mpoints[middleindex / 3][middleindex % 3]; if (middlepoint.state != point.state_check) { middlepoint.state = point.state_check; spoints.add(middlepoint); } } } this.spoints.add(point); } /** * 转换为string */ private string topointstring() { if (spoints.size() >= pwdminlen && spoints.size() <= pwdmaxlen) { stringbuffer sf = new stringbuffer(); for (point p : spoints) { sf.append(p.index); } return sf.tostring(); } else { return ""; } } boolean movingnopoint = false; float moveingx, moveingy; @override public boolean ontouchevent(motionevent event) { // 不可操作 if (!istouch) { return false; } movingnopoint = false; float ex = event.getx(); float ey = event.gety(); boolean isfinish = false; boolean redraw = false; point p = null; switch (event.getaction()) { case motionevent.action_down: //点下 // 如果正在清除密码,则取消 if (task != null) { task.cancel(); task = null; log.d("task", "touch cancel()"); } // 删除之前的点 reset(); p = checkselectpoint(ex, ey); if (p != null) { checking = true; } mcompletelistener.onprompt("完成后松开手指"); break; case motionevent.action_move:// 移动 if (checking) { p = checkselectpoint(ex, ey); if (p == null) { movingnopoint = true; moveingx = ex; moveingy = ey; } } break; case motionevent.action_up:// 提起 p = checkselectpoint(ex, ey); checking = false; isfinish = true; break; } if (!isfinish && checking && p != null) { int rk = crosspoint(p); if (rk == 2) {// 与非最后一重叠 // reset(); // checking = false; movingnopoint = true; moveingx = ex; moveingy = ey; redraw = true; } else if (rk == 0) {// 一个新点 p.state = point.state_check; addpoint(p); redraw = true; } // rk == 1 } // 是否重画 if (redraw) { } if (isfinish) { if (this.spoints.size() == 1) { this.reset(); isfirstpwdempty(); } else if (spoints.size() > 0 && spoints.size() < pwdminlen) { error(); clearpassword(); isfirstpwdempty(); } else if (mcompletelistener != null) { if (this.spoints.size() >= pwdminlen) { this.disabletouch(); ispwdequal(); } } } this.postinvalidate(); return true; } private void isfirstpwdempty() { if (textutils.isempty(firstpassword)) { mcompletelistener.onprompt("至少需连接4个点,请重试。"); } else { mcompletelistener.onprompt("请重试"); } } private void ispwdequal() { if (textutils.isempty(firstpassword)) { mcompletelistener.onprompt("再次绘制图案进行确认"); mcompletelistener.oncomplete(topointstring()); } else { if (firstpassword.equals(topointstring())) { mcompletelistener.onprompt("您的新解锁图案"); mcompletelistener.oncomplete(topointstring()); } else { mcompletelistener.onprompt("请重试"); error(); clearpassword(); } } } /** * 设置已经选中的为错误 */ private void error() { for (point p : spoints) { p.state = point.state_check_error; } } /** * 设置为输入错误 */ public void markerror() { markerror(clear_time); } /** * 设置为输入错误 */ public void markerror(final long time) { for (point p : spoints) { p.state = point.state_check_error; } this.clearpassword(time); } /** * 设置为可操作 */ public void enabletouch() { istouch = true; } /** * 设置为不可操作 */ public void disabletouch() { istouch = false; } private timer timer = new timer(); private timertask task = null; /** * 清除密码 */ public void clearpassword() { clearpassword(clear_time); } /** * 清除密码 */ public void clearpassword(final long time) { if (time > 1) { if (task != null) { task.cancel(); log.d("task", "clearpassword cancel()"); } postinvalidate(); task = new timertask() { public void run() { reset(); postinvalidate(); } }; log.d("task", "clearpassword schedule(" + time + ")"); timer.schedule(task, time); } else { reset(); postinvalidate(); } } private string firstpassword; public string getfirstpassword() { return firstpassword; } public void setfirstpassword(string firstpassword) { this.firstpassword = firstpassword; } private oncompletelistener mcompletelistener; public void setoncompletelistener(oncompletelistener mcompletelistener) { this.mcompletelistener = mcompletelistener; } public interface oncompletelistener { void oncomplete(string password); //密码 void onprompt(string prompt); //提示信息 } }
示例:可支持重绘,需要二次设置密码。
mainactivity.class
import android.app.activity; import android.os.bundle; import android.util.log; import android.view.view; import android.widget.button; import android.widget.textview; import android.widget.toast; import soban.ninelockscreen.r; public class mainactivity extends activity implements view.onclicklistener, locuspasswordview.oncompletelistener { private string tag = main2activity.class.getname(); private textview mexplaintv; private button mrepaintbtn; private button mconfirmbtn; private locuspasswordview mpwdview; private string firstpassword; private string againpassword; private boolean isfirst; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initviews(); } private void initviews() { mexplaintv = (textview) findviewbyid(r.id.tv_explain); mrepaintbtn = (button) findviewbyid(r.id.btn_repaint); mconfirmbtn = (button) findviewbyid(r.id.btn_confirm); mpwdview = (locuspasswordview) findviewbyid(r.id.mpasswordview2); mrepaintbtn.setonclicklistener(this); mconfirmbtn.setonclicklistener(this); mpwdview.setoncompletelistener(this); initchoose(); } @override public void onclick(view view) { switch (view.getid()) { case r.id.btn_repaint: repaint(); break; case r.id.btn_confirm: confirm(); break; } } private void repaint() { mpwdview.clearpassword(0); initchoose(); } private void confirm() { toast.maketext(mainactivity.this, "你设置的密码:" + againpassword, toast.length_short).show(); } @override public void oncomplete(string password) { if (isfirst) { firstchoose(password); } else { secondchoose(password); } log.e(tag, "oncomplete -> " + password); } @override public void onprompt(string prompt) { mexplaintv.settext(prompt); } private void initchoose() { isfirst = true; firstpassword = ""; againpassword = ""; mpwdview.setfirstpassword(""); mrepaintbtn.setvisibility(view.gone); mconfirmbtn.setvisibility(view.gone); } private void firstchoose(string password) { isfirst = false; firstpassword = password; mpwdview.setfirstpassword(password); mpwdview.clearpassword(0); mrepaintbtn.setenabled(true); mconfirmbtn.setenabled(false); mrepaintbtn.setvisibility(view.visible); mconfirmbtn.setvisibility(view.visible); } private void secondchoose(string password) { isfirst = true; againpassword = password; mrepaintbtn.setenabled(true); mconfirmbtn.setenabled(true); } }
布局:activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:orientation="vertical"> <linearlayout android:id="@+id/bottomlayout" android:layout_width="match_parent" android:layout_height="50dip" android:layout_alignparentbottom="true" android:orientation="horizontal"> <button android:id="@+id/btn_repaint" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="重绘" /> <button android:id="@+id/btn_confirm" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="确认" /> </linearlayout> <soban.ninelockscreen.demo.locuspasswordview android:id="@+id/mpasswordview2" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/bottomlayout" /> <textview android:id="@+id/tv_explain" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/mpasswordview2" android:layout_alignparenttop="true" android:background="@color/colorprimary" android:gravity="center" android:text="绘制解锁图案,请至少连接4个点" android:textcolor="#ffffff" /> </relativelayout>
以上所述是小编给大家介绍的android 仿小米锁屏实现九宫格解锁功能(无需图片资源),希望对大家有所帮助