GameTextInput Android Game Development Kit的一部分。
使用 GameTextInput
程式庫是編寫全螢幕 Android 應用程式更為簡單的方式,可使用螢幕鍵盤輸入文字。
GameTextInput
提供簡單明瞭的 API 來顯示或隱藏螢幕鍵盤、設定或取得目前編輯的文字,並在文字變更時接收通知。這並不適用於發展成熟的文字編輯器應用程式,但對於遊戲中的一般用途仍提供選取和撰寫區域的支援。此外,這個程式庫支援進階 輸入法編輯器 (IME) 功能,例如拼字檢查、自動完成和多鍵字元等功能。
如需整合 GameTextInput 的範例,請參閱 games-samples repository。
設定版本
GameTextInput
會以 Android Archive (AAR) 的形式發布。這個 AAR 包含會執行 GameTextInput
原生功能的 Java 類別和 C 原始碼。您必須在建構程序中透過 Prefab
將這些來源檔案納入,如此一來系統便會將原生資料庫和原始碼提供給 CMake 專案或 NDK 建構系統。
如果使用的不是
GameActivity
,請按照 Jetpack Android Games 頁面上的操作說明,將GameTextInput
程式庫的依附元件新增至遊戲的build.gradle
檔案。GameTextInput
程式庫包含在GameActivity
中,因此如果您使用GameActivity
,則可略過這個步驟。確認
gradle.properties
包含下列幾行:# Tell Android Studio we are using AndroidX. android.useAndroidX=true # Use Prefab 1.1.2 or higher, which contains a fix for "header only" libs. android.prefabVersion=1.1.2 # Required only if you're using Android Studio 4.0 (4.1 is recommended). # android.enablePrefab=true
如果未使用
GameActivity
,請在專案的CMakeLists.txt
檔案中匯入game-text-input
套件並將其加入目標:find_package(game-text-input REQUIRED CONFIG) ... target_link_libraries(... game-text-input::game-text-input)
在遊戲的其中一個
.cpp
檔案中加入以下這行,以納入GameTextInput
實作項目:#include <game-text-input/gametextinput.cpp>
如果已整合
GameActivity
,您可能已經執行了這個步驟。編譯並執行應用程式。如果碰到 CMake 錯誤,請確認 AAR 和
build.gradle
檔案的設定正確無誤。如果找不到#include
檔案,請驗證CMakeLists.txt
設定檔。#include <game-text-input/gametextinput.h>
整合版本
將 GameTextInput
整合到您的版本時,可以選擇是否要 使用 或 不使用 GameActivity
。與 GameActivity
一併整合是建議的做法,以便使用 C 或 C++ 進行實作。
與 GameActivity 整合
如要將您的版本與 GameActivity
整合,請完成下列步驟。這裡的某些步驟涉及 Endless Tunnel NDK 範例。遊戲中可能使用不同的使用者介面元件和場景,但概念仍保持不變。
如同 整合遊戲活動 中所述,請確認主要的 Java 活動可擴充為
GameActivity
。下列是 Endless Tunnel 的範例,請建立
mNameEdit
按鈕,並使用GameTextInputState
來處理文字輸入。class WelcomeScene : public UiScene { protected: UiWidget* mNameEdit; GameTextInputState mTextInputState; ... }
使用預設值初始化
GameTextInput
。下列是 Endless Tunnel 的範例,請在WelcomeScene
建構函式中執行這項操作。WelcomeScene::WelcomeScene() : mTextInputState{} { mTextInputState.text_UTF8 = INITIAL_NAME; mTextInputState.text_length = strlen(INITIAL_NAME); mTextInputState.selection.start = 0; mTextInputState.selection.end = mTextInputState.text_length; mTextInputState.composingRegion.start = -1; mTextInputState.composingRegion.end = -1; }
使用
GameActivity_setTextInputState
和GameActivity_showSoftInput
來顯示鍵盤,並設定向使用者顯示的預設編輯文字。在以下 Endless Tunnel 範例中,請在點選按鈕時新增。void WelcomeScene::OnButtonClicked(int id) { if (id == mNameEdit->GetId()) { auto activity = NativeEngine::GetInstance()->GetAndroidApp()->activity; // Note: the UI is resized when the IME is shown and OnCreateWidgets is // called again. sNameEdit = mTextInputState.text_UTF8; mNameEdit->SetText(sNameEdit.c_str()); GameActivity_setTextInputState(activity, &mTextInputState); GameActivity_showSoftInput(activity, 0); }
使用
GameActivity
來監聽文字事件。建議將GameActivity
與android_native_app_glue
程式庫搭配使用,這個程式庫與GameActivity
一同搭售。您也可以直接將其與原生回呼搭配使用。請參考以下這兩個不同方法的通用範例:如果和
android_native_app_glue
程式庫搭配使用,請在遊戲迴圈中檢查文字輸入狀態是否變更:static void yourGameLoop(...) { ... if (mApp->textInputState) { // Handle the event here. See the next step with GameTextInputState_set // and GameActivity_getTextInputState. } }
如要與原生回呼搭配使用,請註冊回呼
onTextInputEvent
。// Use the callback that will handle the event: static void onTextInputEvent(GameActivity* activity, const GameTextInputState* state) { // Store or notify your game thread about the text input. You're not // forced to store the state here, because you can obtain it later. // See the next step. } // Register the callback: JNIEXPORT void GameActivity_onCreate(GameActivity* activity, void* savedState, size_t savedStateSize) { // ... activity->callbacks->onTextInputEvent = onTextInputEvent; }
使用
GameActivity_getTextInputState
、讀取已輸入的文字,並將文字儲存在記憶體中。您應該向使用者顯示這項資訊。在以下範例中,請在WelcomeScene::OnTextInputEvent
中執行這項操作。void WelcomeScene::OnTextInputEvent() { auto activity = NativeEngine::GetInstance()->GetAndroidApp()->activity; GameActivity_getTextInputState(activity, [](void *ctx, const GameTextInputState *state) { auto thiz = static_cast<WelcomeScene *>(ctx); thiz->mTextInputState = *state; // additional input string processing... sNameEdit = thiz->mTextInputState.owned_string; thiz->mNameEdit->SetText(sNameEdit.c_str()); }, this); mNameEdit->SetText(sNameEdit.c_str()); }
不使用 GameActivity 進行整合
如果您已將 GameTextInput
與 GameActivity
整合,請跳至 公用函式。
透過已連接到 JVM 的 C 執行緒或應用程式主執行緒,使用
JNIEnv
指標呼叫GameTextInput_init
。static GameTextInput* gameTextInput = nullptr; extern "C" JNIEXPORT void JNICALL Java_com_gametextinput_testbed_MainActivity_onCreated(JNIEnv* env, jobject this) { { if(!gameTextInput) gameTextInput = GameTextInput_init(env); ... }
建立具有
InputConnection
存取權的InputEnabledTextView
Java 類別。public class InputEnabledTextView extends View implements Listener { public InputConnection mInputConnection; public InputEnabledTextView(Context context, AttributeSet attrs) { super(context, attrs); } public InputEnabledTextView(Context context) { super(context); } public void createInputConnection(int inputType) { EditorInfo editorInfo = new EditorInfo(); editorInfo.inputType = inputType; editorInfo.actionId = IME_ACTION_NONE; editorInfo.imeOptions = IME_FLAG_NO_FULLSCREEN; mInputConnection = new InputConnection(this.getContext(), this, new Settings(editorInfo, true) ).setListener(this); } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { if (outAttrs != null) { GameTextInput.copyEditorInfo(mInputConnection.getEditorInfo(), outAttrs); } return mInputConnection; } // Called when the IME input changes. @Override public void stateChanged(State newState, boolean dismissed) { onTextInputEventNative(newState); } @Override public void onImeInsetsChanged(Insets insets) { // handle Inset changes here } private native void onTextInputEventNative(State softKeyboardEvent); }
將已建立的
InputEnabledTextView
新增至 UI 版面配置。舉例來說,以下activity_main.xml
中的程式碼可將其置於畫面底部:xml <com.android.example.gametextinputjava.InputEnabledTextView android:id="@+id/input_enabled_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"/>
將這個新的
InputEnabledTextView
類別擷取至 Java 活動。這項操作對使用 View binding 而言簡單明瞭:public class MainActivity extends AppCompatActivity { ... private ActivityMainBinding binding; private InputEnabledTextView inputEnabledTextView; private native void setInputConnectionNative(InputConnection c); @Override protected void onCreate(Bundle savedInstanceState) { ... binding = ActivityMainBinding.inflate(getLayoutInflater()); inputEnabledTextView = binding.inputEnabledTextView; inputEnabledTextView.createInputConnection(InputType.TYPE_CLASS_TEXT); setInputConnectionNative(inputEnabledTextView.mInputConnection); }
在 C 程式庫中,將
inputConnection
傳遞至GameTextInput_setInputConnection
。將回呼傳入GameTextInput_setEventCallback
,以接收 C 狀態結構GameTextInputState
的事件通知。extern "C"JNIEXPORT void JNICALL Java_com_gametextinput_testbed_MainActivity_setInputConnectionNative( JNIEnv *env, jobject this, jobject inputConnection) { GameTextInput_setInputConnection(gameTextInput, inputConnection); GameTextInput_setEventCallback(gameTextInput,[](void *ctx, const GameTexgtInputState *state) { if (!env || !state) return; // process the newly arrived text input from user. __android_log_print(ANDROID_LOG_INFO, "TheGreateGameTextInput", state->text_UTF8); }, env); }
在 C 資料庫中,呼叫
GameTextInput_processEvent
以便在狀態變更時處理事件。extern "C" JNIEXPORT void JNICALL Java_com_gametextinput_testbed_InputEnabledTextView_onTextInputEventNative( JNIEnv* env, jobject this, jobject soft_keyboard_event) { GameTextInput_processEvent(gameTextInput, soft_keyboard_event); }
公用函式
GameTextInput
程式庫包含了公用函式,可讓您在 Java 狀態物件和 C 狀態結構之間進行轉換。透過 GameTextInput_showIme
和 GameTextInput_hideIme
函式來存取顯示及隱藏輸入法編輯器的功能。