안 드 로 이 드(Android)구 궁 격 잠 금 해제 쉽게 실현

효과 도

사고의 방향
먼저 우 리 는 구 궁 격 잠 금 해 제 를 실현 하 는 방향 을 분석 합 니 다.사용자 의 손가락 이 특정한 점 을 만 졌 을 때 이 점 이 구 궁 격 의 특정한 범위 안에 있 는 지,범위 안에 있 으 면 이 칸 은 선택 한 상태 가 됩 니 다.이후 사용자 의 손가락 이 미 끄 러 질 때 이 칸 의 원심 을 중심 으로 사용자 의 손가락 을 종점 으로 하고 두 점 을 연결한다.마지막 으로 사용자 가 손가락 을 들 었 을 때 그 어 진 구 궁 격 비밀번호 가 원래 의 비밀번호 와 일치 하 는 지 판단 합 니 다.
대체적인 사고방식 과 절 차 는 바로 위 와 같다.아래 에서 우 리 는 실천 해 볼 수 있다.
Point 클래스
우 리 는 먼저 Point 류 를 만들어 서 구 궁 격자 의 9 칸 을 표시 합 니 다.좌표 x,y 를 제외 하고 세 가지 모델 이 있 습 니 다.정상 모드,누 르 기 모드 와 오류 모드 입 니 다.패턴 에 따라 이 칸 의 색상 이 다 를 수 있 으 므 로 아래 에 설명 하 겠 습 니 다.

public class Point {

 private float x;
 private float y;
 //     
 public static final int NORMAL_MODE = 1;
 //     
 public static final int PRESSED_MODE = 2;
 //     
 public static final int ERROR_MODE = 3;
 private int state = NORMAL_MODE;
 //        ,  “1”、“2” 
 private String mark;

 public String getMark() {
  return mark;
 }

 public void setMark(String mark) {
  this.mark = mark;
 }

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

 public int getState() {
  return state;
 }

 public void setState(int state) {
  this.state = state;
 }

 public float getX() {
  return x;
 }

 public void setX(float x) {
  this.x = x;
 }

 public float getY() {
  return y;
 }

 public void setY(float y) {
  this.y = y;
 }

}
RotateDegrees 클래스
위의 Point 클래스 가 있 으 면 우 리 는 Rotate Degrees 클래스 를 만들어 야 합 니 다.주요 역할 은 두 Point 좌표 간 의 각 도 를 계산 하 는 것 입 니 다.

public class RotateDegrees {

 /**
  *      point          
  * @param a
  * @param b
  * @return
  */
 public static float getDegrees(Point a, Point b) {
  float degrees = 0;
  float aX = a.getX();
  float aY = a.getY();
  float bX = b.getX();
  float bY = b.getY();

  if (aX == bX) {
   if (aY < bY) {
    degrees = 90;
   } else {
    degrees = 270;
   }
  } else if (bY == aY) {
   if (aX < bX) {
    degrees = 0;
   } else {
    degrees = 180;
   }
  } else {
   if (aX > bX) {
    if (aY > bY) { //     
     degrees = 180 + (float) (Math.atan2(aY - bY, aX - bX) * 180 / Math.PI);
    } else { //     
     degrees = 180 - (float) (Math.atan2(bY - aY, aX - bX) * 180 / Math.PI);
    }
   } else {
    if (aY > bY) { //     
     degrees = 360 - (float) (Math.atan2(aY - bY, bX - aX) * 180 / Math.PI);
    } else { //     
     degrees = (float) (Math.atan2(bY - aY, bX - aX) * 180 / Math.PI);
    }
   }
  }
  return degrees;
 }

 /**
  *   point (x,y)          
  * @param a
  * @param bX
  * @param bY
  * @return
  */
 public static float getDegrees(Point a, float bX, float bY) {
  Point b = new Point(bX, bY, null);
  return getDegrees(a, b);
 }

}
ScreenLockView 클래스
그 다음 에 우 리 는 먼저 구 궁 격 에 관 한 몇 장의 사진 을 준비 해 야 한다.예 를 들 어 구 궁 격 의 칸 에서 NORMAL_MODE 모델 에서 파란색 이 고 손가락 에 눌 렸 을 때 구 궁 격 의 칸 은 녹색 이다.즉,위의 Point 가지 중 PRESSED_MODE 모델,그리고 ERROR_MODE 모델 에서 빨간색 이다.또한 도 트 사이 의 연결선 도 있 고 패턴 에 따라 색상 이 다 를 수 있 습 니 다.여기 서 나 는 그림 을 붙 이지 않 겠 다.
그림 자원 이 생 긴 후에 우리 가 해 야 할 일 은 먼저 구조 기 에 그림 을 불 러 오 는 것 이다.

public class ScreenLockView extends View {

 private static final String TAG = "ScreenLockView";
 //        
 private Bitmap errorBitmap;
 //        
 private Bitmap normalBitmap;
 //           
 private Bitmap pressedBitmap;
 //         
 private Bitmap lineErrorBitmap;
 //           
 private Bitmap linePressedBitmap;
 //    ,         
 private int offset;
 //                
 private boolean init;
 //      
 private int radius;
 //   
 private String password = "123456";
 //     
 private Point[][] points = new Point[3][3];
 private int width;
 private int height;
 private Matrix matrix = new Matrix();
 private float moveX = -1;
 private float moveY = -1;
 //        
 private boolean isMove;
 //       ,       ,                 
 private boolean isTouch = true;
 //            
 private List<Point> pressedPoint = new ArrayList<>();
 //        
 private OnScreenLockListener listener;

 public ScreenLockView(Context context) {
  this(context, null);
 }

 public ScreenLockView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public ScreenLockView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_error);
  normalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_normal);
  pressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_pressed);
  lineErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
  linePressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
  radius = normalBitmap.getWidth() / 2;
 }
 ...
}
구조 기 에서 우 리 는 주로 그림 을 불 러 와 완성 하고 격자 의 반지름,즉 그림 너비 의 절반 을 얻 었 다.
그리고 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 방법 을 살 펴 보 겠 습 니 다.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 if (widthSize > heightSize) {
  offset = (widthSize - heightSize) / 2;
 } else {
  offset = (heightSize - widthSize) / 2;
 }
 setMeasuredDimension(widthSize, heightSize);
}
onMeasure(int widthMeasureSpec, int heightMeasureSpec) 방법 에서 주로 대응 하 는 편 이 량 을 얻어 아래 onDraw(Canvas canvas) 에서 구 궁 격 을 화면 중앙 에 그 릴 수 있 도록 한다.
다음은 onDraw(Canvas canvas) 방법 입 니 다.

@Override
protected void onDraw(Canvas canvas) {
 if (!init) {
  width = getWidth();
  height = getHeight();
  initPoint();
  init = true;
 }
 //        
 drawPoint(canvas);

 if (moveX != -1 && moveY != -1) {
  //    
  drawLine(canvas);
 }
}
먼저 onDraw(Canvas canvas) 방법 을 첫 번 째 로 호출 할 지 여 부 를 판단 하고 첫 번 째 로 points 를 초기 화 합 니 다.

//     
private void initPoint() {
 points[0][0] = new Point(width / 4, offset + width / 4, "0");
 points[0][1] = new Point(width / 2, offset + width / 4, "1");
 points[0][2] = new Point(width * 3 / 4, offset + width / 4, "2");

 points[1][0] = new Point(width / 4, offset + width / 2, "3");
 points[1][1] = new Point(width / 2, offset + width / 2, "4");
 points[1][2] = new Point(width * 3 / 4, offset + width / 2, "5");

 points[2][0] = new Point(width / 4, offset + width * 3 / 4, "6");
 points[2][1] = new Point(width / 2, offset + width * 3 / 4, "7");
 points[2][2] = new Point(width * 3 / 4, offset + width * 3 / 4, "8");
}
initPoint() 방법 에서 주로 9 개의 칸 을 만 들 고 해당 하 는 위치 와 비밀 번 호 를 설정 했다.초기 화 완료 후 init 를 false 로 설정 하고 다음 에는 호출 하지 않 습 니 다.
고 개 를 돌려 onDraw(Canvas canvas) 의 다른 논 리 를 살 펴 보고 drawPoint(canvas) 을 사용 하여 칸 을 그립 니 다.

//        
private void drawPoint(Canvas canvas) {
 for (int i = 0; i < points.length; i++) {
  for (int j = 0; j < points[i].length; j++) {
   int state = points[i][j].getState();
   if (state == Point.NORMAL_MODE) {
    canvas.drawBitmap(normalBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
   } else if (state == Point.PRESSED_MODE) {
    canvas.drawBitmap(pressedBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
   } else {
    canvas.drawBitmap(errorBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
   }
  }
 }
}
칸 을 그 리 는 것 은 매우 간단 하 다.주로 세 가지 로 나 뉜 다.일반 모드 에서 의 칸,모드 에서 의 칸 과 오류 모드 에서 의 칸 이다.
onTouchEvent
칸 을 그린 후에 우 리 는 마지막 drawLine(canvas) 방법 을 보지 않 습 니 다.직선 을 그 리 는 것 은 사용자 손가락 의 터치 사건 과 밀접 한 관 계 를 가지 기 때문에 우 리 는 먼저 onTouchEvent(MotionEvent event) 방법 으로 눈 을 돌 립 니 다.

@Override
public boolean onTouchEvent(MotionEvent event) {
 if (isTouch) {
  float x = event.getX();
  float y = event.getY();
  Point point;
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
   //                        
    point = isPoint(x, y);
    if (point != null) {
     point.setState(Point.PRESSED_MODE); //        
     pressedPoint.add(point); 
    }
    break;
   case MotionEvent.ACTION_MOVE:
    if (pressedPoint.size() > 0) {
     point = isPoint(x, y);
     if (point != null) {
      if (!crossPoint(point)) {
       point.setState(Point.PRESSED_MODE);
       pressedPoint.add(point);
      }
     }
     moveX = x;
     moveY = y;
     isMove = true;
    }
    break;
   case MotionEvent.ACTION_UP:
    isMove = false;
    String tempPwd = "";
    for (Point p : pressedPoint) {
     tempPwd += p.getMark();
    }
    if (listener != null) {
     listener.getStringPassword(tempPwd);
    }

    if (tempPwd.equals(password)) {
     if (listener != null) {
      listener.isPassword(true);
     }
    } else {
     for (Point p : pressedPoint) {
      p.setState(Point.ERROR_MODE);
     }
     isTouch = false;
     this.postDelayed(runnable, 1000);
     if (listener != null) {
      listener.isPassword(false);
     }
    }
    break;
  }
  invalidate();
 }
 return true;
}

public interface OnScreenLockListener {
 public void getStringPassword(String password);
 public void isPassword(boolean flag);
}

public void setOnScreenLockListener(OnScreenLockListener listener) {
 this.listener = listener;
}
MotionEvent.ACTION_DOWN 에서 먼저 isPoint(float x, float y) 방법 에서 사용자 가 사건 을 만 지 는 좌표 점 이 구 궁 격 의 임 의 한 칸 안에 있 는 지 판단 했다.그렇다면 이 9 궁 격 의 칸 을 pressedPoint 에 추가 해 야 합 니 다.

//          
private Point isPoint(float x, float y) {
 Point point;
 for (int i = 0; i < points.length; i++) {
  for (int j = 0; j < points[i].length; j++) {
   point = points[i][j];
   if (isContain(point, x, y)) {
    return point;
   }
  }
 }
 return null;
}

//   (x,y)     
private boolean isContain(Point point, float x, float y) {
 //    (x,y)                   
 return Math.sqrt(Math.pow(x - point.getX(), 2f) + Math.pow(y - point.getY(), 2f)) <= radius;
}
다음은 MotionEvent.ACTION_MOVE 의 논 리 를 봐 야 한다.처음에는 사용자 가 만 진 점 이 구 궁 격 의 어떤 칸 인지 판단 했다.그러나 MotionEvent.ACTION_DOWN 보다 한 단계 더 많 았 다.사용자 가 어떤 칸 을 만 졌 다 면 이 칸 이 pressedPoint 에 포함 되 었 는 지 판단 해 야 한다.

//            pressedPoint   
private boolean crossPoint(Point point) {
 if (pressedPoint.contains(point)) {
  return true;
 }
 return false;
}
마지막 으로 MotionEvent.ACTION_UP 을 살 펴 보고 pressedPoint 에 저 장 된 칸 을 옮 겨 다 니 며 사용자 가 그 은 비밀 번 호 를 얻 은 다음 에 미리 설정 한 비밀번호 와 비교 하면 OnScreenLockListene r 모니터 를 되 돌려 줍 니 다.다 르 면 pressedPoint 의 모든 칸 모드 를 오류 모드 로 설정 하고 runnable 에서 reset() 을 호출 하여 pressedPoint 을 비우 고 보 기 를 다시 그리고 모니터 를 다시 조정 합 니 다.

private Runnable runnable = new Runnable() {
 @Override
 public void run() {
  isTouch = true;
  reset();
  invalidate();
 }
};

//     
private void reset(){
 for (int i = 0; i < points.length; i++) {
  for (int j = 0; j < points[i].length; j++) {
   points[i][j].setState(Point.NORMAL_MODE);
  }
 }
 pressedPoint.clear();
}
지금 우 리 는 고 개 를 돌려 이전에 onDraw(Canvas canvas) 안에 있 었 던 drawLine(Canvas canvas) 방법 을 살 펴 보 자.

//    
private void drawLine(Canvas canvas) {

 //  pressedPoint          ,    
 for (int i = 0; i < pressedPoint.size() - 1; i++) {
  //       
  Point point = pressedPoint.get(i);
  //        
  Point nextPoint = pressedPoint.get(i + 1);
  //     
  canvas.rotate(RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());

  matrix.reset();
  //            
  matrix.setScale(getDistance(point, nextPoint) / linePressedBitmap.getWidth(), 1f);
  //     
  matrix.postTranslate(point.getX(), point.getY() - linePressedBitmap.getWidth() / 2);


  if (point.getState() == Point.PRESSED_MODE) {
   canvas.drawBitmap(linePressedBitmap, matrix, null);
  } else {
   canvas.drawBitmap(lineErrorBitmap, matrix, null);
  }
  //        
  canvas.rotate(-RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());
 }

 //            
 if (isMove) {
  Point lastPoint = pressedPoint.get(pressedPoint.size() - 1);
  canvas.rotate(RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());

  matrix.reset();
  Log.i(TAG, "the distance : " + getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth());
  matrix.setScale(getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth(), 1f);
  matrix.postTranslate(lastPoint.getX(), lastPoint.getY() - linePressedBitmap.getWidth() / 2);
  canvas.drawBitmap(linePressedBitmap, matrix, null);

  canvas.rotate(-RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());
 }
}

//   point            
private float getDistance(Point point, float moveX, float moveY) {
 Point b = new Point(moveX,moveY,null);
 return getDistance(point,b);
}

//     point        
private float getDistance(Point point, Point nextPoint) {
 return (float) Math.sqrt(Math.pow(nextPoint.getX() - point.getX(), 2f) + Math.pow(nextPoint.getY() - point.getY(), 2f));
}
drawLine(Canvas canvas) 의 전체적인 논 리 는 복잡 하지 않 습 니 다.먼저 pressedPoint 의 모든 칸 을 순서대로 옮 겨 다 니 며 연결 합 니 다.이후 사용자 의 손가락 이 미 끄 러 지면 마지막 칸 과 사용자 의 손가락 이 만 지 는 점 을 연결한다.
총결산
ScreenLockView 의 코드 차이 가 많 지 않 은 것 이 바로 이것 입 니 다.실현 효과 가 좋 은 편 입 니 다.물론 좋아 하 는 구 궁 격 그림 을 스스로 설정 할 수 있 습 니 다.교체 만 하면 됩 니 다.이 글 에 의문 이 있 으 면 메 시 지 를 남 겨 도 됩 니 다.이 글 의 내용 이 여러분 의 안 드 로 이 드 개발 에 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기