国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View手勢密碼

2019-10-21 21:48:33
字體:
來源:轉載
供稿:網友

 Android 自定義View 當然是十分重要的,筆者這兩天寫了一個自定義 View 的手勢密碼,和大家分享分享:

Android,自定義View,手勢密碼

首先,我們來創建一個表示點的類,Point.java:

public class Point {  // 點的三種狀態 public static final int POINT_STATUS_NORMAL = 0; public static final int POINT_STATUS_CLICK = 1; public static final int POINT_STATUS_ERROR = 2;  // 默認狀態 public int state = POINT_STATUS_NORMAL;  // 點的坐標 public float mX; public float mY;  public Point(float x,float y){  this.mX = x;  this.mY = y; }  // 獲取兩個點的距離 public float getInstance(Point a){  return (float) Math.sqrt((mX-a.mX)*(mX-a.mX)+(mY-a.mY)*(mY-a.mY)); } }

然后我們創建一個 HandleLock.java 繼承自 View,并重寫其三種構造方法(不重寫帶兩個參數的構造方法會導致程序出錯):

首先,我們先把后面需要用的變量寫出來,方便大家明白這些變量是干嘛的:

// 三種畫筆 private Paint mNormalPaint; private Paint mClickPaint; private Paint mErrorPaint;  // 點的半徑 private float mRadius;  // 九個點,使用二維數組 private Point[][] mPoints = new Point[3][3];  // 保存手勢劃過的點 private ArrayList<Point> mClickPointsList = new ArrayList<Point>(); // 手勢的 x 坐標,y 坐標 private float mHandleX; private float mHandleY;  private OnDrawFinishListener mListener;  // 保存滑動路徑 private StringBuilder mRoute = new StringBuilder(); // 是否在畫錯誤狀態 private boolean isDrawError = false; 接下來我們來初始化數據:// 初始化數據 private void initData() {   // 初始化三種畫筆,正常狀態為灰色,點下狀態為藍色,錯誤為紅色  mNormalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  mNormalPaint.setColor(Color.parseColor("#ABABAB"));  mClickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  mClickPaint.setColor(Color.parseColor("#1296db"));  mErrorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  mErrorPaint.setColor(Color.parseColor("#FB0C13"));   // 獲取點間隔  float offset = 0;  if (getWidth() > getHeight()) {   // 橫屏   offset = getHeight() / 7;   mRadius = offset / 2;   mPoints[0][0] = new Point(getWidth() / 2 - offset * 2, offset + mRadius);   mPoints[0][1] = new Point(getWidth() / 2, offset + mRadius);   mPoints[0][2] = new Point(getWidth() / 2 + offset * 2, offset + mRadius);   mPoints[1][0] = new Point(getWidth() / 2 - offset * 2, offset * 3 + mRadius);   mPoints[1][1] = new Point(getWidth() / 2, offset * 3 + mRadius);   mPoints[1][2] = new Point(getWidth() / 2 + offset * 2, offset * 3 + mRadius);   mPoints[2][0] = new Point(getWidth() / 2 - offset * 2, offset * 5 + mRadius);   mPoints[2][1] = new Point(getWidth() / 2, offset * 5 + mRadius);   mPoints[2][2] = new Point(getWidth() / 2 + offset * 2, offset * 5 + mRadius);  } else {   // 豎屏   offset = getWidth() / 7;   mRadius = offset / 2;   mPoints[0][0] = new Point(offset + mRadius, getHeight() / 2 - 2 * offset);   mPoints[0][1] = new Point(offset * 3 + mRadius, getHeight() / 2 - 2 * offset);   mPoints[0][2] = new Point(offset * 5 + mRadius, getHeight() / 2 - 2 * offset);   mPoints[1][0] = new Point(offset + mRadius, getHeight() / 2);   mPoints[1][1] = new Point(offset * 3 + mRadius, getHeight() / 2);   mPoints[1][2] = new Point(offset * 5 + mRadius, getHeight() / 2);   mPoints[2][0] = new Point(offset + mRadius, getHeight() / 2 + 2 * offset);   mPoints[2][1] = new Point(offset * 3 + mRadius, getHeight() / 2 + 2 * offset);   mPoints[2][2] = new Point(offset * 5 + mRadius, getHeight() / 2 + 2 * offset);  }   }

大家可以看到,我來給點定坐標是,是按照比較窄的邊的 1/7 作為點的直徑,這樣保證了,不管你怎么定義 handleLock 的寬高,都可以使里面的九個點看起來位置很舒服。

接下來我們就需要寫一些函數,將點、線繪制到控件上,我自己把繪制分成了三部分,一部分是點,一部分是點與點之間的線,一部分是手勢的小點和手勢到最新點的線。

// 畫點,按照我們選擇的半徑畫九個圓 private void drawPoints(Canvas canvas) {  // 便利所有的點,并且判斷這些點的狀態  for (int i = 0; i < 3; i++) {   for (int j = 0; j < 3; j++) {    Point point = mPoints[i][j];    switch (point.state) {     case Point.POINT_STATUS_NORMAL:      canvas.drawCircle(point.mX, point.mY, mRadius, mNormalPaint);      break;     case Point.POINT_STATUS_CLICK:      canvas.drawCircle(point.mX, point.mY, mRadius, mClickPaint);      break;     case Point.POINT_STATUS_ERROR:      canvas.drawCircle(point.mX, point.mY, mRadius, mErrorPaint);      break;     default:      break;     }   }  } } // 畫點與點之間的線 private void drawLines(Canvas canvas) {  // 判斷手勢是否已經劃過點了  if (mClickPointsList.size() > 0) {   Point prePoint = mClickPointsList.get(0);   // 將所有已選擇點的按順序連線   for (int i = 1; i < mClickPointsList.size(); i++) {    // 判斷已選擇點的狀態    if (prePoint.state == Point.POINT_STATUS_CLICK) {     mClickPaint.setStrokeWidth(7);     canvas.drawLine(prePoint.mX, prePoint.mY, mClickPointsList.get(i).mX, mClickPointsList.get(i).mY, mClickPaint);    }    if (prePoint.state == Point.POINT_STATUS_ERROR) {     mErrorPaint.setStrokeWidth(7);     canvas.drawLine(prePoint.mX, prePoint.mY, mClickPointsList.get(i).mX, mClickPointsList.get(i).mY, mErrorPaint);    }    prePoint = mClickPointsList.get(i);   }   }  } // 畫手勢點 private void drawFinger(Canvas canvas) {  // 有選擇點后再出現手勢點  if (mClickPointsList.size() > 0) {   canvas.drawCircle(mHandleX, mHandleY, mRadius / 2, mClickPaint);  }  // 最新點到手指的連線,判斷是否有已選擇的點,有才能畫  if (mClickPointsList.size() > 0) {   canvas.drawLine(mClickPointsList.get(mClickPointsList.size() - 1).mX, mClickPointsList.get(mClickPointsList.size() - 1).mY,     mHandleX, mHandleY, mClickPaint);  } }

上面的代碼我們看到需要使用到手勢劃過的點,我們是怎么選擇的呢?

// 獲取手指移動中選取的點private int[] getPositions() { Point point = new Point(mHandleX, mHandleY); int[] position = new int[2]; // 遍歷九個點,看手勢的坐標是否在九個圓內,有則返回這個點的兩個下標 for (int i = 0; i < 3; i++) {  for (int j = 0; j < 3; j++) {   if (mPoints[i][j].getInstance(point) <= mRadius) {    position[0] = i;    position[1] = j;    return position;   }  }  } return null;}

我們需要重寫其 onTouchEvent 來通過手勢動作來提交選擇的點,并更新視圖:

// 重寫點擊事件 @Override public boolean onTouchEvent(MotionEvent event) {  // 獲取手勢的坐標  mHandleX = event.getX();  mHandleY = event.getY();  int[] position;  switch (event.getAction()) {   case MotionEvent.ACTION_DOWN:    position = getPositions();    // 判斷點下時是否選擇到點    if (position != null) {     // 添加到已選擇點中,并改變其狀態     mClickPointsList.add(mPoints[position[0]][position[1]]);     mPoints[position[0]][position[1]].state = Point.POINT_STATUS_CLICK;     // 保存路徑,依次保存其橫縱下標     mRoute.append(position[0]);     mRoute.append(position[1]);    }    break;   case MotionEvent.ACTION_MOVE:    position = getPositions();    // 判斷手勢移動時是否選擇到點    if (position != null) {     // 判斷當前選擇的點是否已經被選擇過     if (!mClickPointsList.contains(mPoints[position[0]][position[1]])) {      // 添加到已選擇點中,并改變其狀態      mClickPointsList.add(mPoints[position[0]][position[1]]);      mPoints[position[0]][position[1]].state = Point.POINT_STATUS_CLICK;      // 保存路徑,依次保存其橫縱下標      mRoute.append(position[0]);      mRoute.append(position[1]);     }    }    break;   case MotionEvent.ACTION_UP:    // 重置數據    resetData();    break;   default:    break;  }  // 更新視圖  invalidate();   return true; }// 重置數據 private void resetData() {  // 將所有選擇過的點的狀態改為正常  for (Point point :    mClickPointsList) {   point.state = Point.POINT_STATUS_NORMAL;  }  // 清空已選擇點  mClickPointsList.clear();  // 清空保存的路徑  mRoute = new StringBuilder();  // 不再畫錯誤狀態  isDrawError = false; }

那我們怎么繪制視圖呢?我們通過重寫其 onDraw() 方法:

@Override protected void onDraw(Canvas canvas) {  super.onDraw(canvas);  // 判斷是否畫錯誤狀態,畫錯誤狀態不需要畫手勢點已經于最新選擇點的連線  if (isDrawError) {   drawPoints(canvas);   drawLines(canvas);  } else {   drawPoints(canvas);   drawLines(canvas);   drawFinger(canvas);  } }

那么這個手勢密碼繪制過程就結束了,但是整個控件還沒有結束,我們還需要給它一個監聽器,監聽其繪制完成,選擇后續事件:

private OnDrawFinishListener mListener;  // 定義繪制完成的接口 public interface OnDrawFinishListener {  public boolean drawFinish(String route); }  // 定義繪制完成的方法,傳入接口 public void setOnDrawFinishListener(OnDrawFinishListener listener) {  this.mListener = listener; }

然后我們就需要在手勢離開的時候 ,來進行繪制完成時的事件:

case MotionEvent.ACTION_UP:    // 完成時回調繪制完成的方法,返回比對結果,判斷手勢密碼是否正確    mListener.drawFinish(mRoute.toString());    // 返回錯誤,則將所有已選擇點狀態改為錯誤    if (!mListener.drawFinish(mRoute.toString())) {     for (Point point :       mClickPointsList) {      point.state = Point.POINT_STATUS_ERROR;     }     // 將是否繪制錯誤設為 true     isDrawError = true;     // 刷新視圖     invalidate();     // 這里我們使用 handler 異步操作,使其錯誤狀態保持 0.5s     new Thread(new Runnable() {      @Override      public void run() {       if (!mListener.drawFinish(mRoute.toString())) {        Message message = new Message();        message.arg1 = 0;        handler.sendMessage(message);       }      }     }).run();    } else {     resetData();    }    invalidate();     break;private Handler handler = new Handler() {  @Override  public void handleMessage(Message msg) {   switch (msg.arg1) {    case 0:     try {      // 沉睡 0.5s      Thread.sleep(500);     } catch (InterruptedException e) {      e.printStackTrace();     }     // 重置數據,并刷新視圖     resetData();     invalidate();     break;    default:     break;   }   } };

 好了,handleLock,整個過程就結束了,筆者這里定義了一個監聽器只是給大家提供一種思路,筆者將保存的大路徑傳給了使用者,是為了保證使用者可以自己保存密碼,并作相關操作,大家也可以使用 HandleLock 來  保存密碼,不傳給使用者,根據自己的需求寫出更多更豐富的監聽器,而且這里筆者在 MotionEvent.ACTION_UP 中直接回調了 drawFinish() 方法,就意味著要使用該 HandleLock 就必須給它設置監聽器。

接下來我們說說 HandleLock 的使用,首先是在布局文件中使用:

<com.example.a01378359.testapp.lock.HandleLock  android:id="@+id/handlelock_test"  android:layout_width="match_parent"  android:layout_height="match_parent" />

接下來是代碼中使用:

handleLock = findViewById(R.id.handlelock_test);  handleLock.setOnDrawFinishListener(new HandleLock.OnDrawFinishListener() {   @Override   public boolean drawFinish(String route) {    // 第一次滑動,則保存密碼    if (count == 0){     password = route;     count++;     Toast.makeText(LockTestActivity.this,"已保存密碼",Toast.LENGTH_SHORT).show();     return true;    }else {     // 與保存密碼比較,返回結果,并且做出相應事件     if (password.equals(route)){      Toast.makeText(LockTestActivity.this,"密碼正確",Toast.LENGTH_SHORT).show();      return true;     }else {      Toast.makeText(LockTestActivity.this,"密碼錯誤",Toast.LENGTH_SHORT).show();      return false;     }    }   }  });

項目地址:源代碼

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 威海市| 镇原县| 宁阳县| 襄樊市| 吐鲁番市| 连州市| 阳江市| 黎平县| 开原市| 东港市| 万荣县| 兴国县| 左权县| 青海省| 古田县| 泰宁县| 武乡县| 宁远县| 兴化市| 泗阳县| 精河县| 武冈市| 镇安县| 双辽市| 六枝特区| 龙江县| 厦门市| 沂南县| 龙海市| 当涂县| 新绛县| 甘德县| 清新县| 阜宁县| 湟源县| 余姚市| 牡丹江市| 贡山| 额济纳旗| 石阡县| 高台县|