(Android,NDK,C++,OpenGL ES)
我需要一种方法可靠地从(软)键盘接收文本输入.解决方案可以通过Java使用NativeActivity子类或任何有效的方法.最后我需要输入任何文本,所以我可以用OpenGL自己渲染它
一些背景: 到目前为止,我通过调用showSoftInput或hideSoftInputFromWindow来调用JNI来触发软键盘.这到目前为止从未失败过.但是,问题是本机活动不会发送所有字符.特别是ASCII范围之外的一些unicode字符,或某些运动软键盘不起作用(AKeyEvent_getKeyCode)
以前可以获取一些其他unicode字符,以便检查KeyEvent.ACTION_MULTIPLE并读取一串字符.但即使这样也不会再可靠.
到目前为止,我没有找到替代方法.我尝试以编程方式添加EditText,但从未让它工作.即使尝试添加一个简单的Button也导致OpenGL视图不再被渲染.
在iOS上我通过隐藏编辑框来解决它,我只是激活它以使键盘显示.然后我会读出编辑框并使用字符串在OpenGL中渲染自己.
我有同样的问题,我已经使用一个'Character'事件解决了它,该事件与InputEvent分开处理。
问题是这样的:AKeyEvent_getKeyCode
在某些软键事件中,尤其是在您按住某个键时,扩展的“ unicode / latin”字符不会返回KeyCode。这会阻止方法@Shammi和@eozgonul起作用,因为KeyEvent
Java方面的重构没有足够的信息来获取Unicode字符。
另一个问题是,在触发事件InputQueue
之前,C ++ / Native端已经耗尽dispatchKeyEvent
了。这意味着KEYDOWN / KEYUP事件都在Java代码可以处理事件之前触发。(它们没有交错)。
我的解决方案是通过覆盖dispatchKeyEvent
字符并将其发送到Java端来捕获Java端的unicode字符Queue<Integer> queueLastInputCharacter = new ConcurrentLinkedQueue<Integer>();
// [JAVA] @Override public boolean dispatchKeyEvent (KeyEvent event) { int metaState = event.getMetaState(); int unichar = event.getUnicodeChar(metaState); // We are queuing the Unicode version of the characters for // sending to the app during processEvents() call. // We Queue the KeyDown and ActionMultiple Event UnicodeCharacters if(event.getAction()==KeyEvent.ACTION_DOWN){ if(unichar != 0){ queueLastInputCharacter.offer(Integer.valueOf(unichar)); } else{ unichar = event.getUnicodeChar(); if(unichar != 0){ queueLastInputCharacter.offer(Integer.valueOf(unichar)); } else if (event.getDisplayLabel() != 0){ String aText = new String(); aText = ""; aText += event.getDisplayLabel(); queueLastInputCharacter.offer(Integer.valueOf(Character.codePointAt(aText, 0))); } else queueLastInputCharacter.offer(Integer.valueOf(0)); } } else if(event.getAction()==KeyEvent.ACTION_MULTIPLE){ unichar = (Character.codePointAt(event.getCharacters(), 0)); queueLastInputCharacter.offer(Integer.valueOf(unichar)); } return super.dispatchKeyEvent(event); }
并发队列将使线程一起运行。
我有一个Java端方法,该方法返回最后一个输入字符:
// [JAVA] public int getLastUnicodeChar(){ if(!queueLastInputCharacter.isEmpty()) return queueLastInputCharacter.poll().intValue(); return 0; }
在循环代码的结尾,我进行了额外的检查,以查看队列是否保留了任何unicode字符:
// [C++] int ident; int events; struct android_poll_source* source; // If not rendering, we will block 250ms waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while ((ident = ALooper_pollAll(((nv_app_status_focused(_lpApp)) ? 1 : 250), NULL, &events, (void**)&source)) >= 0) { // Process this event. if (source != NULL) source->process(_lpApp, source); // Check if we are exiting. If so, dump out if (!nv_app_status_running(_lpApp)) return; } static int modtime = 10; // let's not run on every call if(--modtime == 0) { long uniChar = androidUnicodeCharFromKeyEvent(); while (uniChar != 0) { KEvent kCharEvent; // Game engine event kCharEvent.ptkKey = K_VK_ERROR; kCharEvent.unicodeChar = uniChar; kCharEvent.character = uniChar; /* Send unicode char */ kCharEvent.type = K_EVENT_UNICHAR; _lpPortableHandler(&kCharEvent); if (kCharEvent.character < 127) { /* Send ascii char for source compatibility as well */ kCharEvent.type = K_EVENT_CHAR; _lpPortableHandler(&kCharEvent); } uniChar = androidUnicodeCharFromKeyEvent(); } modtime = 10; }
该androidUnicodeCharFromKeyEvent
功能与@Shammi的GetStringFromAInputEvent
方法非常相似,仅用于CallIntMethod
返回jint
。
注意
这确实需要修改引擎以处理与按键事件分开的字符事件。Android仍具有诸如AKEYCODE_BACK
或AKEYCODE_ENTER
不是字符事件之类的键代码,并且仍需要进行处理(可以在主输入循环程序上进行处理)。
编辑框,控制台等...可以修改期望用户输入的内容,以接收构建字符串的单独字符事件。如果您在多个平台上工作,那么除了正常的按键输入事件之外,您还需要生成这些新的字符事件。
我希望这对您有用,到目前为止对我有用。
int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState) { JavaVM* javaVM = app->activity->vm; JNIEnv* jniEnv = app->activity->env; JavaVMAttachArgs attachArgs; attachArgs.version = JNI_VERSION_1_6; attachArgs.name = "NativeThread"; attachArgs.group = NULL; jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs); if(result == JNI_ERR) { return 0; } jclass class_key_event = jniEnv->FindClass("android/view/KeyEvent"); int unicodeKey; if(metaState == 0) { jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "()I"); jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V"); jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode); unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char); } else { jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "(I)I"); jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V"); jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode); unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char, metaState); } javaVM->DetachCurrentThread(); LOGI("Unicode key is: %d", unicodeKey); return unicodeKey; }
只需从您的输入处理程序调用它,我的结构大致如下:
switch (AInputEvent_getType(event)) { case AINPUT_EVENT_TYPE_KEY: switch (AKeyEvent_getAction(event)) { case AKEY_EVENT_ACTION_DOWN: int key = AKeyEvent_getKeyCode(event); int metaState = AKeyEvent_getMetaState(event); int uniValue; if(metaState != 0) uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, metaState); else uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, 0);
既然您说过您已经打开了软键盘,所以我不再赘述,但是代码很简单。我基本上使用具有GetUnicodeChar函数的KeyEvent类的Java函数。