SoFunction
Updated on 2025-03-02

Android nine-grid gesture password code design

Recently, because the project requires the use of the nine-grid password (also called the gesture track password), I specially learned how to implement the nine-grid password. Some people may think that there are examples of nine-grid passwords everywhere on the Internet, and many of them can even be run by copying them directly. Why do you still need to learn? Is it necessary to write it online? In fact, the purpose of writing this is to enhance and exercise your language organization skills. In fact, if it is just learning, it is not only about knowing the answer (complete effect) but also about knowing what it achieves.
OK, I won’t say much about it off topic. The problems to solve to implement the nine-grid password are:

1. Lay out nine points for the nine-grid password interface, that is, determine the location of each node. The distance from each point to up, down, left and right is the same. That is, nine points just form a square. Therefore, such a layout interface cannot be completed with the existing five layouts to customize this control.

2. Each node can only be selected once, so the selected status of each point must be recorded.

3. When the gesture starts to slide, how each node knows the trajectory of the gesture movement passes through itself.

4. Connecting. If the connection step is taken into account, it is of course very simple to directly use the canvas drawline method to draw the connection. But if you use a graph (that is, use a Bitmap) to draw, how to draw. After solving these four problems, we can make a nine-grid password.

5. Record the order of selected nodes.

With this question, we start to achieve the effect we want to achieve. Because there are many examples on the Internet, I will directly use other people’s examples to digest them. In order to better interpret this question, I will post the code and explain it later. I think I will just talk about it, and it will be easier for everyone to understand.

package .custon_view; 
 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
import ; 
 
/**
  * @Title:
  * @Description: Nine-grid password
  * @author lhz
  * @date September 16, 2013 3:48:10 pm
  * @version V1.0 Copyright (c) 2013 Company, Inc. All Rights Reserved.
  *
  */ 
public class SquaredPassWord extends View { 
 ImageView i; 
 private int length;// The nine-grid password is a square so you can just know the side length private Point[] points = new Point[9];// Nine-grid node private Bitmap defualtPointMap = (getResources(), .locus_round_original);// Under normal circumstances, the bitmap of the point private int poitleght = ();// The side length of the node; here the value takes into account the square state private Bitmap selectPointMap = (getResources(), .locus_round_click);// Select the bitmap of the point private Point startPoint;// Starting point private Point tempPoint;// Temporarily store the previous node private StringBuffer passWBuffer = new StringBuffer();// Save the password for the track order private Bitmap lineBitmap = (getResources(), .locus_line); 
 private int lineBitmapheight = (); 
 private double lineBitmapWidth = (); 
 // The following four variables are to draw the connection between the last one and the finger private int startX;// Move starting point X private int startY;// Move starting point Y private int moveX;// The moving X private int moveY;// The moving Y 
 public SquaredPassWord(Context context) { 
  super(context); 
 
 } 
 
 public SquaredPassWord(Context context, AttributeSet attrs) { 
  super(context, attrs); 
 
 } 
 
 public SquaredPassWord(Context context, AttributeSet attrs, int defStyle) { 
  super(context, attrs, defStyle); 
 
 } 
 
 @Override 
 public boolean onTouchEvent(MotionEvent event) { 
  boolean flag = true; 
  switch (()) { 
  case MotionEvent.ACTION_DOWN: 
   (0, ()); 
   int x = (int) (); 
   int y = (int) (); 
   for (Point point : points) { 
    if ((x, y)) { 
     (true); 
     tempPoint = startPoint = point; 
     startX = (); 
     startY = (); 
     (()); 
    } 
   } 
   invalidate(); 
   break; 
  case MotionEvent.ACTION_MOVE: 
   moveX = (int) (); 
   moveY = (int) (); 
   for (Point point : points) { 
    if ((moveX, moveY) && !()) { 
     (()); 
     (true); 
     tempPoint = point; 
     startX = (); 
     startY = (); 
     (()); 
    } 
   } 
   invalidate(); 
   break; 
  case MotionEvent.ACTION_UP: 
   reSetData(); 
   startX = startY = moveX = moveY = 0; 
   invalidate(); 
   flag = false; 
   break; 
 
  default: 
   break; 
  } 
  return flag; 
 } 
 
 /**
   * Reply to each point to the initial state
   */ 
 private void reSetData() { 
  for (Point point : points) { 
   (false); 
   (()); 
  } 
 } 
 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  // TODO Auto-generated method stub 
  int width = getWidth() - getPaddingLeft() - getPaddingRight(); 
  int height = getHeight() - getPaddingTop() - getPaddingBottom(); 
  length = (width < height ? width : height);// Get side length  if(!(length>0)){ 
    
  } 
  (length); 
  initPionts(points); 
  (widthMeasureSpec, heightMeasureSpec); 
 } 
 
 @Override 
 protected void onDraw(Canvas canvas) { 
  // TODO Auto-generated method stub 
  if (moveX != 0 && moveY != 0 && startX != 0 && startY != 0) { 
   // Draw the currently active line segment   drawLine(startX, startY, moveX, moveY, canvas); 
  } 
  drawLinePoint(canvas); 
  (canvas); 
 } 
 
 /**
   * Initial nodes
   *
   * @param pions
   */ 
 @SuppressWarnings("null") 
 private void initPionts(Point[] points) { 
  int spacing = (length - poitleght * 3) / 2; 
 
  if (points == null &&  != 9) {// Only nine grid processing   return; 
  } else { 
   for (int i = 0; i < 9; i++) { 
    int row = i / 3;// Number of rows    int column = i % 3;// Number of columns; find the balance 
    int x = (poitleght + spacing) * column + getPaddingLeft();// x coordinate    int y = (poitleght + spacing) * row + getPaddingTop();// y coordinate    Point point = new Point((i + 1), x, y, poitleght); 
    points[i] = point; 
   } 
  } 
 } 
 
 /**
   * Draw the connection trajectory between each node and the selected nodes
   *
   * @param canvas
   */ 
 private void drawLinePoint(Canvas canvas) { 
  if (startPoint != null) { 
   drawP2POrbit(startPoint, canvas); 
  } 
  Paint paint = null;// new Paint(); 
  // Draw a picture of each point  for (Point point : points) { 
   if (()) {// Draw a large circle    (selectPointMap, (), (), paint); 
   } else { 
    (defualtPointMap, (), (), paint); 
   } 
  } 
 } 
 
 /**
   * Draw the trajectory between points
   *
   * @param canvas
   */ 
 private void drawP2POrbit(Point point, Canvas canvas) { 
  if (() != ) { 
   // (matrix); 
   Point endPoint = null; 
   // Get the target node   for (Point point3 : points) { 
    if (() == ()) { 
     endPoint = point3; 
     break; 
    } 
   } 
   if (endPoint != null) { 
    // Draw lines    drawLine((), (), (), (), canvas); 
    // recursion    drawP2POrbit(endPoint, canvas); 
   } 
  } 
 } 
 
 /**
   * Draw lines
   *
   * @param startX
   * Starting point X
   * @param startY
   * Starting point Y
   * @param stopX
   * End point X
   * @param stopY
   * End point Y
   * @param canvas
   */ 
 private void drawLine(int startX, int startY, int stopX, int stopY, Canvas canvas) { 
  Paint paint = new Paint(); 
  // Get the oblique length  double hypotenuse = ((stopX - startX), (stopY - startY)); 
  // double side = stopX - startX;// adjacent side  // double piAngle = (side / hypotenuse);// pi angle  // float rotate = (float) (180 / * piAngle);// The angle of conversion  float rotate = getDegrees(startX, startY, stopX, stopY); 
  Matrix matrix = new Matrix(); 
  // (rotate);// Cannot use this matrixx to select angles only use canvas to choose lazy    // If you use matrix, it will cause the lines represented by the picture to be no longer at the center point.    (rotate, startX, startY); 
  (0, 0); 
  ((float) (hypotenuse / lineBitmapWidth), 1.0f); 
  (startX, startY - lineBitmapheight / ); 
  (lineBitmap, matrix, paint); 
  (-rotate, startX, startY);//recover  (); 
 
//  Paint paint1 = new Paint(); 
//  (); 
// (8);// Thickness//  (Paint.ANTI_ALIAS_FLAG); 
//  (startX, startY, stopX, stopY, paint1); 
 
 } 
 
 /**
   * Get angle
   *
   * @param startX
   * Starting point X
   * @param startY
   * Starting point Y
   * @param stopX
   * End point X
   * @param stopY
   * End point Y
   */ 
 private float getDegrees(int startX, int startY, int stopX, int stopY) { 
  // Get the oblique length  double hypotenuse = ((stopX - startX), (stopY - startY)); 
  double side = stopX - startX;// Neighboring side  double piAngle = (side / hypotenuse);// Pi angle  float rotate = (float) (180 /  * piAngle);// The angle of conversion (0--180);  if (stopY - startY < 0) {// If Y is less than 0, it means that the angle is in the third or fourth image limit   rotate = 360 - rotate; 
  } 
  return rotate; 
 } 
 
 /**
   * Track order password
   *
   * @return
   */ 
 public String getOrbitString() { 
  return (passWBuffer == null ? null : ()); 
 } 
 
 /**
   * Indicates a point
   *
   * @author lanhaizhong
   *
   */ 
 class Point { 
 
  private int id;// The id of the dot  private int nextID;// Continue to the next borrow point id  private int x;// The x-coordinate of the upper left corner of the point  private int y;// The y-coordinate in the upper left corner of the point  private boolean isSelected;// Is this node selected  private int width;// The length of the point is only considered here. 
  public Point() { 
   super(); 
   // TODO Auto-generated constructor stub 
  } 
 
  public int getId() { 
   return id; 
  } 
 
  public void setId(int id) { 
    = id; 
  } 
 
  public int getNextID() { 
   return nextID; 
  } 
 
  public void setNextID(int nextID) { 
    = nextID; 
  } 
 
  public int getX() { 
   return x; 
  } 
 
  public void setX(int x) { 
    = x; 
  } 
 
  public int getY() { 
   return y; 
  } 
 
  public void setY(int y) { 
    = y; 
  } 
 
  public boolean isSelected() { 
   return isSelected; 
  } 
 
  public void setSelected(boolean isSelected) { 
    = isSelected; 
  } 
 
  public int getWidth() { 
   return width; 
  } 
 
  public void setWidth(int width) { 
    = width; 
  } 
 
  public Point(int id, int x, int y, int width) { 
   super(); 
    = id; 
    = x; 
    = y; 
    = id; 
    = false; 
    = width; 
  } 
 
  public int getCenterX() { 
   return x + (width / 2); 
  } 
 
  private int getCenterY() { 
   return y + (width / 2); 
  } 
 
  /**
    * Determine whether a coordinate is in the graphical area of ​​a point
    *
    * @param x
    * @param y
    * @return
    */ 
  public boolean isInMyArea(int x, int y) { 
 
   // return ( < x && x < ( + width)) && ( < y && y < 
   // ( + width)); 
   return ((() - lineBitmapWidth / 2) < x && x < (() + lineBitmapWidth / 2)) 
     && ((() - lineBitmapWidth / 2) < y && y < (() + lineBitmapWidth / 2)); 
  } 
 } 
} 

1. Layout nine nodes.To layout the nine nodes, we must first know the control width and height, so that we can layout it. In the onMeasure(int widthMeasureSpec, int heightMeasureSpec) method, we can get the height and width by using getWidth() and getHeight(); method, because the custom layout also has a padding property, so we also need to calculate this step. So calculate the side length of the square that really lays out these nine nodes (we are making the nine-grid squares here) as:
int width = getWidth() - getPaddingLeft() - getPaddingRight();
int height = getHeight() - getPaddingTop() - getPaddingBottom();
length = (width < height ? width : height);//
Get the side length If this side length is greater than 0, I will start to determine the coordinate positions of the nine nodes. Side length = variable length of three nodes + two gap lengths.

Then now we can determine the location of each node. The first node is (0, 0), the second node is (0+pointwidth+spacing, 0),…. But what we need to do now is how to conclude the location of each node, that is, define a class of a node. Based on the several questions listed above, it can be concluded that the node will record the node's id, start and end coordinates, selected state, the coordinates of the node's center point, and the left side of the next node connected to the node. Based on these properties, we wrote the definition of Point's internal class as shown in the above code starting with lines 286. After defining the node's classes, we start to initialize the initPions method for them, such as 144.

2. Draw dots and draw lines. The drawLinePoint method shows the drawLinePoint method. You can draw all the points in one loop.To draw the connection, there is a situation to analyze now. 1) The connection between points means that the node has determined the id of the adding node. 2) The connection between the node and the gesture, that is, the node has not determined the connection between the node and the finger before the next node. Putting aside the method of drawing lines, not to mention drawing lines is to draw the line from the starting point to the end point. In this way, we can first define an empty-send method, pass canvas, and pass parameters representing the coordinates of the two points to drawLine method (starting from line 220), and then process drawing lines. According to the principle of easy first and difficult, we can easily draw the connection between points such as the 187-line drawP2POrbit method. Please click the connection line to your finger. According to common sense, we know that this point must be the last point selected. Moreover, this point is not static, so we have to define a temporary variable tempPoint to save this node (startPoit cannot be used to record the position of the startPoint to save the starting point). The end point of the connection is the coordinate point of the finger. This way, the two nodes can be disadvantaged. See the onTouchEvent method starting from line 57 of the code. The selected order of nodes recorded in this method is also recorded. In this way we can draw points and lines in the ondraw method, such as 128 lines to start.

3. Draw the line, drawLine (220 line) I don’t have anything to say here. The only thing I want to mention is the inverse trigonometric function problem and the angle conversion problem. I won’t talk about the inverse trigonometric function, everyone will just have not used it for too long. Forgot to take a look at it now. For this angle conversion, we need to pay special attention to the angle we want to convert is the angle of canvas instead of bitmap. It is difficult to determine the starting coordinate of bitmap because bitmap uses the upper left corner as the starting coordinate, and the starting coordinate becomes difficult to calculate as the angle changes. Therefore, it is relatively simple to convert the angle of the canvas. Finally, don’t forget to turn the angle back after drawing the evening line. And save.

4. Return the password string getOrbitString (line 276) is a method to return the password string. Just call it in the ontouch Action_UP of the caller (activity) ontouch.

The above is my understanding and explanation of the trajectory password. There are many shortcomings.
Sample download:Android Nine Grid Password Demo

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.