├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── ableqing │ │ └── androidkeyboardviewdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── ableqing │ │ │ └── androidkeyboardviewdemo │ │ │ ├── MainActivity.java │ │ │ ├── MyApp.java │ │ │ ├── MyInputMethodService.java │ │ │ ├── constants │ │ │ └── DemoKeyCode.java │ │ │ ├── keyboard │ │ │ └── MyKeyboardView.java │ │ │ └── util │ │ │ └── MyUtil.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_key_preview.xml │ │ ├── btn_keyboard_gray.xml │ │ ├── btn_keyboard_key.xml │ │ ├── ic_launcher_background.xml │ │ ├── keyboard_delete.xml │ │ ├── keyboard_enter.xml │ │ ├── keyboard_gray.xml │ │ ├── keyboard_radio_btn.xml │ │ ├── keyboard_shift.xml │ │ ├── keyboard_switch.xml │ │ ├── layer_key_delete_normal.xml │ │ ├── layer_key_delete_press.xml │ │ ├── layer_key_enter.xml │ │ ├── layer_key_enter_press.xml │ │ ├── layer_key_gray.xml │ │ ├── layer_key_normal.xml │ │ ├── layer_key_press.xml │ │ ├── layer_key_shift_normal.xml │ │ ├── layer_key_shift_press.xml │ │ ├── layer_key_switch_normal.xml │ │ └── layer_key_switch_press.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── keyboard_global.xml │ │ └── keyboard_preview.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── key_add.png │ │ ├── key_arrow_l.png │ │ ├── key_arrow_r.png │ │ ├── key_back.png │ │ ├── key_enter.png │ │ ├── key_keyboard.png │ │ ├── key_lock.png │ │ ├── key_setting.png │ │ ├── key_shift.png │ │ ├── key_shift_double.png │ │ ├── key_shift_down.png │ │ ├── key_switch.png │ │ └── key_unlock.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── digit.xml │ │ ├── method.xml │ │ ├── qwerty.xml │ │ └── symbol.xml │ └── test │ └── java │ └── com │ └── example │ └── ableqing │ └── androidkeyboardviewdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 94 | 106 | 107 | 108 | 109 | 110 | 111 | 113 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidKeyboardViewDemo 2 | 安卓键盘的一个简单demo 3 | https://www.jianshu.com/p/12bcfd8c2c6e 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.example.ableqing.androidkeyboardviewdemo" 7 | minSdkVersion 26 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/ableqing/androidkeyboardviewdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.ableqing.androidkeyboardviewdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.res.Resources; 6 | import android.graphics.Point; 7 | import android.os.Bundle; 8 | import android.provider.Settings; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.util.DisplayMetrics; 11 | import android.view.View; 12 | import android.view.WindowManager; 13 | import android.view.inputmethod.InputMethodManager; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | 24 | findViewById(R.id.btn_go_settings).setOnClickListener(this); 25 | findViewById(R.id.btn_select_keyboard).setOnClickListener(this); 26 | 27 | 28 | // int statusBarHeight = getStatusBarHeight(getApplicationContext()); 29 | 30 | // Log.e("statusBarHeight ",statusBarHeight+"=========="); 31 | 32 | 33 | View decorView = getWindow().getDecorView(); 34 | decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { 35 | @Override 36 | public void onSystemUiVisibilityChange(int visibility) { 37 | // Note that system bars will only be "visible" if none of the 38 | // LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set. 39 | if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { 40 | // TODO: The system bars are visible. Make any desired 41 | // adjustments to your UI, such as showing the action bar or 42 | // other navigational controls. 43 | } else { 44 | // TODO: The system bars are NOT visible. Make any desired 45 | // adjustments to your UI, such as hiding the action bar or 46 | // other navigational controls. 47 | } 48 | } 49 | }); 50 | 51 | 52 | } 53 | 54 | /** 55 | * 获取状态栏高度 56 | * 57 | * @param context 58 | * @return 59 | */ 60 | public int getStatusBarHeight(Context context) { 61 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 62 | Point point = new Point(); 63 | wm.getDefaultDisplay().getSize(point); 64 | wm.getDefaultDisplay().getRealSize(point); 65 | 66 | 67 | DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); 68 | int width = displayMetrics.heightPixels; 69 | int height = displayMetrics.widthPixels; 70 | 71 | View decorView = getWindow().getDecorView(); 72 | int uiOptions = decorView.getSystemUiVisibility(); 73 | 74 | boolean hasNavigationBar = checkDeviceHasNavigationBar(context); 75 | 76 | int navigationBarHeight = 0; 77 | Resources rs = context.getResources(); 78 | int id = rs.getIdentifier("navigation_bar_height", "dimen", "android"); 79 | if (id > 0 && checkDeviceHasNavigationBar(context)) { 80 | navigationBarHeight = rs.getDimensionPixelSize(id); 81 | } 82 | 83 | 84 | return 1; 85 | } 86 | 87 | 88 | //获取是否存在NavigationBar 89 | public static boolean checkDeviceHasNavigationBar(Context context) { 90 | boolean hasNavigationBar = false; 91 | Resources rs = context.getResources(); 92 | int id = rs.getIdentifier("config_showNavigationBar", "bool", "android"); 93 | if (id > 0) { 94 | hasNavigationBar = rs.getBoolean(id); 95 | } 96 | try { 97 | Class systemPropertiesClass = Class.forName("android.os.SystemProperties"); 98 | Method m = systemPropertiesClass.getMethod("get", String.class); 99 | String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys"); 100 | if ("1".equals(navBarOverride)) { 101 | hasNavigationBar = false; 102 | } else if ("0".equals(navBarOverride)) { 103 | hasNavigationBar = true; 104 | } 105 | } catch (Exception e) { 106 | 107 | } 108 | return hasNavigationBar; 109 | 110 | } 111 | 112 | @Override 113 | public void onClick(View v) { 114 | switch (v.getId()) { 115 | case R.id.btn_go_settings: 116 | 117 | Intent intent = new Intent(); 118 | intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); 119 | startActivity(intent); 120 | 121 | 122 | break; 123 | 124 | case R.id.btn_select_keyboard: 125 | 126 | // getStatusBarHeight(getApplicationContext()); 127 | ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).showInputMethodPicker(); 128 | break; 129 | } 130 | 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/MyApp.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * 7 | * 类说明: 8 | * 9 | * @author qing 10 | * @time 31/01/2018 17:59 11 | */ 12 | public class MyApp extends Application { 13 | 14 | private static MyApp sInstance; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | sInstance = this; 20 | } 21 | 22 | public static MyApp getInstance() { 23 | return sInstance; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/MyInputMethodService.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo; 2 | 3 | import android.inputmethodservice.InputMethodService; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | /** 8 | * 9 | * 类说明: 10 | * 11 | * @author qing 12 | * @time 31/01/2018 17:59 13 | */ 14 | public class MyInputMethodService extends InputMethodService { 15 | 16 | 17 | /** 18 | * 初始化键盘视图 19 | * 20 | * @return 21 | */ 22 | @Override 23 | public View onCreateInputView() { 24 | 25 | View view = getLayoutInflater(). 26 | inflate(R.layout.keyboard_global, null); 27 | 28 | return view; 29 | } 30 | 31 | @Override 32 | public View onCreateCandidatesView() { 33 | TextView textView = new TextView(getBaseContext()); 34 | textView.setText("fdsfdsf"); 35 | return textView; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/constants/DemoKeyCode.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo.constants; 2 | 3 | 4 | /** 5 | * 类说明: 按键的键值 6 | */ 7 | public class DemoKeyCode { 8 | 9 | //安卓默认的配置 10 | public static final int KEYCODE_SHIFT = -1; 11 | public static final int KEYCODE_MODE_CHANGE = -2; 12 | public static final int KEYCODE_CANCEL = -3; 13 | public static final int KEYCODE_DONE = -4; 14 | public static final int KEYCODE_DELETE = -5; 15 | public static final int KEYCODE_ALT = -6; 16 | 17 | 18 | 19 | 20 | //setting 21 | public static final int CODE_SETTING = -100; 22 | //数字键盘切换 23 | public static final int CODE_TYPE_NUM = -101; 24 | //字母 25 | public static final int CODE_TYPE_QWERTY = -102; 26 | //符号 27 | public static final int CODE_TYPE_SYMBOL = -103; 28 | //切换输入法 29 | public static final int CODE_TYPE_SWITCH_INPUT = -105; 30 | //space 31 | public static final int CODE_SPACE = 32; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/keyboard/MyKeyboardView.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo.keyboard; 2 | 3 | 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.Paint; 8 | import android.graphics.Rect; 9 | import android.graphics.Typeface; 10 | import android.graphics.drawable.Drawable; 11 | import android.inputmethodservice.Keyboard; 12 | import android.inputmethodservice.KeyboardView; 13 | import android.support.v4.content.ContextCompat; 14 | import android.util.AttributeSet; 15 | import android.view.KeyEvent; 16 | import android.view.MotionEvent; 17 | import android.view.inputmethod.InputConnection; 18 | import android.view.inputmethod.InputMethodManager; 19 | import android.widget.Toast; 20 | 21 | import com.example.ableqing.androidkeyboardviewdemo.MyApp; 22 | import com.example.ableqing.androidkeyboardviewdemo.MyInputMethodService; 23 | import com.example.ableqing.androidkeyboardviewdemo.R; 24 | import com.example.ableqing.androidkeyboardviewdemo.constants.DemoKeyCode; 25 | import com.example.ableqing.androidkeyboardviewdemo.util.MyUtil; 26 | 27 | import java.util.List; 28 | 29 | /** 30 | * 类说明: 自定义键盘 31 | * 32 | * @author qing 33 | * @time 31/01/2018 17:57 34 | */ 35 | public class MyKeyboardView extends KeyboardView { 36 | 37 | //点击键盘时 按下的位置坐标 38 | private float mDownX; 39 | private float mDownY; 40 | 41 | 42 | // 数字键盘/字母键盘/符号键盘 43 | private Keyboard mKeyboardNum, mKeyboardLetter, mKeyboardSymbol; 44 | 45 | 46 | public MyKeyboardView(Context context, AttributeSet attrs) { 47 | super(context, attrs); 48 | initKeyboardView(context); 49 | } 50 | 51 | public MyKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) { 52 | super(context, attrs, defStyleAttr); 53 | 54 | 55 | initKeyboardView(context); 56 | 57 | } 58 | 59 | private void initKeyboardView(Context context) { 60 | mKeyboardLetter = new Keyboard(context, R.xml.qwerty); 61 | mKeyboardNum = new Keyboard(context, R.xml.digit); 62 | mKeyboardSymbol = new Keyboard(context, R.xml.symbol); 63 | 64 | 65 | //默认显示字母键盘 66 | setKeyboard(mKeyboardNum); 67 | setOnKeyboardActionListener(new MyOnKeyboardActionListener()); 68 | } 69 | 70 | 71 | @Override 72 | public void onDraw(Canvas canvas) { 73 | super.onDraw(canvas); 74 | 75 | 76 | List keys = getKeyboard().getKeys(); 77 | for (Keyboard.Key key : keys) { 78 | 79 | int code = key.codes[0]; 80 | // LogUtil.e("KEY", "Drawing key with code " + code); 81 | //ABC 82 | if (code == Keyboard.KEYCODE_SHIFT) { 83 | drawKeyBackground(R.drawable.keyboard_shift, canvas, key); 84 | } 85 | //切换输入法 86 | if (code == DemoKeyCode.CODE_TYPE_SWITCH_INPUT) { 87 | drawKeyBackground(R.drawable.keyboard_switch, canvas, key); 88 | 89 | } 90 | //删除 91 | if (code == DemoKeyCode.KEYCODE_DELETE) { 92 | drawKeyBackground(R.drawable.keyboard_delete, canvas, key); 93 | 94 | } 95 | //完成 return 96 | else if (code == Keyboard.KEYCODE_DONE) { 97 | drawKeyBackground(R.drawable.keyboard_enter, canvas, key); 98 | 99 | } 100 | 101 | // 符号 数字 abc 102 | else if (code == DemoKeyCode.CODE_TYPE_SYMBOL || code == DemoKeyCode.CODE_TYPE_QWERTY || code == DemoKeyCode.CODE_TYPE_NUM) { 103 | drawKeyBackground(R.drawable.keyboard_gray, canvas, key); 104 | drawText(canvas, key); 105 | } 106 | 107 | } 108 | } 109 | 110 | 111 | @Override 112 | public boolean onTouchEvent(MotionEvent me) { 113 | 114 | 115 | float x = me.getX(); 116 | float y = me.getY(); 117 | switch (me.getAction()) { 118 | 119 | case MotionEvent.ACTION_DOWN: 120 | mDownX = x; 121 | mDownY = y; 122 | break; 123 | case MotionEvent.ACTION_MOVE: 124 | setPreviewEnabled(false); 125 | //滑动距离小于10dp时不隐藏键盘预览 大于10dp时隐藏键盘按键预览 126 | if (Math.abs(x - mDownX) > MyUtil.dp2px(10) || Math.abs(y - mDownY) > MyUtil.dp2px(10)) { 127 | //取消预览 128 | setPopupOffset(0, MyUtil.dp2px(0)); 129 | } 130 | break; 131 | } 132 | 133 | 134 | return super.onTouchEvent(me); 135 | 136 | } 137 | 138 | /** 139 | * 绘制按键背景 140 | * 141 | * @param drawableId 142 | * @param canvas 143 | * @param key 144 | */ 145 | private void drawKeyBackground(int drawableId, Canvas canvas, Keyboard.Key key) { 146 | Drawable npd = (Drawable) getContext().getResources().getDrawable(drawableId); 147 | int[] drawableState = key.getCurrentDrawableState(); 148 | if (key.codes[0] != 0) { 149 | npd.setState(drawableState); 150 | } 151 | //绘制按键背景 加上 MyUtil.dp2px(4) 152 | npd.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); 153 | npd.draw(canvas); 154 | } 155 | 156 | 157 | /** 158 | * 绘制文字 159 | * 160 | * @param canvas 161 | * @param key 162 | */ 163 | private void drawText(Canvas canvas, Keyboard.Key key) { 164 | Rect bounds = new Rect(); 165 | bounds.set(key.x, key.y, key.x + key.width, key.y + key.height); 166 | Paint paint = new Paint(); 167 | paint.setTextAlign(Paint.Align.CENTER); 168 | paint.setTextSize(40); 169 | 170 | paint.setAntiAlias(true); 171 | paint.setTypeface(Typeface.DEFAULT_BOLD); 172 | if (Keyboard.KEYCODE_DONE == key.codes[0]) { 173 | paint.setColor(Color.WHITE); 174 | } else { 175 | paint.setColor(ContextCompat.getColor(MyApp.getInstance(), android.R.color.black)); 176 | } 177 | 178 | if (key.label != null) { 179 | paint.getTextBounds(key.label.toString(), 0, key.label.toString().length(), bounds); 180 | canvas.drawText(key.label.toString(), key.x + (key.width / 2), (key.y + key.height / 2) + bounds 181 | .height() / 2, paint); 182 | } 183 | } 184 | 185 | 186 | class MyOnKeyboardActionListener implements OnKeyboardActionListener { 187 | 188 | 189 | //是否是大写字母 190 | private boolean mIsUpper; 191 | 192 | @Override 193 | public void onPress(int primaryCode) { 194 | 195 | 196 | //设置某些按键不显示预览的view 197 | if (primaryCode == Keyboard.KEYCODE_SHIFT || primaryCode == Keyboard.KEYCODE_DELETE // 198 | || primaryCode == Keyboard.KEYCODE_DONE || primaryCode == DemoKeyCode.CODE_SPACE // 199 | || primaryCode == DemoKeyCode.CODE_TYPE_QWERTY || primaryCode == DemoKeyCode.CODE_TYPE_NUM // 200 | || primaryCode == DemoKeyCode.CODE_TYPE_SYMBOL || primaryCode == DemoKeyCode.CODE_SETTING // 201 | || primaryCode == DemoKeyCode.CODE_TYPE_SWITCH_INPUT || primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { 202 | setPreviewEnabled(false); 203 | } else { 204 | setPreviewEnabled(true); 205 | } 206 | } 207 | 208 | @Override 209 | public void onRelease(int primaryCode) { 210 | 211 | } 212 | 213 | @Override 214 | public void onKey(int primaryCode, int[] keyCodes) { 215 | 216 | 217 | //键盘服务 218 | MyInputMethodService service = (MyInputMethodService) getContext(); 219 | //当前输入的连接 220 | InputConnection ic = service.getCurrentInputConnection(); 221 | 222 | 223 | switch (primaryCode) { 224 | 225 | //删除 226 | case Keyboard.KEYCODE_DELETE: 227 | 228 | 229 | ic.deleteSurroundingText(1, 0); 230 | 231 | 232 | break; 233 | //完成 234 | case Keyboard.KEYCODE_DONE: 235 | 236 | 237 | ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)); 238 | 239 | 240 | break; 241 | 242 | // 大小写切换 243 | case Keyboard.KEYCODE_SHIFT: 244 | 245 | MyUtil.switchUpperOrLowerCase(mIsUpper, mKeyboardLetter); 246 | mIsUpper = !mIsUpper; 247 | setKeyboard(mKeyboardLetter); 248 | 249 | break; 250 | 251 | // 数字键盘切换 252 | case DemoKeyCode.CODE_TYPE_NUM: 253 | setKeyboard(mKeyboardNum); 254 | 255 | break; 256 | // 字母键盘切换 257 | case DemoKeyCode.CODE_TYPE_QWERTY: 258 | setKeyboard(mKeyboardLetter); 259 | 260 | break; 261 | 262 | // 符号键盘切换 263 | case DemoKeyCode.CODE_TYPE_SYMBOL: 264 | setKeyboard(mKeyboardSymbol); 265 | 266 | break; 267 | 268 | //settings 269 | case DemoKeyCode.CODE_SETTING: 270 | Toast.makeText(service, "Settings==", Toast.LENGTH_SHORT).show(); 271 | 272 | break; 273 | 274 | //切换输入法 275 | case DemoKeyCode.CODE_TYPE_SWITCH_INPUT: 276 | 277 | 278 | ((InputMethodManager) service.getSystemService(Context.INPUT_METHOD_SERVICE)).showInputMethodPicker(); 279 | 280 | break; 281 | 282 | //一般文本 283 | default: 284 | char inputChar = (char) primaryCode; 285 | 286 | ic.commitText(String.valueOf(inputChar), 1); 287 | 288 | 289 | } 290 | 291 | } 292 | 293 | @Override 294 | public void onText(CharSequence text) { 295 | 296 | } 297 | 298 | @Override 299 | public void swipeLeft() { 300 | 301 | } 302 | 303 | @Override 304 | public void swipeRight() { 305 | 306 | } 307 | 308 | @Override 309 | public void swipeDown() { 310 | 311 | } 312 | 313 | @Override 314 | public void swipeUp() { 315 | 316 | } 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/ableqing/androidkeyboardviewdemo/util/MyUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.ableqing.androidkeyboardviewdemo.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.inputmethodservice.Keyboard; 6 | import android.text.TextUtils; 7 | import android.view.WindowManager; 8 | 9 | import com.example.ableqing.androidkeyboardviewdemo.MyApp; 10 | import com.example.ableqing.androidkeyboardviewdemo.R; 11 | import com.example.ableqing.androidkeyboardviewdemo.constants.DemoKeyCode; 12 | 13 | import java.util.List; 14 | 15 | public class MyUtil { 16 | 17 | 18 | /** 19 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 20 | */ 21 | public static int dp2px(float dpValue) { 22 | final float scale = MyApp.getInstance().getResources().getDisplayMetrics().density; 23 | return (int) (dpValue * scale + 0.5f); 24 | } 25 | 26 | /** 27 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 28 | */ 29 | public static int px2dp(float pxValue) { 30 | final float scale = MyApp.getInstance().getResources().getDisplayMetrics().density; 31 | return (int) (pxValue / scale + 0.5f); 32 | } 33 | 34 | 35 | /** 36 | * 获得屏幕的宽度 37 | * 38 | * @return 39 | */ 40 | public static int getScreenWidth(Context context) { 41 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 42 | return wm.getDefaultDisplay().getWidth(); 43 | } 44 | 45 | /** 46 | * 获得屏幕的高度 47 | * 48 | * @param context 49 | * @return 50 | */ 51 | public static int getScreenHeight(Context context) { 52 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 53 | return wm.getDefaultDisplay().getHeight(); 54 | } 55 | 56 | 57 | 58 | 59 | 60 | /** 61 | * 键盘大小写切换 62 | */ 63 | public static void switchUpperOrLowerCase(boolean isUpper, Keyboard keyboard) { 64 | Resources res = MyApp.getInstance().getResources(); 65 | List keyList = keyboard.getKeys(); 66 | if (isUpper) {// 大写切换小写 67 | for (Keyboard.Key key : keyList) { 68 | CharSequence label = key.label; 69 | if (!TextUtils.isEmpty(label) && key.codes[0] != DemoKeyCode.CODE_SPACE && key.codes[0] != DemoKeyCode.KEYCODE_DONE) { 70 | String str = label.toString(); 71 | if (key.label != null && isLetter(str)) { 72 | key.label = str.toLowerCase(); 73 | key.codes[0] = key.codes[0] + 32; 74 | } 75 | } else if (key.sticky && key.codes[0] == Keyboard.KEYCODE_SHIFT) { 76 | key.icon = res.getDrawable(R.mipmap.key_shift); 77 | key.pressed = false; 78 | 79 | } 80 | } 81 | } else {// 小写切换大写 82 | for (Keyboard.Key key : keyList) { 83 | CharSequence label = key.label; 84 | if (!TextUtils.isEmpty(label) && key.codes[0] != DemoKeyCode.CODE_SPACE && key.codes[0] != DemoKeyCode.KEYCODE_DONE) { 85 | String str = label.toString(); 86 | if (key.label != null && isLetter(str)) { 87 | key.label = str.toUpperCase(); 88 | key.codes[0] = key.codes[0] - 32; 89 | } 90 | } else if (key.sticky && key.codes[0] == Keyboard.KEYCODE_SHIFT) { 91 | key.icon = res.getDrawable(R.mipmap.key_shift_down); 92 | key.pressed = true; 93 | 94 | } 95 | 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * 是否为字母 102 | * 103 | * @param str 104 | * @return 105 | */ 106 | public static boolean isLetter(String str) { 107 | if (!TextUtils.isEmpty(str)) { 108 | char c = str.charAt(0); 109 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 110 | } 111 | return false; 112 | } 113 | 114 | 115 | 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_key_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_keyboard_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_keyboard_key.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_delete.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_radio_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_shift.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_switch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_delete_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_delete_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_enter_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_shift_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_shift_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_switch_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/layer_key_switch_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 |