一、涉及编写自己的自己的键盘
由于只使用英文字母跟数字等简单的字符而已,没有包括全角或者中文字符。所以直接使用跟手机的输入密码键盘一样的键盘即可。
遇到的问题:1、使用法keybd_event()的方式进行发送消息,不管发送大小写字母都是显示小写字母
2、键盘不能有焦点状态,否则使用的keybd_event则无法将字符发送给edit框。如果使用有焦点的状态则会导致edit控件跟我自己编写的键盘耦合性太大。
3、由于使用duilib的tilelayout而出现键盘的所有按键无法正常对齐。
解决方案:
1、使用keybd_event()则是模拟咱们在普通的键盘上的各种按键操作。要出现大写字母或者其他的"@#$%"等字符,则需要模拟键盘的输入 shift+对应的位置的字符。(这里我使用查表的方式,进行操作。当需要按shift键+其他按键才能实现的放在同一组,而另外的不许shift按键的以相同的顺序放在另一组。)(这里有一个技巧来获取每个字符对应的键值:编写一个能够获取键值的小软件,然后将每个按键的键值获取到。并通过刚才使用的查表方式对应的字符在第几个位置,从而发送该按键的键值)
1 #pragma once 2 3 namespace DuiLib { 4 5 class KeyValMag 6 { 7 public: 8 KeyValMag(void); 9 ~KeyValMag(void); 10 11 void SendMsg(int nVal); 12 13 private: 14 int FindSiftData(char cData); 15 int FindLowData(char cData); 16 }; 17 }
#include "StdAfx.h" #include <string> #include "KeyValMag.h" #include <Windows.h> #ifndef KEYEVENTF_SILENT #define KEYEVENTF_SILENT 0 #endif namespace DuiLib { KeyValMag::KeyValMag(void) { } KeyValMag::~KeyValMag(void) { } int KeyValMag::FindSiftData(char cData) { static std::string strSiftData("~!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"|ZXCVBNM<>?"); for (std::string::size_type i = 0; i < strSiftData.length(); ++i) if (cData == strSiftData[i]) return i; return -1; } int KeyValMag::FindLowData(char cData) { static std::string strLowData("`1234567890-=qwertyuiop[]asdfghjkl;\'\\zxcvbnm,./ "); for (std::string::size_type i = 0; i < strLowData.length(); ++i) if (cData == strLowData[i]) return i; return -1; } void KeyValMag::SendMsg( int nVal ) { if (nVal == 0) return; if (nVal == VK_RETURN) { keybd_event(VK_RETURN , 0, KEYEVENTF_SILENT, 0); keybd_event(VK_RETURN , 0, KEYEVENTF_SILENT | KEYEVENTF_KEYUP, 0); } else if (nVal == VK_BACK) { keybd_event(VK_BACK , 0, KEYEVENTF_SILENT, 0); keybd_event(VK_BACK , 0, KEYEVENTF_SILENT | KEYEVENTF_KEYUP, 0); } else { int nPrintIndex = FindSiftData(nVal); bool bUpper = false; if (nPrintIndex >= 0) bUpper = true; else nPrintIndex = FindLowData(nVal); if (nPrintIndex < 0) return; static unsigned char czPrintData[] = {192,49,50,51,52,53,54,55,56,57,48,189,187,81,87,69,82,84,89,85,73,79,80,219,221,65,83,68,70,71,72,74,75,76,186,222,220,90,88,67,86,66,78,77,188,190,191,32}; if (bUpper) keybd_event(VK_SHIFT, 0, KEYEVENTF_SILENT, 0); keybd_event((UCHAR)czPrintData[nPrintIndex], 0, KEYEVENTF_SILENT, 0); keybd_event((UCHAR)czPrintData[nPrintIndex], 0, KEYEVENTF_SILENT | KEYEVENTF_KEYUP, 0); if (bUpper) keybd_event(VK_SHIFT, 0, KEYEVENTF_SILENT | KEYEVENTF_KEYUP, 0); } } }
2、键盘不能有焦点状态,则需要在创建这个键盘的dialog的时候设置GLW_EXSTYLE的字段WS_EX_NOACTIVATE
#pragma once //#include <list> /// std::list<int> MoveLst; #include "UIlib.h" namespace DuiLib { class KeyValMag; HWND UILIB_API ShowKeyBoard(HWND hwndParent, RECT editPos); void UILIB_API HideKeyBoard(HWND hwndKeyBoard); class UILIB_API BoardWnd : public CWindowWnd, public INotifyUI { friend HWND ShowKeyBoard(HWND , RECT ); public: BoardWnd(HWND hwndParent, RECT editPos); ~BoardWnd(void); public: LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); static RECT GetCurPos(); //static void ShowKeyBorard(HWND hwndParent, RECT editPos); //static void HideKeyBoard(); protected: LPCTSTR GetWindowClassName() const { return _T("KeyBoradWnd"); } void OnFinalMessage(HWND /*hWnd*/) {delete this;} void Notify(TNotifyUI& msg); LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); private: void OnWindowInit(); void OnBtnClose(); private: void SetBtnInfo(CControlUI* pCtl, int nVal); void DigitCharacterKeyBoardOption(); void AlphaDigitCharacterKeyBoardOption(); void ShiftKeyBoardOption(); void AlphaKeyBoard(); void DigitKeyBoard(); void CharacterKeyBoard(); CControlUI* CreateOneBtn(int nWidth, int nHeight, int nVal); bool IsChecked(LPCTSTR strName); public: void MoveWindow2RightPos(HWND hwndParent, RECT editPos); void ResetWindowPos(bool bFinishBtn = false); private: //int m_lstMoveSize[3]; //int m_nEndMoveIndex; //int m_nBeginMoveIndex; //bool m_bCloseBtnClick; int m_nHaveMoveUpSize; HWND m_hwndParent; RECT m_editPos; CPaintManagerUI m_pm; KeyValMag* m_pKeyValMag; //static BoardWnd* m_pBoardWnd; }; }
#include "StdAfx.h" #include <string> #include "BoardWnd.h" #include "KeyValMag.h" #include <Windows.h> namespace DuiLib { int g_nShowCount = 0; HWND ShowKeyBoard(HWND hwndParent, RECT editPos) { if (g_nShowCount < 0) { g_nShowCount = 0; return NULL; } BoardWnd* pKeyBoard = new BoardWnd(hwndParent, editPos); pKeyBoard->Create(NULL, L"KeyBoardWnd", WS_VISIBLE, 0, BoardWnd::GetCurPos()); pKeyBoard->ShowWindow(); return *pKeyBoard; } void HideKeyBoard(HWND hwndKeyBoard) { if (hwndKeyBoard != NULL) ::PostMessage(hwndKeyBoard, WM_CLOSE, 0, 0); } BoardWnd::BoardWnd(HWND hwndParent, RECT editPos) : m_hwndParent(hwndParent), m_editPos(editPos), m_nHaveMoveUpSize(0) { MoveWindow2RightPos(hwndParent, editPos); m_pKeyValMag = new KeyValMag; } BoardWnd::~BoardWnd(void) { ResetWindowPos(); delete m_pKeyValMag; } void BoardWnd::ResetWindowPos(bool bFinishBtn /*= false*/) { int nHaveMoveUpSize = m_nHaveMoveUpSize; m_nHaveMoveUpSize = 0; if (nHaveMoveUpSize > 0) { if (bFinishBtn) g_nShowCount = -1; RECT basePos; GetWindowRect(m_hwndParent, &basePos); MoveWindow(m_hwndParent, basePos.left, basePos.top+nHaveMoveUpSize, basePos.right-basePos.left, basePos.bottom-basePos.top, FALSE); } } LRESULT BoardWnd::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { LONG styleValue = ::GetWindowLong(*this, GWL_EXSTYLE); styleValue &= ~WS_CAPTION; ::SetWindowLong(*this, GWL_EXSTYLE, styleValue | WS_EX_NOACTIVATE); m_pm.Init(m_hWnd); CDialogBuilder builder; std::wstring file_name = L"KeyBoard.xml"; CControlUI* pRoot = builder.Create(file_name.c_str(), 0, NULL, &m_pm); ASSERT(pRoot && "Failed to parse XML"); m_pm.AttachDialog(pRoot); m_pm.AddNotifier(this); RECT rect = BoardWnd::GetCurPos(); SetWindowPos(m_hWnd, HWND_TOPMOST, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, SWP_SHOWWINDOW); return 0; } RECT BoardWnd::GetCurPos() { static RECT curPos = {0,272-130,480,272}; return curPos; } void BoardWnd::Notify( TNotifyUI& msg ) { if (msg.sType == L"click") { if (msg.pSender == m_pm.FindControl(L"closebtn")) { OnBtnClose(); } else { m_pKeyValMag->SendMsg(msg.pSender->GetTag()); } } else if (msg.sType == L"windowinit") { OnWindowInit(); } else if (msg.sType == L"selectchanged") { if (msg.pSender->GetName() == L"AlphaDigitChange") AlphaDigitCharacterKeyBoardOption(); else if (msg.pSender->GetName() == L"BtnShift") ShiftKeyBoardOption(); else if (msg.pSender->GetName() == L"DigitCharacterChange") DigitCharacterKeyBoardOption(); } } void BoardWnd::OnWindowInit() { AlphaDigitCharacterKeyBoardOption(); ShiftKeyBoardOption(); DigitKeyBoard(); CharacterKeyBoard(); DigitCharacterKeyBoardOption(); CControlUI* pCtl = m_pm.FindControl(L"BtnBackSpace"); if (NULL != pCtl) pCtl->SetTag(VK_BACK); pCtl = m_pm.FindControl(L"BtnBackSpace1"); if (pCtl!= NULL) pCtl->SetTag(VK_BACK); m_pm.FindControl(L"BtnReturn")->SetTag(VK_RETURN); m_pm.FindControl(L"BtnSpace")->SetTag(VK_SPACE); } void BoardWnd::OnBtnClose() { ::ShowWindow(m_hWnd, SW_HIDE); ResetWindowPos(true); } LRESULT BoardWnd::HandleMessage( UINT uMsg, WPARAM wParam, LPARAM lParam ) { LRESULT lRes = 0; BOOL bHandled = TRUE; switch( uMsg ) { case WM_CREATE: lRes = OnCreate(uMsg, wParam, lParam, bHandled); break; default: bHandled = FALSE; break; } if( bHandled ) return lRes; if( m_pm.MessageHandler(uMsg, wParam, lParam, lRes) ) return lRes; return CWindowWnd::HandleMessage(uMsg, wParam, lParam); } void BoardWnd::AlphaKeyBoard() { char alphaLine[] = {"qwertyuiopasdfghjklzxcvbnm"}; bool bShift = IsChecked(L"BtnShift"); int nCurAlphaIndex = 0; WCHAR strName[MAX_PATH]; for (int i = 0; i < 3; ++i) { swprintf_s(strName, MAX_PATH, L"AlphaLine%d", i + 1); CHorizontalLayoutUI* pHor = (CHorizontalLayoutUI*)m_pm.FindControl(strName); if (NULL == pHor) continue; CControlUI* pCtl = NULL; int nButtonIndex = 0; for (int nColIndex = 0; nColIndex < pHor->GetCount(); ++nColIndex) { pCtl = m_pm.FindSubControlByClass(pHor, L"ButtonUI", nButtonIndex); if (pCtl == NULL) break; ++nButtonIndex; if (!pCtl->GetName().IsEmpty()) continue; if (pCtl == NULL) pHor->Add(CreateOneBtn(40, 26, alphaLine[nCurAlphaIndex++] - (bShift?('a'-'A'):0))); else SetBtnInfo(pCtl, alphaLine[nCurAlphaIndex++] - (bShift?('a'-'A'):0)); } } } void BoardWnd::SetBtnInfo(CControlUI* pCtl, int nVal) { WCHAR strVal[MAX_PATH]; pCtl->SetTag(nVal); swprintf_s(strVal, MAX_PATH, L"%c", nVal); pCtl->SetText(strVal); } void BoardWnd::DigitKeyBoard() { char digitLine[] = {"1234567890-/:;()$&@\".,?!'"}; int nCurDigitIndex = 0; WCHAR strName[MAX_PATH]; for (int i = 0; i < 3; ++i) { swprintf_s(strName, MAX_PATH, L"DigitLine%d", i + 1); CHorizontalLayoutUI* pHor = (CHorizontalLayoutUI*)m_pm.FindControl(strName); if (NULL == pHor) continue; CControlUI* pCtl = NULL; for (int nColIndex = 0; nColIndex < pHor->GetCount(); ++nColIndex) { pCtl = m_pm.FindSubControlByClass(pHor, L"ButtonUI", nColIndex); if (pCtl == NULL) pHor->Add(CreateOneBtn(i == 2?60:40, 26, digitLine[nCurDigitIndex++])); else SetBtnInfo(pCtl, digitLine[nCurDigitIndex++]); } } } void BoardWnd::CharacterKeyBoard() { char characterLine[] = {"[]{}#%^*+=_\\|~<>$$$$"}; int nCurCharacterIndex = 0; WCHAR strName[MAX_PATH]; for (int i = 0; i < 2; ++i) { swprintf_s(strName, MAX_PATH, L"Character%d", i + 1); CHorizontalLayoutUI* pHor = (CHorizontalLayoutUI*)m_pm.FindControl(strName); if (NULL == pHor) continue; CControlUI* pCtl = NULL; for (int nColIndex = 0; nColIndex < pHor->GetCount(); ++nColIndex) { pCtl = m_pm.FindSubControlByClass(pHor, L"ButtonUI", nColIndex); if (pCtl == NULL) pHor->Add(CreateOneBtn(40, 26, characterLine[nCurCharacterIndex++])); else SetBtnInfo(pCtl, characterLine[nCurCharacterIndex++]); } } } CControlUI* BoardWnd::CreateOneBtn(int nWidth, int nHeight, int nVal ) { WCHAR strVal[MAX_PATH]; CButtonUI* pBtn = new CButtonUI; pBtn->SetFixedHeight(nHeight); pBtn->SetFixedWidth(nWidth); pBtn->SetTag(nVal); swprintf_s(strVal, MAX_PATH, L"%c", nVal); pBtn->SetText(strVal); pBtn->SetAttribute(L"textcolor", L"#FF000000"); pBtn->SetAttribute(L"align", L"center"); pBtn->SetAttribute(L"normalimage", L"file='keyboardNormal.png' corner='8,8,8,8'"); pBtn->SetAttribute(L"focusedimage", L"file='keyboardHot.png' corner='8,8,8,8'"); pBtn->SetAttribute(L"pushedimage", L"file='keyboardPush.png' corner='8,8,8,8'"); return pBtn; } bool BoardWnd::IsChecked( LPCTSTR strName ) { COptionUI* pOption = (COptionUI*)m_pm.FindControl(strName); return pOption->IsSelected(); } void BoardWnd::DigitCharacterKeyBoardOption() { COptionUI* pOption = (COptionUI*)m_pm.FindControl(L"DigitCharacterChange"); if (pOption == NULL) return; std::wstring strText = pOption->IsSelected()?L"123":L"#+="; pOption->SetText(strText.c_str()); CControlUI* pCtl = m_pm.FindControl(L"DigitKeyBoard"); pCtl->SetVisible(!pOption->IsSelected()); pCtl = m_pm.FindControl(L"CharacterKeyBoard"); pCtl->SetVisible(pOption->IsSelected()); } void BoardWnd::AlphaDigitCharacterKeyBoardOption() { COptionUI* pOption = (COptionUI*)m_pm.FindControl(L"AlphaDigitChange"); if (pOption == NULL) return; std::wstring strText = pOption->IsSelected()?L"ABC":L".?123"; pOption->SetText(strText.c_str()); CControlUI* pCtl = m_pm.FindControl(L"AlphaKeyBoard"); pCtl->SetVisible(!pOption->IsSelected()); pCtl = m_pm.FindControl(L"DigitCharacterKeyBoard"); pCtl->SetVisible(pOption->IsSelected()); } void BoardWnd::ShiftKeyBoardOption() { COptionUI* pOption = (COptionUI*)m_pm.FindControl(L"BtnShift"); if (pOption == NULL) return; if (pOption->IsSelected()) pOption->SetText(L"SHIFT"); else pOption->SetText(L"shift"); AlphaKeyBoard(); } void BoardWnd::MoveWindow2RightPos(HWND hwndParent, RECT editPos) { RECT curKeyBoardRect = BoardWnd::GetCurPos(); m_editPos = editPos; m_hwndParent = hwndParent; if (editPos.bottom <= curKeyBoardRect.top) return; int nWouldMoveHeight = editPos.bottom - curKeyBoardRect.top; if (editPos.top - nWouldMoveHeight < 30) nWouldMoveHeight -= 30; m_nHaveMoveUpSize = nWouldMoveHeight; if (nWouldMoveHeight < 0) return; RECT basePos; GetWindowRect(m_hwndParent, &basePos); MoveWindow(m_hwndParent, basePos.left, basePos.top-nWouldMoveHeight, basePos.right-basePos.left, basePos.bottom-basePos.top, FALSE); } }
3、经过研究tilelayout跟horizontallayout的源码发现tilelayout的布局方式跟horizontallayout的布局计算方式不一样(SetPos函数),而导致了使用tilelayout布局方式出现无法正常对其的方式。(该计算方式应该是tilelayout的计算思路有问题导致的)。 最后使用了horizontalalyout的简单布局方式进行操作。
二、如何让edit控件获取到焦点的时候,调用我的键盘
我的软件使用的是duilib界面库,则当CEditUI获取到焦点的时候会创建CEditWnd,该类是创建原生态的Edit。当CEditUI失去焦点的时候就会销毁CEditWnd类,从而销毁临时创建的原生态Edit。所以很简单的思路就是在构造函数里调用我的键盘,在析构函数里面销毁我的键盘。
而在构造函数里面没有对应的CEditUI该窗口所对应的dialog句柄,所以修改了CEditWnd的源码
从 void Init(CEditUI* pOwner)该pOwner参数提到构造函数CEditWnd(CEditUI* pOwner)
从而可以简单的实现了键盘的调用跟对键盘的使用。
CEditWnd::CEditWnd(CEditUI* pOwner) : m_pOwner(pOwner), m_hBkBrush(NULL)//, m_nHaveMoveUpSize(0) { if (!m_pOwner->IsReadOnly() && m_pOwner->IsEnabled()) { RECT pos = m_pOwner->GetPos(); POINT pt = {pos.left, pos.top}; ClientToScreen(m_pOwner->m_pManager->GetPaintWindow(), &pt); OffsetRect(&pos, 0, pt.y - pos.top); m_hwndKeyBoard = ShowKeyBoard(m_pOwner->m_pManager->GetPaintWindow(), pos); } } CEditWnd::~CEditWnd() { if (!m_pOwner->IsReadOnly() && m_pOwner->IsEnabled()) HideKeyBoard(m_hwndKeyBoard); }
三、因为我使用的是下位机wince的移动设备,从而导致的一个现象是:当edit在屏幕靠下的地方时,键盘会挡住edit
需要解决的第一个办法是让用户手动移动键盘,这样子就能看到对应edit控件。但是这样子用户体验不好。
所以使用了另一种方式:当键盘把edit挡住的时候,软件自动上移,以防挡住edit。(这里遇到上移时候,刚开始调试时候出现了直接失去焦点的现象,具体原因是什么,怎样解决的,突然忘记了。。。)。
四、有时候用户在获得键盘之后,输入完成了,不想让键盘失去焦点,只想让键盘消失。则我在键盘上面做了一个失去点击隐藏键盘的按钮之后使键盘消失。但是焦点还在edit上面
需要解决的是:当点击隐藏按钮之后,会出现edit也失去焦点了(因为软件下移过程中导致失去了焦点,具体原因忘记了/qiao),当移动完成之后则edit会重新获得焦点,而重新调用键盘。所以我这里做了一个小聪明的技巧:放置一个全局的计数。当点击隐藏按钮时属于异常情况所以设置该计数小于零,当程序再次进来要调用键盘按钮的时候,设回该变量的初始状态,然后直接返回而不去显示键盘。
ps:因为某些原因,只写出了对应的设计思路。并且前段时间比较忙而没有记录对应的出现问题,而出现断片。。。,所以有的问题的原因忘记了。
但是使用该最终的思路还是可以正常的做出键盘调用。和键盘制作。