自定義view——仿京東手勢解鎖
先直接上效果吧。
京東金融手勢解鎖
如何使用呢?
(1)對象的獲取并設置正確的密碼
mUnLockView = (UnLockView) findViewById(R.id.unlockview);
mUnLockView.setmRightPsw("14789");
(2)然后在當前Activity或者Fragment中實現 UnLockView.ResponseInput接口。例子如下:
@Override
public void inputOK() {
//TODO
Toast.makeText(this, "密碼正確", Toast.LENGTH_SHORT).show();
}
@Override
public void inputErr() {
//TODO
Toast.makeText(this, "密碼錯誤", Toast.LENGTH_SHORT).show();
}
自定義view(viewgroup)的步驟就是下面這個樣子,很官方呢。
我們的主要工作在onMeasure()和onDraw()中。在onMeasure()中負責測量view的大小,在onDraw()中負責view的繪制。
觀察效果為9個圓,在未點擊時為灰色,在點擊或者劃過的時候將背景變為藍色并畫連線,在該圓外圍畫一個藍色線條的大圓。確實很簡單,但是在實際的過程中遇到了幾個問題,分享一下。
1.對象的存儲
static class Circle{
private int x;//x坐標
private int y;//y坐標
private int innderRadius; //小圓半徑
private int outterRadius; //大圓半徑
private boolean isClicked; //是否點擊
}
當某個圓被劃過或者被點擊的時候,將isClicked置為true。
2.線條的繪制
我用path存儲用戶手勢的路徑,從點擊屏幕到手指抬起為止。在畫圓與圓之間的線條時又有所不同,涉及到一個小知識點與大家分享下。那就是Path的lineTo與setLastPoint方法的區別。先看下使用lineTo的效果。
因為onTouchEvent是個回調方法,會不停被系統回調,所以如果用lineTo這個方法的話,因為坐標不停地變會畫出曲線來,這個時候我們就需要用另外一個方法setLastPoint,這個會改變上一次繪制的點的位置,所以會畫出一條直線來。
3.如何判斷輸入是否正確
用一個StringBuilder對象保存用戶的輸入數據,當用戶手指抬起來時對比輸入的內容與正確的密碼,并告知用戶。那么如何采集用戶的輸入呢?我們在點擊圓或者劃過某個圓的時候將圓的下標采集。
4.將密碼的判斷結果反饋給用戶
在UnLockView中有個接口ResponseInput,只需要在當前的Activity或者Fragment中實現該接口即可。
public interface ResponseInput{
public void inputOK();
public void inputErr();
}
看下核心的代碼吧,發現要將一個東西說明白真的挺難的,讀書人的事就不要多說了,大家直接看代碼吧。
判斷點擊或者劃過的是哪個圓
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;
}
這個里面最重要的就是事件的處理了,我們簡單看看事件處理的代碼吧。
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);
//這個地方是為了解決第一次點擊時畫點擊點與點(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中,我們分別進行了處理,那是因為如果return true的話,之后的MotionEvent.ACTION_UP
與MotionEvent.ACTION_MOVE事件才會被捕獲,如果返回false則不會被捕獲。
還有其他一些模塊簡單介紹下,屬于不重要的部分。
1. 采集用戶的輸入
gatherInput()
2. 判斷輸入是否正確
isInputOK()
3. circles初始化
init()
4. 畫筆的初始化與設置
initResources(Context context)
3. circles反初始化(在MotionEvent.ACTION_UP時調用將circles的isClicked置為false,path清空,input數據清空)
uninit()。