去年发了高仿QQ2012登录界面,最近又优化了下代码,先看效果图
换肤 的代码和高仿QQ2012登录界面一样,代码请看那边;
这次主要是优化了控件的组合,和贴图方式
整体的框架:
首先是一个窗口,这里用的是对话框,然后根据鼠标消息和窗口消息,对界面上的控件进行状态处理并更新显示
鼠标移动的处理
void CDlgBase::OnMouseMove(UINT nFlags, CPoint point)
{
// 监控鼠标离开
if (!m_bTracking)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;
m_bTracking = ::_TrackMouseEvent(&tme);
}
// 鼠标已经在某个控件上
if (m_pControl)
{
if((m_pControl->PtInRect(point) || m_bIsLButtonDown) && m_bTracking)
{
m_pControl->OnMouseMove(nFlags, point);
return;
}
}
// 左键按下后不处理鼠标移动消息
if (!m_bIsLButtonDown)
{
CControlBase * pOldControl = m_pControl;
BOOL bIsSelect = FALSE;
BOOL bIsSystemSelect = FALSE;
if(m_bTracking)
{
// 默认控件
for (size_t i = 0; i < m_vecBaseControl.size(); i++)
{
CControlBase * pControlBase = m_vecBaseControl.at(i);
if (pControlBase)
{
pControlBase->OnMouseMove(nFlags, point);
if(pControlBase->PtInRect(point) && pControlBase->GetRresponse())
{
m_pControl = pControlBase;
bIsSystemSelect = TRUE;
}
}
}
// 用户控件
for (size_t i = 0; i < m_vecControl.size(); i++)
{
CControlBase * pControlBase = m_vecControl.at(i);
if (pControlBase)
{
pControlBase->OnMouseMove(nFlags, point);
if (!bIsSystemSelect)
{
if(pControlBase->PtInRect(point) && pControlBase->GetRresponse())
{
m_pControl = pControlBase;
bIsSelect = TRUE;
}
}
}
}
}
if (!bIsSelect && !bIsSystemSelect)
{
m_pControl = NULL;
}
}
//CDialog::OnMouseMove(nFlags, point);
}
控件组合:
DirectUI 这种方式要实现不同的界面效果是很好实现的,只是控件都要自己重新写,所以要能实现控件的组合,这样当基础控件多了组合新控件就方便了
每个控件都有自己的消息处理 如鼠标移动,每个控件都能增加自己的子控件,也有当前被选中控件这些处理,代码如下(基本和窗口那处理是一样的)
BOOL CControlBase::OnMouseMove(UINT nFlags, CPoint point)
{
if(!m_bIsVisible || !m_bRresponse) return false;
BOOL bRresponse = false;
if(m_pControl)
{
if(m_pControl->PtInRect(point) || m_bMouseDown)
{
if(m_pControl->OnMouseMove(nFlags, point))
{
return true;
}
return false;
}
}
CControlBase * pOldControl = m_pControl;
m_pControl = NULL;
bRresponse = OnControlMouseMove(nFlags, point);
if(!m_bMouseDown)
{
for (size_t i = 0; i < m_vecControl.size(); i++)
{
CControlBase * pControlBase = m_vecControl.at(i);
if (pControlBase)
{
if(pControlBase->OnMouseMove(nFlags, point))
{
if(pControlBase->PtInRect(point))
{
m_pControl = pControlBase;
}
bRresponse = true;
}
}
}
if (pOldControl)
{
bRresponse = true;
}
if (m_pControl)
{
bRresponse = true;
}
}
return bRresponse;
}
控件显示主要是为每个控件增加上个DC,只在每次控件要重绘时才去更新贴图,其他情况如只是状态更改之类的就只是从DC复制到窗口上(就是空间换速度),代码如下
void CImageButton::DrawControl(CDC &dc, CRect rcUpdate)
{
int nWidth = m_rc.Width();
int nHeight = m_rc.Height();
//更新标志(如在换肤、移动位置、比他更下层的控件变化时等等一些会使控件就变化的都会更新)
if(!m_bUpdate)
{
// 更新DC 如果控件大小改变会更新
UpdateMemDC(dc, nWidth * (4 + m_nMaxIndex), nHeight);
ColorMatrix matrix;
ImageAttributes imageAttr;
ZeroMemory(&matrix, sizeof(matrix));
for(int i = 0; i < 5; i++)
{
matrix.m[i][i] = 1.0f;
}
Graphics graphics(m_memDC);
CRect rcTemp(0, 0, nWidth, nHeight);
// 按钮有四个状态 所以DC为按钮大小的4倍分别保存四个状态,如果只是单纯的按钮状态改变的话,就不用重绘,而是通过状态值去取不同的DC区域显示
for(int i = 0; i < 4; i++)
{
// 先贴上控的背景
m_memDC.BitBlt(i * nWidth, 0, nWidth, nHeight, &dc, m_rc.left ,m_rc.top, SRCCOPY);
// 贴上不同状态的图
graphics.DrawImage(m_pImage, Rect(rcTemp.left, rcTemp.top, rcTemp.Width(), rcTemp.Height()),
i * m_sizeImage.cx, 0, m_sizeImage.cx, m_sizeImage.cy, UnitPixel);
rcTemp.OffsetRect(nWidth, 0);
}
// 实现动画效果 原理同上
for (int i = 4; i < 4 + m_nMaxIndex; i++)
{
m_memDC.BitBlt(i * nWidth, 0, nWidth, nHeight, &dc, m_rc.left ,m_rc.top, SRCCOPY);
matrix.m[3][3] = 1 - ((float)(i - 4 + 1)) / m_nMaxIndex;
imageAttr.SetColorMatrix(&matrix);
graphics.DrawImage(m_pImage, Rect(rcTemp.left ,rcTemp.top, rcTemp.Width(), rcTemp.Height()),
m_sizeImage.cx * enBSNormal, 0, m_sizeImage.cx, m_sizeImage.cy, UnitPixel ,&imageAttr);
matrix.m[3][3] = ((float)(i - 4 + 1)) / m_nMaxIndex;
imageAttr.SetColorMatrix(&matrix);
graphics.DrawImage(m_pImage, Rect(rcTemp.left ,rcTemp.top, rcTemp.Width(), rcTemp.Height()),
m_sizeImage.cx * enBSHover, 0, m_sizeImage.cx, m_sizeImage.cy, UnitPixel ,&imageAttr);
rcTemp.OffsetRect(nWidth, 0);
}
}
// 根据不同状态显示
if(m_nIndex != 0 && m_bRunTime && m_bTimer)
{
dc.BitBlt(m_rc.left,m_rc.top, m_rc.Width(), m_rc.Height(), &m_memDC, (4 + m_nIndex - 1) * nWidth, 0, SRCCOPY);
}
else
{
dc.BitBlt(m_rc.left,m_rc.top, m_rc.Width(), m_rc.Height(), &m_memDC, m_enButtonState * nWidth, 0, SRCCOPY);
}
}
这个现在也不算库的界面库吧,还有很多问题没解决,如不支持TAB键,不支持键盘操作,控件太少等等,要完善还有很多东西要做,代码也发了,如果有好想法或解决了BUG的希望能告诉我