欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 仿小米锁屏实现九宫格解锁功能(无需图片资源)

程序员文章站 2024-02-26 21:19:04
 最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现。 尊重每个辛苦的博主,在http:/...

 最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现。

尊重每个辛苦的博主,在http://blog.csdn.net/mu399/article/details/38734449的基础上进行修改

效果图:

Android 仿小米锁屏实现九宫格解锁功能(无需图片资源)

关键代码类:

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 仿小米锁屏实现九宫格解锁功能(无需图片资源),希望对大家有所帮助