早上看個(gè)bug,忘了吃飯,最后定位到ROM的問題,反饋了。忙完了是時(shí)候享受了,現(xiàn)在戴上耳機(jī),寫個(gè)清爽點(diǎn)的文章。雖然很簡單,刷刷存在感也好啊,畢竟好久沒寫文章了。
先直接上效果吧。
如何使用呢?
(1)對象的獲取并設(shè)置正確的密碼
mUnLockView = (UnLockView) findViewById(R.id.unlockview);
mUnLockView.setmRightPsw("14789");
(2)然后在當(dāng)前Activity或者Fragment中實(shí)現(xiàn) UnLockView.ResponseInput接口。例子如下:
@Override
public void inputOK() {
//TODO
Toast.makeText(this, "密碼正確", Toast.LENGTH_SHORT).show();
}
@Override
public void inputErr() {
//TODO
Toast.makeText(this, "密碼錯(cuò)誤", Toast.LENGTH_SHORT).show();
}
自定義view(viewgroup)的步驟就是下面這個(gè)樣子,很官方呢。
我們的主要工作在onMeasure()和onDraw()中。在onMeasure()中負(fù)責(zé)測量view的大小,在onDraw()中負(fù)責(zé)view的繪制。
觀察效果為9個(gè)圓,在未點(diǎn)擊時(shí)為灰色,在點(diǎn)擊或者劃過的時(shí)候?qū)⒈尘白優(yōu)樗{(lán)色并畫連線,在該圓外圍畫一個(gè)藍(lán)色線條的大圓。確實(shí)很簡單,但是在實(shí)際的過程中遇到了幾個(gè)問題,分享一下。
1.對象的存儲
static class Circle{
private int x;//x坐標(biāo)
private int y;//y坐標(biāo)
private int innderRadius; //小圓半徑
private int outterRadius; //大圓半徑
private boolean isClicked; //是否點(diǎn)擊
}
當(dāng)某個(gè)圓被劃過或者被點(diǎn)擊的時(shí)候,將isClicked置為true。
2.線條的繪制
我用path存儲用戶手勢的路徑,從點(diǎn)擊屏幕到手指抬起為止。在畫圓與圓之間的線條時(shí)又有所不同,涉及到一個(gè)小知識點(diǎn)與大家分享下。那就是Path的lineTo與setLastPoint方法的區(qū)別。先看下使用lineTo的效果。
因?yàn)閛nTouchEvent是個(gè)回調(diào)方法,會不停被系統(tǒng)回調(diào),所以如果用lineTo這個(gè)方法的話,因?yàn)樽鴺?biāo)不停地變會畫出曲線來,這個(gè)時(shí)候我們就需要用另外一個(gè)方法setLastPoint,這個(gè)會改變上一次繪制的點(diǎn)的位置,所以會畫出一條直線來。
3.如何判斷輸入是否正確
用一個(gè)StringBuilder對象保存用戶的輸入數(shù)據(jù),當(dāng)用戶手指抬起來時(shí)對比輸入的內(nèi)容與正確的密碼,并告知用戶。那么如何采集用戶的輸入呢?我們在點(diǎn)擊圓或者劃過某個(gè)圓的時(shí)候?qū)A的下標(biāo)采集。
4.將密碼的判斷結(jié)果反饋給用戶
在UnLockView中有個(gè)接口ResponseInput,只需要在當(dāng)前的Activity或者Fragment中實(shí)現(xiàn)該接口即可。
public interface ResponseInput{
public void inputOK();
public void inputErr();
}
看下核心的代碼吧,發(fā)現(xiàn)要將一個(gè)東西說明白真的挺難的,讀書人的事就不要多說了,大家直接看代碼吧。
判斷點(diǎn)擊或者劃過的是哪個(gè)圓
public int getClickedIndex(float x,float y){
for(int i=0;i<circles.length;i++){
Circle cirlce = circles[i];
if( x >= cirlce.x - cirlce.outterRadius
&& x <= cirlce.x + cirlce.outterRadius
&& y<= cirlce.y + cirlce.outterRadius
&& y >= cirlce.y - cirlce.outterRadius){
return i;
}
}
return -1;
}
這個(gè)里面最重要的就是事件的處理了,我們簡單看看事件處理的代碼吧。
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:{
int index = getClickedIndex(event.getX(),event.getY());
if(index >= 0 && index <= circles.length){
//采集用戶輸入
gatherInput(index);
mPath.moveTo(circles[index].x,circles[index].y);
return true;
}else{
//TODO 第一次沒觸到任何塊則提示
return false;
}
}
case MotionEvent.ACTION_MOVE:{
float x = event.getX();
float y = event.getY();
int index = getClickedIndex(x,y);
if(index >= 0 && index < circles.length){
circles[index].isClicked = true;
//采集用戶輸入
gatherInput(index);
//這個(gè)地方是為了解決第一次點(diǎn)擊時(shí)畫點(diǎn)擊點(diǎn)與點(diǎn)(0,0)的bug
if(getClickedIndex(mNextX,mNextY) >= 0){
mPath.lineTo(circles[index].x,circles[index].y);
}else{
mPath.setLastPoint(circles[index].x,circles[index].y);
}
mNextX = circles[index].x;
mNextY = circles[index].y;
}else{
mNextX = x;
mNextY = y;
mPath.setLastPoint(mNextX,mNextY);
}
invalidate();
}break;
case MotionEvent.ACTION_UP:{
//TODO 判斷密碼是否正確
if(isInputOK()){
object.inputOK();
}else{
object.inputErr();
}
uninit();
}break;
}
return super.onTouchEvent(event);
}
從代碼可以看出來,在MotionEvent.ACTION_DOWN中,我們分別進(jìn)行了處理,那是因?yàn)槿绻鹯eturn true的話,之后的MotionEvent.ACTION_UP
與MotionEvent.ACTION_MOVE事件才會被捕獲,如果返回false則不會被捕獲。
還有其他一些模塊簡單介紹下,屬于不重要的部分。
1. 采集用戶的輸入
gatherInput()
2. 判斷輸入是否正確
isInputOK()
3. circles初始化
init()
4. 畫筆的初始化與設(shè)置
initResources(Context context)
3. circles反初始化(在MotionEvent.ACTION_UP時(shí)調(diào)用將circles的isClicked置為false,path清空,input數(shù)據(jù)清空)
uninit()。
代碼在git@github.com:rainyandsunny/GestureUnLock.git,歡迎star與下載。