정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

Windows Phone 8용 Direct3D 앱에서 키보드 입력을 처리하는 방법

2014-06-18

적용 대상: Windows Phone 8 및 Windows Phone Silverlight 8.1만

 

이 항목에서는 사용자가 Windows Phone Direct3D 앱 용 SIP(가상 입력판) 키보드를 사용하여 텍스트를 입력할 수 있는 사용자 지정 텍스트 상자를 구현하는 방법을 보여 줍니다. 텍스트 입력은 거의 모든 앱이 필요로 하는 기능입니다. Windows Phone 관리되는 앱 을 사용하면 TextBox 컨트롤을 사용하여 텍스트 입력을 앱에 쉽게 추가할 수 있습니다. 안타깝게도, Direct3D 앱 에서는 XAML 컨트롤을 사용할 수 없습니다. 하지만, 작은 양의 코드로 SIP 키보드를 표시하고 사용자가 입력함에 따라 업데이트되는 사용자 지정 텍스트 상자 컨트롤을 만들 수 있습니다.

사용자 지정 텍스트 상자를 구현하는 방법에는 두 가지가 있습니다. 자동으로 키 입력을 집계하고, 현재 텍스트가 들어 있는 버퍼를 제공하는 KeyboardInputBuffer 클래스를 사용하거나 사용자가 키를 누를 때마다 시작되는 키 이벤트를 사용할 수 있습니다. 키 이벤트를 사용하는 경우, 자신의 텍스트 버퍼를 관리해야 합니다.

IsKeyboardInputEnabled()를 사용하면 SIP 키보드의 화면 표시 설정 및 해제를 토글할 수 있지만 키보드가 현재 표시되어 있는지 여부를 확인할 수는 없습니다. 사용자가 뒤로 버튼을 눌러 키보드를 닫으면 이 속성의 값이 업데이트되지 않습니다. 대신 사용자가 뒤로 버튼을 사용하여 키보드를 닫을 때 발생되는 Hiding 이벤트를 수신 대기해야 합니다. 또한 언제든지 OccludedRect 속성을 확인할 수 있으며 직사각형의 너비와 높이가 0인 경우 SIP가 현재 표시되어 있지 않음을 알 수 있습니다. 이러한 기술에 대한 간단한 데모는 연습 아래쪽에서 제공합니다.

참고참고:

이러한 프로시저를 간단하게 유지하기 위해 다음 예제에서 텍스트를 실제로 화면에 렌더링하는 데 필요한 Direct3D 코드를 생략했습니다.

 

KeyboardInputBuffer를 사용하여 사용자 지정 텍스트 상자 만들기

  1. 새 Direct3D 프로젝트를 만듭니다. 이 예제에서는 프로젝트 이름을 “NativeTextInput”으로 지정한다고 가정합니다.

  2. Visual Studio 에서, 프로젝트->클래스 추가로 이동하여 새 클래스를 프로젝트에 추가합니다. 새 클래스의 이름을 “InputBufferTextBox”로 지정합니다.

  3. InputBufferTextBox.h 헤더 파일에 다음 코드를 붙여 넣습니다. 여기에서 선언한 모든 메서드 및 멤버 변수는 다음에 나오는 단계들에서 설명됩니다.

  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)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    
    	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 값으로 호출하면 이것은 텍스트 상자에 포커스가 있고 텍스트 입력을 처리할 준비를 해야 함을 의미합니다. 이렇게 하려면 앱의 CoreWindow에 대한 KeyboardInputBuffer 속성을 텍스트 상자 KeyboardInputBuffer 멤버 변수로 설정하고 키보드의 입력 범위를 설정합니다. 그런 다음 CoreWindowIsKeyboardInputEnabled() 속성을 true로 설정합니다. 이렇게 되면 SIP 키보드가 나타납니다.

    SetFocusfalse 값으로 호출하는 경우, IsKeyboardInputEnabled()false로 설정하여 SIP 키보드가 표시되지 않도록 합니다.

    
    void InputBufferTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    	
    	if(m_hasFocus)
    	{
    		m_parentWindow->KeyboardInputBuffer = m_inputBuffer;
    		m_parentWindow->IsKeyboardInputEnabled = true;
    	}
    	else
    	{
    		m_parentWindow->IsKeyboardInputEnabled = false;
    	}
    }
    
    
    
  8. TextChanged 이벤트 처리기의 구현을 InputBufferTextBox.cpp에 붙여넣습니다. 이 이벤트는 텍스트 버퍼의 텍스트를 수정하면 발생합니다. 이런 일은 사용자가 SIP 키보드의 키를 탭하면 발생하지만, 사용자가 자동 수정 및 바꾸기 제안 목록에서 단어를 탭하는 경우처럼 다른 이유로도 발생할 수 있습니다. Text() 속성에 액세스하여 버퍼의 현재 텍스트를 가져오세요. 이 방법은 텍스트 버퍼에, 사용자가 Enter 키를 쳤음을 나타내는 줄 바꿈 문자가 있는지를 확인합니다. 있을 경우, 텍스트 상자의 포커스를 false로 설정합니다. 여기에 논리를 추가하여 다른 텍스트 상자로 이동할 수도 있습니다. 또한, 텍스트 상자를 화면 외부 버퍼로 렌더링하는 경우 코드를 추가하여 여기에 컨트롤을 다시 그리는 렌더링 반복에 대해 경고를 할 수도 있습니다.

    
    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의 private 멤버 변수 섹션에서 텍스트 상자에 대한 변수 선언을 붙여넣습니다.

    
    	InputBufferTextBox^ m_inputBufferTextBox;
    
    
    
  12. NativeTextInput.cpp에서, 프로젝트 템플릿의 일부로서 포함된 OnSetWindow 메서드에 다음 줄을 붙여넣습니다. 이 코드는 텍스트 상자의 크기, 위치 및 초기 텍스트를 설정하여 텍스트 상자를 초기화한 다음 참조를 앱의 CoreWindow에 전달합니다.

    
    	m_inputBufferTextBox = ref new InputBufferTextBox(window, 0, 0, 480, 100, "Initial Text");
    
    
    
  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 
    	{
    		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);
    	~KeyEventTextBox(void);
    
    	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;
    };
    
    
    
  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)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    	m_text = initialText;
    }
    
    
    
  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 키보드가 표시됩니다. 다음으로, 처리기의 KeyDownCharacterReceived 이벤트를 등록합니다. 이 처리기들을 등록하면 반환되는 토큰은 나중에 등록을 해제할 수 있도록 저장합니다.

    SetFocusfalse 값으로 호출하는 경우, IsKeyboardInputEnabled()false로 설정하여 SIP 키보드가 표시되지 않도록 합니다. 그런 다음, KeyDownCharacterReceived 이벤트에 대한 이벤트 처리기를 제거합니다.

    
    void KeyEventTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    
    	if(m_hasFocus)
    	{
    		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 키보드의 키를 탭하면 발생합니다. 이 메서드는 누른 키를 확인합니다. 뒤로 키의 경우에는 사용자가 백스페이스 키를 탭한 것입니다. 이 경우 텍스트 버퍼의 마지막 문자가 제거되고 이벤트 인수의 Handled 속성은 true로 설정됩니다. 이렇게 되면 시스템에서는 백스페이스 키 입력이 텍스트 버퍼에 추가되지 않도록 CharacterReceived 발생을 중지시킵니다. 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의 private 멤버 변수 섹션에서 텍스트 상자에 대한 변수 선언을 붙여넣습니다.

    
    	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 파일의 protected 메서드 섹션에 추가합니다.

  2. SetWindow 처리기가 끝나면 다음 코드 줄을 추가하여 앱의 InputPane에 대한 참조를 가져오고 Hiding 이벤트를 후크합니다.

  3. 이제 Hiding 이벤트 처리기 구현을 추가합니다. 이 예제에서는 텍스트 상자 클래스의 SetFocus 메서드를 호출하고 false로 전달합니다. 앱에 따라 입력 창이 숨겨져 있으면 다른 작업을 수행합니다.

  4. 마지막으로 OccludedRect 속성을 검사하여 화면이 키보드에 의해 가려지는지 여부를 확인하는 IsInputPanelShowing 메서드를 구현할 수 있습니다. 직사각형의 너비와 높이가 0보다 크면 키보드가 현재 표시되고 있는 것입니다.

표시: