編輯:關於Android編程
Android手勢解鎖密碼效果圖
首先呢想寫這個手勢密碼的想法呢,完全是憑空而來的,然後筆者就花了一天時間弄出來了。本以為這個東西很簡單,實際上手的時候發現,還有很多邏輯需要處理,稍不注意就容易亂套。寫個UI效果圖大約只花了3個小時,但是處理邏輯就處理了2個小時!廢話不多說,下面開始講解。
樓主呢,自己比較自定義控件,什麼東西都掌握在自己的手裡感覺那是相當不錯(對於趕工期的小伙瓣兒們還是別手賤了,非常容易掉坑),一有了這個目標,我就開始構思實現方式。
1、整個自定義控件是繼承View還是SurfaceView呢?我的經驗告訴我:需要一直不斷繪制的最好繼承SurfaceView,而需要頻繁與用戶交互的最好就繼承View。(求大神來打臉)
2、為了實現控件的屏幕適配性,當然必須重寫onMeasure方法,然後在onDraw方法中進行繪制。
3、面向對象性:這個控件其實由兩個對象組成:1、9個圓球;2、圓球之間的連線。
4、仔細觀察圓球的特征:普通狀態是白色、touch狀態是藍色、錯誤狀態是紅色、整體分為外圍空心圓和內實心圓、所代表的位置信息(密碼值)
5、仔細觀察連線的特征:普通狀態為藍色、錯誤狀態為紅色、始終連接兩個圓的中心、跟隨手指移動而拓展連線、連線之間未點亮的圓球也要點亮。
6、通過外露參數來設置圓球的顏色、大小等等
7、通過上面的分析,真個控件可模塊化為三個任務:onMeasure計算控件寬高以及小球半徑、onDraw繪制小球與連線、onTouchEvent控制繪制變化。
我把整個源碼分為三個類文件:LockView、Circle、Util,其中LockView代表整個控件,Circle代表小圓球、Util封裝工具方法(Path因為太簡單就沒封裝,若有代碼潔癖請自行封裝),下面展示Util類的源代碼。
public class Util{ private static final String SP_NAME = "LOCKVIEW"; private static final String SP_KEY = "PASSWORD"; public static void savePwd(Context mContext ,List<Integer> password){ SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); sp.edit().putString(SP_KEY, listToString(password)).commit(); } public static String getPwd(Context mContext){ SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); return sp.getString(SP_KEY, ""); } public static void clearPwd(Context mContext){ SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); sp.edit().remove(SP_KEY).commit(); } public static String listToString(List<Integer> lists){ StringBuffer sb = new StringBuffer(); for(int i = 0; i < lists.size(); i++){ sb.append(lists.get(i)); } return sb.toString(); } public static List<Integer> stringToList(String string){ List<Integer> lists = new ArrayList<>(); for(int i = 0; i < string.length(); i++){ lists.add(Integer.parseInt(string.charAt(i) + "")); } return lists; } }
這個工具方法其實很簡單,就是對SharedPreferences的一個讀寫,還有就是List與String類型的互相轉換。這裡就不描述了。下面展示Circle的源碼
public class Circle{ //默認值 public static final int DEFAULT_COLOR = Color.WHITE; public static final int DEFAULT_BOUND = 5; public static final int DEFAULT_CENTER_BOUND = 15; //狀態值 public static final int STATUS_DEFAULT = 0; public static final int STATUS_TOUCH = 1; public static final int STATUS_SUCCESS = 2; public static final int STATUS_FAILED = 3; //圓形的中點X、Y坐標 private int centerX; private int centerY; //圓形的顏色值 private int colorDefault = DEFAULT_COLOR; private int colorSuccess; private int colorFailed; //圓形的寬度 private int bound = DEFAULT_BOUND; //中心的寬度 private int centerBound = DEFAULT_CENTER_BOUND; //圓形的半徑 private int radius; //圓形的狀態 private int status = STATUS_DEFAULT; //圓形的位置 private int position; public Circle(int centerX, int centerY, int colorSuccess, int colorFailed, int radius, int position){ super(); this.centerX = centerX; this.centerY = centerY; this.colorSuccess = colorSuccess; this.colorFailed = colorFailed; this.radius = radius; this.position = position; } public Circle(int centerX, int centerY, int colorDefault, int colorSuccess, int colorFailed, int bound, int centerBound, int radius, int status, int position){ super(); this.centerX = centerX; this.centerY = centerY; this.colorDefault = colorDefault; this.colorSuccess = colorSuccess; this.colorFailed = colorFailed; this.bound = bound; this.centerBound = centerBound; this.radius = radius; this.status = status; this.position = position; } public int getCenterX(){ return centerX; } public void setCenterX(int centerX){ this.centerX = centerX; } public int getCenterY(){ return centerY; } public void setCenterY(int centerY){ this.centerY = centerY; } public int getColorDefault(){ return colorDefault; } public void setColorDefault(int colorDefault){ this.colorDefault = colorDefault; } public int getColorSuccess(){ return colorSuccess; } public void setColorSuccess(int colorSuccess){ this.colorSuccess = colorSuccess; } public int getColorFailed(){ return colorFailed; } public void setColorFailed(int colorFailed){ this.colorFailed = colorFailed; } public int getBound(){ return bound; } public void setBound(int bound){ this.bound = bound; } public int getCenterBound(){ return centerBound; } public void setCenterBound(int centerBound){ this.centerBound = centerBound; } public int getRadius(){ return radius; } public void setRadius(int radius){ this.radius = radius; } public int getStatus(){ return status; } public void setStatus(int status){ this.status = status; } public int getPosition(){ return position; } public void setPosition(int position){ this.position = position; } /** * @Description:改變圓球當前狀態 */ public void changeStatus(int status){ this.status = status; } /** * @Description:繪制這個圓形 */ public void draw(Canvas canvas ,Paint paint){ switch(status){ case STATUS_DEFAULT: paint.setColor(colorDefault); break; case STATUS_TOUCH: case STATUS_SUCCESS: paint.setColor(colorSuccess); break; case STATUS_FAILED: paint.setColor(colorFailed); break; default: paint.setColor(colorDefault); break; } paint.setStyle(Paint.Style.FILL); //繪制中心實心圓 canvas.drawCircle(centerX, centerY, centerBound, paint); //繪制空心圓 paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(bound); canvas.drawCircle(centerX, centerY, radius, paint); } }
這個Circle其實也非常簡單。上面定義的成員變量一眼便明,並且有注釋。重點在最後的draw方法,首先呢根據當前圓球的不同狀態設置不同的顏色值,然後繪制中心的實心圓,再繪制外圍的空心圓。所有的參數要麼是外界傳遞,要麼是默認值。(ps:面向對象真的非常有用,解耦良好的代碼寫起來也舒服看起來也舒服)。
最後的重點來了,LockView的源碼,首先貼源碼,然後再針對性講解。
public class LockView extends View{ private static final int COUNT_PER_RAW = 3; private static final int DURATION = 1500; private static final int MIN_PWD_NUMBER = 6; //@Fields STATUS_NO_PWD : 當前沒有保存密碼 public static final int STATUS_NO_PWD = 0; //@Fields STATUS_RETRY_PWD : 需要再輸入一次密碼 public static final int STATUS_RETRY_PWD = 1; //@Fields STATUS_SAVE_PWD : 成功保存密碼 public static final int STATUS_SAVE_PWD = 2; //@Fields STATUS_SUCCESS_PWD : 成功驗證密碼 public static final int STATUS_SUCCESS_PWD = 3; //@Fields STATUS_FAILED_PWD : 驗證密碼失敗 public static final int STATUS_FAILED_PWD = 4; //@Fields STATUS_ERROR : 輸入密碼長度不夠 public static final int STATUS_ERROR = 5; private int width; private int height; private int padding = 0; private int colorSuccess = Color.BLUE; private int colorFailed = Color.RED; private int minPwdNumber = MIN_PWD_NUMBER; private List<Circle> circles = new ArrayList<>(); private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Path mPath = new Path(); private Path backupsPath = new Path(); private List<Integer> result = new ArrayList<>(); private int status = STATUS_NO_PWD; private OnLockListener listener; private Handler handler = new Handler(); public LockView(Context context, AttributeSet attrs, int defStyle){ super(context, attrs, defStyle); initStatus(); } public LockView(Context context, AttributeSet attrs){ super(context, attrs); initStatus(); } public LockView(Context context){ super(context); initStatus(); } /** * @Description:初始化當前密碼的狀態 */ public void initStatus(){ if(TextUtils.isEmpty(Util.getPwd(getContext()))){ status = STATUS_NO_PWD; }else{ status = STATUS_SAVE_PWD; } } public int getCurrentStatus(){ return status; } /** * @Description:初始化參數,若不調用則使用默認值 * @param padding 圓球之間的間距 * @param colorSuccess 密碼正確時圓球的顏色 * @param colorFailed 密碼錯誤時圓球的顏色 * @return LockView */ public LockView initParam(int padding ,int colorSuccess ,int colorFailed ,int minPwdNumber){ this.padding = padding; this.colorSuccess = colorSuccess; this.colorFailed = colorFailed; this.minPwdNumber = minPwdNumber; init(); return this; } /** * @Description:若第一次調用則創建圓球,否則更新圓球 */ private void init(){ int circleRadius = (width - (COUNT_PER_RAW + 1) * padding) / COUNT_PER_RAW /2; if(circles.size() == 0){ for(int i = 0; i < COUNT_PER_RAW * COUNT_PER_RAW; i++){ createCircles(circleRadius, i); } }else{ for(int i = 0; i < COUNT_PER_RAW * COUNT_PER_RAW; i++){ updateCircles(circles.get(i), circleRadius); } } } private void createCircles(int radius, int position){ int centerX = (position % 3 + 1) * padding + (position % 3 * 2 + 1) * radius; int centerY = (position / 3 + 1) * padding + (position / 3 * 2 + 1) * radius; Circle circle = new Circle(centerX, centerY, colorSuccess, colorFailed, radius, position); circles.add(circle); } private void updateCircles(Circle circle ,int radius){ int centerX = (circle.getPosition() % 3 + 1) * padding + (circle.getPosition() % 3 * 2 + 1) * radius; int centerY = (circle.getPosition() / 3 + 1) * padding + (circle.getPosition() / 3 * 2 + 1) * radius; circle.setCenterX(centerX); circle.setCenterY(centerY); circle.setRadius(radius); circle.setColorSuccess(colorSuccess); circle.setColorFailed(colorFailed); } @Override protected void onDraw(Canvas canvas){ init(); //繪制圓 for(int i = 0; i < circles.size() ;i++){ circles.get(i).draw(canvas, mPaint); } if(result.size() != 0){ //繪制Path Circle temp = circles.get(result.get(0)); mPaint.setColor(temp.getStatus() == Circle.STATUS_FAILED ? colorFailed : colorSuccess); mPaint.setStrokeWidth(Circle.DEFAULT_CENTER_BOUND); canvas.drawPath(mPath, mPaint); } } @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()){ case MotionEvent.ACTION_DOWN: backupsPath.reset(); for(int i = 0; i < circles.size() ;i++){ Circle circle = circles.get(i); if(event.getX() >= circle.getCenterX() - circle.getRadius() && event.getX() <= circle.getCenterX() + circle.getRadius() && event.getY() >= circle.getCenterY() - circle.getRadius() && event.getY() <= circle.getCenterY() + circle.getRadius()){ circle.setStatus(Circle.STATUS_TOUCH); //將這個點放入Path backupsPath.moveTo(circle.getCenterX(), circle.getCenterY()); //放入結果 result.add(circle.getPosition()); break; } } invalidate(); return true; case MotionEvent.ACTION_MOVE: for(int i = 0; i < circles.size() ;i++){ Circle circle = circles.get(i); if(event.getX() >= circle.getCenterX() - circle.getRadius() && event.getX() <= circle.getCenterX() + circle.getRadius() && event.getY() >= circle.getCenterY() - circle.getRadius() && event.getY() <= circle.getCenterY() + circle.getRadius()){ if(!result.contains(circle.getPosition())){ circle.setStatus(Circle.STATUS_TOUCH); //首先判斷是否連線中間也有滿足條件的圓 Circle lastCircle = circles.get(result.get(result.size() - 1)); int cx = (lastCircle.getCenterX() + circle.getCenterX()) / 2; int cy = (lastCircle.getCenterY() + circle.getCenterY()) / 2; for(int j = 0; j < circles.size(); j++){ Circle tempCircle = circles.get(j); if(cx >= tempCircle.getCenterX() - tempCircle.getRadius() && cx <= tempCircle.getCenterX() + tempCircle.getRadius() && cy >= tempCircle.getCenterY() - tempCircle.getRadius() && cy <= tempCircle.getCenterY() + tempCircle.getRadius()){ //處理滿足條件的圓 backupsPath.lineTo(tempCircle.getCenterX(), tempCircle.getCenterY()); //放入結果 tempCircle.setStatus(Circle.STATUS_TOUCH); result.add(tempCircle.getPosition()); } } //處理現在的圓 backupsPath.lineTo(circle.getCenterX(), circle.getCenterY()); //放入結果 circle.setStatus(Circle.STATUS_TOUCH); result.add(circle.getPosition()); break; } } } mPath.reset(); mPath.addPath(backupsPath); mPath.lineTo(event.getX(), event.getY()); invalidate(); break; case MotionEvent.ACTION_UP: mPath.reset(); mPath.addPath(backupsPath); invalidate(); if(result.size() < minPwdNumber){ if(listener != null){ listener.onError(); } if(status == STATUS_RETRY_PWD){ Util.clearPwd(getContext()); } status = STATUS_ERROR; for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } }else{ if(status == STATUS_NO_PWD){ //當前沒有密碼 //保存密碼,重新錄入 Util.savePwd(getContext(), result); status = STATUS_RETRY_PWD; if(listener != null){ listener.onTypeInOnce(Util.listToString(result)); } }else if(status == STATUS_RETRY_PWD){ //需要重新繪制密碼 //判斷兩次輸入是否相等 if(Util.getPwd(getContext()).equals(Util.listToString(result))){ status = STATUS_SAVE_PWD; if(listener != null){ listener.onTypeInTwice(Util.listToString(result), true); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_SUCCESS); } }else{ status = STATUS_NO_PWD; Util.clearPwd(getContext()); if(listener != null){ listener.onTypeInTwice(Util.listToString(result), false); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } } }else if(status == STATUS_SAVE_PWD){ //驗證密碼 //判斷密碼是否正確 if(Util.getPwd(getContext()).equals(Util.listToString(result))){ status = STATUS_SUCCESS_PWD; if(listener != null){ listener.onUnLock(Util.listToString(result), true); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_SUCCESS); } }else{ status = STATUS_FAILED_PWD; if(listener != null){ listener.onUnLock(Util.listToString(result), false); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } } } } invalidate(); handler.postDelayed(new Runnable(){ @Override public void run(){ result.clear(); mPath.reset(); backupsPath.reset(); // initStatus(); // 重置下狀態 if(status == STATUS_SUCCESS_PWD || status == STATUS_FAILED_PWD){ status = STATUS_SAVE_PWD; }else if(status == STATUS_ERROR){ initStatus(); } for(int i = 0; i < circles.size(); i++){ circles.get(i).setStatus(Circle.STATUS_DEFAULT); } invalidate(); } }, DURATION); break; default: break; } return super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ width = MeasureSpec.getSize(widthMeasureSpec); height = width - getPaddingLeft() - getPaddingRight() + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); } public void setOnLockListener(OnLockListener listener){ this.listener = listener; } public interface OnLockListener{ /** * @Description:沒有密碼時,第一次錄入密碼觸發器 */ void onTypeInOnce(String input); /** * @Description:已經錄入第一次密碼,錄入第二次密碼觸發器 */ void onTypeInTwice(String input ,boolean isSuccess); /** * @Description:驗證密碼觸發器 */ void onUnLock(String input ,boolean isSuccess); /** * @Description:密碼長度不夠 */ void onError(); } }
好了,逐次講解。
首先是對status的初始化,其實在static域我已經申明了6個狀態,分別是:
//當前沒有保存密碼 public static final int STATUS_NO_PWD = 0; //需要再輸入一次密碼 public static final int STATUS_RETRY_PWD = 1; //成功保存密碼 public static final int STATUS_SAVE_PWD = 2; //成功驗證密碼 public static final int STATUS_SUCCESS_PWD = 3; //驗證密碼失敗 public static final int STATUS_FAILED_PWD = 4; //輸入密碼長度不夠 public static final int STATUS_ERROR = 5;
在剛初始化的時候,就初始化當前的狀態,初始化狀態就只有2個狀態:有密碼、無密碼。
public void initStatus(){ if(TextUtils.isEmpty(Util.getPwd(getContext()))){ status = STATUS_NO_PWD; }else{ status = STATUS_SAVE_PWD; } } public int getCurrentStatus(){ return status; }
然後就是通過外界的設置初始化一些參數(若不調用initParam方法,則采用默認值):
public LockView initParam(int padding ,int colorSuccess ,int colorFailed ,int minPwdNumber){ this.padding = padding; this.colorSuccess = colorSuccess; this.colorFailed = colorFailed; this.minPwdNumber = minPwdNumber; init(); return this; } /** * @Description:若第一次調用則創建圓球,否則更新圓球 */ private void init(){ int circleRadius = (width - (COUNT_PER_RAW + 1) * padding) / COUNT_PER_RAW /2; if(circles.size() == 0){ for(int i = 0; i < COUNT_PER_RAW * COUNT_PER_RAW; i++){ createCircles(circleRadius, i); } }else{ for(int i = 0; i < COUNT_PER_RAW * COUNT_PER_RAW; i++){ updateCircles(circles.get(i), circleRadius); } } }
上述代碼主要根據設置的padding值,計算出小球的大小,然後判斷是否是初始化小球,還是更新小球。
private void createCircles(int radius, int position){ int centerX = (position % 3 + 1) * padding + (position % 3 * 2 + 1) * radius; int centerY = (position / 3 + 1) * padding + (position / 3 * 2 + 1) * radius; Circle circle = new Circle(centerX, centerY, colorSuccess, colorFailed, radius, position); circles.add(circle); } private void updateCircles(Circle circle ,int radius){ int centerX = (circle.getPosition() % 3 + 1) * padding + (circle.getPosition() % 3 * 2 + 1) * radius; int centerY = (circle.getPosition() / 3 + 1) * padding + (circle.getPosition() / 3 * 2 + 1) * radius; circle.setCenterX(centerX); circle.setCenterY(centerY); circle.setRadius(radius); circle.setColorSuccess(colorSuccess); circle.setColorFailed(colorFailed); }
別忘了上面的方法依賴一個width值,這個值是在onMeasure中計算出來的
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ width = MeasureSpec.getSize(widthMeasureSpec); height = width - getPaddingLeft() - getPaddingRight() + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); }
然後就是繪制方法了,因為我們的高度解耦性,本應該非常復雜的onDraw方法,卻如此簡單。就只繪制了小球和路徑。
@Override protected void onDraw(Canvas canvas){ init(); //繪制圓 for(int i = 0; i < circles.size() ;i++){ circles.get(i).draw(canvas, mPaint); } if(result.size() != 0){ //繪制Path Circle temp = circles.get(result.get(0)); mPaint.setColor(temp.getStatus() == Circle.STATUS_FAILED ? colorFailed : colorSuccess); mPaint.setStrokeWidth(Circle.DEFAULT_CENTER_BOUND); canvas.drawPath(mPath, mPaint); } }
控件是需要和外界進行交互的,我喜歡的方法就是自定義監聽器,然後接口回調。
public void setOnLockListener(OnLockListener listener){ this.listener = listener; } public interface OnLockListener{ /** * @Description:沒有密碼時,第一次錄入密碼觸發器 */ void onTypeInOnce(String input); /** * @Description:已經錄入第一次密碼,錄入第二次密碼觸發器 */ void onTypeInTwice(String input ,boolean isSuccess); /** * @Description:驗證密碼觸發器 */ void onUnLock(String input ,boolean isSuccess); /** * @Description:密碼長度不夠 */ void onError(); }
最後最最最重要的一個部分來了,onTouchEvent方法,這個方法其實也可以分為三個部分講解:down事件、move事件和up事件。首先貼出down事件代碼
case MotionEvent.ACTION_DOWN: backupsPath.reset(); for(int i = 0; i < circles.size() ;i++){ Circle circle = circles.get(i); if(event.getX() >= circle.getCenterX() - circle.getRadius() && event.getX() <= circle.getCenterX() + circle.getRadius() && event.getY() >= circle.getCenterY() - circle.getRadius() && event.getY() <= circle.getCenterY() + circle.getRadius()){ circle.setStatus(Circle.STATUS_TOUCH); //將這個點放入Path backupsPath.moveTo(circle.getCenterX(), circle.getCenterY()); //放入結果 result.add(circle.getPosition()); break; } } invalidate(); return true;
也就是對按下的x、y坐標進行判斷,是否屬於我們的小球范圍內,若屬於,則放入路徑集合、更改狀態、加入密碼結果集。這裡別忘了return true,大家都知道吧。
然後是move事件,move事件主要做三件事情:變更小球的狀態、添加到路徑集合、對路徑覆蓋的未點亮小球進行點亮。代碼有詳細注釋就不過多講解了。
case MotionEvent.ACTION_MOVE: for(int i = 0; i < circles.size() ;i++){ Circle circle = circles.get(i); if(event.getX() >= circle.getCenterX() - circle.getRadius() && event.getX() <= circle.getCenterX() + circle.getRadius() && event.getY() >= circle.getCenterY() - circle.getRadius() && event.getY() <= circle.getCenterY() + circle.getRadius()){ if(!result.contains(circle.getPosition())){ circle.setStatus(Circle.STATUS_TOUCH); //首先判斷是否連線中間也有滿足條件的圓 Circle lastCircle = circles.get(result.get(result.size() - 1)); int cx = (lastCircle.getCenterX() + circle.getCenterX()) / 2; int cy = (lastCircle.getCenterY() + circle.getCenterY()) / 2; for(int j = 0; j < circles.size(); j++){ Circle tempCircle = circles.get(j); if(cx >= tempCircle.getCenterX() - tempCircle.getRadius() && cx <= tempCircle.getCenterX() + tempCircle.getRadius() && cy >= tempCircle.getCenterY() - tempCircle.getRadius() && cy <= tempCircle.getCenterY() + tempCircle.getRadius()){ //處理滿足條件的圓 backupsPath.lineTo(tempCircle.getCenterX(), tempCircle.getCenterY()); //放入結果 tempCircle.setStatus(Circle.STATUS_TOUCH); result.add(tempCircle.getPosition()); } } //處理現在的圓 backupsPath.lineTo(circle.getCenterX(), circle.getCenterY()); //放入結果 circle.setStatus(Circle.STATUS_TOUCH); result.add(circle.getPosition()); break; } } } mPath.reset(); mPath.addPath(backupsPath); mPath.lineTo(event.getX(), event.getY()); invalidate(); break;
這裡我用了兩個Path對象,backupsPath用於只存放小球的中點坐標,mPath不僅要存儲小球的中點坐標,還要存儲當前手指觸碰坐標,為了實現連線跟隨手指運動的效果。
最後是up事件,這裡有太多復雜的狀態轉換,我估計文字講解是描述不清的,大家還是看源代碼吧。
case MotionEvent.ACTION_UP: mPath.reset(); mPath.addPath(backupsPath); invalidate(); if(result.size() < minPwdNumber){ if(listener != null){ listener.onError(); } if(status == STATUS_RETRY_PWD){ Util.clearPwd(getContext()); } status = STATUS_ERROR; for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } }else{ if(status == STATUS_NO_PWD){ //當前沒有密碼 //保存密碼,重新錄入 Util.savePwd(getContext(), result); status = STATUS_RETRY_PWD; if(listener != null){ listener.onTypeInOnce(Util.listToString(result)); } }else if(status == STATUS_RETRY_PWD){ //需要重新繪制密碼 //判斷兩次輸入是否相等 if(Util.getPwd(getContext()).equals(Util.listToString(result))){ status = STATUS_SAVE_PWD; if(listener != null){ listener.onTypeInTwice(Util.listToString(result), true); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_SUCCESS); } }else{ status = STATUS_NO_PWD; Util.clearPwd(getContext()); if(listener != null){ listener.onTypeInTwice(Util.listToString(result), false); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } } }else if(status == STATUS_SAVE_PWD){ //驗證密碼 //判斷密碼是否正確 if(Util.getPwd(getContext()).equals(Util.listToString(result))){ status = STATUS_SUCCESS_PWD; if(listener != null){ listener.onUnLock(Util.listToString(result), true); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_SUCCESS); } }else{ status = STATUS_FAILED_PWD; if(listener != null){ listener.onUnLock(Util.listToString(result), false); } for(int i = 0; i < result.size(); i++){ circles.get(result.get(i)).setStatus(Circle.STATUS_FAILED); } } } } invalidate(); handler.postDelayed(new Runnable(){ @Override public void run(){ result.clear(); mPath.reset(); backupsPath.reset(); // initStatus(); // 重置下狀態 if(status == STATUS_SUCCESS_PWD || status == STATUS_FAILED_PWD){ status = STATUS_SAVE_PWD; }else if(status == STATUS_ERROR){ initStatus(); } for(int i = 0; i < circles.size(); i++){ circles.get(i).setStatus(Circle.STATUS_DEFAULT); } invalidate(); } }, DURATION); break;
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
1、QuantityView 類似購物車數量調節:效果圖:項目在GitHub上的地址:https://github.com/himanshu-soni/QuantityV
1、當有兩三個不同類的類且所用方法都一樣,需要完成不能的功能,而這個三個類可能都需要同另外一個類進行交互,而直接把這個類的應用到處傳遞也是很頭疼的,這時可用抽象了類來解決
前段時間研究了不少android二次開發,其中有一種方法就是通過aidl通信,留接口提供給外面二次開發。從這裡也可以看出:aidl通信是兩個應用程序之間的進程通信了。在這
在AChat項目的開發過程中,項目要求無論終端是什麼時區設置、地處何方,終端的時間是否正確,post到服務器的數據包裡面的時間字段均要求跟服務器同步,也就是說,用戶買來一