2014-01-14 20 views
4

(機器人,NDK,C++,OpenGL ES的)接收完成機器人Unicode輸入在C/C++

我需要一種方法來從一個(軟)鍵盤可靠地接收文本輸入。 該解決方案可以通過使用NativeActivity子類的Java或任何可用的Java。 在我需要的任何文本在鍵入時結束,這樣我就可以用OpenGL

一些背景使它自己: 到現在爲止,我通過調用showSoftInput或hideSoftInputFromWindow認爲JNI觸發軟鍵盤。迄今爲止這從未失敗。 但是,問題是本地活動不會發送所有字符。特別是一些Unicode字符的ASCII範圍,或一些運動軟鍵盤將無法正常工作(AKeyEvent_getKeyCode)

它曾經是可能得到一些與其他Unicode字符的原因檢查KeyEvent.ACTION_MULTIPLE和讀取字符的字符串之外。 但即使這樣也不能可靠地工作了。

到目前爲止,我沒有找到一種替代方法。 我嘗試以編程方式添加EditText,但從未得到它的工作。即使試圖添加一個簡單的按鈕導致OpenGL視圖不再呈現。

在iOS上我通過隱藏編輯框來解決這個問題,我只是簡單地激活它來讓鍵盤顯示出來。然後我會讀出編輯框並使用字符串在OpenGL中呈現自己。

+0

謝謝你問,但我不能跟着你。你指的是什麼變量,它與它有什麼關係? – Pierre

回答

3

我希望這適用於你,爲我工作到目前爲止。

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函數。

+0

感謝您分享您的代碼。它不能解決問題。你的代碼確實有用,實際上它和我已經做的非常相似。問題是,它不適用於所有的鍵。例。觸摸鍵盤上的軟鍵並按住它,一些鍵將打開其他鍵的選擇。在這裏,一些鍵不起作用。輸入處理程序使用AINPUT_EVENT_TYPE_KEY觸發,但unicode結果將爲「0」。這一切都很好,直到Android 4.1,並且似乎從4.3開始已經發生了根本性的變化(某些事情) – Pierre

0

基本上這會解決這個問題。
NativeActivity override onKeyDown()

但你必須實現比NDK鍵輸入一些其他的方式來獲得onKeyMultiple的event.getCharacters()串到你的代碼。

1

Eozgonul的解決方案爲我工作。我採用了它,並對其進行了修改,將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編寫的

+0

您的回覆使我重新訪問我的代碼。我注意到你正在使用「AKeyEvent_getEventAction」,但我沒有找到這樣的功能。無論我是在一箇舊的NDK,或者你實際上的意思是「AKeyEvent_getAction」 – Pierre

+0

謝謝指出。你是對的。沒有AKeyInput_getEventAction。當我重構代碼以適應這篇文章的所有內容時,這可能是一個錯字。 – Shammi

3

我有同樣的問題,我已經解決了它使用'Character'事件,我分開處理InputEvent。

問題是這樣的:AKeyEvent_getKeyCode對於某些軟鍵事件沒有返回KeyCode,特別是當您按住某個鍵時展開的「unicode/latin」字符。這可以防止方法@Shammi和@eozgonul工作,因爲在Java端重建的KeyEvent沒有足夠的信息來獲取unicode字符。

另一個問題是在觸發dispatchKeyEvent事件之前,在C++/Native端耗盡InputQueue。這意味着KEYDOWN/KEYUP事件在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端的Unicode字符。

我有一個返回的最後一個輸入字符一個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_BACKAKEYCODE_ENTER這樣的關鍵代碼,它們不是字符事件,仍然需要處理(並且可以在主輸入活套上處理)。

編輯框,控制檯等...期望用戶輸入的內容可以修改爲接收一個單獨的字符事件來構建字符串。如果您正在多個平臺上工作,那麼除了正常的鍵輸入事件之外,還需要生成這些新的字符事件。

相關問題