Android 사용자 정의 View 구 궁 격 제스처 비밀번호 잠 금 해제

회사 의 새로운 프로젝트 는 구 궁 격 제스처 암호 로 잠 금 을 푸 는 기능 을 사용 해 야 하기 때문에 자신 이 쓰 는 것 을 느 꼈 다.쓸데없는 말 은 그만 하고 바로 효과 도 에 올 려 라.

우선 우리 가 실현 하 는 방향 을 분석 해 보 자.
1.이 View 의 가운데 에 있 는 9 개의 점 을 기본 상태 로 그립 니 다.
2.화면 을 눌 렀 을 때 이 9 개 지점 을 눌 렀 는 지 여부
3.화면 이 미 끄 러 질 때 두 점 사이 의 선 을 그리고 선택 한 상태의 점 을 그립 니 다.
4.손가락 이 화면 을 떠 날 때 제스처 비밀번호 가 정확 한 지 판단 합 니 다.잘못된 상태 에서 점 과 선 을 그립 니 다.
구체 적 인 실현:
우선 우 리 는 기본 정상 상태의 9 개의 점 을 그 려 야 한다.

/**
 *   bean
 * Created by Administrator on 2015/9/21.
 */
public class Point {

 //     
 public static final int STATE_NORMAL = 1;
 //     
 public static final int STATE_PRESS = 2;
 //     
 public static final int STATE_ERROR = 3;

 float x;
 float y;
 int state = STATE_NORMAL;

 public Point(float x, float y){
  this.x = x;
  this.y = y;
 }

 /**
  *         
  * @param a      
  * @return
  */
 public float getInstance(Point a){
  return (float) Math.sqrt((x-a.x)*(x-a.x) + (y-a.y)*(y-a.y));
 }
}
이 를 통 해 알 수 있 듯 이 한 점 에 x,y 값 과 이 점 의 상 태 는 세 가지 가 있 고 기본 적 인 초기 상 태 는 정상 적 인 상태 이다.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 init();//             ,                
}

private void init() {
  mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mPressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  mErrorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  //        
  mPressPaint.setColor(Color.parseColor("#00B7EE"));
  mPressPaint.setStrokeWidth(7);
  //        
  mErrorPaint.setColor(Color.parseColor("#FB0C13"));
  mErrorPaint.setStrokeWidth(7);

  //         
  mNormalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_normal);
  mPressBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_press);
  mErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock_point_error);
  mPointRadius = mNormalBitmap.getWidth() / 2;

  //        
  int width = getWidth();
  int height = getHeight();
  //         
  int offSet = Math.abs(width - height) / 2;
  // x、y      
  int offSetX = 0, offSetY = 0;
  int pointItemWidth = 0; //            
  if (width > height){ //      
   offSetX = offSet;
   offSetY = 0;
   pointItemWidth = height / 4;
  }
  if (width < height){ //      
   offSetX = 0;
   offSetY = offSet;
   pointItemWidth = width / 4;
  }

  //       
  mPoints[0][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth);
  mPoints[0][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth);
  mPoints[0][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth);

  mPoints[1][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth * 2);
  mPoints[1][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth * 2);
  mPoints[1][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth * 2);

  mPoints[2][0] = new Point(offSetX + pointItemWidth, offSetY + pointItemWidth * 3);
  mPoints[2][1] = new Point(offSetX + pointItemWidth * 2, offSetY + pointItemWidth * 3);
  mPoints[2][2] = new Point(offSetX + pointItemWidth * 3, offSetY + pointItemWidth * 3);
 }
이 코드 에서 주의해 야 할 것 은 가로 세로 화면의 편 이 량 이다.가로 화면 을 사용 할 때 X 축의 편 이 량 을 계산 하고 세로 화면 을 사용 할 때 Y 축의 편 이 량 을 계산한다.x y 의 오프셋 을 계산 한 후 9 개의 점 의 위 치 를 초기 화 합 니 다.구 궁 칸 의 점 을 현재 사용자 정의 보기 View 의 한가운데 에 그 려 야 합 니 다.위의 그림 에서 보 듯 이 첫 번 째 점 의 시작 점 은 x=x 축의 오프셋+격자 너비,y=y 축의 오프셋+격자 너비 입 니 다.이 를 통 해 두 번 째 열의 점 의 x 값=두 칸 의 너비+x 축의 오프셋,두 번 째 줄 의 점 의 y 값=두 칸 의 너비+y 주의 오프셋 을 볼 수 있다.이런 식 으로 9 개의 점 의 위 치 를 초기 화 합 니 다.
9 개의 위치 가 초기 화 된 후에 우 리 는 9 개의 점 을 그 려 야 한다.여기 서 나 는 세 가지 상태의 그림 으로 정점 을 삼 았 다.init()방법 에서 세 가지 bitmap 그림 대상 을 초기 화 했 습 니 다.그리고 점 의 반지름 을 계산 한 것 이 바로 그림 의 절반 이다.물론 내 가 있 는 이곳 의 세 장의 그림 크기 는 같다.만약 다르다 면 다시 계산 해 야 한다.
다음은 onDraw 방법 에서 9 개의 점 을 그립 니 다.

@Override
 protected void onDraw(Canvas canvas) {

  //    
  drawPoints(canvas);

  //     
  drawLines(canvas);
 }

 /**
  *       
  * @param canvas
  */
 private void drawPoints(Canvas canvas){
  for (int i = 0; i < mPoints.length; i++){
   for (int j = 0; j < mPoints[i].length; j++){
    Point point = mPoints[i][j];
    //        
    switch (point.state){
     case Point.STATE_NORMAL:
      canvas.drawBitmap(mNormalBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);
      break;
     case Point.STATE_PRESS:
      canvas.drawBitmap(mPressBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);
      break;
     case Point.STATE_ERROR:
      canvas.drawBitmap(mErrorBitmap, point.x - mPointRadius, point.y - mPointRadius, mPaint);
      break;
    }
   }
  }
 }
우리 변 수 는 9 개의 점 대상 의 상 태 를 초기 화하 고 상태 에 따라 다른 그림 을 그립 니 다.여기 서 그림 을 그 릴 때 초기 화 점 의 x,y 값 은 점 원 의 반지름 을 포함 하고 그림 을 그 릴 때 왼쪽 상단 에서 시작 하기 때문에 그림 을 그 릴 때 그림 자체 의 반지름 을 빼 야 합 니 다.
그림%1 개의 캡 션 을 편 집 했 습 니 다.

 /**
  *          
  * @return
  */
 private int[] getSelectedPointPosition(){
  Point point = new Point(mX, mY);
  for (int i = 0; i < mPoints.length; i++) {
   for (int j = 0; j < mPoints[i].length; j++) {
    //                           
    if(mPoints[i][j].getInstance(point) < mPointRadius){
     //           ,         
     int[] result = new int[2];
     result[0] = i;
     result[1] = j;
     return result;
    }
   }
  }
  return null;
 }
먼저 우 리 는 손가락 이 클릭 한 위치 가 점 에 있 는 지 판단 하고 화면 터치 점 의 위치 mX,mY 를 가 져 와 점 하 나 를 초기 화한 다음 에 모든 점 과 터치 점 의 거 리 를 한 점 보다 작은 그림 의 반지름 으로 옮 겨 다 니 며 터치 하 는 위치 가 이 9 개의 점 중 하나 에 있다 는 것 을 나타 낸다.getInstance(point)는 두 점 사이 의 거 리 를 계산 하 는 방법 이다.공식 은 distance=Math.sqrt(x1-x2)(x1-x2)+(y1-y2)(y1-y2))이다.만 지 는 위치 가 점 위 에 있다 면 이 점 의 구 궁 격 배열 에 있 는 아래 표 시 된 위 치 를 되 돌려 줍 니 다.
onTouchEvent 방법 을 살 펴 보 겠 습 니 다.

@Override
 public boolean onTouchEvent(MotionEvent event) {
  //        xy  
  mX = event.getX();
  mY = event.getY();
  int[] position;
  int i, j;
  switch (event.getAction()){
   case MotionEvent.ACTION_DOWN:
    //       
    resetPoints();
    //          
    position = getSelectedPointPosition();
    if (position != null){
     isDraw = true; //        
     i = position[0];
     j = position[1];
     mPoints[i][j].state = Point.STATE_PRESS;
     //             
     mSelectedPoints.add(mPoints[i][j]);
     mPassPositions.add(i * 3 + j); //                    
    }
    break;
   case MotionEvent.ACTION_MOVE:
    if (isDraw){
     position = getSelectedPointPosition();
     if (position != null){
      i = position[0];
      j = position[1];
      if (!mSelectedPoints.contains(mPoints[i][j])){
       mPoints[i][j].state = Point.STATE_PRESS;
       mSelectedPoints.add(mPoints[i][j]);
       mPassPositions.add(i * 3 + j);
      }
     }
    }
    break;
   case MotionEvent.ACTION_UP:
    //        
    isDraw = false;
    break;
  }
  //       
  invalidate();
  return true;
 }
눌 렀 을 때 터치 위치 가 점 에 있 을 때 까지 버 티 고 이 점 의 상 태 를 눌 린 상태 로 바 꾸 고 mSelected Points 가 선 택 된 점 의 집합 에 저장 합 니 다.현재 그 려 진 상태 로 표시 합 니 다.마찬가지 로 손가락 을 움 직 일 때 도 검출 된 점 을 저장 하고 상 태 를 누 르 는 것 으로 수정한다.손가락 이 화면 을 떠 날 때 표 시 를 그리 지 않 는 것 으로 바 꿉 니 다.그리고 invalidate()방법 으로 보 기 를 업데이트 합 니 다(onDraw 방법 으로 그립 니 다).
위의 절 차 를 통 해 우 리 는 선택 한 점 을 모두 모 았 습 니 다.다음은 두 개의 점 연결선 을 그립 니 다.

/**
  *         
  * @param canvas
  * @param a
  * @param b
*/
 private void drawLine(Canvas canvas, Point a, Point b){
  if (a.state == Point.STATE_PRESS){
   canvas.drawLine(a.x, a.y, b.x, b.y, mPressPaint);
  }
  if (a.state == Point.STATE_ERROR){
   canvas.drawLine(a.x, a.y, b.x, b.y, mErrorPaint);
  }
 }
두 점 의 연결선 을 그 리 는 것 은 비교적 간단 합 니 다.우 리 는 누 르 고 잘못 되 었 을 때의 연결선 만 그립 니 다.이것 은 단일 연결선 을 그 리 는 것 이다.그리고 우리 의 제스처 암호 경 로 는 여러 가지 가 있 습 니 다.계속 보 세 요.

 /**
  *       
  * @param canvas
  */
 private void drawLines(Canvas canvas){
  if (mSelectedPoints.size() > 0){
   //              
   Point a = mSelectedPoints.get(0);
   for (int i = 1; i < mSelectedPoints.size(); i++){
    Point b = mSelectedPoints.get(i);
    drawLine(canvas, a, b); //      
    a = b; //                  
   }
   if (isDraw){//         ,         
    drawLine(canvas, a, new Point(mX, mY));
   }
  }
 }
선택 한 점 의 집합 이 비어 있 지 않 으 면 첫 번 째 선택 한 중심 점 부터 연결선 을 그립 니 다.선택 한 점 을 모두 옮 겨 다 닐 때 두 번 째 점,즉 index 가 1 일 때 한 점 을 그 리 려 면 다음 연결선 을 그 리 는 시작 점 을 이번 연결선 의 종점 으로 바 꾸 고 a=b 로 바 꾸 어야 합 니 다.이 문장의 작용.선 택 된 모든 점 을 다 그린 후에 도 현재 그리 기 상태(핸드폰 이 화면 을 떠 나 지 않 았 다)라면 new 손가락 터치 위 치 를 연결선 의 종점 으로 합 니 다.자,모든 선 을 다 그 렸 습 니 다.그러면 우 리 는 onDraw 에서 호출 하기 만 하면 됩 니 다.

@Override
 protected void onDraw(Canvas canvas) {

  //    
  drawPoints(canvas);

  //     
  drawLines(canvas);
 }
이렇게 그 리 는 작업 은 기본적으로 완성 되 었 습 니 다.그 다음 에 우 리 는 제스처 가 미 끄 러 진 후의 인 터 페 이 스 를 감청 해 야 합 니 다.

public interface OnDrawFinishedListener{
 boolean onDrawFinished(List<Integer> passPositions);
}
리 셋 방법 에 있 는 passPositions 는 제스처 가 미 끄 러 질 때 저 장 된 구 궁 격 경로 입 니 다.구 궁 격 경로 에 대한 정 의 는 그림 과 같 습 니 다.

onTouchEvent 에서 Action 동작 이 Up 일 때(손가락 이 화면 을 떠 날 때):제스처 암 호 를 터치 하여 완 성 된 인 터 페 이 스 를 그립 니 다.

case MotionEvent.ACTION_UP:
    boolean valid = false;
    if (mListener != null && isDraw){
     //           
     valid = mListener.onDrawFinished(mPassPositions);
    }
    if (!valid){//                         
     for (Point p: mSelectedPoints){
      p.state = Point.STATE_ERROR;
     }
    }
    isDraw = false;
    break;
감청 인 터 페 이 스 를 설정 하고 그 려 진 상태 일 때 인 터 페 이 스 를 되 돌려 이 인 터 페 이 스 를 실현 하 는 사용자 에 게 경 로 를 전달 한 다음 에 이 인터페이스 방법 을 실현 하 는 곳 에서 이전에 설 치 된 제스처 비밀번호 와 일치 하 는 지 판단 하고 일치 하지 않 으 면 false 로 되 돌려 줍 니 다.그리고 선택 한 점 의 상 태 를 모두 수정 하 는 것 이 잘못 되 었 습 니 다.그리고 invalidate()는 보 기 를 업데이트 합 니 다.
경로 가 나 갔 습 니 다.처음에 설정 할 때 md5 등 거 스 를 수 없 는 암호 화 방식 으로 핸드폰 에 저장 할 수 있 습 니 다.잠 금 을 풀 어야 할 때 이 md5 값 과 잠 금 을 풀 때 그 려 진 경로 의 md5 값 을 비교 하면 됩 니 다.

//             :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"  android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="com.jerry.testproject.ui.ScreenLockActivity">

 <com.jerry.testproject.widget.lockview.LockView
  android:id="@+id/lockView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#2B2B2B"
  />

</FrameLayout>

LockView lockView = (LockView) findViewById(R.id.lockView);
  lockView.setOnDrawFinishedListener(new LockView.OnDrawFinishedListener() {
   @Override
   public boolean onDrawFinished(List<Integer> passPositions) {
    StringBuilder sb = new StringBuilder();
    for (Integer i :
      passPositions) {
     sb.append(i.intValue());
    }
    //     md5
    String md5Str = MD5Utils.getMD5String(sb.toString());
    //         
    boolean valid = comparePath(sb.toString());
    if (valid){
     Toast.makeText(ScreenLockActivity.this, "      !", Toast.LENGTH_SHORT).show();
    } else {
     Toast.makeText(ScreenLockActivity.this, "      ,   !", Toast.LENGTH_SHORT).show();
    }

    return valid;
   }
  });
이로써 사용자 정의 구 궁 격 제스처 비밀번호 View 소개 가 끝 났 습 니 다.
아래 에 컨트롤 의 소스 코드 와 사용 하 는 자원 을 첨부 합 니 다Android 9 궁 격 제스처 비밀번호 잠 금 해제
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기