Intereting Posts
Android BroadcastReceiver при запуске – продолжайте работать, когда Activity находится в фоновом режиме Задержка в эмуляторе и genymotion при закрытии / запуске новых действий (eglSurfaceAttrib не реализован) Не удалось выполнить adb в Ubuntu. Загруженный файл предназначен для x86-64, в то время как у меня есть i686 Переключение между gps и сетевым провайдером в зависимости от доступности Android Google Analytics – подключение к службе не выполнено Android: Как поместить Enum в Bundle? Являются ли возмещения для Android в приложениях, доступных для покупки? – В API биллинга приложений V3 Adobe AIR / FlashBuilder 4.6: создание напоминания в приложении для Android Как ускорить загрузку фрагментов? Создание проекта Android из командной строки с помощью Eclipse Как изменить фон переполнения menuitem Не удалось установить ClubDangoV2Released.apk на устройство: слишком много открытых файлов Как получить данные из базы данных с помощью курсора? (Android, SQLite) Android – анимация при удалении элементов из списка Скрытие представлений декларативно на основе размера экрана в Android

Получите полный ввод unicode в Android в C / C ++

(Android, NDK, C ++, OpenGL ES)

Мне нужен способ надежно получать текстовый ввод с (мягкой) клавиатуры. Решение может быть через Java, используя подкласс NativeActivity или что-нибудь, что работает. В конце мне нужен любой текст, который я набираю, поэтому я могу сделать его сам с OpenGL

Некоторые предпосылки: до сих пор я запускал мягкую клавиатуру, вызывая showSoftInput или hideSoftInputFromWindow думал JNI. Это так и не удалось. Однако проблема заключается в том, что нативная активность не будет отправлять все символы. Особенно некоторые символы Unicode вне диапазона ASCII, или некоторая подвижная мягкая клавиатура не будет работать (AKeyEvent_getKeyCode)

Раньше было возможно получить некоторые из этих других символов Юникода, которые проверяют KeyEvent.ACTION_MULTIPLE и читают строку символов. Но даже это не будет работать надежно.

До сих пор мне не удалось найти альтернативный метод. Я экспериментировал с программным добавлением EditText, но так и не получил его на работу. Даже попытка добавить простую кнопку привела к тому, что представление OpenGL больше не отображается.

На iOS я работал вокруг него, имея скрытое окно редактирования, которое я просто активировал, чтобы показать клавиатуру. Затем я зачитал окно редактирования и использовал строку для рендеринга в OpenGL.

Надеюсь, это сработает для вас, я работал до сих пор.

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); 

Поскольку вы заявили, что уже открываете мягкую клавиатуру, я не вхожу в эту часть, но код прост. В основном я использую функцию Java класса KeyEvent, которая имеет функцию GetUnicodeChar.

У меня такие же проблемы, и я решил это с помощью события «Character», которое я обрабатываю отдельно от InputEvent.

Проблема заключается в следующем: AKeyEvent_getKeyCode не возвращает KeyCode для некоторых событий функциональных клавиш, особенно расширенных символов «unicode / latin», когда вы удерживаете клавишу. Это предотвращает работу методов @Shammi и @eozgonul, потому что KeyEvent восстановленный на стороне Java, не имеет достаточной информации для получения символа юникода.

Другая проблема заключается в том, что InputQueue сбрасывается на стороне C ++ / Native до того, как событие (ы) dispatchKeyEvent InputQueue . Это означает, что все события KEYDOWN / KEYUP запускаются до того, как код Java обработает события. (Они не чередуются).

Мое решение состоит в том, чтобы захватить символы юникода на стороне Java, переопределив dispatchKeyEvent и отправив символы в 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; } 

В конце моего кода петлителя я применил дополнительную проверку, чтобы убедиться, что в очереди остались какие-либо символы Юникода:

 // [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 очень похожа на метод GetStringFromAInputEvent от GetStringFromAInputEvent , используйте CallIntMethod чтобы вернуть jint .

Примечания. Это требует модификации вашего движка для обработки событий символа отдельно от событий Key. У Android по-прежнему есть такие ключевые коды, как AKEYCODE_BACK или AKEYCODE_ENTER , которые не являются символьными событиями и по-прежнему нуждаются в обработке (и их можно обрабатывать в основном петлевом устройстве ввода).

Editboxes, console и т. Д. Вещи, ожидающие ввода пользователя, могут быть изменены для получения отдельного символьного события, которое строит строку. Если вы работаете на нескольких платформах, вам нужно будет генерировать эти новые символьные события в дополнение к обычным событиям ввода ключа.

Решение Еозгонула работало для меня. Я принял его и изменил его, чтобы разделить работу между Java и родной. В основном я расширяю NativeActivity, чтобы получить свой собственный класс, который позволяет мне перемещаться как можно больше на Java. Я также закончил передачу всех данных из входного события. Я хотел убедиться, что я захватил как можно больше в созданном объекте KeyEvent.

 package com.MyCompany.MyApp; import android.os.Bundle; import android.view.inputmethod.InputMethodManager; import android.content.Context; import android.view.KeyEvent; public class MyNativeActivity extends android.app.NativeActivity { // Need this for screen rotation to send configuration changed callbacks to native @Override public void onConfigurationChanged( android.content.res.Configuration newConfig ) { super.onConfigurationChanged( newConfig ); } public void showKeyboard() { InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE ); imm.showSoftInput( this.getWindow().getDecorView(), InputMethodManager.SHOW_FORCED ); } public void hideKeyboard() { InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE ); imm.hideSoftInputFromWindow( this.getWindow().getDecorView().getWindowToken(), 0 ); } public String stringFromKeyCode( long downTime, long eventTime, int eventAction, int keyCode, int repeatCount, int metaState, int deviceId, int scanCode, int flags, int source ) { String strReturn; KeyEvent keyEvent = new KeyEvent( downTime, eventTime, eventAction, keyCode, repeatCount, metaState, deviceId, scanCode, flags, source ); if ( metaState == 0 ) { int unicodeChar = keyEvent.getUnicodeChar(); if ( eventAction == KeyEvent.ACTION_MULTIPLE && unicodeChar == keyEvent.KEYCODE_UNKNOWN ) { strReturn = keyEvent.getCharacters(); } else { strReturn = Character.toString( ( char )unicodeChar ); } } else { strReturn = Character.toString( ( char )( keyEvent.getUnicodeChar( metaState ) ) ); } return strReturn; } } 

С родной стороны …

 std::string GetStringFromAInputEvent( android_app* pApp, AInputEvent* pInputEvent ) { std::string strReturn; JavaVM* pJavaVM = pApp->activity->vm; JNIEnv* pJNIEnv = pApp->activity->env; JavaVMAttachArgs javaVMAttachArgs; javaVMAttachArgs.version = JNI_VERSION_1_6; javaVMAttachArgs.name = "NativeThread"; javaVMAttachArgs.group = NULL; jint jResult; jResult = pJavaVM->AttachCurrentThread( &pJNIEnv, &javaVMAttachArgs ); if ( jResult != JNI_ERR ) { // Retrieves NativeActivity. jobject nativeActivity = pNativeActivity->clazz; jclass ClassNativeActivity = pJNIEnv->GetObjectClass( nativeActivity ); jmethodID MethodStringFromKeyCode = pJNIEnv->GetMethodID( ClassNativeActivity, "stringFromKeyCode", "(JJIIIIIIII)Ljava/lang/String;" ); jlong jDownTime = AKeyEvent_getDownTime( pInputEvent ); jlong jEventTime = AKeyEvent_getEventTime( pInputEvent ); jint jEventAction = AKeyEvent_getAction( pInputEvent ); jint jKeyCode = AKeyEvent_getKeyCode( pInputEvent ); jint jRepeatCount = AKeyEvent_getRepeatCount( pInputEvent ); jint jMetaState = AKeyEvent_getMetaState( pInputEvent ); jint jDeviceID = AInputEvent_getDeviceId( pInputEvent ); jint jScanCode = AKeyEvent_getScanCode( pInputEvent ); jint jFlags = AKeyEvent_getFlags( pInputEvent ); jint jSource = AInputEvent_getSource( pInputEvent ); jstring jKeyCodeString = ( jstring )pJNIEnv->CallObjectMethod( nativeActivity, MethodStringFromKeyCode, jDownTime, jEventTime, jEventAction, jKeyCode, jRepeatCount, jMetaState, jDeviceID, jScanCode, jFlags, jSource ); const char* keyCodeString = pJNIEnv->GetStringUTFChars( keyCodeString, nullptr ); strReturn = std::string( keyCodeString ); pJNIEnv->ReleaseStringUTFChars( jKeyCodeString, keyCodeString ); // Finished with the JVM. pJavaVM->DetachCurrentThread(); } return strReturn; } 

2 причины, по которым я пошел с таким подходом.

  • Уменьшает сложность синтаксиса кода, перемещая код в java и только вы можете вызвать один метод обертки jni с внутренней стороны.

  • Java является предпочтительным языком Android, и это позволяет мне быстро переходить на Java-решения. Более того, большинство существующих решений находятся в Java.

В основном это решит проблему.
NativeActivity override onKeyDown ()

Но вам придется реализовать другой способ, чем ввод ключа NDK, чтобы получить строку event.getCharacters() в event.getCharacters() в вашем коде.