- 浏览: 65735 次
- 性别:
- 来自: 南京
最新评论
-
firojre:
我觉得你把OSI model 和Network socket ...
Linux协议栈之BSD和INET socket层(一) -
firojre:
Linux的BSD和INET socket层分别对应于ISO ...
Linux协议栈之BSD和INET socket层(一) -
guoyu04:
一个UI中,new 两个 handler 是什么情况?是一个h ...
Android Framework系列之IPC(一)
Android通过 KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,并放入到KeyQ消息队列中。
KeyInputQueue.java
Thread mThread = new Thread("InputDeviceReader") { public void run() { if (DEBUG) Log.v(TAG, "InputDeviceReader.run()"); android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); RawInputEvent ev = new RawInputEvent(); while (true) { try { InputDevice di; // block, doesn't release the monitor readEvent(ev); boolean send = false; boolean configChanged = false; if (false) { Log.i(TAG, "Input event: dev=0x" + Integer.toHexString(ev.deviceId) + " type=0x" + Integer.toHexString(ev.type) + " scancode=" + ev.scancode + " keycode=" + ev.keycode + " value=" + ev.value); } if (ev.type == RawInputEvent.EV_DEVICE_ADDED) { synchronized (mFirst) { di = newInputDevice(ev.deviceId); if (di.classes != 0) { // If this device is some kind of input class, // we care about it. mDevices.put(ev.deviceId, di); if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) { readVirtualKeys(di.name); } // The configuration may have changed because // of this device. configChanged = true; } else { // We won't do anything with this device. mIgnoredDevices.put(ev.deviceId, di); Log.i(TAG, "Ignoring non-input device: id=0x" + Integer.toHexString(di.id) + ", name=" + di.name); } } } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) { synchronized (mFirst) { if (false) { Log.i(TAG, "Device removed: id=0x" + Integer.toHexString(ev.deviceId)); } di = mDevices.get(ev.deviceId); if (di != null) { mDevices.delete(ev.deviceId); // The configuration may have changed because // of this device. configChanged = true; } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) { mIgnoredDevices.remove(ev.deviceId); } else { Log.w(TAG, "Removing bad device id: " + Integer.toHexString(ev.deviceId)); continue; } } } else { di = getInputDevice(ev.deviceId); if (di == null) { // This may be some junk from an ignored device. continue; } // first crack at it send = preprocessEvent(di, ev); if (ev.type == RawInputEvent.EV_KEY) { di.mMetaKeysState = makeMetaState(ev.keycode, ev.value != 0, di.mMetaKeysState); mHaveGlobalMetaState = false; } } if (configChanged) { synchronized (mFirst) { addLocked(di, System.nanoTime(), 0, RawInputEvent.CLASS_CONFIGURATION_CHANGED, null); } } if (!send) { continue; } synchronized (mFirst) { // NOTE: The event timebase absolutely must be the same // timebase as SystemClock.uptimeMillis(). //curTime = gotOne ? ev.when : SystemClock.uptimeMillis(); final long curTime = SystemClock.uptimeMillis(); final long curTimeNano = System.nanoTime(); //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis()); final int classes = di.classes; final int type = ev.type; final int scancode = ev.scancode; send = false; // Is it a key event? if (type == RawInputEvent.EV_KEY && (classes&RawInputEvent.CLASS_KEYBOARD) != 0 && (scancode < RawInputEvent.BTN_FIRST || scancode > RawInputEvent.BTN_LAST)) { boolean down; if (ev.value != 0) { down = true; di.mKeyDownTime = curTime; } else { down = false; } int keycode = rotateKeyCodeLocked(ev.keycode); addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD, newKeyEvent(di, di.mKeyDownTime, curTime, down, keycode, 0, scancode, ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) ? KeyEvent.FLAG_WOKE_HERE : 0)); } else if (ev.type == RawInputEvent.EV_KEY) { // Single touch protocol: touch going down or up. if (ev.scancode == RawInputEvent.BTN_TOUCH && (classes&(RawInputEvent.CLASS_TOUCHSCREEN |RawInputEvent.CLASS_TOUCHSCREEN_MT)) == RawInputEvent.CLASS_TOUCHSCREEN) { di.mAbs.changed = true; di.mAbs.mDown[0] = ev.value != 0; // Trackball (mouse) protocol: press down or up. } else if (ev.scancode == RawInputEvent.BTN_MOUSE && (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { di.mRel.changed = true; di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0; send = true; } // Process position events from multitouch protocol. } else if (ev.type == RawInputEvent.EV_ABS && (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) { if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_PRESSURE] = ev.value; } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_X] = ev.value; if (DEBUG_POINTERS) Log.v(TAG, "MT @" + di.mAbs.mAddingPointerOffset + " X:" + ev.value); } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_Y] = ev.value; if (DEBUG_POINTERS) Log.v(TAG, "MT @" + di.mAbs.mAddingPointerOffset + " Y:" + ev.value); } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) { di.mAbs.changed = true; di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_SIZE] = ev.value; } // Process position events from single touch protocol. } else if (ev.type == RawInputEvent.EV_ABS && (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) { if (ev.scancode == RawInputEvent.ABS_X) { di.mAbs.changed = true; di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value; } else if (ev.scancode == RawInputEvent.ABS_Y) { di.mAbs.changed = true; di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value; } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) { di.mAbs.changed = true; di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value; di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA + MotionEvent.SAMPLE_PRESSURE] = ev.value; } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) { di.mAbs.changed = true; di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value; di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA + MotionEvent.SAMPLE_SIZE] = ev.value; } // Process movement events from trackball (mouse) protocol. } else if (ev.type == RawInputEvent.EV_REL && (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { // Add this relative movement into our totals. if (ev.scancode == RawInputEvent.REL_X) { di.mRel.changed = true; di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value; } else if (ev.scancode == RawInputEvent.REL_Y) { di.mRel.changed = true; di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value; } } // Handle multitouch protocol sync: tells us that the // driver has returned all data for -one- of the pointers // that is currently down. if (ev.type == RawInputEvent.EV_SYN && ev.scancode == RawInputEvent.SYN_MT_REPORT && di.mAbs != null) { di.mAbs.changed = true; if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) { // If the value is <= 0, the pointer is not // down, so keep it in the count. if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset + MotionEvent.SAMPLE_PRESSURE] != 0) { final int num = di.mAbs.mNextNumPointers+1; di.mAbs.mNextNumPointers = num; if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: now have " + num + " pointers"); final int newOffset = (num <= InputDevice.MAX_POINTERS) ? (num * MotionEvent.NUM_SAMPLE_DATA) : (InputDevice.MAX_POINTERS * MotionEvent.NUM_SAMPLE_DATA); di.mAbs.mAddingPointerOffset = newOffset; di.mAbs.mNextData[newOffset + MotionEvent.SAMPLE_PRESSURE] = 0; } else { if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer"); } } // Handle general event sync: all data for the current // event update has been delivered. } else if (send || (ev.type == RawInputEvent.EV_SYN && ev.scancode == RawInputEvent.SYN_REPORT)) { if (mDisplay != null) { if (!mHaveGlobalMetaState) { computeGlobalMetaStateLocked(); } MotionEvent me; InputDevice.MotionState ms = di.mAbs; if (ms.changed) { ms.changed = false; if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN |RawInputEvent.CLASS_TOUCHSCREEN_MT)) == RawInputEvent.CLASS_TOUCHSCREEN) { ms.mNextNumPointers = 0; if (ms.mDown[0]) { System.arraycopy(di.curTouchVals, 0, ms.mNextData, 0, MotionEvent.NUM_SAMPLE_DATA); ms.mNextNumPointers++; } } if (BAD_TOUCH_HACK) { ms.dropBadPoint(di); } boolean doMotion = !monitorVirtualKey(di, ev, curTime, curTimeNano); if (doMotion && ms.mNextNumPointers > 0 && (ms.mLastNumPointers == 0 || ms.mSkipLastPointers)) { doMotion = !generateVirtualKeyDown(di, ev, curTime, curTimeNano); } if (doMotion) { // XXX Need to be able to generate // multiple events here, for example // if two fingers change up/down state // at the same time. do { me = ms.generateAbsMotion(di, curTime, curTimeNano, mDisplay, mOrientation, mGlobalMetaState); if (DEBUG_POINTERS) Log.v(TAG, "Absolute: x=" + di.mAbs.mNextData[MotionEvent.SAMPLE_X] + " y=" + di.mAbs.mNextData[MotionEvent.SAMPLE_Y] + " ev=" + me); if (me != null) { if (WindowManagerPolicy.WATCH_POINTER) { Log.i(TAG, "Enqueueing: " + me); } addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_TOUCHSCREEN, me); } } while (ms.hasMore()); } else { // We are consuming movement in the // virtual key area... but still // propagate this to the previous // data for comparisons. int num = ms.mNextNumPointers; if (num > InputDevice.MAX_POINTERS) { num = InputDevice.MAX_POINTERS; } System.arraycopy(ms.mNextData, 0, ms.mLastData, 0, num * MotionEvent.NUM_SAMPLE_DATA); ms.mLastNumPointers = num; ms.mSkipLastPointers = true; } ms.finish(); } ms = di.mRel; if (ms.changed) { ms.changed = false; me = ms.generateRelMotion(di, curTime, curTimeNano, mOrientation, mGlobalMetaState); if (false) Log.v(TAG, "Relative: x=" + di.mRel.mNextData[MotionEvent.SAMPLE_X] + " y=" + di.mRel.mNextData[MotionEvent.SAMPLE_Y] + " ev=" + me); if (me != null) { addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_TRACKBALL, me); } ms.finish(); } } } } } catch (RuntimeException exc) { Log.e(TAG, "InputReaderThread uncaught exception", exc); } } } };
private static native boolean readEvent(RawInputEvent outEvent);
framworks.base.server.jni
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event) { gLock.lock(); sp<EventHub> hub = gHub; if (hub == NULL) { hub = new EventHub; gHub = hub; } gLock.unlock(); int32_t deviceId; int32_t type; int32_t scancode, keycode; uint32_t flags; int32_t value; nsecs_t when; bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when); env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId); env->SetIntField(event, gInputOffsets.mType, (jint)type); env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode); env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode); env->SetIntField(event, gInputOffsets.mFlags, (jint)flags); env->SetIntField(event, gInputOffsets.mValue, value); env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when))); return res; }
InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。Client端在根据自己的Focus Path传递事件,直到事件被处理。
private final class InputDispatcherThread extends Thread { // Time to wait when there is nothing to do: 9999 seconds. static final int LONG_WAIT=9999*1000; public InputDispatcherThread() { super("InputDispatcher"); } @Override public void run() { while (true) { try { process(); } catch (Exception e) { Log.e(TAG, "Exception in input dispatcher", e); } } } private void process() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); // The last key event we saw KeyEvent lastKey = null; // Last keydown time for auto-repeating keys long lastKeyTime = SystemClock.uptimeMillis(); long nextKeyTime = lastKeyTime+LONG_WAIT; long downTime = 0; // How many successive repeats we generated int keyRepeatCount = 0; // Need to report that configuration has changed? boolean configChanged = false; while (true) { long curTime = SystemClock.uptimeMillis(); if (DEBUG_INPUT) Log.v( TAG, "Waiting for next key: now=" + curTime + ", repeat @ " + nextKeyTime); // Retrieve next event, waiting only as long as the next // repeat timeout. If the configuration has changed, then // don't wait at all -- we'll report the change as soon as // we have processed all events. QueuedEvent ev = mQueue.getEvent( (int)((!configChanged && curTime < nextKeyTime) ? (nextKeyTime-curTime) : 0)); if (DEBUG_INPUT && ev != null) Log.v( TAG, "Event: type=" + ev.classType + " data=" + ev.event); if (MEASURE_LATENCY) { lt.sample("2 got event ", System.nanoTime() - ev.whenNano); } if (lastKey != null && !mPolicy.allowKeyRepeat()) { // cancel key repeat at the request of the policy. lastKey = null; downTime = 0; lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } try { if (ev != null) { curTime = SystemClock.uptimeMillis(); int eventType; if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) { eventType = eventType((MotionEvent)ev.event); } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD || ev.classType == RawInputEvent.CLASS_TRACKBALL) { eventType = LocalPowerManager.BUTTON_EVENT; } else { eventType = LocalPowerManager.OTHER_EVENT; } try { if ((curTime - mLastBatteryStatsCallTime) >= MIN_TIME_BETWEEN_USERACTIVITIES) { mLastBatteryStatsCallTime = curTime; mBatteryStats.noteInputEvent(); } } catch (RemoteException e) { // Ignore } if (eventType != TOUCH_EVENT && eventType != LONG_TOUCH_EVENT && eventType != CHEEK_EVENT) { mPowerManager.userActivity(curTime, false, eventType, false); } else if (mLastTouchEventType != eventType || (curTime - mLastUserActivityCallTime) >= MIN_TIME_BETWEEN_USERACTIVITIES) { mLastUserActivityCallTime = curTime; mLastTouchEventType = eventType; mPowerManager.userActivity(curTime, false, eventType, false); } switch (ev.classType) { case RawInputEvent.CLASS_KEYBOARD: KeyEvent ke = (KeyEvent)ev.event; if (ke.isDown()) { lastKey = ke; downTime = curTime; keyRepeatCount = 0; lastKeyTime = curTime; nextKeyTime = lastKeyTime + ViewConfiguration.getLongPressTimeout(); //a21966,Creekside: if it is a SLIDER close event do not wait the key up event if (ke.getScanCode() == 254){ lastKey = null; downTime = 0; lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } if (DEBUG_INPUT) Log.v( TAG, "Received key down: first repeat @ " + nextKeyTime); } else { lastKey = null; downTime = 0; // Arbitrary long timeout. lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; if (DEBUG_INPUT) Log.v( TAG, "Received key up: ignore repeat @ " + nextKeyTime); } dispatchKey((KeyEvent)ev.event, 0, 0); mQueue.recycleEvent(ev); break; case RawInputEvent.CLASS_TOUCHSCREEN: //Log.i(TAG, "Read next event " + ev); dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_TRACKBALL: dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_CONFIGURATION_CHANGED: configChanged = true; break; default: mQueue.recycleEvent(ev); break; } } else if (configChanged) { configChanged = false; sendNewConfiguration(); } else if (lastKey != null) { curTime = SystemClock.uptimeMillis(); // Timeout occurred while key was down. If it is at or // past the key repeat time, dispatch the repeat. if (DEBUG_INPUT) Log.v( TAG, "Key timeout: repeat=" + nextKeyTime + ", now=" + curTime); if (curTime < nextKeyTime) { continue; } lastKeyTime = nextKeyTime; nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY; keyRepeatCount++; if (DEBUG_INPUT) Log.v( TAG, "Key repeat: count=" + keyRepeatCount + ", next @ " + nextKeyTime); KeyEvent newEvent; if (downTime != 0 && (downTime + ViewConfiguration.getLongPressTimeout()) <= curTime) { newEvent = KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount, lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS); downTime = 0; } else { newEvent = KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount); } dispatchKey(newEvent, 0, 0); } else { curTime = SystemClock.uptimeMillis(); lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } } catch (Exception e) { Log.e(TAG, "Input thread received uncaught exception: " + e, e); } } } }
发表评论
-
android API对应版本号及甜品
2014-10-11 22:20 1Android版本名和API Level关系全称 ... -
Android shareperference 多线程并发读写
2014-10-11 21:56 0TBD -
Android多线程之控制animation走走停停
2011-09-20 12:50 2300原创文章,转载请标注出处---- 首先,定义一个rot ... -
有人对研究Android 平台上H264感兴趣的么?
2011-07-16 20:18 742RT,感兴趣的一起交流,邮件:waterlife2001@12 ... -
一个comment导致的Android import编译错误
2011-06-12 08:43 1006日前,因为需要修改了Google android自带的prov ... -
Android Framework系列之IPC(二)
2010-11-08 20:03 1938对于Android的IPC来说,除了Handler和Loope ... -
Android Framework系列之IPC(一)
2010-11-05 19:46 3150原创文章,转载请标注出处---- 说到Android的 ... -
Android Framework系列之IMF(三)
2010-11-01 20:55 5160原创文章,转载请标注出处---- 我们知道当一个编辑框 ... -
Android Framework系列之IMF(二)
2010-10-14 22:01 4385原创文章,转载请标注出处---- InputConne ... -
Android Framework系列之IMF(一)
2010-10-12 21:41 3851原创文章,转载请标注出处---- IMF(Input ...
相关推荐
1.调用显示系统默认的输入法 方法一、 InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(m_receiverView(接受软键盘输入的视图(View)),...
社会在发展,科技在进步。曾经交通不畅信息闭塞,如今通过互联网络我们足不出户便闻天下事;...我国作为智能手机用户的大国,如何提高中文输入的速率、优化输入法字符的输入方式已经成为急需解决的问题。
搜狗输入法陆续推出Android、iOS版本,乌班图版本,MAC版本,成为最强大的第三方输入法之一。 此安装包为搜狗拼音输入法的早期版本,无广告,运行流畅,适合安装在windowXP等早期的操作系统中。
谷歌拼音输入法安卓版是谷歌官方推出适用于专为android安卓系统制订的手机输入法。全新谷歌手机输入法具有视觉上质感样式的输入法界面,去掉了键帽的设计,没有了分割的线条,给人一种更加简单、纯粹、统一的感觉,...
百度输入法是百度官方开发的适用于Android平台的输入法,支持拼音,笔画输入,经网友再次开发,支持五笔86输入,是广大五笔用户的必备输入法软件,软件还处于测试版,不可避免存在一起bug. 百度手机输入法特点: ...
维语输入法 v3.0 当前版本:3.0 软件语言:中文 软件类别:输入 法软件大小:11.35 MB ...目前版本已经更新至1.3,将会持续更新,它完美支持安卓系统4.0以上安装输入维文字,让你精准的触摸输入维文
gboard 语言以及键盘支持如下: 波斯语 阿拉伯语 英文 法语 德语 希伯来语 印尼语 日本语 韩语 ...1.解压文件后 在文件...2运行脚本install_inputmethod.bat,然后根据提示输入设备ip 按回车键 脚本自动执行
智能全拼整句输入、中英文无切换混合输入、快速下滑输入符号数字、一键切换英文精确输入、智能调整候选词优先级、笔画输入支持基于语境的预测、多种特定模式支持以及强大的按键纠错功能等一系列创新设计让您的手机...
目前很多网银类的APP和支付宝等软件在用户输入密码时,都会弹出自定义的输入法而不是直接使用系统输入法。 这里介绍的就是如何实现一个简单的自定义输入法。当然,也可以自己写一个Dialog加上几十个按钮让用户输入,...
灵云智能输入法是基于Android2.2及以上平台开发的一套全系列输入法,该输入法采用了灵云平台能力(由捷通华声灵云提供),包括语音输入、全屏手写、窗格手写、图像识别输入(包括文本和名片识别)和虚拟全键盘等多种...
标准打字机俄语输入法,与标准俄文键盘基本相同,安装好后,在需使用时要在系统设置里使此输入法可用(勾选)并在具体应用时“选择输入法”(安卓4.0在屏幕上方选择)
适用平台:Android 搜狗手机输入法与搜狗拼音输入法一脉相传,以用户体验为指导+技术创新,致力于为亿万手机用户提供手机端最智能、最易用的输入法产品。拥有10万大词库、算法智能、首选率高、响应速度快等优秀基础...
Android原生的文字选择 复制粘贴、全选功能样式代码,呵呵,或许你在定制Android系统的时候,都是从这些小功能开始的,android原生的复制粘贴,可能和现在最新的android 8不太一样,这个是Android3、4版本时候的样子...
Oppo滑行输入法 应用分类:系统管理 资源大小:45.6 MB 语言种类:简体中文 系统要求:Android 2.2或更高
适用平台:Android 安装国笔输入法,每天签到送流量。 国笔输入法,界面简洁,操作简单,无需学习,安装即可使用。同时专注于提升基本的输入体验,输入速度快,预测精准。致力于打造最简洁、专业的手机输入法。 ...
适用平台:Android 讯飞输入法,说话秒变文字,还能听懂你的家乡话,快来试试吧! 首创"随意写"功能,无需切换,键盘界面直接手写,连续手写不用停,输入更便捷。 【功能特点】 ★速度快:全新“蜂巢Ⅱ代”输入...
天行输入法是在捷通华声公司灵云平台手写识别、...1、天行输入法支持Android2.2及其以上版本操作系统; 2、解决了符号和数字输入的一些问题; 3、优化了符号输入的操作; 4、修改和优化了界面显示与功能性的一些问题。
Android提供了一个可扩展的的输入法的框架,它允许应用程序给用户提供另外的输入法,如软键盘或语音输入。这些输入法一旦安装,用户就可以从系统的设置中选择他们想要使用的IME,并且这个设置对整个系统都是有效的,...
您可能感兴趣的文章:Android中系统默认输入法设置的方法(输入法的显示和隐藏)Android 显示和隐藏输入法实现代码Android程序打开和对输入法的操作(打开/关闭)Android实现输入法弹出时把布局顶上去和登录按钮顶上去
1.android手机连接扫描枪有些手机显示不了系统键盘 2.连接扫描枪使用的是百度输入法,条码是字母数字组合的扫码会出现乱码(如条码:A0011305150081;小米没有键盘的扫描出现:a0011305150081,啊1305150081,等;...