android Custom View (Custom Digital Keyboard)

Keywords: Android github

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

This is an android Xiaobai group I built. Interested Xiaobai welcomes you to join the group to learn together, and you are also welcome to join us in guiding and encouraging. Group number: 541144061

Posted by garry_224 on Thu, 21 Mar 2019 23:48:52 -0700