Preface: in last week's project, you need to make a password lock function, and then you need to have a keyboard under the password, which is similar to Alipay's payment.
Of course, our project needs a little simpler, pure digital on it, and then last week Baidu customized keyboard, casually find a modification to use in the project.
Thank you very much for your Jane Friend. [Android] Customized Soft Keyboard for Input Payment Password
Today I took the time to write a custom View keyboard control. Now I want to share it with you.
Train of thought:
1. Layout:
(1) Palace: We can think of this layout as a palace layout, then we need to calculate the position (coordinates) of each small palace in the screen, and then draw the corresponding rectangle with canvas. (2) Numbers: We need to calculate the position of the center point (coordinates) in each small palace, and then draw the number with canvas. Of course, we know that the last one is the deletion key, not the number. We can prepare a picture and draw the picture to the corresponding position.
2. User actions:
(1) Press: Every time a user presses, it indicates the beginning of this action. So first, it is necessary to set all kinds of identification bits (user-defined identification bits) to the initial state. Then, it is necessary to record the coordinates pressed. Then, it is necessary to calculate which point in the palace corresponds to the coordinates pressed by the user and record the corresponding numbers. Finally refresh the layout (2) Lift: The user needs to refresh the layout when he lifts it, then calls back the number or delete key information recorded in the process of pressing to the user through the interface, and finally restores the identification bit. (3) Cancellation: Restore all identification bits to their initial state.
Okay, that's all. Let's take a look at the onDraw method.
protected void onDraw(Canvas canvas) { if (!isInit) { initData(); } mPaint.setColor(Color.WHITE); //Painting Palace //First row canvas.drawRoundRect(10, mHeight / 2 + 10, 10 + mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 10, 20 + 2 * mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 10, 30 + 3 * mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint); //The second row canvas.drawRoundRect(10, mHeight / 2 + 20 + mRectHeight, 10 + mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 20 + mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 20 + mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint); //The third row canvas.drawRoundRect(10, mHeight / 2 + 30 + 2 * mRectHeight, 10 + mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 30 + 2 * mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint); canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 30 + 2 * mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint); //The fourth row mPaint.setColor(Color.GRAY); canvas.drawRoundRect(10, mHeight / 2 + 40 + 3 * mRectHeight, 10 + mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint); mPaint.setColor(Color.WHITE); canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 40 + 3 * mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint); mPaint.setColor(Color.GRAY); canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 40 + 3 * mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint); mPaint.setColor(Color.BLACK); mPaint.setTextSize(60);// Set font size mPaint.setStrokeWidth(2); //Draw figures //First row canvas.drawText("1", xs[0], ys[0], mPaint); canvas.drawText("2", xs[1], ys[0], mPaint); canvas.drawText("3", xs[2], ys[0], mPaint); //The second row canvas.drawText("4", xs[0], ys[1], mPaint); canvas.drawText("5", xs[1], ys[1], mPaint); canvas.drawText("6", xs[2], ys[1], mPaint); //The third row canvas.drawText("7", xs[0], ys[2], mPaint); canvas.drawText("8", xs[1], ys[2], mPaint); canvas.drawText("9", xs[2], ys[2], mPaint); //The fourth row canvas.drawText(".", xs[0], ys[3], mPaint); canvas.drawText("0", xs[1], ys[3], mPaint); canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint); }
Note: The coordinates above need to be worked out by ourselves. Be patient and easy to work out. You just need to figure out what coordinate system the screen is in Android. After calculating the coordinates, we draw the palace first, then draw the number and delete the key. Someone here wants to ask, can I draw the number first, NO, because when you draw the number and then draw the palace, you will find that the number is missing. Why? Blocked by your palace > < So don't do that. Don't forget to set the brush to some attributes you need in the process of drawing.
Okay, after drawing, let's see how it works.
Styles come out Ha! But the design is not so good, you will have a look at Ha>._<
Then we need to rewrite the onTouch event to determine the user's behavior:
switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //Press down //Restore default values setDefault(); /** *Decide which palace the point is pressed */ invalidate();//Refresh interface return true; case MotionEvent.ACTION_UP: //Bounce up invalidate();//Refresh interface /** *Click once to finish, and return the number of clicks */ //Restore default setDefault(); return true; case MotionEvent.ACTION_CANCEL: //cancel //Restore default values setDefault(); return true; }
As shown in the pseudocode above, I wrote a way to determine which palace the point was pressed:
private void handleDown(float x, float y) { if (y < mHeight / 2) { return; } if (x >= 10 && x <= 10 + mRectWidth) { //First column clickX = xs[0]; if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //The first row (1) clickY = ys[0]; x1 = 10; y1 = mHeight / 2 + 10; x2 = 10 + mRectWidth; y2 = mHeight / 2 + 10 + mRectHeight; number = "1"; } else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //The second row (4) x1 = 10; y1 = mHeight / 2 + 20 + mRectHeight; x2 = 10 + mRectWidth; y2 = mHeight / 2 + 20 + 2 * mRectHeight; clickY = ys[1]; number = "4"; } else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //The third row (7) x1 = 10; y1 = mHeight / 2 + 30 + 2 * mRectHeight; x2 = 10 + mRectWidth; y2 = mHeight / 2 + 30 + 3 * mRectHeight; clickY = ys[2]; number = "7"; } else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //The fourth row (0) x1 = 10; y1 = mHeight / 2 + 40 + 3 * mRectHeight; x2 = 10 + mRectWidth; y2 = mHeight / 2 + 40 + 4 * mRectHeight; clickY = ys[3]; number = "."; } } else if (x >= 20 + mRectWidth && x <= 20 + 2 * mRectWidth) { //The second column clickX = xs[1]; if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //The first row (2) x1 = 20 + mRectWidth; y1 = mHeight / 2 + 10; x2 = 20 + 2 * mRectWidth; y2 = mHeight / 2 + 10 + mRectHeight; clickY = ys[0]; number = "2"; } else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //The second row (5) x1 = 20 + mRectWidth; y1 = mHeight / 2 + 20 + mRectHeight; x2 = 20 + 2 * mRectWidth; y2 = mHeight / 2 + 20 + 2 * mRectHeight; clickY = ys[1]; number = "5"; } else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //The third row (8) x1 = 20 + mRectWidth; y1 = mHeight / 2 + 30 + 2 * mRectHeight; x2 = 20 + 2 * mRectWidth; y2 = mHeight / 2 + 30 + 3 * mRectHeight; clickY = ys[2]; number = "8"; } else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //The fourth row (0) x1 = 20 + mRectWidth; y1 = mHeight / 2 + 40 + 3 * mRectHeight; x2 = 20 + 2 * mRectWidth; y2 = mHeight / 2 + 40 + 4 * mRectHeight; clickY = ys[3]; number = "0"; } } else if (x >= 30 + 2 * mRectWidth && x <= 30 + 3 * mRectWidth) { //The third column clickX = xs[2]; if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //The first row (3) x1 = 30 + 2 * mRectWidth; y1 = mHeight / 2 + 10; x2 = 30 + 3 * mRectWidth; y2 = mHeight / 2 + 10 + mRectHeight; clickY = ys[0]; number = "3"; } else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //The second row (6) x1 = 30 + 2 * mRectWidth; y1 = mHeight / 2 + 20 + mRectHeight; x2 = 30 + 3 * mRectWidth; y2 = mHeight / 2 + 20 + 2 * mRectHeight; clickY = ys[1]; number = "6"; } else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //The third row (9) x1 = 30 + 2 * mRectWidth; y1 = mHeight / 2 + 30 + 2 * mRectHeight; x2 = 30 + 3 * mRectWidth; y2 = mHeight / 2 + 30 + 3 * mRectHeight; clickY = ys[2]; number = "9"; } else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //Row 4 (Delete key) x1 = 30 + 2 * mRectWidth; y1 = mHeight / 2 + 40 + 3 * mRectHeight; x2 = 30 + 3 * mRectWidth; y2 = mHeight / 2 + 40 + 4 * mRectHeight; clickY = ys[3]; number = "delete"; } } }
Note: This method is related to the grid coordinates you calculated earlier, so you must not make mistakes in calculation.
So far, we have almost written, and then we need to provide an interface, open to the outside world, call it when it is convenient to use, and get digital or other information:
public interface OnNumberClickListener { //Number of callback clicks public void onNumberReturn(String number); //Callbacks to delete keys public void onNumberDelete(); }
Use in onTouch events:
case MotionEvent.ACTION_UP: //Bounce up invalidate();//Refresh interface //Click once to finish, and return the number of clicks if (onNumberClickListener != null) { if (number != null) { if (number.equals("delete")) { onNumberClickListener.onNumberDelete(); } else { onNumberClickListener.onNumberReturn(number); } } } //Restore default setDefault(); return true;
Then let's see how it works.
Function has also been achieved, but the obsessive-compulsive disorder is very strong, I look very uncomfortable, I do not know if you have, somehow, this is also a keyboard bar! Press the pop-up effect is not (did not change the background of the press), here we set a flag, press the pop-up refresh interface on it. Change the value of the identifier bit in the onTouch event, and then judge the identifier bit in the onDraw method.
The onTouch method adds:
case MotionEvent.ACTION_DOWN: //Press down type=0; case MotionEvent.ACTION_UP: //Bounce up type=1;
The onDraw method adds:
if (clickX > 0 && clickY > 0) { if (type == 0) { //Press refresh if (number.equals("delete")) { mPaint.setColor(Color.WHITE); canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint); canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint); } else { if (number.equals(".")) { mPaint.setColor(Color.WHITE); } else { mPaint.setColor(Color.GRAY); } canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint); mPaint.setColor(Color.BLACK); mPaint.setTextSize(60);// Set font size mPaint.setStrokeWidth(2); canvas.drawText(number, clickX, clickY, mPaint); } } else if (type == 1) { //Lifting refresh if (number.equals("delete")) { mPaint.setColor(Color.GRAY); canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint); canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint); } else { if (number.equals(".")) { mPaint.setColor(Color.GRAY); } else { mPaint.setColor(Color.WHITE); } canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint); mPaint.setColor(Color.BLACK); mPaint.setTextSize(60);// Set font size mPaint.setStrokeWidth(2); canvas.drawText(number, clickX, clickY, mPaint); } //After drawing, reset clickX = 0; clickY = 0; } }
Next, let's look at the effect.
Now it looks much more comfortable.~~
I have uploaded the code to Github portal Welcome to fork,star