大家想不想要这样一台Android  Surface平板,看着就过瘾吧。

我们知道,android眼下的输入都是通过软键盘实现的,用外接键盘的少。这个在手机上是能够理解的。当手机接上外接键盘后。总体会显得头重脚轻。而且用键盘输入时。人离手机的距离就远了,自然不太适合看清手机上的内容。那在平板上呢?假设平板仅仅是平时用来浏览看视频,不进行大量输入。自然也用不上外接键盘。

那到底什么时候须要用到外接键盘呢?本人认为首先要满足例如以下两个条件。

1)   平板和外接键盘完美融合,组合后非常像笔记本使用模式。类似上面Android Surface的机器,平板和键盘通过磁性自己主动粘合,变身笔记本模式

2)    Android用在类办公等须要高速输入场景,比方写文章。长时间聊qq等。事实上linux一直以来没法进入桌面系统的关键原因是window在这方面太优秀,它垄断了用户的办公习惯,即用Microsoft office系列软件办公。可是如今类linux。尤其Android在这边已经有了非常大进步,一方面,ubuntu帮组linux积累了一部分用户。比方libre office体验好多了。同一时候据说微软正在为Android开发Microsoft office的响应产品,这个是利好消息。

从上面看来。事实上市面上已经有满足上面两个条件的机器了。比方联想的A10        

它是一台超级本, 但它支持翻转,当翻转过来就是平板。

那为啥这样的Android超极本就不够火呢?当然有非常多原因啊,比方平板本身需求量小,Android本身就不适合办公。当然肯定也有另外一个小原因。它这个物理键盘居然不能中文输入。

因此,Android平板要进入办公领域并流行,须要实现类似PC端中文输入的体验。

本文说到的外接键盘中文输入,重在中文两字。其实,Android本身是支持外接键盘的。可是仅仅可以实现英文输入。其实。我们在前几篇文章已经说到了输入法,也已经分析到,Android要想输入中文,必须通过输入法。

那为啥Android的中文输入法不能像PC那样直接通过外接键盘输入呢?以下一一分析。

Android没法通过外接键盘中文输入原因

输入法和外接键盘不能共存

Android系统里,当有外接键盘时。输入法就会消失。这样自然没法通过输入法输入中文。

这个是由Configuration的keyboard配置项决定的。正常情况下。Configuration的keyboard值是nokeys,而当系统检測到外接键盘(蓝牙键盘等等)插入时,就会更新系统的Configuration,并将当中的keyboard置为非nokeys(比方Configuration.KEYBOARD_QWERTY),然后系统会将新的Configuration通知给全部程序,包含输入法。

当输入法程序检測到新的Configuration时,它会运行更新操作,然后发现已经有外接设备就会隐藏自己。这样输入法就不见了。

详细逻辑例如以下:

    //系统端 :WindowManagerService.javaboolean computeScreenConfigurationLocked(Configuration config, boolean forceRotate) {final InputDevice[] devices = mInputManager.getInputDevices();final int len = devices.length;for (int i = 0; i < len; i++) {InputDevice device = devices[i];if (!device.isVirtual()) {final int sources = device.getSources();final int presenceFlag = device.isExternal() ?WindowManagerPolicy.PRESENCE_EXTERNAL :WindowManagerPolicy.PRESENCE_INTERNAL;if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {//检測到外接键盘config.keyboard = Configuration.KEYBOARD_QWERTY;keyboardPresence |= presenceFlag;}}}// Determine whether a hard keyboard is available and enabled.boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;if (hardKeyboardAvailable != mHardKeyboardAvailable) {mHardKeyboardAvailable = hardKeyboardAvailable;mHardKeyboardEnabled = hardKeyboardAvailable;mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);}if (!mHardKeyboardEnabled) {config.keyboard = Configuration.KEYBOARD_NOKEYS;}}return true;}//输入法端: InputMethodService.java@Override public void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);if (visible) {if (showingInput) {// onShowInputRequested就会影响输入法的显示//当有外接键盘时,它会返回falseif (onShowInputRequested(showFlags, true)) {showWindow(true);} else {doHideWindow();}}// onEvaluateInputViewShown也会影响输入法的显示//当有外接键盘时,它会返回falseboolean showing = onEvaluateInputViewShown();mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ?
IME_VISIBLE : 0), mBackDisposition);}}public boolean onEvaluateInputViewShown() {Configuration config = getResources().getConfiguration();//检測Configuration是否标示了有外接键盘return config.keyboard == Configuration.KEYBOARD_NOKEYS|| config.hardKeyboardHidden ==Configuration.HARDKEYBOARDHIDDEN_YES;}public boolean onShowInputRequested(int flags, boolean configChange) {if (!onEvaluateInputViewShown()) {return false;}if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {Configuration config = getResources().getConfiguration();//检測Configuration是否标示了有外接键盘if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {return false;}}if ((flags&InputMethod.SHOW_FORCED) != 0) {mShowInputForced = true;}return true;}

输入法没法获得按键事件

我们知道,假设要想输入法通过外接键盘输出中文,它肯定须要从外接键盘读取到英文输入。而在Android系统中,按键等key事件仅仅发送给焦点程序,可是输入法本身没法获得焦点,因此它自然就没法读取到外接键盘的输入。

问题的解决

让输入法和外接键盘共存

从上面的分析可知。输入法和外接键盘没法共存的根本原因是,输入法会读取configuration里的键盘属性值。

解决问题有两个方法:

1)  改动用到Configuration的相关函数,比方onEvaluateInputViewShown ,onShowInputRequested函数的实现

这种方法看起来可行,可是不行。由于非常多地方可能用到了这个Configuration,改动量比較大,且非常多函数并不是protected或者public,子类是没法直接改动的。

2)  改动输入法的Configuration的值

这种方法可行。从源头上攻克了这个问题,这样InputMethodService觉得系统没有外接键盘。自然就不会隐藏输入法了。

方法2详细实现例如以下:

在输入法初始化和更新Configuration的点主动改动输入法的Configuration。

public class RemoteInputMethod extends InputMethodService { @Override public void onCreate() {super.onCreate();updateResources();}@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);updateResources();}public void updateResources() {Configuration config = new Configuration(getResources().getConfiguration());//改动Configuration,让输入法觉得系统中没有外接键盘config.keyboard = Configuration.KEYBOARD_NOKEYS;getResources().updateConfiguration(config, getResources().getDisplayMetrics());}
}

让输入法获取外接键盘输入

输入法实现输入有两部分。一是获取按键事件。二是获取输入目标

获取按键事件

上面已经提到过。输入法window是没法获取外接键盘事件的。怎么办?非常好办,让输入法service创建另外一个普通的window(本文称作bridge window),并将这个window标示为可接受key事件的window,当它是最top的可接受key事件的window时, 它就能够获得焦点并获得外接键盘的输入。

这样,它作为中间桥梁就能将外接键盘事件传给输入法 (同一程序里,非常好做的)。输入法然后进行翻译,比方拼音转为中文。

获取并更新输入目标

输入法的输入目标是textView的通信接口InputConnection。它是在程序获得焦点时候或焦点程序中的焦点view发生变化的时候。焦点程序传递给输入法的。

所以,问题来了?一旦上面的bridge window获得焦点后,输入法的输入目标就跟着更新了,变成了bridge window的view的InputConnection。这样即使输入法完毕了英文到中文的转换,最后也仅仅能将中文发送给bridge window,并不能发送给用户想输入的程序。怎么解?还好Android系统有一个特殊window flag-----WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,当一个window设置了这个flag, 它成为焦点时。输入法并不会将输入目标切换为当前焦点window的InputConnection,而是仍旧保持原来的InputConnection。这为我们带来了希望,也就是说,我们仅仅需将我们的bridge window加入这个flag就可以,其实确实如此。

可是还存在一个问题。我们知道InputConnection是相应textView的一个通信接口,当用户改变输入view时,输入法中的InputConnection是须要改动的,可是如今因为目标程序已经不是焦点程序了,当用户触摸目标程序其它textView导致输入view改变时,系统并不会通知输入法去更新InputConnection,这样一来,输入法的中文始终仅仅能传递给一个textView了。

又怎么解呢?灵光一动,继续解。当用户触摸时。我们能够让bridge window临时失去焦点,这样目标程序就又一次获取了焦点,然后输入view切换时,输入法就能得到通知,也就是能又一次获取到新的textView的InputConnection。然后。bridge window又一次获取焦点,也就是非常短时间后它继续能够接受外接键盘的输入了。

这个方法的重点在bridge window的实现:实现的重点有两个:

1)     加入WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM flag

2)  监听OUT_SIDE事件,这样,当用户单击目标程序。切换焦点view时,bridge window可以提前获知,然后释放焦点,

让目标程序成为焦点,然后完毕焦点view的切换,进而完毕输入法中的输入目标InputConnection的更新。

   public class BridgeWindow extends Dialog {private static final boolean DEBUG = false;private static final String TAG = "MDialog";private static final int flagsNask = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;private static final int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;private static final int flags_nofocus = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;private Window mWindow = null;private Handler mHandler = new Handler();private MInputMethod mAttachedInputMethod = null;public BridgeWindow (Context context) {super(context);// TODO Auto-generated constructor stubinit();}public void setAttachedInputMethod(MInputMethod inputMethod) {mAttachedInputMethod = inputMethod;}View mRootView = null;public void setContentView(View view) {super.setContentView(view);mRootView = view;}private void init() {// TODO Auto-generated method stubrequestWindowFeature(Window.FEATURE_NO_TITLE);setTitle("HardInputMethod");mWindow = this.getWindow();LayoutParams lp = mWindow.getAttributes();lp.gravity = Gravity.LEFT|Gravity.TOP;lp.x = 0;lp.y = 0;mWindow.setType(WindowManager.LayoutParams.TYPE_PHONE);//初始化window的flagmWindow.setFlags(flags, flagsNask);}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {//检測到用户触摸了bridge window外的区域,那么焦点view可能要发生//变化了,输入法的InputConnection须要更新了。所以在此临时取消自己//的focusif (DEBUG) Log.d(TAG, "release focus");releaseFocus();}return super.onTouchEvent(event);}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (DEBUG) Log.d(TAG, "onKeyDown" + keyCode);//将事件传递给输入法mAttachedInputMethod.onKeyDown(keyCode,  event);return super.onKeyDown(keyCode, event);}protected void releaseFocus() {// TODO Auto-generated method stub//将自己配置成不可获取焦点来让自己失去焦点mWindow.setFlags(flags_nofocus, flagsNask);mHandler.removeCallbacks(mFocusRunnable);//1s钟后。让自己又一次获取焦点mHandler.postDelayed(mFocusRunnable, 1000);}Runnable mFocusRunnable = new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubmWindow.setFlags(flags, flagsNask);}};Point mDownPosition = new Point();public void onDown(int x, int y) {// TODO Auto-generated method stubint[] loc = new int[2];mRootView.getLocationOnScreen(loc);mDownPosition.x = loc[0];mDownPosition.y = loc[1] - 50;if (DEBUG) Log.d(TAG, "on down position x:" + loc[0] + " y:" + loc[1]);}public void onMove(int offsetX, int offsetY) {// TODO Auto-generated method stubupdatePositioin(mDownPosition.x + offsetX, mDownPosition.y + offsetY);}private void updatePositioin(int x, int y) {LayoutParams lp = mWindow.getAttributes();lp.x = x;lp.y = y;mWindow.setAttributes(lp);}
}

完美解决方式

上面的解决方式是直接在输入法程序内部改动达到实现外接键盘输入中文。属于应用程范畴。可是仍有一些问题,而这些问题在程序端是没法解决的。

那该怎么完美解决呢。Andorid后来的版本号已经攻克了这个。是怎样解决的?

即全部的按键事件先发送给程序。然后程序端的代码会先将key发送给输入法,即让输入法有一个翻译转换过程的机会,然后输入法再将转化过的key或者字符发送回程序,也就是说key事件绕了一圈。最后再让程序端处理。

附录

近期工作比較忙。代码还没有整理好,等整理好后,我会将源代码发出来。大家能够一起学习。

/********************************

* 本文来自博客  “爱踢门”

* 转载请标明出处:http://blog.csdn.net/itleaks

******************************************/

本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5114169.html,如需转载请自行联系原作者

Android输入法扩展之外接键盘中文输入相关推荐

  1. 笔划码、五笔码、拼音码软键盘中文输入

    很长时间没来Show一下了,把最近搞的一个软键盘中文输入程序发上来 show一下 一,拼音输入法,模糊输入单字联想. 二,五笔输入,模糊输入单字联想. 三,笔划输入,模糊输入单字联想. 四,特殊字符输 ...

  2. mac怎么切换输入法(在外接键盘下)

    首先,如果你就用的mac的键盘,有三种方式可以切换输入法,第一个是地球键,可以见下图 第二个是大写键来切换输入法,见下图,但是这样的话大写键用起来就不是很方便. 第三个是^空格键,见下图 但是现在我是 ...

  3. 轻量级Qt键盘-中文输入

    在原有的键盘基础上新增中文输入功能. 中文候选栏   中文输入候选栏ChineseWidget使用QListWidget和样式表实现: setText输入对应拼音字母,即会加载符合的拼音中文. pre ...

  4. android输入法状态监听 js,中文拼音输入法在input监听的问题(监听字节数)

    监听文本输入框的input 和propertychange事件,在拼写汉字(输入法)但汉字并未实际填充到文本框中(选词)时会触发input 和propertychange 事件 现在有一个需求需要监听 ...

  5. 关于Cocos2D-X 3.10在android平台上使用外接键盘无法响应的问题

    众所周知,当今大部分智能手机是没有物理键盘的,在使用cocos开发Android应用时,自然也就不会去注意不必要的键值的注册.然而有些时候,我们需要游戏具有高精度的操控体验,就会用到键盘.然而当我们在 ...

  6. Win10输入法突然无法切换回中文输入

    解决问题:输入法可以输入输入英文,但是没有办法切换回中文 WIN+R 输入taskschd.msc打开任务计划程序 taskschd.msc 依次找到\Microsoft\Windows\TextSe ...

  7. Android模拟器中EditText能够调出中文输入

    做一个项目,发现EditText只能输入英文和符号等,中文调不出来.结果鼓捣了一小时终于发现应该怎么弄了... 1.一开始的EditText界面,只能输入英文. 2.返回主界面,找到Settings这 ...

  8. python显示输入法候选框_CentOS6.4 中文输入不显示候选框问题解决方案

    /usr/libexec/ibus-ui-gtk /usr/bin/ibus-setup /usr/libexec/ibus-engine-table 将以上三个文件中的  "exec py ...

  9. Android 输入法 微信公众号搜索定制版

    1. 一个简单的Android 输入法 (1) 使用Android Studio建立一个没有Activity的项目,对于一个简单的输入法而言,MainActivity并不是必要的,只需要一个输入法服务 ...

最新文章

  1. 你看的每一篇Nature论文,都是这样出炉的!
  2. python字典转字符串后里面的冒号还在吗_Python字典、字符串及列表的相互转换
  3. golang中的数字签名
  4. 谋定智慧农业-农民丰收节·万祥军:京东共聚黑山县三方合力
  5. 2012年初的10个绝对让你惊喜的jQuery插件
  6. 【从传统方法到深度学习】图像分类
  7. CSS之Responsive网页设计的三个特性
  8. leetcode 2 --- 两数相加
  9. php里面没有mssql,为什么没有正确使用PHP / MSSQL的日期/时间?
  10. Mybatis批量删除
  11. python中re模块的函数_python中的re模块,常用函数介绍
  12. ssh转发代理:ssh-agent用法详解
  13. osg多线程_尝试使用Osg编译上下文实现多线程编译显示列表--总结
  14. Java基础复习——访问权限
  15. 运行控制器方法之前先执行注解@ModelAttribute的方法
  16. Matlab 几种画图方式总结
  17. NOPI导出到excel
  18. python 循环语句s =2+22+222+2222之和_python基础2
  19. 【转-参考】九宫格时间管理-第三代时间管理方法:人生平衡发展的八个方向
  20. 三分钟带你快速了解网站开发的整个流程

热门文章

  1. 飞桨领航团表彰来了!
  2. 计算机毕设 SSM+Vue健康管理系统 健康体检管理系统 身体健康管理系统 身体健康信息系统Java Vue MySQL数据库 远程调试 代码讲解
  3. 国航WIFI:安全性是否可以保障?
  4. .NET之对接口和抽象类
  5. 修改android系统 字体
  6. 荣威r5新性能服务器,用实力诠释国潮 荣威RX5 PLUS性能分析抢先看
  7. 前香港电台古典音乐主持人梁继璋给儿子的备忘录
  8. 预装Win10改Win7出错了?这些失误一定不要犯!
  9. 3DS Max、Maya、SoftImage、houdini对比
  10. Raspberry Pi智能设备开发-人脸识别门禁系统设计与实现