早上看個(gè)bug泽艘,忘了吃飯,最后定位到ROM的問題镐依,反饋了匹涮。忙完了是時(shí)候享受了,現(xiàn)在戴上耳機(jī)馋吗,寫個(gè)清爽點(diǎn)的文章焕盟。雖然很簡(jiǎn)單,刷刷存在感也好啊宏粤,畢竟好久沒寫文章了脚翘。
先直接上效果吧。
如何使用呢绍哎?
(1)對(duì)象的獲取并設(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é)測(cè)量view的大小,在onDraw()中負(fù)責(zé)view的繪制海诲。
觀察效果為9個(gè)圓繁莹,在未點(diǎn)擊時(shí)為灰色,在點(diǎn)擊或者劃過的時(shí)候?qū)⒈尘白優(yōu)樗{(lán)色并畫連線特幔,在該圓外圍畫一個(gè)藍(lán)色線條的大圓咨演。確實(shí)很簡(jiǎn)單,但是在實(shí)際的過程中遇到了幾個(gè)問題蚯斯,分享一下薄风。
1.對(duì)象的存儲(chǔ)
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存儲(chǔ)用戶手勢(shì)的路徑遭赂,從點(diǎn)擊屏幕到手指抬起為止。在畫圓與圓之間的線條時(shí)又有所不同横辆,涉及到一個(gè)小知識(shí)點(diǎn)與大家分享下撇他。那就是Path的lineTo與setLastPoint方法的區(qū)別。先看下使用lineTo的效果。
因?yàn)閛nTouchEvent是個(gè)回調(diào)方法逆粹,會(huì)不停被系統(tǒng)回調(diào)募疮,所以如果用lineTo這個(gè)方法的話,因?yàn)樽鴺?biāo)不停地變會(huì)畫出曲線來僻弹,這個(gè)時(shí)候我們就需要用另外一個(gè)方法setLastPoint,這個(gè)會(huì)改變上一次繪制的點(diǎn)的位置阿浓,所以會(huì)畫出一條直線來。
3.如何判斷輸入是否正確
用一個(gè)StringBuilder對(duì)象保存用戶的輸入數(shù)據(jù)蹋绽,當(dāng)用戶手指抬起來時(shí)對(duì)比輸入的內(nèi)容與正確的密碼芭毙,并告知用戶。那么如何采集用戶的輸入呢卸耘?我們?cè)邳c(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è)里面最重要的就是事件的處理了,我們簡(jiǎn)單看看事件處理的代碼吧锭魔。
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事件才會(huì)被捕獲,如果返回false則不會(huì)被捕獲漠秋。
還有其他一些模塊簡(jiǎn)單介紹下笙蒙,屬于不重要的部分。
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與下載。