一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android自定義View手勢密碼

Android自定義View手勢密碼

2022-03-08 16:00Young_Time Android

這篇文章主要為大家詳細介紹了Android自定義View手勢密碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下

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

Android自定義View手勢密碼

首先,我們來創(chuàng)建一個表示點的類,Point.java:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Point {
 
 // 點的三種狀態(tài)
 public static final int POINT_STATUS_NORMAL = 0;
 public static final int POINT_STATUS_CLICK = 1;
 public static final int POINT_STATUS_ERROR = 2;
 
 // 默認狀態(tài)
 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));
 }
 
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// 三種畫筆
 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();
 // 是否在畫錯誤狀態(tài)
 private boolean isDrawError = false;
 接下來我們來初始化數據:
 
// 初始化數據
 private void initData() {
 
  // 初始化三種畫筆,正常狀態(tài)為灰色,點下狀態(tài)為藍色,錯誤為紅色
  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 的寬高,都可以使里面的九個點看起來位置很舒服。

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 畫點,按照我們選擇的半徑畫九個圓
 private void drawPoints(Canvas canvas) {
  // 便利所有的點,并且判斷這些點的狀態(tài)
  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++) {
    // 判斷已選擇點的狀態(tà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) {
  // 有選擇點后再出現(xiàn)手勢點
  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);
  }
 }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 獲取手指移動中選取的點
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 來通過手勢動作來提交選擇的點,并更新視圖:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 重寫點擊事件
 @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) {
     // 添加到已選擇點中,并改變其狀態(tài)
     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]])) {
      // 添加到已選擇點中,并改變其狀態(tài)
      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() {
  // 將所有選擇過的點的狀態(tài)改為正常
  for (Point point :
    mClickPointsList) {
   point.state = Point.POINT_STATUS_NORMAL;
  }
  // 清空已選擇點
  mClickPointsList.clear();
  // 清空保存的路徑
  mRoute = new StringBuilder();
  // 不再畫錯誤狀態(tài)
  isDrawError = false;
 }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  // 判斷是否畫錯誤狀態(tài),畫錯誤狀態(tài)不需要畫手勢點已經于最新選擇點的連線
  if (isDrawError) {
   drawPoints(canvas);
   drawLines(canvas);
  } else {
   drawPoints(canvas);
   drawLines(canvas);
   drawFinger(canvas);
  }
 }

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

?
1
2
3
4
5
6
7
8
9
10
11
private OnDrawFinishListener mListener;
 
 // 定義繪制完成的接口
 public interface OnDrawFinishListener {
  public boolean drawFinish(String route);
 }
 
 // 定義繪制完成的方法,傳入接口
 public void setOnDrawFinishListener(OnDrawFinishListener listener) {
  this.mListener = listener;
 }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
case MotionEvent.ACTION_UP:
    // 完成時回調繪制完成的方法,返回比對結果,判斷手勢密碼是否正確
    mListener.drawFinish(mRoute.toString());
    // 返回錯誤,則將所有已選擇點狀態(tài)改為錯誤
    if (!mListener.drawFinish(mRoute.toString())) {
     for (Point point :
       mClickPointsList) {
      point.state = Point.POINT_STATUS_ERROR;
     }
     // 將是否繪制錯誤設為 true
     isDrawError = true;
     // 刷新視圖
     invalidate();
     // 這里我們使用 handler 異步操作,使其錯誤狀態(tài)保持 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,整個過程就結束了,筆者這里定義了一個監(jiān)聽器只是給大家提供一種思路,筆者將保存的大路徑傳給了使用者,是為了保證使用者可以自己保存密碼,并作相關操作,大家也可以使用 HandleLock 來  保存密碼,不傳給使用者,根據自己的需求寫出更多更豐富的監(jiān)聽器,而且這里筆者在 MotionEvent.ACTION_UP 中直接回調了 drawFinish() 方法,就意味著要使用該 HandleLock 就必須給它設置監(jiān)聽器。

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

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

接下來是代碼中使用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
     }
    }
   }
  });

項目地址:源代碼

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/Young_Time/article/details/80856817

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产精品青青青高清在线观看 | 欧美一区二区三区精品国产 | 欧美一区二区三区四区五区六区 | 亚洲第一天堂网 | 幻女free性zozo交体内谢 | 好大夫在线个人空间 | 国产日产国无高清码2020 | 韩国美女豪爽一级毛片 | 午夜福到在线4国产 | 嫩草影院永久在线一二三四 | 日本免费高清在线 | 国产高清不卡码一区二区三区 | 四虎影院新地址 | 国产精品视频人人做人人爱 | 男人j进女屁股视频在线观看 | 9999网站| 国产在线欧美日韩精品一区二区 | 国产欧美一区二区三区精品 | 7个黑人玩北条麻妃 | 天天干天天日天天射天天操毛片 | 日本偷偷操 | 精品综合 | 99久久免费国产精品热 | 日韩高清成人毛片不卡 | 欧美日韩国产成人综合在线 | 日韩一卡2卡3卡新区网站 | 欧美日韩亚洲第一区在线 | 无码一区国产欧美在线资源 | 调教催眠改造np总攻 | 亚洲国产精品婷婷久久久久 | 国产一卡二卡3卡4卡四卡在线 | 99久久精品国产免看国产一区 | 12345国产精品高清在线 | 香蕉tv亚洲专区在线观看 | 国产a不卡片精品免费观看 国产aaa伦理片 | 91碰碰| 四虎最新永久免费视频 | 脱女学小内内摸出水网站免费 | 暖暖的免费观看高清视频韩国 | 男人操女人免费视频 | 亚洲国产无线码在线观看 |