このトピックはまだ評価されていません - このトピックを評価する

Windows Phone 8 の Direct3D アプリでのキーボード入力の処理方法

2013/12/05

対象: Windows Phone 8 のみ

このトピックでは、ユーザーが Windows Phone Direct3D アプリ 用のソフトウェア入力パネル (SIP) キーボードを使用してテキストを入力できるカスタム テキスト ボックスを実装する方法について説明します。テキスト入力は、ほとんどすべてのアプリに必要な機能です。Windows Phone 管理されているアプリ では、TextBox コントロールを使用してテキスト入力をアプリに簡単に追加できます。残念ながら、XAML コントロールを Direct3D アプリ で使用することはできません。ただし、少量のコードを使用して、SIP キーボードを表示させ、ユーザーの入力に合わせて更新されるカスタム テキスト ボックス コントロールを作成することができます。

カスタム テキスト ボックスは 2 つの異なる方法で実装できます。キーストロークを集計し、現在のテキストを含むバッファーを提供する KeyboardInputBuffer クラスを使用するか、ユーザーがキーを押すたびにトリガーされるキー イベントを使用することができます。キー イベントを使用する場合は、独自のテキスト バッファーを管理する必要があります。

IsKeyboardInputEnabled により、SIP キーボードの表示のオンとオフを切り替えることができますが、これは、キーボードが現在表示されているかどうかを判別する上では信頼できません。ユーザーが [戻る] ボタンを押してキーボードを閉じても、このプロパティの値は更新されません。代わりに、Hiding イベントをリッスンする必要があります。このイベントは、ユーザーが [戻る] ボタンでキーボードを消去したときに発生します。また、いつでも OccludedRect プロパティを確認することができ、四角形の幅と高さがゼロの場合は、SIP が現在表示されていないことがわかります。チュートリアルの最後に、これらの手法について簡潔に説明します。

メモメモ:

これらの手順を簡素化するために、実際にテキストを画面にレンダリングするために必要な Direct3D コードはこれらの例から省略されています。

KeyboardInputBuffer を使用したカスタム テキスト ボックスの作成

  1. 新しい Direct3D プロジェクトを作成します。この例では、プロジェクト名を “NativeTextInput” と仮定します。

  2. Visual Studio で、[プロジェクト]、[クラスの追加] を順に選択し、プロジェクトに新しいクラスを追加します。新しいクラスに "InputBufferTextBox" という名前を付けます。

  3. InputBufferTextBox.h ヘッダー ファイルに次のコードを貼り付けます。ここで宣言するすべてのメソッドおよびメンバー変数について、この後の各ステップで説明します。

    
    ref class InputBufferTextBox sealed
    {
    public:
    	InputBufferTextBox(void);
    	InputBufferTextBox(Windows::UI::Core::CoreWindow^ parentWindow, 
    							int x, int y, int width, int height, 
    							Platform::String^ initialText,  
    							Windows::Phone::UI::Core::CoreInputScope inputScope);
    
    	bool HitTest(int x, int y);
    	void SetFocus(bool hasFocus);
    	property bool HasFocus
        {
                bool get() { return m_hasFocus; }
        }
    
    
    protected:
    	void OnTextChanged(Windows::Phone::UI::Core::KeyboardInputBuffer^ sender, Windows::Phone::UI::Core::CoreTextChangedEventArgs^ args);
    	void OnRender();
    
    private:
    	Windows::UI::Core::CoreWindow^ m_parentWindow;
    	Windows::Phone::UI::Core::KeyboardInputBuffer^ m_inputBuffer;
    
    	int m_x, m_y, m_width, m_height;
    	bool m_hasFocus;
    	Windows::Phone::UI::Core::CoreInputScope m_inputScope;
    
    };
    
    
    
    
  4. InputBufferTextBox.cpp ファイルの先頭にある #include ディレクティブの下に、テキスト ボックスが使用する名前空間に対応する次の using ステートメントを貼り付けます。

    
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    using namespace Windows::Phone::UI::Core;
    using namespace Windows::System;
    using namespace Platform;
    
    
    
  5. カスタム コンストラクターのコードを InputBufferTextBox.cpp 内に貼り付けます。このメソッドでは、メンバー変数 m_parentWindow が割り当てられます。この値はアプリの CoreWindow インスタンスになります。次に、テキスト ボックスの位置とサイズを定義するメンバー変数を設定し、入力スコープを設定します。これは、ソフトウェア キーボードに表示するキーを決定します。その後で、KeyboardInputBuffer オブジェクトのインスタンスが初期化されます。Text プロパティは、テキスト ボックスに表示される初期テキストに設定されます。最後に、TextChanged イベントのヘッダーが設定されます。

    
    InputBufferTextBox::InputBufferTextBox(CoreWindow^ parentWindow, 
    									   int x, int y, int width, int height, 
    									   String^ initialText, 
    									   CoreInputScope inputScope)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    	m_inputScope = inputScope;
    
    	m_inputBuffer = ref new KeyboardInputBuffer();
    	m_inputBuffer->Text = initialText;
    	m_inputBuffer->TextChanged += ref new TypedEventHandler<KeyboardInputBuffer^, CoreTextChangedEventArgs^>(this, &InputBufferTextBox::OnTextChanged);
    	
    
    }
    
    
    
  6. HitTest メソッドを定義するコードを InputBufferTextBox.cpp 内に貼り付けます。ユーザーがタップした画面座標がこのメソッドに渡されます。これは単に、タッチ ポイントがテキスト ボックスの境界内かどうかをチェックするだけです。

    
    bool InputBufferTextBox::HitTest(int x, int y)
    {
    	if(x >= m_x && x <= m_x + m_width && y >= m_y && y <= m_y + m_height)
    	{	
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    
    
  7. SetFocus メソッドを定義したコードを InputBufferTextBox.cpp に貼り付けます。このメソッドが true で呼び出されると、テキスト ボックスにフォーカスが設定され、テキスト入力を処理する準備が必要になります。これを行うには、アプリの CoreWindowKeyboardInputBuffer プロパティをテキスト ボックスの KeyboardInputBuffer メンバー変数に設定し、キーボードの入力スコープを設定します。その後で、CoreWindowIsKeyboardInputEnabled プロパティを true に設定します。これにより SIP キーボードが表示されます。

    SetFocusfalse で呼び出されると、IsKeyboardInputEnabledfalse に設定され、SIP キーボードが非表示になります。

    
    void InputBufferTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    	
    	if(m_hasFocus)
    	{
    		m_inputBuffer->InputScope = m_inputScope;
    		m_parentWindow->KeyboardInputBuffer = m_inputBuffer;
    		m_parentWindow->IsKeyboardInputEnabled = true;
    	}
    	else
    	{
    		m_parentWindow->IsKeyboardInputEnabled = false;
    	}
    }
    
    
    
  8. TextChanged イベント ハンドラーの実装を InputBufferTextBox.cpp に貼り付けます。このイベントは、テキスト バッファー内のテキストが変更されたときに発生します。これは、ユーザーが SIP キーボード上のキーをタップしたときに発生しますが、ユーザーが自動修正や置換の候補の一覧の単語をタップした場合など、他の理由で発生する可能性もあります。Text プロパティにアクセスすることによってバッファー内の現在のテキストを取得します。このメソッドは、ユーザーが Enter キーを押したことを示す改行がテキスト バッファー内に存在するかどうかを確認します。存在する場合、テキスト ボックスのフォーカスが false に設定されます。ここにロジックを追加して、別のテキスト ボックスに Tab キーで移動できるようにすることができます。また、テキスト ボックスを画面外のバッファーにレンダリングする場合は、レンダリング ループにコントロールの再描画を警告するコードをここに追加することができます。

    
    void InputBufferTextBox::OnTextChanged(KeyboardInputBuffer^ sender, CoreTextChangedEventArgs^ args)
    {
    	auto text = m_inputBuffer->Text;
    	if(std::find<const wchar_t*, wchar_t>(text->Begin(), text->End(), '\r') != text->End())
    	{
    		SetFocus(false);
    		// You could add logic here to tab to another control.
    	}
    
    	// Let your render loop know that this control needs to be rendered again.
    	// MyRenderLoop::IsDirty = true;
    }
    
    
    
  9. OnRender メソッドを InputBufferTextBox.cpp 内に貼り付けます。この例では、画面に描画するために必要な Direct3D コードが省略されていますが、この簡素化されたメソッドは一般的な実装を示しています。テキスト ボックス コンテナーを描画し、テキスト バッファー内の文字ごとにループを実行して、その文字のビットマップを描画します。このメソッドの実際の実装は、複数行のテキスト、クリッピング、またはスクロールを含める場合は特に、さらに複雑になります。

    
    void InputBufferTextBox::OnRender()
    {
    	// Draw the text box boundary
    	// MyDrawRectangle(m_x, m_y, m_width, m_height);
    
    	auto characters = m_inputBuffer->Text->Data();
    	for(int i = 0; i < m_inputBuffer->Text->Length(); i++)
    	{
    		// Draw each character in the buffer
    		// MyDrawCharacter(characters[i], m_x + i * characterWidth, y);
    	}
    
    }
    
    
    
  10. 次の各ステップでは、このテキスト ボックスを使用するようメイン アプリ内のコードを変更します。NativeTextInput.h の先頭に次のコード行を貼り付け、テキスト ボックスのヘッダー ファイルを含めます。

    
    #include "InputBufferTextBox.h";
    
    
    
  11. NativeTextInput.h のプライベート メンバー変数のセクションに、テキスト ボックスの変数宣言を貼り付けます。

    
    	InputBufferTextBox^ m_inputBufferTextBox;
    
    
    
  12. NativeTextInput.cpp で、プロジェクト テンプレートの一部分として含まれている OnSetWindow メソッドに次の行を貼り付けます。このコードは、テキスト ボックスのサイズ、位置、初期のテキストを設定してテキスト ボックスを初期化した後、アプリの CoreWindow に参照を渡します。

    
    	m_inputBufferTextBox = ref new InputBufferTextBox(window, 0, 0, 480, 100, "Initial Text", Windows::Phone::UI::Core::CoreInputScope::Url);
    
    
    
  13. 最後に、次のコードを使用して、NativeTextInput.cpp 内で、プロジェクト テンプレートと共に含まれている OnPointerPressed メソッドを置き換えます。ユーザーが画面をタップした時点で、このメソッドが呼び出されます。このメソッドはテキスト ボックスの HitTest メソッドを呼び出し、ユーザーがテキスト ボックスの境界内をタップしたかどうかをチェックします。テキスト ボックスの内部がタップされた場合、テキスト ボックスのフォーカスが true に設定されます。テキスト ボックスの外側がタップされた場合、そのテキスト ボックスに現在フォーカスがあれば、フォーカスは false に設定されます。テキスト ボックスの SetFocus メソッドで、IsKeyboardInputEnabled が切り替えられ、SIP キーボードが表示または非表示になります。

    
    void NativeTextInput::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
    {
    
        CoreWindow^ window = CoreWindow::GetForCurrentThread();
    
    	auto position = args->CurrentPoint->Position;
    	bool isTouchInTextBox = m_inputBufferTextBox->HitTest(position.X, position.Y);
    
    	if(isTouchInTextBox)
    	{	
    		m_inputBufferTextBox->SetFocus(true);
    	}
    	else 
    	{
    		IsInputPaneShowing();
    		if(m_inputBufferTextBox->HasFocus)
    		{
    			// If the touch is outside of the active text box, hide the keyboard
    			m_inputBufferTextBox->SetFocus(false);
    		}
    	}
    
    }
    
    
    

キー イベントを使用したカスタム テキスト ボックスの作成

  1. 新しい Direct3D プロジェクトを作成します。この例では、プロジェクト名を “NativeTextInput” と仮定します。

  2. Visual Studio で、[プロジェクト]、[クラスの追加] を順に選択し、プロジェクトに新しいクラスを追加します。新しいクラスに "KeyEventTextBox" という名前を付けます。

  3. 次のコードを KeyEventTextBox.h ヘッダー ファイル内に貼り付けます。ここで宣言するすべてのメソッドおよびメンバー変数について、この後の各ステップで説明します。

    
    ref class KeyEventTextBox sealed
    {
    public:
    	KeyEventTextBox(void);
    	KeyEventTextBox(Windows::UI::Core::CoreWindow^ parentWindow, 
    					int x, int y, int width, int height, 
    					Platform::String^ initialText,
    					Windows::Phone::UI::Core::CoreInputScope inputScope);
    
    
    	bool HitTest(int x, int y);
    	void SetFocus(bool hasFocus);
    
    	property bool HasFocus
        {
                bool get() { return m_hasFocus; }
        }
    
    protected:
    	void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
    	void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args);
    	void OnRender();
    
    private:
    	Windows::UI::Core::CoreWindow^ m_parentWindow;
    	Windows::Phone::UI::Core::KeyboardInputBuffer^ m_inputBuffer;
    	Windows::Foundation::EventRegistrationToken m_keydownToken;
    	Windows::Foundation::EventRegistrationToken m_characterReceivedToken;
    
    	int m_x, m_y, m_width, m_height;
    	Platform::String^ m_text;
    	bool m_hasFocus;
    
    	Windows::Phone::UI::Core::CoreInputScope m_inputScope;
    };
    
    
    
  4. KeyEventTextBox.cpp ファイルの先頭にある #include ディレクティブの下に、テキスト ボックスが使用する名前空間に対応する次の using ステートメントを貼り付けます。

    
    #include <string>
    
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    using namespace Windows::Phone::UI::Core;
    using namespace Windows::System;
    using namespace Platform;
    using namespace std;
    
    
    
  5. カスタム コンストラクターのコードを KeyEventTextBox.cpp 内に貼り付けます。このメソッドでは、メンバー変数 m_parentWindow が割り当てられます。この値はアプリの CoreWindow インスタンスになります。次に、テキスト ボックスの位置とサイズを定義するメンバー変数を設定し、入力スコープを設定します。これは、ソフトウェア キーボードに表示するキーを決定します。最後に、テキスト バッファーとして機能する m_text メンバー変数が、テキスト ボックスに表示される初期テキストに設定されます。

    
    KeyEventTextBox::KeyEventTextBox(CoreWindow^ parentWindow, 
    								 int x, int y, int width, int height, 
    								 String^ initialText,
    								 CoreInputScope inputScope)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    	m_text = initialText;
    	m_inputScope = inputScope;
    }
    
    
    
  6. HitTest メソッドを定義するコードを KeyEventTextBox.cpp 内に貼り付けます。ユーザーがタップした画面座標がこのメソッドに渡されます。これは単に、タッチ ポイントがテキスト ボックスの境界内かどうかをチェックするだけです。

    
    bool KeyEventTextBox::HitTest(int x, int y)
    {
    	if(x >= m_x && x <= m_x + m_width && y >= m_y && y <= m_y + m_height)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    
    
  7. SetFocus メソッドを定義したコードを KeyEventTextBox.cpp に貼り付けます。このメソッドが true で呼び出されると、テキスト ボックスにフォーカスが設定され、テキスト入力を処理する準備が必要になります。これを行うには、キーボードの入力スコープを設定してから、CoreWindowIsKeyboardInputEnabled プロパティを true に設定します。これにより SIP キーボードが表示されます。次に、KeyDown および CharacterReceived イベントのハンドラーが登録されます。ハンドラーを後で登録解除できるように、これらのハンドラーが登録されるときに返されるトークンが保存されます。

    SetFocusfalse 値で呼び出されると、IsKeyboardInputEnabledfalse に設定され、SIP キーボードが非表示になります。次に、KeyDown および CharacterReceived イベントのイベント ハンドラーを削除します。

    
    void KeyEventTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    
    	if(m_hasFocus)
    	{
    		m_inputBuffer->InputScope = m_inputScope;
    		m_parentWindow->IsKeyboardInputEnabled = true;
    		m_keydownToken = m_parentWindow->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &KeyEventTextBox::OnKeyDown);	
    		m_characterReceivedToken = m_parentWindow->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &KeyEventTextBox::OnCharacterReceived); 
    	}
    	else
    	{
    		m_parentWindow->IsKeyboardInputEnabled = false;
    		m_parentWindow->KeyDown -= m_keydownToken;
    		m_parentWindow->CharacterReceived -= m_characterReceivedToken;
    	}
    }
    
    
    
  8. KeyDown イベント ハンドラーの次の定義を KeyEventTextBox.cpp に貼り付けます。このイベントは、CharacterReceived イベントが発生する前に、ユーザーが SIP キーボード上のキーをタップしたときに発生します。このメソッドは、どのキーが押されたかをチェックします。これが Back キーの場合、ユーザーは BackSpace キーをタップしています。この場合、テキスト バッファーの最後の文字が削除され、イベント引数の Handled プロパティが true に設定されます。これにより、システムが CharacterReceived の発生を停止し、BackSpace キーストロークがテキスト バッファーに追加されません。Enter キーが押された場合、テキスト ボックスのフォーカスは false に設定され、SIP キーボードが非表示になります。次に、イベント引数の Handled プロパティを true に設定し、CharacterReceived が呼び出されないようにします。

    
    void KeyEventTextBox::OnKeyDown(CoreWindow^ sender, KeyEventArgs^ args)
    {
    	if(args->VirtualKey == VirtualKey::Back)
    	{
    		if(m_text->Length() > 0)
    		{
    			wstring text = wstring(m_text->Data());
    			text.resize(text.length() - 1);
    			m_text = ref new String(text.c_str());
    		}
    		args->Handled = true;
    	}
    	else if(args->VirtualKey == VirtualKey::Enter)
    	{
    		SetFocus(false);
    		args->Handled = true;
    	}
    }
    
    
    
  9. CharacterReceived イベント ハンドラーの次の定義を KeyEventTextBox.cpp 内に貼り付けます。このメソッドは単純にイベント引数内の文字をテキスト バッファーの末尾に追加します。

    
    void KeyEventTextBox::OnCharacterReceived(CoreWindow^ sender, CharacterReceivedEventArgs^ args)
    {
    	wchar_t c = args->KeyCode;
     	m_text += ref new String(&c, 1);
    
    	// Let your render loop know that this control needs to be rendered again.
    	// MyRenderLoop::IsDirty = true;
    }
    
    
    
  10. OnRender メソッドを KeyEventTextBox.cpp 内に貼り付けます。この例では、画面に描画するために必要な Direct3D コードが省略されていますが、この簡素化されたメソッドは一般的な実装を示しています。テキスト ボックス コンテナーを描画し、テキスト バッファー内の文字ごとにループを実行して、その文字のビットマップを描画します。このメソッドの実際の実装は、複数行のテキスト、クリッピング、またはスクロールを含める場合は特に、さらに複雑になります。

  11. 次の各ステップでは、このテキスト ボックスを使用するようメイン アプリ内のコードを変更します。NativeTextInput.h の先頭に次のコード行を貼り付け、テキスト ボックスのヘッダー ファイルを含めます。

    
    #include "KeyEventTextBox.h";
    
    
    
  12. NativeTextInput.h のプライベート メンバー変数のセクションに、テキスト ボックスの変数宣言を貼り付けます。

    
    	KeyEventTextBox^ m_keyEventTextBox;
    
    
    
  13. NativeTextInput.cpp で、プロジェクト テンプレートの一部分として含まれている OnSetWindow メソッドに次の行を貼り付けます。このコードは、テキスト ボックスのサイズ、位置、初期のテキストを設定してテキスト ボックスを初期化した後、アプリの CoreWindow に参照を渡します。

  14. 最後に、次のコードを使用して、NativeTextInput.cpp 内で、プロジェクト テンプレートと共に含まれている OnPointerPressed メソッドを置き換えます。ユーザーが画面をタップした時点で、このメソッドが呼び出されます。このメソッドはテキスト ボックスの HitTest メソッドを呼び出し、ユーザーがテキスト ボックスの境界内をタップしたかどうかをチェックします。テキスト ボックスの内部がタップされた場合、テキスト ボックスのフォーカスが true に設定されます。テキスト ボックスの外側がタップされた場合、そのテキスト ボックスに現在フォーカスがあれば、フォーカスは false に設定されます。テキスト ボックスの SetFocus メソッドで、IsKeyboardInputEnabled が切り替えられ、SIP キーボードが表示または非表示になります。

    
    void NativeTextInput::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
    {
        CoreWindow^ window = CoreWindow::GetForCurrentThread();
    
    	auto position = args->CurrentPoint->Position;
    	bool isTouchInTextBox = m_keyEventTextBox->HitTest(position.X, position.Y);
    	
    	if(isTouchInTextBox)
    	{
    		m_keyEventTextBox->SetFocus(true);
    	}
    	else
    	{
    		// If the touch is outside of the active text box, hide the keyboard
    		if(m_keyEventTextBox->HasFocus)
    		{
    			m_keyEventTextBox->SetFocus(false);
    		}
    	}
    }
    
    
    

ソフトウェア キーボードが表示されたときの検出

  1. 上記のアプリの例のいずれかを使用して、NativeTextInput.h ファイルのプロテクト メソッドに次のコードを追加します。

    
    	void OnInputPaneHiding(Windows::UI::ViewManagement::InputPane^ sender, Windows::UI::ViewManagement::InputPaneVisibilityEventArgs^ args);
    	bool IsInputPaneShowing();
    
    
    
  2. SetWindow ハンドラーの末尾に、アプリの InputPane への参照を取得して Hiding イベントをフックするための次のコード行を追加します。

    
    	auto inputPane = InputPane::GetForCurrentView();
    	inputPane->Hiding += ref new TypedEventHandler<InputPane^, InputPaneVisibilityEventArgs^>(this, &NativeTextInput::OnInputPaneHiding);
    
    
    
  3. 次に、Hiding イベント ハンドラーの実装を追加します。この例では、テキスト ボックス クラスの SetFocus メソッドを呼び出し、false を渡します。アプリに応じて、入力ペインが非表示の場合に他のアクションを実行します。

    
    void NativeTextInput::OnInputPaneHiding(InputPane^ sender, InputPaneVisibilityEventArgs^ args)
    {
    	m_inputBufferTextBox->SetFocus(false);
    }
    
    
    
  4. 最後に、IsInputPanelShowing メソッドを実装できます。このメソッドは、OccludedRect プロパティを調べ、画面がキーボードで遮られているかどうかを判別します。四角形の幅と高さがゼロよりも大きい場合、キーボードは現在表示されています。

    
    bool NativeTextInput::IsInputPaneShowing()
    {
    	auto inputPane = InputPane::GetForCurrentView();
    	auto occ = inputPane->OccludedRect;
    	
    	return (occ.Width > 0 && occ.Height > 0);
    
    }
    
    
    

表示:
© 2014 Microsoft. All rights reserved.