If you want an Android surface tablet like this, it's fun to watch.
We know that Android's current input is achieved through a soft keyboard, with less external keyboard, which is understandable on the phone. When the phone connected to the external keyboard, the overall will appear top-heavy, and with the keyboard input, the distance from the phone is far away, nature is not very suitable to see the contents of the phone. What about on the plate? If the tablet is only used to browse the video, do not make a lot of input, naturally also use the external keyboard. When is it necessary to use an external keyboard? I think we should meet the following two conditions first.
1) The combination of tablet and external keyboard is very similar to notebook usage mode. Like the android surface on top of the machine, tablet and keyboard automatically bonded by magnetic, change the notebook mode
2) Android used in the class office and other needs to enter the scene quickly, such as writing articles, long chat QQ and so on. The key reason why Linux has never been able to get into the desktop system is that Windows is too good at this point, it's a monopoly on the user's office habits, that is, working with Microsoft Office Series software. But now Linux, especially Android has made a lot of progress here, on the one hand, Ubuntu Group Linux has accumulated a part of the users, such as Libre Office experience much better. It is also said that Microsoft is developing a response product for Microsoft Office for Android, which is good news.
from the above, in fact, there are already on the market to meet the above two conditions of the machine, such as Lenovo's A10
It is a super-Ben, but it supports flipping when flipped over is a tablet.
So why is this Android hyper-polar not enough to fire? Of course there are many reasons ah, such as the tablet itself, the demand is small, Android itself is not suitable for office, of course, there must be another small reason, it is not the physical keyboard of Chinese input. Therefore, the Android tablet to enter the office area and popular, need to achieve similar PC-side Chinese input experience.
This article is about the external keyboard Chinese input, heavy in Chinese two words. In fact, Android natively supports external keyboards, but only English input is possible. In fact, we have already talked about the input method in the previous articles, also has analyzed, the android wants to enter the Chinese, must pass the input method. So why can't Android's Chinese input method be entered directly from the external keyboard like a PC? Analyzed below.
Android does not use external keyboard chinese input reason
Input method and external keyboard cannot coexist
Android system, when there is an external keyboard, the input method will disappear, so naturally unable to input Chinese through the input method. This is determined by the configuration items of the keyboard. Normally, the keyboard value of the configuration is Nokeys, and when the system detects that an external keyboard (Bluetooth keyboard, and so on) is plugged in, the system's configuration is updated and the keyboard is set to non-Nokeys ( such as Configuration.keyboard_qwerty), and the system will notify all programs, including input methods, of the new configuration. When the IME detects a new configuration, it performs an update operation and then discovers that an external device already hides itself so that the input method is gone.
The specific logic is as follows:
System side: Windowmanagerservice.java boolean computescreenconfigurationlocked (Configuration config, Boolean Forcero Tate) {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) {//External keyboard detected 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; }//Input port: Inputmethodservice.java @Override public void onconfigurationchanged (Configuration newconfig) {sup Er.onconfigurationchanged (Newconfig); if (visible) {if (showinginput) {///onshowinputrequested will affect the display of the input method//when there is an external keyboard, it will Returns False if (Onshowinputrequested (Showflags, True)) { ShowWindow (TRUE); } else {Dohidewindow (); }}//Onevaluateinputviewshown will also affect the display of IME//when there is an external keyboard, it will return false Boolean showing = Onevaluateinputviewshown (); Mimm.setimewindowstatus (Mtoken, ime_active | (showing?) ime_visible:0), mbackdisposition); }} public Boolean Onevaluateinputviewshown () {Configuration config = getresources (). GetConfiguration (); Detects if the Configuration is marked with an external keyboard 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 (). Getconfigurati On (); Detects if the configuration is marked with an external keyboard if (config.keyboard! = Configuration.keyboard_nokeys) {return false; }} if ((flags&inputmethod.show_forced)! = 0) {mshowinputforced = true; } return true; }
Input method cannot get key event
We know that if you want the input method to output Chinese through an external keyboard, it must be read from the external keyboard into English input. In the Android system, key events such as keystrokes are sent only to the focus program, but the input method itself does not have the focus, so it is naturally unable to read the input to the external keyboard.
Solution to the problem
To co-exist with the IME and the external keyboard
from the above analysis, the basic reason why the input method and the external keyboard cannot coexist is that the IME reads the keyboard property values in the configuration. There are two ways to solve this problem:
1) Modify the relevant functions used in the configuration, such as Onevaluateinputviewshown, the implementation of the Onshowinputrequested function
This method looks workable, but it doesn't work. Because many places may use this configuration, the modification quantity is large, and many functions are not protected or public, the subclass cannot modify directly.
2) Modify the value of the input method's configuration
This method is feasible, from the source to solve the problem, so inputmethodservice that the system does not have an external keyboard, nature will not hide the input method.
Method 2 specifically implements the following:
Active modification of the input method's configuration at the point where the input method initializes and updates the configuration.
public class Remoteinputmethod extends Inputmethodservice { @Override public void OnCreate () { Super.oncreate (); Updateresources (); } @Override public void onconfigurationchanged (Configuration newconfig) { super.onconfigurationchanged ( Newconfig); Updateresources (); } public void Updateresources () {Configuration config = new Configuration (Getresources (). GetConfiguration ()); Modify the Configuration so that the input method does not think the system has an external keyboard config.keyboard = Configuration.keyboard_nokeys;getresources (). Updateconfiguration (config, getresources (). Getdisplaymetrics ());}}
let IME Get external keyboard input
Input method implements two parts, one is to get the key event, and the other is to get the input target
Get key Events
as mentioned above, Input Method window is unable to get external keyboard events, how to do? Good, let the Input method service create another normal window (this article is called Bridge window), and this window is marked as an acceptable key event window, when it is the top of the window to accept the key event, It gets the focus and gets the input to the external keyboard. In this way, it can be used as an intermediate bridge to pass the external keyboard events to the input method (in the same program, very good), input method and then translate, such as pinyin to Chinese.
get and update an input target
The input target of the input method is TextView communication interface inputconnection. It is the focus program that is passed to the IME when the program gets the focus or when the focus view in the focus program changes.
So, here's the problem? Once the above bridge window gets the focus, the input target of the input method is updated, which becomes the inputconnection of the view of Bridge window. So even if the input method completed the English to Chinese conversion, and finally can only send the Chinese to bridge window, and can not be sent to the user want to enter the program. What's the solution? Fortunately the Android system has a special window flag-----WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, when a window is set with this flag, it becomes the focus, The IME does not switch the input target to the inputconnection of the current focus window, but still retains the original inputconnection. This gives us hope, that is to say, we simply add the flag to our bridge window, which is actually true.
But there is a problem. We know that inputconnection is a communication interface for the corresponding TextView, and when the user changes the input view, the inputconnection in the input method needs to be modified, but now because the target program is no longer the focus program, When the user touches the target program other TextView causes the input view to change, the system does not inform the input method to update the inputconnection, thus, the input method's Chinese always can only pass to one textview. And how do you solve it? The light moves and continues the solution. When the user touches, we can let Bridge window temporarily lose focus, so that the target program to regain focus, and then enter the view switch, the input method can be notified, that is, to regain access to the new TextView inputconnection. Bridge window then gets the focus back, which is a short time before it continues to accept input from the external keyboard.
The focus of this program is on Bridge window implementation: There are two key implementations:
1) Add WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM FLAG
2) Monitor the Out_side event so that when the user clicks on the target program and switches the Focus view, Bridge window can be notified in advance and then release the focus
The target program becomes the focus, then completes the switch of the focus view, then completes the input target inputconnection update in the input method.
public class Bridgewindow extends Dialog {private static final Boolean DEBUG = false;private static final String TAG = "Mdialog";p rivate 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 Stub requestwindowfeature (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); Initializes the flag Mwindow.setflags of the window (flags, flagsnask); } @Override public boolean ontouchevent (Motionevent event) {if (event.getaction () = = Motionevent.action_outs IDE) {//detects that the user has touched the area outside of Bridge window, then the focus view may have to occur//change, the input method's inputconnection need to be updated, so temporarily cancel their The focus if (DEBUG) log.d (TAG, "release Focus"); Releasefocus (); } return Super.ontouchevent (evenT); } @Override public boolean onKeyDown (int keycode, keyevent event) {if (DEBUG) log.d (TAG, "OnKeyDown" + keycode); Passing events to IME mattachedinputmethod.onkeydown (KeyCode, event); Return Super.onkeydown (KeyCode, event); } protected void Releasefocus () {//TODO auto-generated Method Stub//Configure Yourself to lose focus by configuring yourself as not to get focus Mwindow.setflag S (Flags_nofocus, flagsnask); Mhandler.removecallbacks (mfocusrunnable); 1s clock, let yourself regain focus mhandler.postdelayed (mfocusrunnable, 1000);} Runnable mfocusrunnable = new Runnable () {@Overridepublic void Run () {//TODO auto-generated method Stubmwindow.setflags (f lags, flagsnask);}; Point mdownposition = new Point ();p ublic 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);}}
The Perfect solution
The above solution is directly in the Input method program internal modification to achieve external keyboard input Chinese, belongs to the scope of the application, but there are still some problems, and these problems at the terminal can not be solved. How to solve it perfectly, Andorid later version has solved this, how to solve?
That is, all the key events first sent to the program, and then the code will first send the key to the input method, that is, let the input method has a translation conversion process opportunity, and then the input method will be converted key or word character back to the program, that is, key event around a circle, and finally let the terminal processing.
Appendix
Recently work relatively busy, code has not been collated well, and so on, I will send out the source, we can learn together.
/********************************
* This article from the blog "Love Kick Door"
* Reprint Please indicate the source : Http://blog.csdn.net/itleaks
******************************************/