1 功能需求

  • 触屏设备主界面中有一个文本编辑框,底部区域固定显示一个数字键盘,键盘中除数字键外,还带有*和#键功能;
  • 提供一个自定义的数字输入法,生成apk安装包文件,嵌入到img镜像文件中去。

2 设计思路

  • 借鉴Android SDK目录下Sample中的 SoftKeyboard这个例子,参考demo演示,模仿实现IME(Input Method Editor);
  • 在平台版本Android 1.5、SDK版本3、版本名称CUPCAKE版本之后,Android就开放它的IMF(Input Method Framework),让我们能够开发自己的输入法。而开发输入法最好的入手例子就是SoftKeyboard,虽然这个例子只是简单的英文和数字等的输入,但是它本身也算是写得非常清楚和完整的输入法实现。

3 源码分析

3.1 生命周期

inputmethod_lifecycle_image.png

接下来我们介绍一些比较重要的生命周期方法
  
onCreate()
输入法创建过程时,首先调用该方法进行输入法组件的主要初始化,该方法只会调用一次。
  
onCreateInputView()
当需要去生成用于创建输入的视图时,会调用此方法。在首次显示你的输入法时,以及每次由于配置更改而需要重新创建输入法时将被调用。
  
onCreateCandidateView()
当需要去生成用于显示候选键的视图时,会调用此方法,类似onCreateInputView()方法。
  
onStartInputView()
当输入视图正在显示并且在新的编辑框上开始输入时被调用。这将始终在onStartInput()方法之后被调用,允许你在此处进行常规设置(设置空格键Icon,并刷新整个键盘视图)和仅进行特定视图的设置(将所选键盘应用于输入视图)。你应该保证onCreateInputView方法在该方法被调用之前调用。
  
onCurrentInputMethodSubtypeChanged()
子类型改变时被调用。设置空格键Icon,并刷新整个键盘视图。
  
onFinishInput()
用户完成字段编辑后,将调用此方法。我们可以使用它来重置我们的状态。比如清除当前的已输入文字和候选内容,还有控制候选键显示区域的可见性。默认情况下它是隐藏的。
  
onDestroy()
输入法服务结束时调用,只调用一次,在方法中做好资源释放的工作。

3.2 实现原理

  1. 自定义输入法相关的类,都放在com.example.android.softkeyboard包的下面
  2. 在AndroidMainifest.xml中声明输入法组件
    在清单文件中声明输入法服务,以及申请绑定输入法权限。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.softkeyboard"><application android:label="@string/ime_name"><service android:name="SoftKeyboard"android:permission="android.permission.BIND_INPUT_METHOD"><intent-filter><action android:name="android.view.InputMethod" /></intent-filter><meta-data android:name="android.view.im" android:resource="@xml/method" /></service><activity android:name=".ImePreferences" android:label="@string/settings_name"><intent-filter><action android:name="android.intent.action.MAIN"/></intent-filter></activity></application>
</manifest>
  1. CandidateView.java
    CandidateView类是一个候选键的视图类,它直接继承于 View类即可。在用户输入字符时,它应该能根据字符显示一定的提示,比如拼音同音字,联想的字之类的情况。
    类中主要的方法如下
    /*** 是设置宿主输入法,用于回到服务的连接以与文本字段进行通信。** @param listener*/public void setService(SoftKeyboard listener) {mService = listener;}
    /*** 设置候选词,之后进行绘制候选键视图。* @param suggestions* @param completions* @param typedWordValid*/public void setSuggestions(List<String> suggestions, boolean completions,boolean typedWordValid) {clear();if (suggestions != null) {mSuggestions = new ArrayList<String>(suggestions);}mTypedWordValid = typedWordValid;scrollTo(0, 0);mTargetScrollX = 0;// Compute the total widthonDraw(null);invalidate();requestLayout();}
    /*** 移除高亮显示*/private void removeHighlight() {mTouchX = OUT_OF_BOUNDS;invalidate();}
  1. LatinKeyboard.java
    软键盘类,直接继承了 Keyboard类,来实现一个输入拉丁文的键盘。
    类中主要的私有变量如下
   /*** 存储更改模式键的当前状态。它的宽度将会动态更新,当变为不可见时来匹配这个区域。*/private Key mModeChangeKey;/*** 存储语言切换键的当前状态(例如 地球键),当返回true时就应该可见。当这个key变得不可见时,它的宽度将会缩小为零。*/private Key mLanguageSwitchKey;/*** 当mLanguageSwitchKey可见时,存储大小和mModeChangeKey的其他信息。* 这应该是不可变的,当改变mLanguageSwitchKey的可见性时,仅用作参考大小。*/private Key mSavedModeChangeKey;/*** 当mLanguageSwitchKey可见时,存储大小和mLanguageSwitchKey的其他信息。* 这应该是不可变的,当改变mLanguageSwitchKey的可见性时,仅用作参考大小。*/private Key mSavedLanguageSwitchKey;

类中主要的方法如下

  /*** 动态改变语言切换键的可见性(例如 全球键)** @param visible*/void setLanguageSwitchKeyVisibility(boolean visible) {if (visible) {// 语言切换键应该显示。使用保存的布局来恢复模式变化键和语言切换键的大小。mModeChangeKey.width = mSavedModeChangeKey.width;mModeChangeKey.x = mSavedModeChangeKey.x;mLanguageSwitchKey.width = mSavedLanguageSwitchKey.width;mLanguageSwitchKey.icon = mSavedLanguageSwitchKey.icon;mLanguageSwitchKey.iconPreview = mSavedLanguageSwitchKey.iconPreview;} else {// 语言切换键应该隐藏。改变模式变化键的宽度去填充语言键的空间,用户不会看到任何奇怪的间隙。mModeChangeKey.width = mSavedModeChangeKey.width + mSavedLanguageSwitchKey.width;mLanguageSwitchKey.width = 0;mLanguageSwitchKey.icon = null;mLanguageSwitchKey.iconPreview = null;}}
    /*** 查看当前编辑器提供的IME选项,在键盘Enter键设置合适的标签(如果有一个)。** @param res* @param options*/void setImeOptions(Resources res, int options) {if (mEnterKey == null) {return;}switch (options & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {case EditorInfo.IME_ACTION_GO:mEnterKey.iconPreview = null;mEnterKey.icon = null;mEnterKey.label = res.getText(R.string.label_go_key);break;case EditorInfo.IME_ACTION_NEXT:mEnterKey.iconPreview = null;mEnterKey.icon = null;mEnterKey.label = res.getText(R.string.label_next_key);break;case EditorInfo.IME_ACTION_SEARCH:mEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_search);mEnterKey.label = null;break;case EditorInfo.IME_ACTION_SEND:mEnterKey.iconPreview = null;mEnterKey.icon = null;mEnterKey.label = res.getText(R.string.label_send_key);break;default:// 设置回车键IconmEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_return);mEnterKey.label = null;break;}}
    /*** 它还定义了一个内部类,叫做LatinKey,它直接继承了Key,来定义一个单独的键,* 它唯一重载的函数是isInside(int x , int y ),用来判断一个坐标是否在该键内。* 它重载为判断该键是否是CANCEL键,如果是则把Y坐标减少10px,* 按照他的解释是用来还原这个可以关掉键盘的键的目标区域。*/static class LatinKey extends Keyboard.Key {public LatinKey(Resources res, Keyboard.Row parent, int x, int y,XmlResourceParser parser) {super(res, parent, x, y, parser);}/*** 重写此方法,以便我们可以减少关闭键盘键的目标区域。** @param x* @param y*/@Overridepublic boolean isInside(int x, int y) {return super.isInside(x, codes[0] == KEYCODE_CANCEL ? y - 10 : y);}}
  1. LatinKeyboardView.java
    因为前面定义的LatinKeyboard这个类来说就只是一个概念而已,并不能实例出来一个UI,所以需要借助于一个View类来进行绘制。这个类简单的继承了KeyboardView类,然后重载了一个方法,就是onLongPress方法。
  2. SoftKeyboard.java
    编写软键盘输入法的示例.这段代码注重于简单性而不是完整性,因此绝对不应将其视为完整的软键盘实现其目的是提供一个基础示例,说明如何开始编写输入方法,并在适当时充实。IME其实就是一Service,用户在点击文本编辑框输入字符时,便会按照输入法生命周期顺序执行。整个输入法包括什么时候建,什么时候显示输入法,和怎样和文本框进行通讯等等。上面的几个.java文件,都是为了这个类服务的。总体来说,一个输入法需要的是一个输入视图,一个候选词视图,还有一个就是和应用程序的链接。
    其类中主要的方法如下,从上到下顺序执行
    /*** 输入法组件的主要初始化。一定要调用父类InputMethodService的onCreate()方法。* 用来创建输入法管理对象和字符串分隔符的初始化。*/@Overridepublic void onCreate() {super.onCreate();mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);mWordSeparators = getResources().getString(R.string.word_separators);}
    /*** 这是你可以进行所有UI初始化的地方。在创建和任何配置更改后将调用这个方法。* 在这里对Keyboard进行了初始化,从XML文件中读取软键盘信息,封装进Keyboard对象。*/@Overridepublic void onInitializeInterface() {if (mQwertyKeyboard != null) {// 重新创建键盘后,可能会发生配置更改,因此,如果可用空间已更改,我们需要能够重新构建键盘。int displayWidth = getMaxWidth();if (displayWidth == mLastDisplayWidth) return;mLastDisplayWidth = displayWidth;}mQwertyKeyboard = new LatinKeyboard(this, R.xml.qwerty);mSymbolsKeyboard = new LatinKeyboard(this, R.xml.symbols);mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);}
    /*** 这是我们初始化输入方法以开始在应用程序上进行操作的要点。* 在这里,我们已经绑定到客户端,并且现在正在接收有关我们的编辑对象的所有详细信息。** @param attribute* @param restarting*/@Overridepublic void onStartInput(EditorInfo attribute, boolean restarting) {super.onStartInput(attribute, restarting);// Reset our state.  We want to do this even if restarting, because// the underlying state of the text editor could have changed in any way.mComposing.setLength(0);updateCandidates();if (!restarting) {// Clear shift states.mMetaState = 0;}mPredictionOn = false;mCompletionOn = false;mCompletions = null;// We are now going to initialize our state based on the type of// text being edited.switch (attribute.inputType & InputType.TYPE_MASK_CLASS) {case InputType.TYPE_CLASS_NUMBER:case InputType.TYPE_CLASS_DATETIME:// Numbers and dates default to the symbols keyboard, with// no extra features.mCurKeyboard = mSymbolsKeyboard;break;case InputType.TYPE_CLASS_PHONE:// Phones will also default to the symbols keyboard, though// often you will want to have a dedicated phone keyboard.mCurKeyboard = mSymbolsKeyboard;break;case InputType.TYPE_CLASS_TEXT:// This is general text editing.  We will default to the// normal alphabetic keyboard, and assume that we should// be doing predictive text (showing candidates as the// user types).mCurKeyboard = mQwertyKeyboard;mPredictionOn = true;// We now look for a few special variations of text that will// modify our behavior.int variation = attribute.inputType & InputType.TYPE_MASK_VARIATION;if (variation == InputType.TYPE_TEXT_VARIATION_PASSWORD ||variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {// Do not display predictions / what the user is typing// when they are entering a password.mPredictionOn = false;}if (variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS|| variation == InputType.TYPE_TEXT_VARIATION_URI|| variation == InputType.TYPE_TEXT_VARIATION_FILTER) {// Our predictions are not useful for e-mail addresses// or URIs.mPredictionOn = false;}if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {// If this is an auto-complete text view, then our predictions// will not be shown and instead we will allow the editor// to supply their own.  We only show the editor's// candidates when in fullscreen mode, otherwise relying// own it displaying its own UI.mPredictionOn = false;mCompletionOn = isFullscreenMode();}// We also want to look at the current state of the editor// to decide whether our alphabetic keyboard should start out// shifted.updateShiftKeyState(attribute);break;default:// For all unknown input types, default to the alphabetic// keyboard with no special features.mCurKeyboard = mQwertyKeyboard;updateShiftKeyState(attribute);}// Update the label on the enter key, depending on what the application// says it will do.mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);}
    /*** 在用户输入的区域要显示时,调用这个方法,输入法首次显示时,或者配置信息改变时,该方法就会被执行。* 在该方法中,对InputView进行初始化:读取布局文件信息,设置onKeyboardActionListener,并初始设置 keyboard。*/@Overridepublic View onCreateInputView() {mInputView = (LatinKeyboardView) getLayoutInflater().inflate(R.layout.input, null);mInputView.setOnKeyboardActionListener(this);setLatinKeyboard(mQwertyKeyboard);return mInputView;}
    /*** 在需要显示候选词汇的视图时,调用这个方法。和onCreateInputView()这个方法类似。* 在这个方法中,对CandidateView进行初始化。*/@Overridepublic View onCreateCandidatesView() {mCandidateView = new CandidateView(this);mCandidateView.setService(this);return mCandidateView;}
    /*** 当显示输入视图并且在新的文本编辑框上开始输入时调用,将InputView和当前Keyboard重新关联起来。* 这将始终在onStartInput()之后调用,允许你在此处进行常规设置,而在此处仅进行特定视图的设置。* 你可以保证在调用此函数之前一定时间会调用onCreateInputView()。** @param attribute  对要编辑的文本类型的描述* @param restarting 如果我们要像以前一样在相同的文本字段上重新开始输入,请设置为true。*/@Overridepublic void onStartInputView(EditorInfo attribute, boolean restarting) {super.onStartInputView(attribute, restarting);// Apply the selected keyboard to the input view.setLatinKeyboard(mCurKeyboard);mInputView.closing();final InputMethodSubtype subtype = mInputMethodManager.getCurrentInputMethodSubtype();mInputView.setSubtypeOnSpaceKey(subtype);}

在上面的六个方法中,onCreateInputView()和onCreateCandidatesView()两个方法只有在初始化时才执行一次,除非有配置信息发生改变。

4 示例截图

Android输入法官方SoftKeyboard示例解析相关推荐

  1. 【Android 应用开发】Android 图表绘制 achartengine 示例解析

    作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38420197 一. AChartEngine 简介 1. 项 ...

  2. Android 图表绘制 achartengine 示例解析

    作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38420197 一. AChartEngine 简介 1. 项 ...

  3. android输入法01:SoftKeyboard源码解析01

      本文主要介绍android自带输入法实例SoftKeyboard的源码,共分为两篇:第一篇为SoftKeyboard框架概述,第二篇为源码注释. 1.IMF简介 一个IMF结构中包含三个主要的部分 ...

  4. android 输入法如何启动流程_android输入法02:openwnn源码解析01—输入流程

    android 输入法 02:openwnn 源码解析 01-输入流程 之后要开始 android 日文输入法的测试,因此现在开始研究 android 输入法.之前两 篇文章已经对 android 自 ...

  5. 【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )

    文章目录 一.Google 官方 Bitmap 相关示例参考 二.磁盘缓存类 DiskLruCache 三.磁盘缓存初始化 四.存储数据到磁盘缓存中 五.从磁盘缓存中读取数据 六. Android 1 ...

  6. AXI-IIC官方示例解析

    AXI-IIC官方示例解析 说明:本文是作者自己对Xilinx的AXI-IIC的官方示例的解析,如有错误望各位指正. 文章目录 AXI-IIC官方示例解析 前言 xiic_eeprom_example ...

  7. android输入法架构解析

    android输入法架构解析 简介: 前阵子接手维护了一个密码键盘的项目,之前还没有接触过android输入法这块的知识点,所以在熟悉项目的同时将android系统输入法实现框架整理了一遍,记录在此. ...

  8. 开发android 输入法,Android输入法开发实例解析 Android开发技术

    Android输入法开发实例解析 Android开发技术 2013 年 4 月 13 日 这里我们建立表1为BiHua,同时构建两个字段,字段1为"input"来存放输入的,字段2 ...

  9. Android ApiDemos示例解析(95):Views-Animation-3D Transition

    Android中的Animation支持无处不在,不同Activity切换,不同View之间切换,显示列表,显示表格都可以使用动画效果.前面例子Android ApiDemos示例解析(3): App ...

最新文章

  1. 机载雷达发展简史:从蝙蝠到机载相控阵
  2. Java的异常:Error与Exception
  3. LinkedList和ArrayList的区别
  4. JQuery选中的对象和非选中的其他对象分别执行不同动作
  5. eclipse 导入zip
  6. wxWidgets:wxIdleEvent类用法
  7. rabbitmq延迟队列实现
  8. 交付量强劲增长 特斯拉营收却将出现7年来首次下滑
  9. 关闭ArcGIS9.3时 .NET Framework出现尝试读取或写入受保护的内存问题
  10. 医药公司java,医药管理系统java版
  11. 一位腾讯离职创业 6 年 CEO 的忠告:不要等公司倒闭,才思考这 3 个问题
  12. golang实现微信模板消息推送
  13. NSCalendar 日历类
  14. 空气炸锅如何挑选研究
  15. cNc数控Z轴服务器在什么位置,五轴数控机床A、C旋转轴分别安装在哪里?
  16. spring mvc 响应处理post请求和get请求 测试
  17. 01背包问题---动态规划
  18. C语言已知三边求三角形的面积
  19. 江南大学计算机专业就业率,江南大学这些本科和硕士专业就业率100%!2019江大毕业生就业报告...
  20. 教你如何查看自己操作系统是32位还是64位?XP系统【转】

热门文章

  1. 存储器容量的扩充以及DRAM
  2. 【Windows】U 盘装系统,无法格式化所选磁盘分区[错误: 0x8004242d]
  3. 看我如何偷取别人的云储币(Siacoin)
  4. 华为云灾备解决方案,助力企业高效构建安全合规云灾备系统
  5. java中输入日期_Java中的日期操作
  6. Docker、Docker、Docker
  7. 老男孩mysql运维dba实战21部完整版_老男孩MySQL高级专业DBA实战课程/高级运维DBA课程/MySQL视频教程 零基础全套...
  8. 线程共享地址空间的问题
  9. linux系统下载及安装(CentOS-7-x86_64-DVD-1810.iso)
  10. Http请求状态详解