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">"@"</string> <string name="keyboard_key2">"&"</string> <string name="keyboard_key3">\"</string> <string name="keyboard_key4">"<"</string> <string name="keyboard_key5">">"</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>