手机上输入文字,都是通过系统自带的软键盘,这个软键盘可以是Android自带的,也可以是第三方软键盘如搜狗输入法。多数情况下面,系统自带的软键盘已经够用了,可是总有少数情况,系统软键盘无法满足开发者的要求,比如以下几个需求,系统软键盘就无法处理:
1、像手机号码与支付密码,只需要输入数字,连标点符号都不需要。然而系统软键盘即使切换到123数字模式,依旧显示包括标点符号在内的冗余按键。
2、系统软键盘固定在屏幕下方弹出,无法做为控件嵌入到页面布局中,更无法指定软键盘的显示位置。
3、系统软键盘会自动响应EditText的焦点变更事件,常常在意料之外突然之间蹦出来,弄得开发者要么剥夺EditText的焦点,要么强行关闭软键盘显示,但无论哪种方式都得开发者强行**,很不方便。
基于以上情况,要想满足这些定制需求,只能对输入法自定义软键盘了。全数字的软键盘界面倒也简单,下面先来个数字键盘的效果图。

这个键盘只有0-9十个数字,再加一个退格键,可谓十个兄弟家徒四壁,真是再直白不过了。那么这个软键盘又是如何实现的呢?其实它跟平常的自定义控件基本类似,只在细节上有所差异,下面分步说明自定义软键盘的过程。
1、我们知道,自定义控件要么重写onDraw方法来绘制控件界面,要么从layout布局文件中加载控件界面。软键盘采取的是后一种方式,只不过它的布局文件不是放在res/layout目录,而是保存在res/xml目录。
2、自定义控件的主要工作是书写自定义的控件类,自定义软键盘也不例外,有了自定义的控件类,才能处理十个数字键的按键动作,才能把软键盘做为普通的控件嵌入到其它布局文件中。
3、软键盘不是一个孤立的控件,它的按键动作需要实时在某个编辑框中把数字显示出来,所以在使用时还得给它绑定一个EditText,这样软键盘才知道我的按键要输出给这个EditText,而不是输出给那个EditText。

俗话说,百闻不如一见,所以在说明具体的实现步骤之前,还是先看看最终的软键盘使用动图,带上这个感性认识去学习会更有帮助。

接下来阐述自定义软键盘的三个步骤,首先要定义软键盘的布局文件,在res/xml目录创建名为inputkeyboard.xml的文件,内部的根节点为Keyboard,其下挂了四个Row节点表示有四行,每个Row节点下又挂了三个Key节点,表示每行有三个按键。完整的键盘布局文件如下所示:

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"android:keyWidth="34%p" android:horizontalGap="1px"android:verticalGap="1px" android:keyHeight="55dp"><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="-3"android:keyEdgeFlags="left"android:keyIcon="@drawable/sym_keyboard_done" /><Key android:codes="48" android:keyLabel="0" /><Key android:codes="-5"android:isRepeatable="true"android:keyEdgeFlags="right"android:keyIcon="@drawable/sym_keyboard_delete" /></Row>
</Keyboard>

上面这个xml键盘布局,到时候将作为自定义属性传给软键盘控件,所以要在res/values/attrs.xml中补充下列属性配置:

    <declare-styleable name="keyboard"><attr name="xml" format="reference" /></declare-styleable>

然后是编写自定义软键盘的控件代码了,这里的关键是用自定义的键盘布局替换掉系统默认的键盘布局,自定义代码如下所示:

public class KeyboardLayout extends LinearLayout {private KeyboardView mKeyboardView;private Keyboard mKeyboard;public KeyboardLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);initKeyboard(context, attrs);}private void initKeyboard(Context context, AttributeSet attrs){TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.keyboard);if (a.hasValue(R.styleable.keyboard_xml)) {//从xml文件中获取键盘布局int xmlid = a.getResourceId(R.styleable.keyboard_xml,0);mKeyboard = new Keyboard(context, xmlid);mKeyboardView = (KeyboardView)LayoutInflater.from(context).inflate(R.layout.keyboardview, null);//为键盘视图设置自定义的键盘布局mKeyboardView.setKeyboard(mKeyboard);mKeyboardView.setEnabled(true);  mKeyboardView.setPreviewEnabled(false);  addView(mKeyboardView);}}
}

最后要给软键盘绑定对应的EditText对象,即当软键盘发生按键动作时,要把按键结果显示在哪个EditText上。这个操作就是调用KeyboardView的setOnKeyboardActionListener方法,设置一个键盘事件监听器,监听器内部主要实现了onKey方法,每当发现合法的按键事件(0-9与退格键),则同步修改EditText对象的文本。这部分代码补充到前面的自定义控件类KeyboardLayout之中:

 public void setInputWidget(EditText et) {mKeyboardView.setOnKeyboardActionListener(new KeyboardListener(et));}private class KeyboardListener implements OnKeyboardActionListener {private EditText et;public KeyboardListener(EditText et) {this.et = et;}@Overridepublic void onKey(int primaryCode, int[] keyCodes) {Editable editable = et.getText();int start = et.getSelectionStart();if (primaryCode == Keyboard.KEYCODE_DELETE) { //退格键if (editable != null && editable.length() > 0) {if (start > 0) {editable.delete(start - 1, start);}}} else if(primaryCode>='0' && primaryCode<='9') {//可以直接输入的字符(如0-9),它们在键盘映射xml中的keycode值必须配置为该字符的ASCII码editable.insert(start, Character.toString((char) primaryCode));}}//此处省略其它无需具体实现的Override函数};

至此我们可以像使用其它控件一样直接把软键盘加入到页面布局啦,注意指定键盘布局的自定义属性:

    <com.example.exmtextinput.widget.KeyboardLayoutandroid:id="@+id/kl_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"mykeyboard:xml="@xml/inputkeyboard" />

点此查看Android开发笔记的完整目录

__________________________________________________________________________
本文现已同步发布到微信公众号“老欧说安卓”,打开微信扫一扫下面的二维码,或者直接搜索公众号“老欧说安卓”添加关注,更快更方便地阅读技术干货。

Android开发笔记(一百四十八)自定义输入法软键盘相关推荐

  1. Android开发笔记(六十八)工程库打包

    写好一个Android模块,比如说一个自定义控件或某个功能的sdk,然后开放出来给别人使用,就得通过某种方式把源码提供给对方.常见的打包方式有: 一.直接给源码,由开发者把代码加入到自己的工程中 该方 ...

  2. Android开发笔记(五十八)铃声与震动

    拖动条SeekBar SeekBar继承自进度条ProcessBar,有关ProcessBar的介绍见<Android开发笔记(四十九)异步任务处理AsyncTask>.SeekBar与P ...

  3. Android开发笔记(七十八)异常容错处理

    Exception Java的异常分两类,运行时异常RuntimeException和非运行时异常. 运行时异常包括空指针异常NullPointerException.数组越界异常IndexOutOf ...

  4. Android开发笔记(三十八)列表类视图

    AdapterView AdapterView顾名思义是适配器视图,Spinner.ListView和GridView都间接继承自AdapterView,这三个视图都存在多个元素并排展示的情况,所以需 ...

  5. Android开发笔记(四十八)Thread类实现多线程

    Thread概述 Thread类是真正的线程,查看源码可见Thread也实现了Runnable接口,但它内部有创建新的工作线程,所以Thread对象运行在与主线程不一样的分线程上. 因为Thread对 ...

  6. Android开发笔记(二十八)利用Application实现内存读写

    全局变量 C/C++有所谓的全局变量,因为全局变量保存在内存中,所以操作全局变量就是操作内存,其速度远比操作数据库或者操作文件快得多,而且工程里的任何代码都可以引用全局变量,因此很多时候全局变量是共享 ...

  7. Android开发笔记(七十五)内存泄漏的处理

    内存泄漏的原因 一直以来以为只有C/C++才存在内存泄漏的问题,没想到拥有内存回收机制的Java也可能出现内存泄漏.C/C++存在指针的概念,程序中需要使用指针变量时,就从内存中开辟一块区域,并把该区 ...

  8. Android开发笔记(五十九)巧用传感器

    传感器Sensor 传感器是Android用来感知周围环境以及运动信息的工具.因为具体的感应信息依赖于相关硬件,所以虽然Android提供了众多的感应器,但不是每部手机都能支持这么多感应器,恰恰相反, ...

  9. Android开发笔记(六十六)自定义对话框

    AlertDialog Android中最常用的对话框是AlertDialog,它可以完成常见的交互操作,如提示.确认.选择等等,然后就是进度对话框ProgressDialog(参见< Andr ...

  10. Android开发笔记(七十四)布局文件优化

    include/merge 布局优化中常常用到include/merge标签,include的含义类似C代码中的include,意思是直接把指定布局片段包含进当前的布局文件.include适用于多个布 ...

最新文章

  1. 【Azure Services Platform Step by Step-第11篇】Windows Azure兰州拉面馆-日志与队列的使用...
  2. R语言dplyr包recode函数、recode_factor函数数值或因子替换实战
  3. 安卓图表引擎AChartEngine(一) - 简介
  4. 【Git】Git 标签使用 ( 创建并查询标签 | 推送单个标签到远程仓库 | 推送所有标签到远程仓库 | 删除远程仓库的标签 )
  5. uoj22 外星人(dp)
  6. 【AI视野·今日CV 计算机视觉论文速览 第188期】Wed, 23 Dec 2020
  7. 【Fiddler 实战操作】如何使用 Fiddler 对苹果手机进行抓包
  8. linux命令进入用户模式,linux怎么切换到root用户模式
  9. nginx php-fpm 输出php错误日志
  10. python反射机制
  11. kubernetes之三:service
  12. SpringBoot 日志配置
  13. 基于c的xml文件解析(转)
  14. PHP中 如何将二位数组按某一个或多个字段值(升序/降序)排序?数字索引被重置,关联索引保持不变...
  15. windows Windows Defender彻底删除屏蔽后台启动占用内存 win10防火墙 windows10防火墙
  16. java web代码混淆_JAVA WEB 项目的代码混淆
  17. 汽车诊断之UDS入门-0x19 0x06服务
  18. 注册表右键取得管理员权限
  19. 企业网络管理利器-SpiceWorks(1)
  20. 小心,家中路由器发出的WiFi信号可能让你家变“透明”

热门文章

  1. 【实战技能】不做绑架公司的技术团队成员
  2. 高手速成android开源项目【View篇】(转)
  3. 小熊派STM32-OTA+IOT教程
  4. excel表格数据导入导出
  5. 我为什么要写微信公众号
  6. Error while extracting response for type [class xxx] and content type application/xml;charset=UTF-8
  7. python连接wss走自己的代理
  8. 技术人员的职业发展规划思考书单推荐
  9. Mac 重置 idea
  10. 什么是CRM系统,它如何支持客户营销管理?