Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C/C+中接收完整的android unicode输入+; (Android、NDK、C++、OpenGL ES)_Android_C++_Unicode_Opengl Es_Android Ndk - Fatal编程技术网

在C/C+中接收完整的android unicode输入+; (Android、NDK、C++、OpenGL ES)

在C/C+中接收完整的android unicode输入+; (Android、NDK、C++、OpenGL ES),android,c++,unicode,opengl-es,android-ndk,Android,C++,Unicode,Opengl Es,Android Ndk,我需要一种可靠地从(软)键盘接收文本输入的方法。 解决方案可以通过Java使用NativeActivity子类,或者任何有效的方法。 最后,我需要输入任何文本,这样我就可以自己用OpenGL渲染它了 一些背景: 到目前为止,我通过调用showSoftInput或hideSoftInputFromWindow JNI来触发软键盘。到目前为止,这从未失败过。 但是,问题是本机活动不会发送所有字符。特别是一些超出ASCII范围的unicode字符,或者一些运动软键盘无法工作(AKeyEvent_get

我需要一种可靠地从(软)键盘接收文本输入的方法。 解决方案可以通过Java使用NativeActivity子类,或者任何有效的方法。 最后,我需要输入任何文本,这样我就可以自己用OpenGL渲染它了

一些背景: 到目前为止,我通过调用showSoftInput或hideSoftInputFromWindow JNI来触发软键盘。到目前为止,这从未失败过。 但是,问题是本机活动不会发送所有字符。特别是一些超出ASCII范围的unicode字符,或者一些运动软键盘无法工作(AKeyEvent_getKeyCode)

过去可以获取一些其他unicode字符,为什么要检查KeyEvent.ACTION\u并读取字符串。 但即使这样也不能再可靠地工作了

到目前为止,我还没有找到另一种方法。 我尝试过以编程方式添加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;
}

既然你说你已经打开了软键盘,我就不讨论那部分了,但是代码有点直截了当。我基本上使用了具有GetUnicodeChar函数的class KeyEvent的Java函数。

基本上这将解决问题。


但是您必须实现NDK键输入以外的其他方法,才能将onKeyMultiple的
event.getCharacters()
字符串输入到您的代码中。

EOZGONL的解决方案适合我。我采用并修改了它,以便在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;
}
我采用这种方法的两个原因

  • 通过将代码移动到java并只需在本机端调用一个jni包装器方法,降低了代码语法的复杂性

  • Java是首选的Android语言,这使我能够快速迭代基于Java的解决方案。此外,大多数现有的解决方案都使用java


    • 我也遇到了同样的问题,我使用了一个与InputEvent分开处理的“Character”事件来解决它

      问题在于:
      AKeyEvent\u getKeyCode
      不会返回某些软键事件的键码,尤其是在按住键时扩展的“unicode/latin”字符。这会阻止@Shammi和@eozgonul方法工作,因为在Java端重建的
      KeyEvent
      没有足够的信息来获取unicode字符

      另一个问题是,在触发
      dispatchKeyEvent
      事件之前,
      InputQueue
      在C++/本机端被耗尽。这意味着KEYDOWN/KEYUP事件都是在Java代码处理事件之前触发的。(它们不是交错的)

      我的解决方案是通过覆盖
      dispatchKeyEvent
      并将字符发送到
      队列queueLastInputCharacter=new ConcurrentLinkedQueue(),在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;
      }
      
      /[C++]
      内部识别;
      国际赛事;
      struct android_poll_source*source;
      //如果不渲染,我们将阻止等待事件的250ms。
      //如果设置动画,我们将循环直到读取所有事件,然后继续
      //绘制动画的下一帧。
      而((ident=ALooper_pollAll)((nv_app_status_focused(_lpApp))?1:250),
      无效的
      &事件,
      (void**)和source))>=0)
      {
      //处理此事件。
      如果(源!=NULL)
      源->过程(_lpApp,source);
      //检查我们是否正在退出。如果是,请退出
      如果(!nv_应用程序_状态_运行(_lpApp))
      返回;
      }
      静态int modtime=10;//我们不要每次打电话都跑
      如果(--modtime==0){
      long uniChar=androidUnicodeCharFromKeyEvent();
      while(uniChar!=0){
      KEvent kCharEvent;//游戏引擎事件
      kCharEvent.ptkKey=K_VK_错误;
      kCharEvent.unicodeChar=uniChar;
      kCharEvent.character=uniChar;
      /*发送unicode字符*/
      kCharEvent.type=K_EVENT_UNICHAR;
      _lpPortableHandler(&kCharEvent);
      if(kCharEvent.character<127){
      /*发送ascii字符以实现源代码兼容性*/
      kCharEvent.type=K_EVENT_CHAR;
      _lpPortableHandler(&kCharEvent);
      }
      uniChar=androidUnicodeCharFromKeyEvent();
      }
      modtime=10;
      }
      
      androidUnicodeCharFromKeyEvent
      函数与@Shammi的
      GetStringFromAInputEvent
      方法非常相似,只使用
      CallIntMethod
      返回
      jint

      注释 这确实需要修改引擎以处理与关键事件分开的角色事件。Android仍然有一些关键代码,如
      AKEYCODE\u BACK
      AKEYCODE\u ENTER
      ,它们不是字符事件,仍然需要处理(可以在主输入循环器上处理)

      编辑框、控制台等。。。可以修改期望用户输入的内容,以接收构建字符串的单独字符事件。如果您在多个平台上工作,那么您需要
      // [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;
      }