比较好用的自定义软键盘

1.首先是在AndroidManifest.xml,可以添加权限

<uses-permission android:name="android.permission.VIBRATE" />   <!-- 震动权限 -->

2.如果不想一开始就弹出系统键盘可以在activity中设置

android:windowSoftInputMode="stateAlwaysHidden|adjustPan"

3.用到的layout有keyboard_preview_layout.xml和layout_keyboard_containor.xml

keyboard_preview_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:background="@drawable/keyboard_preview_bg"
    android:gravity="center_horizontal"
    android:textColor="@color/keyboardNormal"
    android:textSize="35sp"
    android:textStyle="bold" />

layout_keyboard_containor.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/keyboardBackColor"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/keyboardHeader"
        android:layout_width="match_parent"
        android:layout_height="@dimen/keyboard_tip_height"
        android:background="@color/keyboardBackColor">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:orientation="horizontal"
            tools:ignore="RelativeOverlap,UseCompoundDrawables">

            <ImageView
                android:layout_width="25sp"
                android:layout_height="25sp"
                android:layout_gravity="center_vertical"
                android:contentDescription="@string/description"
                android:src="@drawable/shield" />

            <TextView
                android:id="@+id/keyboardTip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="@string/safe_keyboard"
                android:textColor="@color/lightGray"
                android:textSize="16sp" />
        </LinearLayout>

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

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

    </RelativeLayout>

    <FrameLayout
        android:id="@+id/keyboardLayer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="2sp"
        android:layout_marginBottom="10sp">

        <com.example.myapplication.keyboard.SafeKeyboardView
            android:id="@+id/safeKeyboardLetter"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:animateLayoutChanges="true"
            android:background="@color/keyboardBackColor"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:keyBackground="@drawable/keyboard_press_bg"
            android:keyPreviewHeight="60dp"
            android:keyPreviewLayout="@layout/keyboard_preview_layout"
            android:keyPreviewOffset="0dp"
            android:keyTextColor="@color/white"
            app:enable_vibrate="false"
            app:random_digit="false"
            app:remember_last_type="true" />

    </FrameLayout>
</LinearLayout>
4.资源文件res中drawable中需要用到bg_keyboard_done.xml,ic_launcher_background.xml,keyboard_change.xml及keyboard_press_bg.xml

bg_keyboard_done.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="1dp" />
            <solid android:color="@color/keyboardNormal" />
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <corners android:radius="1dp" />
            <solid android:color="@color/transparent" />
        </shape>
    </item>
</selector>

ic_launcher_background.xml:

<?xml version="1.0" encoding="utf-8"?>
<vector
    android:height="108dp"
    android:width="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#008577"
          android:pathData="M0,0h108v108h-108z"/>
    <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
    <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
          android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
</vector>
keyboard_change.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/keyboardPress" />
            <corners android:radius="5dp" />
        </shape>
    </item>

    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/keyboardNormal" />
            <corners android:radius="5dp" />
        </shape>
    </item>

</selector>

keyboard_press_bg.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/keyboardPress" />
            <corners android:radius="5dp" />
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/keyboardKeyBack" />
            <corners android:radius="5dp" />
        </shape>
    </item>
</selector>

以及下列图片:icon_capital_default.png,icon_capital_selected.png,icon_capital_selected_lock.png,icon_del.png,

keyboard_done.png,keyboard_done_.png,keyboard_preview_bg.png和shield.png

res/xml中加入如下文件:keyboard_id_card_zn.xml,keyboard_letter.xml,keyboard_letter_num.xml,keyboard_num_only.xml,keyboard_num_symbol.xml,keyboard_symbol.xml

keyboard_id_card_zn.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2.5%p"
    android:keyWidth="30%p"
    android:keyHeight="@dimen/key_num_height"
    android:verticalGap="@dimen/key_vertical_gap">

    <Row>
        <Key
            android:codes="49"
            android:keyLabel="1" />
        <Key
            android:codes="50"
            android:keyLabel="2" />
        <Key
            android:codes="51"
            android:keyLabel="3" />
    </Row>

    <Row>
        <Key
            android:codes="52"
            android:keyLabel="4" />
        <Key
            android:codes="53"
            android:keyLabel="5" />
        <Key
            android:codes="54"
            android:keyLabel="6" />
    </Row>

    <Row>
        <Key
            android:codes="55"
            android:keyLabel="7" />
        <Key
            android:codes="56"
            android:keyLabel="8" />
        <Key
            android:codes="57"
            android:keyLabel="9" />
    </Row>

    <Row>
        <Key
            android:codes="88"
            android:keyLabel="X" />
        <Key
            android:codes="48"
            android:keyLabel="0" />
        <Key
            android:codes="-5"
            android:isRepeatable="true" />
    </Row>
</Keyboard>

keyboard_letter.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1%p"
    android:keyWidth="10%p"
    android:keyHeight="@dimen/key_height"
    android:verticalGap="@dimen/key_vertical_gap">
    <Row>
        <Key
            android:codes="113"
            android:horizontalGap="1%p"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="q" />
        <Key
            android:codes="119"
            android:keyWidth="8.9%p"
            android:keyLabel="w" />
        <Key
            android:codes="101"
            android:keyWidth="8.9%p"
            android:keyLabel="e" />
        <Key
            android:codes="114"
            android:keyWidth="8.9%p"
            android:keyLabel="r" />
        <Key
            android:codes="116"
            android:keyWidth="8.9%p"
            android:keyLabel="t" />
        <Key
            android:codes="121"
            android:keyWidth="8.9%p"
            android:keyLabel="y" />
        <Key
            android:codes="117"
            android:keyWidth="8.9%p"
            android:keyLabel="u" />
        <Key
            android:codes="105"
            android:keyWidth="8.9%p"
            android:keyLabel="i" />
        <Key
            android:codes="111"
            android:keyWidth="8.9%p"
            android:keyLabel="o" />
        <Key
            android:codes="112"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="right"
            android:keyLabel="p" />
    </Row>

    <Row>
        <Key
            android:codes="97"
            android:horizontalGap="5.5%p"
            android:keyWidth="9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="a" />
        <Key
            android:codes="115"
            android:keyWidth="9%p"
            android:keyLabel="s" />
        <Key
            android:codes="100"
            android:keyWidth="9%p"
            android:keyLabel="d" />
        <Key
            android:codes="102"
            android:keyWidth="9%p"
            android:keyLabel="f" />
        <Key
            android:codes="103"
            android:keyWidth="9%p"
            android:keyLabel="g" />
        <Key
            android:codes="104"
            android:keyWidth="9%p"
            android:keyLabel="h" />
        <Key
            android:codes="106"
            android:keyWidth="9%p"
            android:keyLabel="j" />
        <Key
            android:codes="107"
            android:keyWidth="9%p"
            android:keyLabel="k" />
        <Key
            android:codes="108"
            android:keyWidth="9%p"
            android:keyEdgeFlags="right"
            android:keyLabel="l" />
    </Row>

    <Row>
        <Key
            android:codes="-1"
            android:isModifier="true"
            android:isSticky="true"
            android:keyWidth="13%p"
            android:keyEdgeFlags="left" />
        <Key
            android:codes="122"
            android:horizontalGap="1.5%p"
            android:keyWidth="9%p"
            android:keyLabel="z" />
        <Key
            android:codes="120"
            android:keyWidth="9%p"
            android:keyLabel="x" />
        <Key
            android:codes="99"
            android:keyWidth="9%p"
            android:keyLabel="c" />
        <Key
            android:codes="118"
            android:keyWidth="9%p"
            android:keyLabel="v" />
        <Key
            android:codes="98"
            android:keyWidth="9%p"
            android:keyLabel="b" />
        <Key
            android:codes="110"
            android:keyWidth="9%p"
            android:keyLabel="n" />
        <Key
            android:codes="109"
            android:keyWidth="9%p"
            android:keyLabel="m" />
        <Key
            android:codes="-5"
            android:horizontalGap="1.5%p"
            android:isRepeatable="true"
            android:keyWidth="13%p" />
    </Row>

    <Row android:rowEdgeFlags="bottom">
        <Key
            android:codes="-2"
            android:keyWidth="17%p"
            android:keyLabel="123" />
        <Key
            android:codes="44"
            android:keyWidth="12%p"
            android:keyLabel="," />
        <Key
            android:codes="32"
            android:isRepeatable="false"
            android:keyWidth="36%p"
            android:keyLabel="space" />
        <Key
            android:codes="46"
            android:keyWidth="12%p"
            android:keyLabel="." />
        <Key
            android:codes="100860"
            android:keyWidth="17%p"
            android:keyEdgeFlags="right"
            android:keyLabel="#+=" />
    </Row>
</Keyboard>

keyboard_letter_num.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1%p"
    android:keyWidth="10%p"
    android:keyHeight="@dimen/key_height"
    android:verticalGap="@dimen/key_vertical_gap">

    <Row>
        <Key
            android:codes="49"
            android:horizontalGap="1%p"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="1" />
        <Key
            android:codes="50"
            android:keyWidth="8.9%p"
            android:keyLabel="2" />
        <Key
            android:codes="51"
            android:keyWidth="8.9%p"
            android:keyLabel="3" />
        <Key
            android:codes="52"
            android:keyWidth="8.9%p"
            android:keyLabel="4" />
        <Key
            android:codes="53"
            android:keyWidth="8.9%p"
            android:keyLabel="5" />
        <Key
            android:codes="54"
            android:keyWidth="8.9%p"
            android:keyLabel="6" />
        <Key
            android:codes="55"
            android:keyWidth="8.9%p"
            android:keyLabel="7" />
        <Key
            android:codes="56"
            android:keyWidth="8.9%p"
            android:keyLabel="8" />
        <Key
            android:codes="57"
            android:keyWidth="8.9%p"
            android:keyLabel="9" />
        <Key
            android:codes="48"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="right"
            android:keyLabel="0" />
    </Row>

    <Row>
        <Key
            android:codes="113"
            android:horizontalGap="1%p"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="q" />
        <Key
            android:codes="119"
            android:keyWidth="8.9%p"
            android:keyLabel="w" />
        <Key
            android:codes="101"
            android:keyWidth="8.9%p"
            android:keyLabel="e" />
        <Key
            android:codes="114"
            android:keyWidth="8.9%p"
            android:keyLabel="r" />
        <Key
            android:codes="116"
            android:keyWidth="8.9%p"
            android:keyLabel="t" />
        <Key
            android:codes="121"
            android:keyWidth="8.9%p"
            android:keyLabel="y" />
        <Key
            android:codes="117"
            android:keyWidth="8.9%p"
            android:keyLabel="u" />
        <Key
            android:codes="105"
            android:keyWidth="8.9%p"
            android:keyLabel="i" />
        <Key
            android:codes="111"
            android:keyWidth="8.9%p"
            android:keyLabel="o" />
        <Key
            android:codes="112"
            android:keyWidth="8.9%p"
            android:keyEdgeFlags="right"
            android:keyLabel="p" />
    </Row>

    <Row>
        <Key
            android:codes="97"
            android:horizontalGap="5.5%p"
            android:keyWidth="9%p"
            android:keyEdgeFlags="left"
            android:keyLabel="a" />
        <Key
            android:codes="115"
            android:keyWidth="9%p"
            android:keyLabel="s" />
        <Key
            android:codes="100"
            android:keyWidth="9%p"
            android:keyLabel="d" />
        <Key
            android:codes="102"
            android:keyWidth="9%p"
            android:keyLabel="f" />
        <Key
            android:codes="103"
            android:keyWidth="9%p"
            android:keyLabel="g" />
        <Key
            android:codes="104"
            android:keyWidth="9%p"
            android:keyLabel="h" />
        <Key
            android:codes="106"
            android:keyWidth="9%p"
            android:keyLabel="j" />
        <Key
            android:codes="107"
            android:keyWidth="9%p"
            android:keyLabel="k" />
        <Key
            android:codes="108"
            android:keyWidth="9%p"
            android:keyEdgeFlags="right"
            android:keyLabel="l" />
    </Row>

    <Row>
        <Key
            android:codes="-1"
            android:isModifier="true"
            android:isSticky="true"
            android:keyWidth="13%p"
            android:keyEdgeFlags="left" />
        <Key
            android:codes="122"
            android:horizontalGap="1.5%p"
            android:keyWidth="9%p"
            android:keyLabel="z" />
        <Key
            android:codes="120"
            android:keyWidth="9%p"
            android:keyLabel="x" />
        <Key
            android:codes="99"
            android:keyWidth="9%p"
            android:keyLabel="c" />
        <Key
            android:codes="118"
            android:keyWidth="9%p"
            android:keyLabel="v" />
        <Key
            android:codes="98"
            android:keyWidth="9%p"
            android:keyLabel="b" />
        <Key
            android:codes="110"
            android:keyWidth="9%p"
            android:keyLabel="n" />
        <Key
            android:codes="109"
            android:keyWidth="9%p"
            android:keyLabel="m" />
        <Key
            android:codes="-5"
            android:horizontalGap="1.5%p"
            android:isRepeatable="true"
            android:keyWidth="13%p" />
    </Row>

    <Row android:rowEdgeFlags="bottom">
        <Key
            android:codes="-2"
            android:keyWidth="17%p"
            android:keyLabel="123" />
        <Key
            android:codes="44"
            android:keyWidth="12%p"
            android:keyLabel="," />
        <Key
            android:codes="32"
            android:isRepeatable="false"
            android:keyWidth="36%p"
            android:keyLabel="space" />
        <Key
            android:codes="46"
            android:keyWidth="12%p"
            android:keyLabel="." />
        <Key
            android:codes="100860"
            android:keyWidth="17%p"
            android:keyEdgeFlags="right"
            android:keyLabel="#+=" />
    </Row>
</Keyboard>

keyboard_num_only.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2.5%p"
    android:keyWidth="30%p"
    android:keyHeight="@dimen/key_num_height"
    android:verticalGap="@dimen/key_vertical_gap">

    <Row>
        <Key
            android:codes="49"
            android:keyLabel="1" />
        <Key
            android:codes="50"
            android:keyLabel="2" />
        <Key
            android:codes="51"
            android:keyLabel="3" />
    </Row>

    <Row>
        <Key
            android:codes="52"
            android:keyLabel="4" />
        <Key
            android:codes="53"
            android:keyLabel="5" />
        <Key
            android:codes="54"
            android:keyLabel="6" />
    </Row>

    <Row>
        <Key
            android:codes="55"
            android:keyLabel="7" />
        <Key
            android:codes="56"
            android:keyLabel="8" />
        <Key
            android:codes="57"
            android:keyLabel="9" />
    </Row>

    <Row>
        <Key
            android:codes="100861"
            android:keyLabel="SValence" />
        <Key
            android:codes="48"
            android:keyLabel="0" />
        <Key
            android:codes="-5"
            android:isRepeatable="true" />
    </Row>
</Keyboard>

keyboard_num_symbol.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2%p"
    android:keyWidth="20.1%p"
    android:keyHeight="@dimen/key_num_height"
    android:verticalGap="@dimen/key_vertical_gap">

    <Row>
        <Key
            android:codes="43"
            android:horizontalGap="1%p"
            android:keyWidth="14.6%p"
            android:keyLabel="+" />
        <Key
            android:codes="49"
            android:keyLabel="1" />
        <Key
            android:codes="50"
            android:keyLabel="2" />
        <Key
            android:codes="51"
            android:keyLabel="3" />
        <Key
            android:codes="64"
            android:keyWidth="14.6%p"
            android:keyLabel="@string/keyboard_key1" />
    </Row>

    <Row>
        <Key
            android:codes="45"
            android:horizontalGap="1%p"
            android:keyWidth="14.6%p"
            android:keyLabel="-" />
        <Key
            android:codes="52"
            android:keyLabel="4" />
        <Key
            android:codes="53"
            android:keyLabel="5" />
        <Key
            android:codes="54"
            android:keyLabel="6" />
        <Key
            android:codes="95"
            android:keyWidth="14.6%p"
            android:keyLabel="_" />
    </Row>

    <Row>
        <Key
            android:codes="47"
            android:horizontalGap="1%p"
            android:keyWidth="14.6%p"
            android:keyLabel="/" />
        <Key
            android:codes="55"
            android:keyLabel="7" />
        <Key
            android:codes="56"
            android:keyLabel="8" />
        <Key
            android:codes="57"
            android:keyLabel="9" />
        <Key
            android:codes="42"
            android:keyWidth="14.6%p"
            android:keyLabel="*" />
    </Row>

    <Row>
        <Key
            android:codes="-2"
            android:horizontalGap="1%p"
            android:keyWidth="14.6%p"
            android:keyLabel="ABC" />
        <Key
            android:codes="32"
            android:isRepeatable="false"
            android:keyLabel="space" />
        <Key
            android:codes="48"
            android:keyLabel="0" />
        <Key
            android:codes="46"
            android:keyLabel="." />
        <Key
            android:codes="-5"
            android:isRepeatable="true"
            android:keyWidth="14.6%p" />
    </Row>
</Keyboard>

keyboard_symbol.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyHeight="@dimen/key_num_height"
    android:keyWidth="10%p"
    android:horizontalGap="1%p"
    android:verticalGap="@dimen/key_vertical_gap">
    <Row>
        <Key android:keyWidth="8.9%p" android:codes="33" android:keyLabel="!" android:keyEdgeFlags="left" />
        <Key android:keyWidth="8.9%p" android:codes="64" android:keyLabel="@string/keyboard_key1" /><!--@-->
        <Key android:keyWidth="8.9%p" android:codes="35" android:keyLabel="#" />
        <Key android:keyWidth="8.9%p" android:codes="36" android:keyLabel="$" />
        <Key android:keyWidth="8.9%p" android:codes="37" android:keyLabel="%" />
        <Key android:keyWidth="8.9%p" android:codes="94" android:keyLabel="^" />
        <Key android:keyWidth="8.9%p" android:codes="38" android:keyLabel="@string/keyboard_key2" /><!--&-->
        <Key android:keyWidth="8.9%p" android:codes="42" android:keyLabel="*" />
        <Key android:keyWidth="8.9%p" android:codes="40" android:keyLabel="(" />
        <Key android:keyWidth="8.9%p" android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right" />
    </Row>

    <Row>
        <Key android:keyWidth="8.9%p" android:codes="39"
            android:keyEdgeFlags="left" android:keyLabel="'" />
        <Key android:keyWidth="8.9%p" android:codes="34" android:keyLabel="@string/keyboard_key3" /> <!--"-->
        <Key android:keyWidth="8.9%p" android:codes="61" android:keyLabel="=" />
        <Key android:keyWidth="8.9%p" android:codes="95" android:keyLabel="_" />
        <Key android:keyWidth="8.9%p" android:codes="58" android:keyLabel=":" />
        <Key android:keyWidth="8.9%p" android:codes="59" android:keyLabel=";" />
        <Key android:keyWidth="8.9%p" android:codes="63" android:keyLabel="\?" />
        <Key android:keyWidth="8.9%p" android:codes="126" android:keyLabel="~" />
        <Key android:keyWidth="8.9%p" android:codes="124" android:keyLabel="|" />
        <Key android:keyWidth="8.9%p" android:codes="183" android:keyEdgeFlags="right"
            android:keyLabel="·" />
    </Row>

    <Row>
        <Key android:horizontalGap="3.5%p" android:keyWidth="9%p" android:codes="43" android:keyLabel="+" />
        <Key android:keyWidth="9%p" android:codes="45" android:keyLabel="-" />
        <Key android:keyWidth="9%p" android:codes="92" android:keyLabel="\\" />
        <Key android:keyWidth="9%p" android:codes="47" android:keyLabel="/" />
        <Key android:keyWidth="9%p" android:codes="91" android:keyLabel="[" />
        <Key android:keyWidth="9%p" android:codes="93" android:keyLabel="]" />
        <Key android:keyWidth="9%p" android:codes="123" android:keyLabel="{" />
        <Key android:keyWidth="9%p" android:codes="125" android:keyLabel="}" />
        <Key android:horizontalGap="3.5%p" android:keyWidth="13%p" android:codes="-5"
            android:isRepeatable="true"/>
    </Row>

    <Row android:rowEdgeFlags="bottom">
        <Key android:keyWidth="13%p" android:codes="-2"
            android:keyLabel="123" />
        <Key android:keyWidth="9%p" android:horizontalGap="1.5%p" android:codes="44" android:keyLabel="," />
        <Key android:keyWidth="9%p" android:codes="46" android:keyLabel="." />
        <Key android:keyWidth="9%p" android:codes="60" android:keyLabel="@string/keyboard_key4" />
        <!--<-->
        <!-->-->
        <Key android:keyWidth="9%p" android:codes="62" android:keyLabel="@string/keyboard_key5" />
        <Key android:keyWidth="9%p" android:codes="8364" android:keyLabel="€" />
        <Key android:keyWidth="9%p" android:codes="163" android:keyLabel="£" />
        <Key android:keyWidth="9%p" android:codes="165" android:keyLabel="¥" />
        <Key android:keyWidth="13%p" android:codes="100860" android:horizontalGap="1.5%p"
            android:keyEdgeFlags="right" android:keyLabel="ABC" />
    </Row>
</Keyboard>

res/values文件夹中需修改attrs.xml,colors.xml,dimens.xml,strings.xml,styles.xml,根据实际情况添加

attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SafeKeyboardView">
        <attr name="random_digit" format="boolean" />                   <!-- 数字随机显示 -->
        <attr name="remember_last_type" format="boolean" />             <!-- 记住上次键盘输入类型 -->
        <attr name="enable_vibrate" format="boolean" />                 <!-- 开启震动 -->
    </declare-styleable>
</resources>

colors.xml:

<color name="keyboardNormal">#1c69ac</color>
    <color name="keyboardBackColor">#212121</color>
    <color name="transparent">#00000000</color>
    <color name="white">#FFFFFF</color>
    <color name="lightGray">#D3D3D3</color>
    <color name="keyboardPress">#1296db</color>
    <color name="keyboardKeyBack">#474749</color>
    <color name="deepDarkTransparent">#99222222</color>

dimens.xml:

<dimen name="key_height">50dp</dimen>
    <dimen name="key_num_height">50dp</dimen>
    <dimen name="key_horizontal_gap">10dp</dimen>
    <dimen name="key_vertical_gap">6dp</dimen>
    <dimen name="keyboard_tip_height">50dp</dimen>
    <dimen name="dialog_horizontal_margin">16dp</dimen>
    <dimen name="dialog_vertical_margin">16dp</dimen>
    <dimen name="dialog_title_size">16sp</dimen>
    <dimen name="dialog_button_text_size">15sp</dimen>

strings.xml:

<string name="mLabelTextSize">mLabelTextSize</string>
    <string name="description">SafeKeyboard</string>
    <string name="safe_keyboard">智能加密安全软键盘</string>
    <string name="keyboard_system">系统键盘</string>
    <string name="keyboard_demo_new">"安全软键盘"</string>
    <string name="keyboard_demo_new2">安全软键盘(数字)</string>
    <string name="keyboard_demo_new3">安全软键盘(IdCard)</string>
    <string name="alert_test">Alert Dialog Test</string>
    <string name="popup_test">Popup Window Test</string>
    <string name="scroll_view_test">Scroll View Test</string>
    <string name="keyboard_key1">"&#64;"</string>
    <string name="keyboard_key2">"&#38;"</string>
    <string name="keyboard_key3">\"</string>
    <string name="keyboard_key4">"&#60;"</string>
    <string name="keyboard_key5">"&#62;"</string>

styles.xml:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="edit_AlertDialog_style" parent="@android:style/Theme.Dialog">
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <!-- 是否启用标题栏 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">true</item>
        <!-- 是否使用背景半透明 -->
    </style>

JAVA文件主要用到SafeKeyboard.java,SafeKeyboardView.java,ViewPoint.java

SafeKeyboard.java:

package com.example.myapplication.keyboard;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.Vibrator;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import com.example.myapplication.R;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * Created by Administrator on 2018/3/7 0007.
 */

public class SafeKeyboard {

    private static final String TAG = "SafeKeyboard";

    private Context mContext;               //上下文

    private LinearLayout keyboardParentView;
    private View keyContainer;              //自定义键盘的容器View
    private SafeKeyboardView keyboardView;  //键盘的View
    private Keyboard keyboardNumber;        //数字键盘
    private Keyboard keyboardNumberOnly;    //纯数字键盘
    private Keyboard keyboardLetterOnly;    //字母键盘
    private Keyboard keyboardLetterNum;     //字母数字键盘
    private Keyboard keyboardSymbol;        //符号键盘
    private Keyboard keyboardIdCard;        //中国身份证号码键盘
    private Keyboard keyboardLetter;        //字母键盘总成
    private static boolean isCapes = false;
    private boolean isCapLock = false;
    private boolean isShowStart = false;
    private boolean isHideStart = false;
    private boolean forbidPreview = false;  // 关闭按键预览功能
    private boolean letterWithNum;  // 字母键盘是否带有数字
    private int keyboardType = 1;           // SafeKeyboard 键盘类型
    private int mCurrentInputTypeInEdit;    // 当前 EditText 的输入类型, (其实这个参数比较鸡肋, 使用 mCurrentEditText 即可)
    private static final long HIDE_TIME = 300;
    private static final long SHOW_DELAY = 200;
    private static final long HIDE_DELAY = 50;
    private static final long SHOW_TIME = 300;
    private static final long DELAY_TIME = 100;
    private Handler safeHandler = new Handler(Looper.getMainLooper());
    private Drawable delDrawable;
    private Drawable lowDrawable;
    private Drawable upDrawable;
    private Drawable upDrawableLock;
    private int keyboardLayoutResId;
    private int mSafeKeyboardViewId;

    private TranslateAnimation showAnimation;
    private TranslateAnimation hideAnimation;
    private long lastTouchTime;
    private EditText mCurrentEditText;
    private SparseArray<Keyboard.Key> randomDigitKeys;
    private SparseArray<Keyboard.Key> randomIdCardDigitKeys;
    private SparseIntArray mEditLastKeyboardTypeArray;

    private HashMap<Integer, EditText> mEditMap;
    private HashMap<Integer, EditText> mIdCardEditMap;
    private View.OnTouchListener onEditTextTouchListener;
    private View rootView;
    private View mScrollLayout;
    private ViewTreeObserver.OnGlobalFocusChangeListener onGlobalFocusChangeListener;
    private ViewTreeObserver treeObserver;
    private ViewPoint downPoint;
    private ViewPoint upPoint;
    private int mScreenWidth;
    private int mScreenHeight;
    private float toBackSize;   // 往上移动的距离, 为负值
    private int[] originalScrollPosInScr;
    private int[] originalScrollPosInPar;

    private Vibrator mVibrator;

    // 已支持多 EditText 共用一个 SafeKeyboard

    /**
     * SafeKeyboard 构造方法, 传入必要的参数
     *
     * @param mContext            上下文Context
     * @param keyboardParentView  界面上显示 SafeKeyboard 的 View
     * @param keyboardLayoutResId SafeKeyboard 样式资源 id
     * @param mSafeKeyboardViewId 自定义的 SafeKeyboardView id
     * @param rootView            含有使用了 SafeKeyboard 的 EditText 的界面根布局 View
     *                            ( 多个 EditText 共用 SafeKeyboard 但其直接父布局不是同一个 View 时, 传入公共父布局)
     * @param scrollLayout        目标 EditText 父布局 View
     */
    public SafeKeyboard(Context mContext, LinearLayout keyboardParentView, int keyboardLayoutResId, int mSafeKeyboardViewId,
                 @NonNull View rootView, @NonNull View scrollLayout) {
        this(mContext, keyboardParentView, keyboardLayoutResId, mSafeKeyboardViewId, rootView, scrollLayout,
                false, false);
    }

    public SafeKeyboard(Context mContext, LinearLayout keyboardParentView, int keyboardLayoutResId, int mSafeKeyboardViewId,
                 @NonNull View rootView, @NonNull View scrollLayout, boolean isPackageByScrollView) {
        this(mContext, keyboardParentView, keyboardLayoutResId, mSafeKeyboardViewId, rootView, scrollLayout,
                false, isPackageByScrollView);
    }

    /**
     * SafeKeyboard 构造方法, 传入必要的参数
     *
     * @param mContext            上下文Context
     * @param keyboardParentView  界面上显示 SafeKeyboard 的 View
     * @param keyboardLayoutResId SafeKeyboard 样式资源 id
     * @param mSafeKeyboardViewId 自定义的 SafeKeyboardView id
     * @param rootView            含有使用了 SafeKeyboard 的 EditText 的界面根布局 View
     * @param scrollLayout        目标 EditText 父布局 View
     *                            ( 多个 EditText 共用 SafeKeyboard 但其直接父布局不是同一个 View 时, 传入公共父布局)
     * @param letterWithNum       字母键盘是否带有数字
     */
    public SafeKeyboard(Context mContext, LinearLayout keyboardParentView, int keyboardLayoutResId, int mSafeKeyboardViewId,
                 @NonNull View rootView, @NonNull View scrollLayout, boolean letterWithNum, boolean isPackageByScrollView) {
        this.mContext = mContext;
        this.keyboardParentView = keyboardParentView;
        this.keyboardLayoutResId = keyboardLayoutResId;
        this.mSafeKeyboardViewId = mSafeKeyboardViewId;
        this.rootView = rootView;
        this.mScrollLayout = scrollLayout;
        this.letterWithNum = letterWithNum;

        initData();
        initKeyboard();
        initAnimation();
    }

    public SafeKeyboard(Context mContext, LinearLayout keyboardParentView, int keyboardLayoutResId, int mSafeKeyboardViewId,
                 Drawable del, Drawable low, Drawable up, Drawable upLock, @NonNull View rootView, @NonNull ViewGroup scrollLayout) {
        this.mContext = mContext;
        this.keyboardParentView = keyboardParentView;
        this.keyboardLayoutResId = keyboardLayoutResId;
        this.mSafeKeyboardViewId = mSafeKeyboardViewId;
        this.delDrawable = del;
        this.lowDrawable = low;
        this.upDrawable = up;
        this.upDrawableLock = upLock;
        this.rootView = rootView;
        this.mScrollLayout = scrollLayout;
        this.letterWithNum = false;

        initData();
        initKeyboard();
        initAnimation();
    }

    public void enableRememberLastKeyboardType(boolean enable) {
        keyboardView.setRememberLastType(enable);
    }

    private void initData() {
        isCapLock = false;
        isCapes = false;
        toBackSize = 0;
        downPoint = new ViewPoint();
        upPoint = new ViewPoint();
        mEditMap = new HashMap<>();
        mIdCardEditMap = new HashMap<>();
        mEditLastKeyboardTypeArray = new SparseIntArray();
        mVibrator = null;
        originalScrollPosInScr = new int[]{0, 0, 0, 0};
        originalScrollPosInPar = new int[]{0, 0, 0, 0};

        // 获取 WindowManager 实例, 得到屏幕的操作权
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        if (wm != null) {
            // 给 metrics 赋值
            DisplayMetrics metrics = new DisplayMetrics();
            wm.getDefaultDisplay().getMetrics(metrics);
            // 设备屏幕的宽度,高度变量
            mScreenWidth = metrics.widthPixels;
            mScreenHeight = metrics.heightPixels;
        }
    }

    private void initAnimation() {
        showAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF
                , 1.0f, Animation.RELATIVE_TO_SELF, 0.0f);
        hideAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF
                , 0.0f, Animation.RELATIVE_TO_SELF, 1.0f);
        showAnimation.setDuration(SHOW_TIME);
        hideAnimation.setDuration(HIDE_TIME);

        showAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isShowStart = true;
                // 在这里设置可见, 会出现第一次显示键盘时直接闪现出来, 没有动画效果, 后面正常
                // keyContainer.setVisibility(View.VISIBLE);
                // 动画持续时间 SHOW_TIME 结束后, 不管什么操作, 都需要执行, 把 isShowStart 值设为 false; 否则
                // 如果 onAnimationEnd 因为某些原因没有执行, 会影响下一次使用
                safeHandler.removeCallbacks(showEnd);
                safeHandler.postDelayed(showEnd, SHOW_TIME);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                isShowStart = false;
                keyContainer.clearAnimation();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });

        hideAnimation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isHideStart = true;
                // 动画持续时间 HIDE_TIME 结束后, 不管什么操作, 都需要执行, 把 isHideStart 值设为 false; 否则
                // 如果 onAnimationEnd 因为某些原因没有执行, 会影响下一次使用
                safeHandler.removeCallbacks(hideEnd);
                safeHandler.postDelayed(hideEnd, HIDE_TIME);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                safeHandler.removeCallbacks(hideEnd);
                if (isHideStart) {
                    // isHideStart 未被置为初试状态, 说明还没有执行 hideEnd 内容, 这里手动执行一下
                    doHideEnd();
                }
                // 说明已经被执行了不需要在执行一遍了, 下面就什么都不用管了
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initKeyboard() {
        keyContainer = LayoutInflater.from(mContext).inflate(keyboardLayoutResId, keyboardParentView, true);
        keyContainer.setVisibility(View.GONE);
        keyboardNumber = new Keyboard(mContext, R.xml.keyboard_num_symbol);     //实例化数字键盘
        // 注: 这里有三个数字键盘,  keyboard_num_symbol:带部分符号;   keyboard_num:可切换的数字键盘;    keyboard_num_only:纯数字键盘, 不可切换
        keyboardNumberOnly = new Keyboard(mContext, R.xml.keyboard_num_only);

        keyboardLetterOnly = new Keyboard(mContext, R.xml.keyboard_letter);         //实例化字母键盘
        keyboardLetterNum = new Keyboard(mContext, R.xml.keyboard_letter_num);         //实例化字母键盘
        keyboardSymbol = new Keyboard(mContext, R.xml.keyboard_symbol);         //实例化符号键盘
        keyboardIdCard = new Keyboard(mContext, R.xml.keyboard_id_card_zn);     //实例化 IdCard(中国身份证) 键盘
        // 由于符号键盘与字母键盘共用一个KeyBoardView, 所以不需要再为符号键盘单独实例化一个KeyBoardView

        keyboardLetter = letterWithNum ? keyboardLetterNum : keyboardLetterOnly;

        lastTouchTime = 0L;

        initRandomDigitKeys();
        initIdCardRandomDigitKeys();

        keyboardView = keyContainer.findViewById(mSafeKeyboardViewId);
        if (delDrawable == null)
            delDrawable = mContext.getDrawable(R.drawable.icon_del);
        if (lowDrawable == null)
            lowDrawable = mContext.getDrawable(R.drawable.icon_capital_default);
        if (upDrawable == null)
            upDrawable = mContext.getDrawable(R.drawable.icon_capital_selected);
        if (upDrawableLock == null)
            upDrawableLock = mContext.getDrawable(R.drawable.icon_capital_selected_lock);
        keyboardView.setDelDrawable(delDrawable);
        keyboardView.setLowDrawable(lowDrawable);
        keyboardView.setUpDrawable(upDrawable);
        keyboardView.setUpDrawableLock(upDrawableLock);
        // setKeyboard(keyboardLetter);                         //给键盘View设置键盘
        keyboardView.setEnabled(true);
        keyboardView.setPreviewEnabled(false);
        keyboardView.setOnKeyboardActionListener(listener);

        FrameLayout done = keyContainer.findViewById(R.id.keyboardDone);
        done.setOnClickListener(v -> {
            if (isKeyboardShown()) {
                safeHandler.removeCallbacks(hideRun);
                safeHandler.removeCallbacks(showRun);
                safeHandler.postDelayed(hideRun, HIDE_DELAY);
            }
        });

        keyboardView.setOnTouchListener((v, event) -> event.getAction() == MotionEvent.ACTION_MOVE);

        if (rootView != null) {
            treeObserver = rootView.getViewTreeObserver();
            onGlobalFocusChangeListener = (oldFocus, newFocus) -> {
                if (oldFocus instanceof EditText) {
                    // 上一个获得焦点的为 EditText
                    EditText oldEdit = (EditText) oldFocus;
                    if (mEditMap.get(oldEdit.getId()) != null) {
                        // 前 EditText 使用了 SafeKeyboard
                        // 新获取焦点的是 EditText
                        if (newFocus instanceof EditText) {
                            EditText newEdit = (EditText) newFocus;
                            if (mEditMap.get(newEdit.getId()) != null) {
                                // 该 EditText 也使用了 SafeKeyboard
                                // Log.i(TAG, "Safe --> Safe, 开始检查是否需要手动 show");
                                keyboardPreShow(newEdit);
                            } else {
                                // 该 EditText 没有使用 SafeKeyboard, 则隐藏 SafeKeyboard
                                // Log.i(TAG, "Safe --> 系统, 开始检查是否需要手动 hide");

                                // 说明: 如果 EditText 外被 ScrollView 包裹, 切换成系统输入法的时候, SafeKeyboard 会被异常顶起
                                // 需要在 Activity 的声明中增加 android:windowSoftInputMode="stateAlwaysHidden|adjustPan" 语句
                                keyboardPreHide();
                            }
                        } else {
                            // 新获取焦点的不是 EditText, 则隐藏 SafeKeyboard
                            // Log.i(TAG, "Safe --> 其他, 开始检查是否需要手动 hide");
                            keyboardPreHide();
                        }
                    } else {
                        // 前 EditText 没有使用 SafeKeyboard
                        // 新获取焦点的是 EditText
                        if (newFocus instanceof EditText) {
                            EditText newEdit = (EditText) newFocus;
                            // 该 EditText 使用了 SafeKeyboard, 则显示
                            if (mEditMap.get(newEdit.getId()) != null) {
                                // Log.i(TAG, "系统 --> Safe, 开始检查是否需要手动 show");
                                keyboardPreShow(newEdit);
                            } else {
                                // Log.i(TAG, "系统 --> 系统, 开始检查是否需要手动 hide");
                                keyboardPreHide();
                            }
                        } else {
                            // ... 否则不需要管理此次事件, 但是为保险起见, 可以隐藏一次 SafeKeyboard, 当然隐藏前需要判断是否已显示
                            // Log.i(TAG, "系统 --> 其他, 开始检查是否需要手动 hide");
                            keyboardPreHide();
                        }
                    }
                } else {
                    // 新获取焦点的是 EditText
                    if (newFocus instanceof EditText) {
                        EditText newEdit = (EditText) newFocus;
                        // 该 EditText 使用了 SafeKeyboard, 则显示
                        if (mEditMap.get(newEdit.getId()) != null) {
                            // Log.i(TAG, "其他 --> Safe, 开始检查是否需要手动 show");
                            keyboardPreShow(newEdit);
                        } else {
                            // Log.i(TAG, "其他 --> 系统, 开始检查是否需要手动 hide");
                            keyboardPreHide();
                        }
                    } else {
                        // ... 否则不需要管理此次事件, 但是为保险起见, 可以隐藏一次 SafeKeyboard, 当然隐藏前需要判断是否已显示
                        // Log.i(TAG, "其他 --> 其他, 开始检查是否需要手动 hide");
                        keyboardPreHide();
                    }
                }
            };
            treeObserver.addOnGlobalFocusChangeListener(onGlobalFocusChangeListener);
        } else {
            Log.e(TAG, "Root View is null!");
            // throw new Exception("Root View is null");
        }

        onEditTextTouchListener = (v, event) -> {
            if (v instanceof EditText) {
                EditText mEditText = (EditText) v;
                hideSystemKeyBoard(mEditText);
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    downPoint.setCoo_x((int) event.getRawX());
                    downPoint.setCoo_y((int) event.getRawY());
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    upPoint.setCoo_x((int) event.getRawX());
                    upPoint.setCoo_y((int) event.getRawY());
                    if (isTouchConsiderClick(downPoint, upPoint, mEditText) && mEditText.hasFocus()) {
                        if (mCurrentEditText == mEditText && isShow()) {
                            return false;
                        }
                        keyboardPreShow(mEditText);
                    }
                    downPoint.clearPoint();
                    upPoint.clearPoint();
                }
            }
            return false;
        };
    }

    /**
     * 用来计算按下和抬起时的两点位置的关系, 是否可以将此次 Touch 事件 看作 Click 事件
     * 两点各自的 x/y 轴距离不超过 10, 且两点中心点在目标 EditText 上 时, 返回 true, 否则 false
     *
     * @param down      按下时的位置点
     * @param up        抬起时的位置点
     * @param mEditText 目标 EditText
     * @return 是否考虑此次为点击事件
     */
    private boolean isTouchConsiderClick(ViewPoint down, ViewPoint up, EditText mEditText) {
        boolean flag = false;
        if (Math.abs(down.getCoo_x() - up.getCoo_x()) < 10 && Math.abs(down.getCoo_y() - up.getCoo_y()) < 10) {
            int[] position = new int[2];
            mEditText.getLocationOnScreen(position);
            int width = mEditText.getWidth();
            int height = mEditText.getHeight();
            int x = (down.getCoo_x() + up.getCoo_x()) / 2;
            int y = (down.getCoo_y() + up.getCoo_y()) / 2;
            if (position[0] + width >= x && position[1] + height >= y)
                flag = true;
        }

        return flag;
    }

    private void keyboardPreHide() {
        safeHandler.removeCallbacks(hideRun);
        safeHandler.removeCallbacks(showRun);
        getOriginalScrollLayoutPos();
        if (stillNeedOptManually(false)) {
            safeHandler.postDelayed(hideRun, HIDE_DELAY);
        }
    }

    private void keyboardPreShow(final EditText mEditText) {
        safeHandler.removeCallbacks(showRun);
        safeHandler.removeCallbacks(hideRun);
        getOriginalScrollLayoutPos();
        if (stillNeedOptManually(true)) {
            setCurrentEditText(mEditText);
            safeHandler.postDelayed(showRun, SHOW_DELAY);
        } else {
            // 说明不需要再手动显示, 只需要切换键盘模式即可 (甚至不用切换)
            // 这里需要检查当前 EditText 的显示是否合理
            final long delay = doScrollLayoutBack(false, mEditText) ? HIDE_TIME + 50 : 0;
            new Handler().postDelayed(() -> {
                // 如果已经显示了, 那么切换键盘即可
                setCurrentEditText(mEditText);
                setKeyboard(getKeyboardByInputType());
            }, delay);
        }
    }

    private void initRandomDigitKeys() {
        randomDigitKeys = new SparseArray<>();
        List<Keyboard.Key> keys = keyboardNumber.getKeys();
        for (Keyboard.Key key : keys) {
            int code = key.codes[0];
            if (code >= 48 && code <= 57)
                randomDigitKeys.put(code, key);
        }
    }

    private void initIdCardRandomDigitKeys() {
        randomIdCardDigitKeys = new SparseArray<>();
        List<Keyboard.Key> keys = keyboardIdCard.getKeys();
        for (Keyboard.Key key : keys) {
            int code = key.codes[0];
            if (code >= 48 && code <= 57)
                randomIdCardDigitKeys.put(code, key);
        }
    }

    /**
     * 更新 mScrollLayout 原始位置, 且只获取一次
     */
    private void getOriginalScrollLayoutPos() {
        if (originalScrollPosInScr[0] == 0 && originalScrollPosInScr[1] == 0) {
            int[] pos = new int[]{0, 0};
            mScrollLayout.getLocationOnScreen(pos);
            originalScrollPosInScr[0] = pos[0];
            originalScrollPosInScr[1] = pos[1];
            originalScrollPosInScr[2] = pos[0] + mScrollLayout.getWidth();
            originalScrollPosInScr[3] = pos[1] + mScrollLayout.getHeight();
        }

        if (originalScrollPosInPar[0] == 0 && originalScrollPosInPar[1] == 0
                && originalScrollPosInPar[2] == 0 && originalScrollPosInPar[3] == 0) {
            originalScrollPosInPar[0] = mScrollLayout.getLeft();
            originalScrollPosInPar[1] = mScrollLayout.getTop();
            originalScrollPosInPar[2] = mScrollLayout.getRight();
            originalScrollPosInPar[3] = mScrollLayout.getBottom();
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    public void putEditText(EditText mEditText) {
        if (mEditMap == null) mEditMap = new HashMap<>();
        mEditMap.put(mEditText.getId(), mEditText);
        mEditText.setOnTouchListener(onEditTextTouchListener);
    }

    public void putEditText2IdCardType(int id, EditText mEditText) {
        if (mIdCardEditMap == null) mIdCardEditMap = new HashMap<>();
        mIdCardEditMap.put(id, mEditText);
    }

    /**
     * 设置是否强制关闭预览功能
     * <p>
     * 解释:因为系统自带的 KeyboardView 的按键预览功能是使用 PopupWindow 来实现的, 那么在
     * PopupWindow 中使用了 SafeKeyboard (本软键盘), 那么必须关闭预览, 否则会直接崩溃.
     * ( 即调用 setForbidPreview(true) )
     *
     * @param forbidPreview 是否关闭预览
     */
    public void setForbidPreview(boolean forbidPreview) {
        this.forbidPreview = forbidPreview;
    }

    // 设置键盘点击监听
    private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {

        @Override
        public void onPress(int primaryCode) {
            if (keyboardType == 3) {
                keyboardView.setPreviewEnabled(false);
            } else {
                keyboardView.setPreviewEnabled(!forbidPreview);
                if (primaryCode == -1 || primaryCode == -5 || primaryCode == 32 || primaryCode == -2
                        || primaryCode == 100860 || primaryCode == 100861 || primaryCode == -35) {
                    keyboardView.setPreviewEnabled(false);
                } else {
                    keyboardView.setPreviewEnabled(!forbidPreview);
                }
            }
        }

        @Override
        public void onRelease(int primaryCode) {
        }

        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            try {
                Editable editable = mCurrentEditText.getText();
                int start = mCurrentEditText.getSelectionStart();
                int end = mCurrentEditText.getSelectionEnd();
                if (primaryCode == Keyboard.KEYCODE_CANCEL) {
                    // 隐藏键盘
                    safeHandler.removeCallbacks(hideRun);
                    safeHandler.removeCallbacks(showRun);
                    safeHandler.post(hideRun/*, HIDE_DELAY*/);
                } else if (primaryCode == Keyboard.KEYCODE_DELETE || primaryCode == -35) {

                    // 回退键,删除字符
                    if (editable != null && editable.length() > 0) {
                        if (start == end) { //光标开始和结束位置相同, 即没有选中内容
                            editable.delete(start - 1, start);
                        } else { //光标开始和结束位置不同, 即选中EditText中的内容
                            editable.delete(start, end);
                        }
                    }
                } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
                    // 大小写切换
                    changeKeyboardLetterCase();
                    // 重新setKeyboard, 进而系统重新加载, 键盘内容才会变化(切换大小写)
                    keyboardType = 1;
                    switchKeyboard();
                } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
                    // 数字与字母键盘互换
                    if (keyboardType == 3) { //当前为数字键盘
                        keyboardType = 1;
                    } else {        //当前不是数字键盘
                        keyboardType = 3;
                    }
                    switchKeyboard();
                } else if (primaryCode == 100860) {
                    // 字母与符号切换
                    if (keyboardType == 2) { //当前是符号键盘
                        keyboardType = 1;
                    } else {        //当前不是符号键盘, 那么切换到符号键盘
                        keyboardType = 2;
                    }
                    switchKeyboard();
                } else if (primaryCode == 100861) {
                    // TODO... 这里啥也不干
                } else {
                    // 输入键盘值
                    // editable.insert(start, Character.toString((char) primaryCode));
                    editable.replace(start, end, Character.toString((char) primaryCode));
                    if (mEditLastKeyboardTypeArray.get(mCurrentEditText.getId(), 1) == 1 && !isCapLock && isCapes) {
                        isCapes = isCapLock = false;
                        toLowerCase();
                        keyboardView.setCap(isCapes);
                        keyboardView.setCapLock(isCapLock);
                        switchKeyboard();
                    }
                }

                // 添加按键震动
                if (keyboardView != null && keyboardView.isVibrateEnable()) {
                    if (mVibrator == null) {
                        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
                    }
                    if (mVibrator != null) {
                        mVibrator.vibrate(20);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onText(CharSequence text) {

        }

        @Override
        public void swipeLeft() {
        }

        @Override
        public void swipeRight() {
        }

        @Override
        public void swipeDown() {
        }

        @Override
        public void swipeUp() {
        }
    };

    private void refreshDigitKeyboard(Keyboard keyboard) {
        if (keyboard != null) {
            SparseArray<Keyboard.Key> randomKeys;
            if (keyboard == keyboardIdCard) {
                // 如果是 IdCard 键盘
                randomKeys = randomIdCardDigitKeys;
            } else {
                // 否则认为是 数字 键盘
                randomKeys = randomDigitKeys;
            }
            HashSet<Integer> set = new HashSet<>();
            while (set.size() < 10) {
                int num = (int) (Math.random() * 10);
                if (set.add(num)) {
                    // set.size() - 1 表示目前是第几个数字按键
                    Keyboard.Key key = randomKeys.get(set.size() - 1 + 48);
                    key.label = num + "";
                    key.codes[0] = 48 + num;
                }
            }
        } else {
            Log.w(TAG, "Refresh Digit ERROR! Keyboard is null");
        }
    }

    private void switchKeyboard() {
        switch (keyboardType) {
            case 1:
                setKeyboard(keyboardLetter);
                break;
            case 2:
                setKeyboard(keyboardSymbol);
                break;
            case 3:
                if (keyboardView.isRandomDigit()) {
                    refreshDigitKeyboard(keyboardNumber);
                }
                setKeyboard(keyboardNumber);
                break;
            default:
                Log.e(TAG, "ERROR keyboard type");
                break;
        }
    }

    private void setKeyboard(Keyboard keyboard) {
        int type;
        if (keyboard == keyboardLetter) {
            type = 1;
        } else if (keyboard == keyboardSymbol) {
            type = 2;
        } else if (keyboard == keyboardNumber || keyboard == keyboardNumberOnly || keyboard == keyboardIdCard
            /*|| mEditIsNumInput(mCurrentEditText)*/) {
            type = 3;
        } else type = 1;
        mEditLastKeyboardTypeArray.put(mCurrentEditText.getId(), type);
        keyboardType = type;
        keyboardView.setKeyboard(keyboard);
        // hideSystemKeyBoard(mCurrentEditText);
    }

    private boolean mEditIsNumInput(EditText mCurrentEditText) {
        return mCurrentEditText.getInputType() == EditorInfo.TYPE_CLASS_NUMBER;
    }

    private void changeKeyboardLetterCase() {
        if (!isCapes) {
            // 为小写时, 改为大写.
            toUpperCase();
        } else if (isCapLock) {
            toLowerCase();
        }
        if (isCapLock) {
            isCapLock = isCapes = false;
        } else if (isCapes) {
            isCapLock = true;
        } else {
            isCapes = true;
            isCapLock = false;
        }
        keyboardView.setCap(isCapes);
        keyboardView.setCapLock(isCapLock);
    }

    private void toLowerCase() {
        List<Keyboard.Key> keyList = keyboardLetter.getKeys();
        for (Keyboard.Key key : keyList) {
            if (key.label != null && isUpCaseLetter(key.label.toString())) {
                key.label = key.label.toString().toLowerCase();
                key.codes[0] += 32;
            }
        }
    }

    private void toUpperCase() {
        List<Keyboard.Key> keyList = keyboardLetter.getKeys();
        for (Keyboard.Key key : keyList) {
            if (key.label != null && isLowCaseLetter(key.label.toString())) {
                key.label = key.label.toString().toUpperCase();
                key.codes[0] -= 32;
            }
        }
    }

    public void hideKeyboard() {
        keyContainer.clearAnimation();
        keyContainer.startAnimation(hideAnimation);
    }

    /**
     * 只起到延时开始显示的作用
     */
    private final Runnable showRun = this::showKeyboard;

    private final Runnable hideRun = this::hideKeyboard;

    private final Runnable hideEnd = this::doHideEnd;

    private void doHideEnd() {
        isHideStart = false;

        doScrollLayoutBack(true, null);

        keyContainer.clearAnimation();
        if (keyContainer.getVisibility() != View.GONE) {
            keyContainer.setVisibility(View.GONE);
        }
    }

    /**
     * 回落
     *
     * @param isHide 回落的同时, SafeKeyboard 是否隐藏
     */
    private boolean doScrollLayoutBack(final boolean isHide, EditText mEditText) {
        int thisScrollY = 0;
        if (!isHide && mEditText != null) {
            // 这种情况说明是点击了一个 EditText, 则需要判断是否需要移动 mScrollLayout 来适应 SafeKeyboard 的显示
            int[] mEditPos = new int[2];
            mEditText.getLocationOnScreen(mEditPos);
            Log.e("SafeKeyboard_Scroll", "0: " + mEditPos[0] + ", 1: " + mEditPos[1]);

            int keyboardHeight = keyContainer.getHeight();
            int keyStartY = mScreenHeight - keyboardHeight;
            getOriginalScrollLayoutPos();

            if (mEditText.getHeight() + 10 > keyStartY - originalScrollPosInScr[1]) {
                // mEditText 的高度 大于 SafeKeyboard 上边界到 mScrollLayout 上边界的距离, 即 mEditText 无法完全显示
                // TODO... 添加一个长文本输入功能

                return false;
            } else {
                // 可以正常显示
                if (mEditPos[1] < originalScrollPosInScr[1]) {
                    // 说明当前的 mEditText 的 top 位置已经被其他布局遮挡, 需要布局往下滑动一点, 使 mEditText 可以完全显示
                    thisScrollY = originalScrollPosInScr[1] - mEditPos[1] + 10; // 正值
                } else if (mEditPos[1] + mEditText.getHeight() > keyStartY) {
                    // 说明当前的 mEditText 的 bottom 位置已经被其他布局遮挡, 需要布局往上滑动一点, 使 mEditText 可以完全显示
                    thisScrollY = keyStartY - mEditPos[1] - mEditText.getHeight(); //负值
                } else {
                    // 各项均正常, 不需要重新滑动
                    Log.i("SafeKeyboard_LOG", "No need to scroll");
                    return false;
                }
            }
        }

        toBackSize += thisScrollY;
        if (isHide) {
            mScrollLayout.animate().setDuration(SHOW_TIME).translationYBy(-toBackSize).start();
            toBackSize = 0;
        } else {
            mScrollLayout.animate().setDuration(SHOW_TIME).translationYBy(thisScrollY).start();
        }

        return true;
    }

    /**
     * 顶起
     */
    private void doScrollLayout() {
        // 计算 SafeKeyboard 显示后是否会遮挡住 EditText
        editNeedScroll(mCurrentEditText);
    }

    private final Runnable showEnd = () -> {
        isShowStart = false;
        // 在迅速点击不同输入框时, 造成自定义软键盘和系统软件盘不停的切换, 偶尔会出现停在使用系统键盘的输入框时, 没有隐藏
        // 自定义软键盘的情况, 为了杜绝这个现象, 加上下面这段代码
        if (!mCurrentEditText.isFocused()) {
            safeHandler.removeCallbacks(hideRun);
            safeHandler.removeCallbacks(showRun);
            safeHandler.postDelayed(hideRun, HIDE_DELAY);
        }

        // 这个只能在 keyContainer 显示后才能调用, 只有这个时候才能获取到 keyContainer 的宽、高值
        doScrollLayout();
    };

    private void showKeyboard() {
        Keyboard mKeyboard = getKeyboardByInputType();
        if (mKeyboard != null && (mKeyboard == keyboardNumber || mKeyboard == keyboardIdCard
                || mKeyboard == keyboardNumberOnly) && keyboardView.isRandomDigit()) {
            refreshDigitKeyboard(mKeyboard);
        }
        setKeyboard(mKeyboard == null ? keyboardLetter : mKeyboard);
        keyContainer.setVisibility(View.VISIBLE);
        keyContainer.clearAnimation();
        keyContainer.startAnimation(showAnimation);
    }

    /**
     * @param mEditText 目标 EditText
     */
    private void editNeedScroll(EditText mEditText) {
        int keyboardHeight = keyContainer.getHeight();
        int keyStartY = mScreenHeight - keyboardHeight;
        int[] position = new int[2];
        mEditText.getLocationOnScreen(position);
        int mEditTextBottomY = position[1] + mEditText.getHeight();
        if (mEditTextBottomY - keyStartY > 0) {
            final float to = keyStartY - mEditTextBottomY - 10; // 为负值
            if (position[1] + to < originalScrollPosInScr[1]) {
                // 说明往上顶起之后 mEditText 会被遮挡, 即 mEditText 的 top 距离顶部的距离 小于 要移动的距离
                // 这里就不需要顶起了, 需要显示一个长文本显示页面
                // TODO... 添加一个长文本显示功能, 不过这里的长文本显示似乎没有什么意义
                return;
            }
            toBackSize = to;
            mScrollLayout.animate().translationYBy(toBackSize).setDuration(SHOW_TIME).start();
        }
    }

    private Keyboard getKeyboardByInputType() {
        Keyboard lastKeyboard = keyboardLetter; // 默认字母键盘

        if (mCurrentInputTypeInEdit == InputType.TYPE_CLASS_NUMBER) {
            lastKeyboard = keyboardNumberOnly;
        } else if (mIdCardEditMap.get(mCurrentEditText.getId()) != null) {
            lastKeyboard = keyboardIdCard;
        } else if (keyboardView.isRememberLastType()) {
            int type = mEditLastKeyboardTypeArray.get(mCurrentEditText.getId(), 1);
            switch (type) {
                case 1:
                    lastKeyboard = keyboardLetter;
                    break;
                case 2:
                    lastKeyboard = keyboardSymbol;
                    break;
                case 3:
                    lastKeyboard = keyboardNumber;
                    break;
                default:
                    Log.e(TAG, "ERROR keyboard type");
                    break;
            }
        }

        return lastKeyboard;
    }

    private boolean isLowCaseLetter(String str) {
        String letters = "abcdefghijklmnopqrstuvwxyz";
        return letters.contains(str);
    }

    private boolean isUpCaseLetter(String str) {
        String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        return letters.contains(str);
    }

    @SuppressLint("ClickableViewAccessibility")
    private void add2MapAllAndEditTextListeners(ArrayList<EditText> mEditTexts) {
        for (EditText mEditText : mEditTexts) {
            mEditMap.put(mEditText.getId(), mEditText);
            mEditText.setOnTouchListener(onEditTextTouchListener);
        }
    }

    private void setCurrentEditText(EditText mEditText) {
        mCurrentEditText = mEditText;
        mCurrentInputTypeInEdit = mEditText.getInputType();
    }

    public boolean isShow() {
        return isKeyboardShown();
    }

    //隐藏系统键盘关键代码
    private void hideSystemKeyBoard(EditText edit) {
        this.mCurrentEditText = edit;
        InputMethodManager imm = (InputMethodManager) this.mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm == null)
            return;
        boolean isOpen = imm.isActive();
        if (isOpen) {
            imm.hideSoftInputFromWindow(edit.getWindowToken(), 0);
        }

        int currentVersion = Build.VERSION.SDK_INT;
        String methodName = null;
        if (currentVersion >= 16) {
            methodName = "setShowSoftInputOnFocus";
        } else if (currentVersion >= 14) {
            methodName = "setSoftInputShownOnFocus";
        }

        if (methodName == null) {
            edit.setInputType(0);
        } else {
            try {
                Method setShowSoftInputOnFocus = EditText.class.getMethod(methodName, Boolean.TYPE);
                setShowSoftInputOnFocus.setAccessible(true);
                setShowSoftInputOnFocus.invoke(edit, Boolean.FALSE);
            } catch (NoSuchMethodException e) {
                edit.setInputType(0);
                e.printStackTrace();
            } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean isKeyboardShown() {
        return keyContainer.getVisibility() == View.VISIBLE;
    }

    public boolean stillNeedOptManually(boolean preferShow) {
        boolean flag;
        if (preferShow) {
            // 想要显示
            flag = isHideStart || (!isKeyboardShown() && !isShowStart);
        } else {
            // 想要隐藏
            flag = isShowStart || (isKeyboardShown() && !isHideStart);
        }
        return flag;
    }

    private boolean isValidTouch() {
        long thisTouchTime = SystemClock.elapsedRealtime();
        if (thisTouchTime - lastTouchTime > 500) {
            lastTouchTime = thisTouchTime;
            return true;
        }
        lastTouchTime = thisTouchTime;
        return false;
    }

    public void setDelDrawable(Drawable delDrawable) {
        this.delDrawable = delDrawable;
        keyboardView.setDelDrawable(delDrawable);
    }

    public void setLowDrawable(Drawable lowDrawable) {
        this.lowDrawable = lowDrawable;
        keyboardView.setLowDrawable(lowDrawable);
    }

    public void setUpDrawable(Drawable upDrawable) {
        this.upDrawable = upDrawable;
        keyboardView.setUpDrawable(upDrawable);
    }

    public void setUpDrawableLock(Drawable upDrawableLock) {
        this.upDrawableLock = upDrawableLock;
        keyboardView.setUpDrawable(upDrawableLock);
    }

    public void release() {
        mContext = null;
        isCapes = false;
        toBackSize = 0;
        onEditTextTouchListener = null;
        if (treeObserver != null && onGlobalFocusChangeListener != null && treeObserver.isAlive()) {
            treeObserver.removeOnGlobalFocusChangeListener(onGlobalFocusChangeListener);
        }
        treeObserver = null;
        onGlobalFocusChangeListener = null;
        if (mEditLastKeyboardTypeArray != null) {
            mEditLastKeyboardTypeArray.clear();
            mEditLastKeyboardTypeArray = null;
        }
        if (mEditMap != null) {
            mEditMap.clear();
            mEditMap = null;
        }
        if (mIdCardEditMap != null) {
            mIdCardEditMap.clear();
            mIdCardEditMap = null;
        }
        mVibrator = null;
    }
}
 

SafeKeyboardView.java:

package com.example.myapplication.keyboard;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.support.annotation.Nullable;
import android.util.AttributeSet;

import com.example.myapplication.R;

import java.lang.reflect.Field;
import java.util.List;

/**
 * Created by Administrator on 2018/3/7 0007.
 */

public class SafeKeyboardView extends KeyboardView {

    private static final String TAG = "SafeKeyboardView";

    private Context mContext;
    private boolean isCap;
    private boolean isCapLock;
    private boolean enableVibrate;
    private Drawable delDrawable;
    private Drawable lowDrawable;
    private Drawable upDrawable;
    private Drawable upDrawableLock;
    private Keyboard lastKeyboard;
    /**
     * 按键的宽高至少是图标宽高的倍数
     */
    private static final int ICON2KEY = 2;

    // 键盘的一些自定义属性
    private boolean randomDigit;    // 数字随机
    private final static boolean DIGIT_RANDOM = false;
    //    private boolean onlyIdCard;     // 仅显示 身份证 键盘
//    private final static boolean ONLY_ID_CARD = false;
    private boolean rememberLastType;     // 仅显示 身份证 键盘
    private final static boolean REM_LAST_TYPE = true;
    private final static boolean DEFAULT_ENABLE_VIBRATE = false;

    public SafeKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
        this.mContext = context;

        initAttrs(context, attrs, 0);
    }

    public SafeKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
        this.mContext = context;

        initAttrs(context, attrs, defStyleAttr);
    }

    private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SafeKeyboardView, defStyleAttr, 0);
            randomDigit = array.getBoolean(R.styleable.SafeKeyboardView_random_digit, DIGIT_RANDOM);
            // onlyIdCard = array.getBoolean(R.styleable.SafeKeyboardView_only_id_card, ONLY_ID_CARD);
            rememberLastType = array.getBoolean(R.styleable.SafeKeyboardView_remember_last_type, REM_LAST_TYPE);
            enableVibrate = array.getBoolean(R.styleable.SafeKeyboardView_enable_vibrate, DEFAULT_ENABLE_VIBRATE);
            array.recycle();
        }
    }

    public void setRememberLastType(boolean remember) {
        rememberLastType = remember;
    }

    private void init(Context context) {
        this.isCap = false;
        this.isCapLock = false;
        // 默认三种图标
        this.delDrawable = context.getDrawable(R.drawable.icon_del);
        this.lowDrawable = context.getDrawable(R.drawable.icon_capital_default);
        this.upDrawable = context.getDrawable(R.drawable.icon_capital_selected);
        this.upDrawableLock = context.getDrawable(R.drawable.icon_capital_selected_lock);
        this.lastKeyboard = null;
    }

    public boolean isRandomDigit() {
        return randomDigit;
    }

//    public boolean isOnlyIdCard() {
//        return onlyIdCard;
//    }

    public boolean isRememberLastType() {
        return rememberLastType;
    }

    public boolean isVibrateEnable() {
        return enableVibrate;
    }

    public void enableVibrate(boolean enableVibrate) {
        this.enableVibrate = enableVibrate;
    }

    @Override
    public void setKeyboard(Keyboard keyboard) {
        super.setKeyboard(keyboard);
        this.lastKeyboard = keyboard;
    }

    public Keyboard getLastKeyboard() {
        return lastKeyboard;
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        try {
            List<Keyboard.Key> keys = getKeyboard().getKeys();
            for (Keyboard.Key key : keys) {
                if (key.codes[0] == -5 || key.codes[0] == -2 || key.codes[0] == 100860 || key.codes[0] == -1)
                    drawSpecialKey(canvas, key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void drawSpecialKey(Canvas canvas, Keyboard.Key key) {
        int color = Color.WHITE;
        if (key.codes[0] == -5) {
            drawKeyBackground(R.drawable.keyboard_change, canvas, key);
            drawTextAndIcon(canvas, key, delDrawable, color);
        } else if (key.codes[0] == -2 || key.codes[0] == 100860 || key.codes[0] == 100861) {
            drawKeyBackground(R.drawable.keyboard_change, canvas, key);
            drawTextAndIcon(canvas, key, null, color);
        } else if (key.codes[0] == -1) {
            if (isCapLock) {
                drawKeyBackground(R.drawable.keyboard_change, canvas, key);
                drawTextAndIcon(canvas, key, upDrawableLock, color);
            } else if (isCap) {
                drawKeyBackground(R.drawable.keyboard_change, canvas, key);
                drawTextAndIcon(canvas, key, upDrawable, color);
            } else {
                drawKeyBackground(R.drawable.keyboard_change, canvas, key);
                drawTextAndIcon(canvas, key, lowDrawable, color);
            }
        }
    }

    private void drawKeyBackground(int id, Canvas canvas, Keyboard.Key key) {
        Drawable drawable = mContext.getResources().getDrawable(id);
        int[] state = key.getCurrentDrawableState();
        if (key.codes[0] != 0) {
            drawable.setState(state);
        }
        drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
        drawable.draw(canvas);
    }

    private void drawTextAndIcon(Canvas canvas, Keyboard.Key key, @Nullable Drawable drawable, int color) {
        try {
            Rect bounds = new Rect();
            Paint paint = new Paint();
            paint.setTextAlign(Paint.Align.CENTER);
            paint.setAntiAlias(true);
            paint.setColor(color);

            if (key.label != null) {
                String label = key.label.toString();

                Field field;

                if (label.length() > 1 && key.codes.length < 2) {
                    int labelTextSize = 0;
                    try {
                        field = KeyboardView.class.getDeclaredField(getContext().getString(R.string.mLabelTextSize));
                        field.setAccessible(true);
                        labelTextSize = (int) field.get(this);
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    paint.setTextSize(labelTextSize);
                    paint.setTypeface(Typeface.DEFAULT_BOLD);
                } else {
                    int keyTextSize = 0;
                    try {
                        field = KeyboardView.class.getDeclaredField(getContext().getString(R.string.mLabelTextSize));
                        field.setAccessible(true);
                        keyTextSize = (int) field.get(this);
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    paint.setTextSize(keyTextSize + 10);
                    paint.setTypeface(Typeface.DEFAULT);
                }

                paint.getTextBounds(key.label.toString(), 0, key.label.toString().length(), bounds);
                canvas.drawText(key.label.toString(), key.x + (1.0f * key.width / 2),
                        (key.y + 1.0f * key.height / 2) + 1.0f * bounds.height() / 2, paint);
            }
            if (drawable == null) return;
            // 约定: 最终图标的宽度和高度都需要在按键的宽度和高度的二分之一以内
            // 如果: 图标的实际宽度和高度都在按键的宽度和高度的二分之一以内, 那就不需要变换, 否则就需要等比例缩小
            int iconSizeWidth, iconSizeHeight;
            key.icon = drawable;
            int iconH = px2dip(mContext, key.icon.getIntrinsicHeight());
            int iconW = px2dip(mContext, key.icon.getIntrinsicWidth());
            if (key.width >= (ICON2KEY * iconW) && key.height >= (ICON2KEY * iconH)) {
                //图标的实际宽度和高度都在按键的宽度和高度的二分之一以内, 不需要缩放, 因为图片已经够小或者按键够大
                setIconSize(canvas, key, iconW, iconH);
            } else {
                //图标的实际宽度和高度至少有一个不在按键的宽度或高度的二分之一以内, 需要等比例缩放, 因为此时图标的宽或者高已经超过按键的二分之一
                //需要把超过的那个值设置为按键的二分之一, 另一个等比例缩放
                //不管图标大小是多少, 都以宽度width为标准, 把图标的宽度缩放到和按键一样大, 并同比例缩放高度
                double multi = 1.0 * iconW / key.width;
                int tempIconH = (int) (iconH / multi);
                if (tempIconH <= key.height) {
                    //宽度相等时, 图标的高度小于等于按键的高度, 按照现在的宽度和高度设置图标的最终宽度和高度
                    iconSizeHeight = tempIconH / ICON2KEY;
                    iconSizeWidth = key.width / ICON2KEY;
                } else {
                    //宽度相等时, 图标的高度大于按键的高度, 这时按键放不下图标, 需要重新按照高度缩放
                    double mul = 1.0 * iconH / key.height;
                    int tempIconW = (int) (iconW / mul);
                    iconSizeHeight = key.height / ICON2KEY;
                    iconSizeWidth = tempIconW / ICON2KEY;
                }
                setIconSize(canvas, key, iconSizeWidth, iconSizeHeight);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void setIconSize(Canvas canvas, Keyboard.Key key, int iconSizeWidth, int iconSizeHeight) {
        int left = key.x + (key.width - iconSizeWidth) / 2;
        int top = key.y + (key.height - iconSizeHeight) / 2;
        int right = key.x + (key.width + iconSizeWidth) / 2;
        int bottom = key.y + (key.height + iconSizeHeight) / 2;
        key.icon.setBounds(left, top, right, bottom);
        key.icon.draw(canvas);
        key.icon = null;
    }

    public void setCap(boolean cap) {
        isCap = cap;
    }

    public void setCapLock(boolean isCapLock) {
        this.isCapLock = isCapLock;
    }

    public void setDelDrawable(Drawable delDrawable) {
        this.delDrawable = delDrawable;
    }

    public void setLowDrawable(Drawable lowDrawable) {
        this.lowDrawable = lowDrawable;
    }

    public void setUpDrawable(Drawable upDrawable) {
        this.upDrawable = upDrawable;
    }

    public void setUpDrawableLock(Drawable upDrawableLock) {
        this.upDrawableLock = upDrawableLock;
    }

    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
}
 

ViewPoint.java:

package com.example.myapplication.keyboard;

/**
 * @author Valence
 * @version 1.0
 * @since 2017/09/13
 */

public class ViewPoint {
    private int coo_x;
    private int coo_y;
    private long coo_time;

    public ViewPoint() {
        this.coo_x = 0;
        this.coo_y = 0;
        this.coo_time = 0;
    }

    public ViewPoint(int coo_x, int coo_y, long coo_time) {
        this.coo_x = coo_x;
        this.coo_y = coo_y;
        this.coo_time = coo_time;
    }

    public int getCoo_x() {
        return coo_x;
    }

    public void setCoo_x(int coo_x) {
        this.coo_x = coo_x;
    }

    public int getCoo_y() {
        return coo_y;
    }

    public void setCoo_y(int coo_y) {
        this.coo_y = coo_y;
    }

    public long getCoo_time() {
        return coo_time;
    }

    public void setCoo_time(long coo_time) {
        this.coo_time = coo_time;
    }

    public void clearPoint() {
        this.coo_x = 0;
        this.coo_y = 0;
        this.coo_time = 0;
    }
}
然后再要使用的页面加上使用方式:

package com.example.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatEditText;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;

import com.example.myapplication.keyboard.SafeKeyboard;

public class MainActivity2 extends AppCompatActivity {

    private SafeKeyboard safeKeyboard;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        EditText safeEdit = findViewById(R.id.safeEditText);
        EditText safeEdit2 = findViewById(R.id.safeEditText2);
        EditText safeEdit3 = findViewById(R.id.safeEditText3);
        EditText safeEdit4 = findViewById(R.id.safeEditText4);
        EditText safeEdit5 = findViewById(R.id.safeEditText5);
        AppCompatEditText appCompatEditText = findViewById(R.id.safeAppCompactEditText);
        View rootView = findViewById(R.id.main_root);
        View scrollLayout = findViewById(R.id.scroll_layout);
        LinearLayout keyboardContainer = findViewById(R.id.keyboardPlace);
        safeKeyboard = new SafeKeyboard(getApplicationContext(), keyboardContainer,
                R.layout.layout_keyboard_containor, R.id.safeKeyboardLetter, rootView, scrollLayout);
        safeKeyboard.putEditText(safeEdit);
        safeKeyboard.putEditText(safeEdit2);
        safeKeyboard.putEditText(safeEdit3);
        safeKeyboard.putEditText(safeEdit4);
        safeKeyboard.putEditText(safeEdit5);
        safeKeyboard.putEditText(appCompatEditText);
        safeKeyboard.putEditText2IdCardType(safeEdit3.getId(), safeEdit3);
//        safeKeyboard.setDelDrawable(this.getResources().getDrawable(R.drawable.icon_del));
//        safeKeyboard.setLowDrawable(this.getResources().getDrawable(R.drawable.icon_capital_default));
//        safeKeyboard.setUpDrawable(this.getResources().getDrawable(R.drawable.icon_capital_selected));

        initView();
    }

    private void initView() {
    }

    // 当点击返回键时, 如果软键盘正在显示, 则隐藏软键盘并是此次返回无效
    @Override
    public void onBackPressed() {
        if (safeKeyboard.stillNeedOptManually(false)) {
            safeKeyboard.hideKeyboard();
            return;
        }
        super.onBackPressed();
    }

    @Override
    protected void onDestroy() {
        if (safeKeyboard != null) {
            safeKeyboard.release();
            safeKeyboard = null;
        }
        super.onDestroy();
    }

    public void onAlertDialogClick(View view) {
        //startActivity(new Intent(MainActivity.this, DialogSafeKeyboardActivity.class));
    }

    public void onPopupWindowTestClick(View view) {
        //startActivity(new Intent(MainActivity.this, PopupWindowActivity.class));
    }

    public void onScrollEditTestClick(View view) {
        //startActivity(new Intent(MainActivity.this, ScrollViewEditActivity.class));
    }
}
引用页面如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/scroll_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <EditText
                android:id="@+id/normalEditText"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_system"
                android:inputType="text" />

            <EditText
                android:id="@+id/safeEditText"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_demo_new"
                android:inputType="textPassword" />

            <EditText
                android:id="@+id/safeEditText2"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_demo_new2"
                android:inputType="number" />

            <EditText
                android:id="@+id/safeEditText3"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_demo_new3" />

            <android.support.v7.widget.AppCompatEditText
                android:id="@+id/safeAppCompactEditText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="AppCompatEditText" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:onClick="onAlertDialogClick"
                android:text="@string/alert_test" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:onClick="onPopupWindowTestClick"
                android:text="@string/popup_test" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="60dp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:onClick="onScrollEditTestClick"
                android:text="@string/scroll_view_test" />

            <EditText
                android:id="@+id/safeEditText4"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_demo_new"
                android:inputType="number" />

            <EditText
                android:id="@+id/safeEditText5"
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_demo_new" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_system" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_system" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_system" />

            <EditText
                android:layout_width="match_parent"
                android:layout_height="50sp"
                android:layout_marginStart="10sp"
                android:layout_marginEnd="10sp"
                android:hint="@string/keyboard_system" />

        </LinearLayout>
    </ScrollView>

    <LinearLayout
        android:id="@+id/keyboardPlace"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="vertical" />

</RelativeLayout>
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
虚拟键盘 键盘 free virtual keyboard ==================== 将英语译成中文 免费多语言虚拟屏幕键盘 屏幕上的所有虚拟键工作只是因为他们如果你是一个标准的电脑键盘打字。选择键和免费的虚拟键盘发送到一个文字处理器,电子邮件,网页或其他Windows应用程序可能正在使用。键盘可以移动屏幕上的任何地方,可以是任意大小。 免费的虚拟键盘可用于残疾,防止他或她从一个物理电脑键盘打字的人。 也与我们的虚拟键盘,你可以: 改变键盘尺寸。 更改键盘的透明度。 打开和关闭一个键复读功能(见上下文菜单)。 规模的虚拟键盘以匹配屏幕宽度(见上下文菜单)。这是一个在纵向和横向模式频繁屏幕旋转有用的功能。 更改键盘(见上下文菜单)的类型。可用3虚拟键盘类型。 更改主题颜色(见上下文菜单)。 任何语言中键入文本。 键入文本(例如左手美国德沃夏克)其他布局。 -------------------------------------------------- ------------------------------ 免费的虚拟键盘是为个人和商业使用免费程序。这个节目作为分发的依据,且无任何担保,甚至没有适销性或针对特定目的的隐含的担保。舒适件集团不能作出对任何直接或间接造成的破坏计划。 舒适件集团保留权利更改日后发牌条件。 朗读 显示对应的拉丁字符的拼音新! 点击上方的字词即可查看其他翻译。 删除 字典 - 查看字典详细内容
在 Android 中,自定义键盘需要实现一个继承自 InputMethodService 的服务,这个服务会在用户打开键盘时被调用。下面是一些步骤来创建自定义键盘: 1. 创建一个新的 Android 项目,并在 AndroidManifest.xml 文件中声明一个新的服务: ```xml <service android:name=".CustomKeyboard" android:label="Custom Keyboard" android:permission="android.permission.BIND_INPUT_METHOD"> <meta-data android:name="android.view.im" android:resource="@xml/method" /> </service> ``` 上面的代码声明了一个名为 CustomKeyboard 的服务,并将其与 android.view.im 绑定。在 res/xml 目录下创建一个名为 method.xml 的文件,用于指定 CustomKeyboard 的布局和行为: ```xml <?xml version="1.0" encoding="utf-8"?> <input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity=".SettingsActivity" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" > </input-method> ``` 上面的代码指定了键盘的设置活动、语言环境和子类型模式。 2. 创建 CustomKeyboard 类,并继承 InputMethodService。在这个类中,你需要重写一些回调方法,例如 onCreateInputView()、onKeyDown() 和 onStartInputView() 等。这些方法将决定键盘的外观和行为。 ```java public class CustomKeyboard extends InputMethodService implements KeyboardView.OnKeyboardActionListener { private KeyboardView keyboardView; private Keyboard keyboard; @Override public View onCreateInputView() { keyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard, null); keyboard = new Keyboard(this, R.xml.qwerty); keyboardView.setKeyboard(keyboard); keyboardView.setOnKeyboardActionListener(this); return keyboardView; } @Override public void onStartInputView(EditorInfo info, boolean restarting) { super.onStartInputView(info, restarting); keyboardView.setPreviewEnabled(false); } @Override public void onKey(int primaryCode, int[] keyCodes) { InputConnection ic = getCurrentInputConnection(); switch (primaryCode) { case Keyboard.KEYCODE_DELETE: ic.deleteSurroundingText(1, 0); break; case Keyboard.KEYCODE_SHIFT: // do something break; default: char c = (char) primaryCode; ic.commitText(String.valueOf(c), 1); } } } ``` 上面的代码创建了一个名为 CustomKeyboard 的类,并在 onCreateInputView() 方法中设置了键盘的布局和行为。在 onStartInputView() 方法中,我们禁用了键盘预览功能。在 onKey() 方法中,我们检查按下的键码并执行相应的操作。 3. 创建键盘布局。在 res/xml 目录下创建一个名为 qwerty.xml 的文件,用于指定键盘布局: ```xml <?xml version="1.0" encoding="utf-8"?> <Keyboard xmlns:android="http://schemas.android.com/apk/res/android" android:keyWidth="10%p" android:keyHeight="60dp" android:horizontalGap="0px" android:verticalGap="0px" android:keyEdgeFlags="left"> <Row> <Key android:keyLabel="q" android:keyEdgeFlags="left"/> <Key android:keyLabel="w"/> <Key android:keyLabel="e"/> <Key android:keyLabel="r"/> <Key android:keyLabel="t"/> <Key android:keyLabel="y"/> <Key android:keyLabel="u"/> <Key android:keyLabel="i"/> <Key android:keyLabel="o"/> <Key android:keyLabel="p" android:keyEdgeFlags="right"/> </Row> <Row> <Key android:keyLabel="a" android:keyEdgeFlags="left"/> <Key android:keyLabel="s"/> <Key android:keyLabel="d"/> <Key android:keyLabel="f"/> <Key android:keyLabel="g"/> <Key android:keyLabel="h"/> <Key android:keyLabel="j"/> <Key android:keyLabel="k"/> <Key android:keyLabel="l android:keyEdgeFlags="right"/> </Row> <Row> <Key android:keyLabel="shift" android:horizontalGap="10%p" android:keyWidth="20%p" android:keyEdgeFlags="left" android:isModifier="true" android:isSticky="true"/> <Key android:keyLabel="z"/> <Key android:keyLabel=""/> <Key android:keyLabel="c"/> <Key android:keyLabel="v"/> <Key android:keyLabel="b"/> <Key android:keyLabel="n"/> <Key android:keyLabel="m"/> <Key android:keyLabel="delete" android:keyWidth="20%p" android:keyEdgeFlags="right" android:icon="@drawable/ic_delete"/> </Row> <Row> <Key android:keyLabel="123" android:keyEdgeFlags="left" android:keyWidth="20%p"/> <Key android:keyLabel=" " android:keyWidth="40%p"/> <Key android:keyLabel="return" android:keyWidth="20%p" android:keyEdgeFlags="right"/> </Row> </Keyboard> ``` 上面的代码指定了一个基本的 QWERTY 键盘布局,包含字母、数字和删除键。 4. 运行应用程序并测试自定义键盘。在测试键盘时,你需要在 Android 设备的输入法设置中激活你的自定义键盘。 以上就是创建自定义键盘的基本步骤,你可以根据需要修改键盘的布局和行为。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值