Or our usual rules, first put the final renderings 😄😄😄

Realize custom soft keyboard

It’s easy to implement a soft keyboard in three simple steps

  • 1. Define the keyboard structure through XML files
  • 2. Bind the defined keyboard structure to KeyboardView
  • 3, implementation,onKeyMethod that handles input and action events

1. Define the keyboard through XML

Define an XML folder under RES and create your soft keyboard layout XML file

We need to calculate the size according to the proportion of each key, %p is the percentage of the whole, pay attention to the spacing distance.


      
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1%p"
    android:keyWidth="10%p"
    android:keyHeight="50dp"
    android:verticalGap="1%p">

    <Row>
        <Key
            android:codes="81"<!--The final presentation of the contentunicode-->
            android:horizontalGap="1%p"<! -- Horizontal spacing ratio -->8.9% android: keyWidth = "p"<! -- Key width ratio -->
            android:keyEdgeFlags="left"<! -- Keyboard spacing on its way -->
            android:keyLabel="Q" <! -- Copy displayed on keyboard --> />
        <Key
            android:codes="87"
            android:keyWidth="8.9% p"
            android:keyLabel="W" />
        <Key
            android:codes="69"
            android:keyWidth="8.9% p"
            android:keyLabel="E" />
        <Key
            android:codes="82"
            android:keyWidth="8.9% p"
            android:keyLabel="R" />
        <Key
            android:codes="84"
            android:keyWidth="8.9% p"
            android:keyLabel="T" />
        <Key
            android:codes="89"
            android:keyWidth="8.9% p"
            android:keyLabel="Y" />
        <Key
            android:codes="85"
            android:keyWidth="8.9% p"
            android:keyLabel="U" />
        <Key
            android:codes="73"
            android:keyWidth="8.9% p"
            android:keyLabel="I" />
        <Key
            android:codes="79"
            android:keyWidth="8.9% p"
            android:keyLabel="O" />
        <Key
            android:codes="80"
            android:keyWidth="8.9% p"
            android:keyEdgeFlags="right"
            android:keyLabel="P" />
    </Row>

    <Row>
        <Key
            android:codes="65"
            android:horizontalGap="5.5% p"
            android:keyWidth="9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="A" />
        <Key
            android:codes="83"
            android:keyWidth="9%p"
            android:keyLabel="S" />
        <Key
            android:codes="68"
            android:keyWidth="9%p"
            android:keyLabel="D" />
        <Key
            android:codes="70"
            android:keyWidth="9%p"
            android:keyLabel="F" />
        <Key
            android:codes="71"
            android:keyWidth="9%p"
            android:keyLabel="G" />
        <Key
            android:codes="72"
            android:keyWidth="9%p"
            android:keyLabel="H" />
        <Key
            android:codes="74"
            android:keyWidth="9%p"
            android:keyLabel="J" />
        <Key
            android:codes="75"
            android:keyWidth="9%p"
            android:keyLabel="K" />
        <Key
            android:codes="76"
            android:keyWidth="9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="L" />
    </Row>

    <Row>
        <Key
            android:codes="1005"
            android:keyWidth="13.5% p"
            android:keyEdgeFlags="left"
            android:keyLabel="In" />
        <Key
            android:codes="90"
            android:keyWidth="9%p"
            android:keyLabel="Z" />
        <Key
            android:codes="88"
            android:keyWidth="9%p"
            android:keyLabel="X" />
        <Key
            android:codes="67"
            android:keyWidth="9%p"
            android:keyLabel="C" />
        <Key
            android:codes="86"
            android:keyWidth="9%p"
            android:keyLabel="V" />
        <Key
            android:codes="66"
            android:keyWidth="9%p"
            android:keyLabel="B" />
        <Key
            android:codes="78"
            android:keyWidth="9%p"
            android:keyLabel="N" />
        <Key
            android:codes="77"
            android:keyWidth="9%p"
            android:keyLabel="M" />
        <Key
            android:codes="- 5"
            android:isRepeatable="true"
            android:keyWidth="13.5% p" />
    </Row>
    <Row>
        <Key
            android:codes="1004"
            android:keyWidth="24%p"
            android:keyEdgeFlags="left"
            android:keyLabel="123" />
        <Key
            android:codes="32"
            android:keyWidth="48%p"
            android:keyLabel="space" />
        <Key
            android:codes="1003"
            android:keyWidth="24%p"
            android:keyEdgeFlags="right"
            android:keyLabel="Sure" />
    </Row>
</Keyboard>
Copy the code

2. Bind the XML file to keyboardView

The resulting Keyboard file is to be used in conjunction with the Keyboard class.

WordKeyboard = new Keyboard(context, R.xml.stock_word_keyboard);
Copy the code

Implement your own KeyboardView, inherited from KeyboardView.

public class MyKeyboardView extends KeyboardView {...init{
  WordKeyboard = new Keyboard(context, R.xml.stock_word_keyboard);
  // Bind your Keyboard to keyboardView
  this.setKeyboard(WordKeyboard);
}
Copy the code

The view we really need to add to the layout is actually a custom MyKeyboardView, which is used just like any other custom view.

3. Handle the click event onKey

If you complete the above two steps and add the View to your layout, you will find that you are ready to display it. But clicking doesn’t do anything.

If you want it to work, you need to implement onKey.

KeyboardView.this.setOnKeyboardActionListener(new OnKeyboardActionListener() {

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {
        try {
            Editable editable = editText.getText();
            int start = editText.getSelectionStart();
            int end = editText.getSelectionEnd();
            String code = String.valueOf(primaryCode);
            switch (code) {
            		// Switch to the numeric keypad
                case KeyboardKeyMap.TOOL_SWITCH_TO_NUM:
                    onKeyboardCallback.switchToNumberKeyboard();
                    break;
                    // Switch to the system keyboard
                case KeyboardKeyMap.TOOL_SWITCH_TO_WORD:
                    onKeyboardCallback.switchToSystemKeyboard();
                    break;
                    // Hide the keyboard
                case KeyboardKeyMap.TOOL_HIDE:
                    onKeyboardCallback.onHideStockKeyboard();
                    break;
                    / / delete
                case KeyboardKeyMap.TOOL_DEL:
                    if(editable ! =null && editable.length() > 0) {
                        if (start == end) {
                            editable.delete(start - 1, start);
                        } else{ editable.delete(start, end); }}break;
                    // Empty the input
                case KeyboardKeyMap.TOOL_CLEAR:
                    if(editable ! =null) {
                        editable.clear();
                    }
                    break;
                    // Confirm button
                case KeyboardKeyMap.TOOL_CONFIRM:
                    onKeyboardCallback.onConfirmStockKeyboard();
                    break;
                default:
                   // Normal input
                    if(editable ! =null) {
                        if (KeyboardKeyMap.isStockPrefix(code)) {
	                        // This handles more specific input definitions,
	                        // For example, you need to enter the name of the city, etc.
                            String resultCode = KeyboardKeyMap.findInputByKey(code);
                            editable.replace(start, end, resultCode);
                        } else {
                            // If it is a normal key (excluding confirm, clear, switch and other functions),
                            // Convert unicode on keys to normal numbers, such as keyboard P
                            // Unicode is 80, because XML defines keys for easy matching
                            // is the unicode used, which converts 80 to the actual P.String resultCode = Character.toString((char) primaryCode); editable.replace(start, end, resultCode); }}break; }}catch(Exception e) { e.printStackTrace(); }}}Copy the code

At this point, the basic custom keyboard definition operations are complete. Of course, if you’re using it for work, it’s not over, because custom keyboards generally need to coexist with system keyboards, so you’ll also need to deal with the glitch of keyboard switching. For the keyboard switch control, I will not introduce more here, you can refer to the soft keyboard + expression switch, processing scheme has been very mature. The principle is the same.

Some practical effect treatments are included

1, click the blank, close the soft keyboard, if there is content, start the content click, and the relationship between the soft keyboard, if it is sliding, only close the soft keyboard

Effect implementation is too simple, here do not do too much explanation, understand the event distribution naturally understand.

class AutoHideKeyboardCstLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : ConstraintLayout(context, attrs) {

    var keyboardHideListener: (() -> Unit)? = null

    override fun onInterceptTouchEvent(ev: MotionEvent?).: Boolean {
        if(ev? .action == MotionEvent.ACTION_DOWN) { keyboardHideListener? .invoke() }return super.onInterceptTouchEvent(ev)
    }
}
Copy the code

The close operation only needs to be performed in the callback method.

contentHideKeyboardCstLayout.keyboardHideListener = {
    hidePanelAndKeyboard()
}
Copy the code

2, switch soft keyboard panel, very simple implementation

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@android:color/white"
    android:elevation="0.5 dp">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvStockNumKeyboard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="10dp"
        android:button="@null"
        android:padding="6dp"
        android:text="123"
        android:textColor="@drawable/stock_switch_label_color"
        android:textSize="16dp"
        android:textStyle="bold" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvStockWordKeyboard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="18dp"
        android:layout_toEndOf="@+id/tvStockNumKeyboard"
        android:button="@null"
        android:padding="6dp"
        android:text="ABC"
        android:textColor="@drawable/stock_switch_label_color"
        android:textSize="16dp"
        android:textStyle="bold" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvSystemKeyboard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginStart="18dp"
        android:layout_toEndOf="@+id/tvStockWordKeyboard"
        android:button="@null"
        android:padding="6dp"
        android:text="Chinese"
        android:textColor="@drawable/stock_switch_label_color"
        android:textSize="16dp"
        android:textStyle="bold" />

    <FrameLayout
        android:id="@+id/keyboardDone"
        android:layout_width="60sp"
        android:layout_height="match_parent"
        android:layout_alignParentEnd="true"
        android:layout_centerVertical="true">

        <ImageView
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_gravity="center"
            android:contentDescription="@null"
            android:scaleType="centerInside"
            android:src="@drawable/keyboard_done_"
            android:textColor="@color/white"
            android:textSize="16sp" />
    </FrameLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5 dp"
        android:background="#EEEEEE" />
</RelativeLayout>
Copy the code

Color selector


      
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#F14400" android:state_selected="true" />
    <item android:color="# 334455" android:state_selected="false" />
</selector>
Copy the code
class KeyboardSwitcher @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {

    private var mViewBinding: RtcKeyboardSwitcherBinding? = null
    private var mStockKeyboardView: StockKeyboardView? = null

    init {
        mViewBinding = RtcKeyboardSwitcherBinding.inflate(LayoutInflater.from(context), this.true)}fun pressNumberKeyboard(a){ mViewBinding? .tvStockNumKeyboard? .performClick() }fun pressWordKeyboard(a){ mViewBinding? .tvStockWordKeyboard? .performClick() }fun pressSystemKeyboard(a){ mViewBinding? .tvSystemKeyboard? .performClick() }fun switchKeyboard(
        _switchKeyboard: (isSystemKeyboard: Boolean) - >Unit,
        _keyboardDone: () -> Unit
    ){ mViewBinding? .apply { tvStockNumKeyboard.setOnClickListener { resetSelectedState() _switchKeyboard.invoke(false) mStockKeyboardView? .showNumberKeyboard() it.isSelected =true
            }
            tvStockWordKeyboard.setOnClickListener {
                resetSelectedState()
                _switchKeyboard.invoke(false) mStockKeyboardView? .showWordKeyboard() it.isSelected =true
            }
            tvSystemKeyboard.setOnClickListener {
                resetSelectedState()
                _switchKeyboard.invoke(true)
                it.isSelected = true
            }
            keyboardDone.setOnClickListener {
                _keyboardDone.invoke()
            }
        }
    }

    fun setDefaultKeyboard(index: Int){ resetSelectedState() mViewBinding? .apply {when (index) {
                0 -> {
                    tvStockNumKeyboard.isSelected = true
                }
                1 -> {
                    tvStockWordKeyboard.isSelected = true
                }
                2 -> {
                    tvSystemKeyboard.isSelected = true}}}}private fun resetSelectedState(a){ mViewBinding? .apply { tvStockNumKeyboard.isSelected =false
            tvStockWordKeyboard.isSelected = false
            tvSystemKeyboard.isSelected = false}}override fun onTouchEvent(event: MotionEvent?).: Boolean {
        if(event? .action == MotionEvent.ACTION_DOWN) { performClick() }return true
    }

    override fun performClick(a): Boolean {
        return super.performClick()
    }

    fun attach(stockKeyboardView: StockKeyboardView) {
        this.mStockKeyboardView = stockKeyboardView
    }

    fun showNumberKeyboard(a) {
        this.mStockKeyboardView? .showNumberKeyboard() } }Copy the code