clayui界面库系列教程之五:仿QQ2010登录下拉框的动画效果
预览图如下:
这次的例子工程是CLAYUIEXP2,与以前的苹果风格按钮 是一个工程,所以在本教程里,关于初始化之类的就不详细解说了,有疑问的请查看本博客内的其他教程
好的,教程开始了。
首先,在MFC对话框里添加一个对下拉框初始化的函数:CreateComboBox,在里面,我们对下拉框进行初始化:
CMyComboBox* combo = new CMyComboBox();
combo->Init(m_mainframe, m_graphics.m_draw, 0);
combo->SetLayout(1, 0, "combobox");
m_mainframe->AddCustomFrame(0, "comboBox", 50, 0, 200, 24, 100, combo);
combo->SetItemsDepth(2);
combo->SetListHeight(350);
combo->AddMyText(L"小强\n123456", swfitem1);
combo->AddMyText(L"张三\n123457", swfitem1);
combo->AddMyText(L"李四\n123458", swfitem1);
combo->AddMyText(L"王五\n123459", swfitem1);
这里创建了一个combobox,并设置了item的深度,下拉框的高度,添加了4行文字
当然,这么几行代码是不可能实现QQ下拉框的效果的,为了实现自定义的效果,还必须做一个自定义的下拉框控件,所以,这里可以看到,有一个新类:CMyComboBox,这个是自定义的一个下拉框控件类,派生自CLAYUI_ComboBox
下面,我们来对 CMyComboBox进行详细说明:
首先是初始化,重载oadFromMem这个函数,然后添加以下代码:
m_sys_listbox->GetChildFrame("_SYS_SELRECT_")->SetColorTrans(0, 100, 100, 100, 0, 0, 0, 0);
m_sys_listbox->GetChildFrame("_SYS_MOUSEINRECT_")->SetColorTrans(0, 100, 100, 100, 0, 0, 0, 0);
先把下拉框里原先的动画元素设为透明,也就是屏蔽掉
m_sys_listbox->SetFrameFlags(CLAYUI_FS_SENDMSG_RULE_CSES, 1);
然后,设置listbox的消息转发模式为:转发子FRAME的基本消息。这一句很重要,如果不设置,那么comboBox将接收不到下拉框里所有ITEM的消息。我们需要ITEM的鼠标移入,鼠标移出消息来实现动画效果
对下拉框的设置就完毕了,然后我们需要对加入的文字item设置自定义的风格,这里添加一个函数:AddMyText,代码如下:
AddString(string);
先加入文字ITEM
CLAYUI_TTEXT* item = dynamic_cast<CLAYUI_TTEXT*>(GetItem(GetCount() - 1));
得到刚加入的ITEM对象
int textoffset = 35;
float height;
m_sys_listbox->GetFrameSize(m_itemwidth, height);
m_itemwidth += 80;
item->SetFrameSize(m_itemwidth, m_itemheight);
item->SetTextColor(0, 255, 0, 255);
将ITEM的大小设置为自定义的大小,这里,我们设置的最终宽度,高度分别为:280, 60;然后设置文字颜色为绿色
item->SetClip(1, m_itemheight + textoffset, 0, m_itemwidth - m_itemheight - textoffset, m_itemheight);
设置文字的裁减矩形,让文字显示在ITEM的(95,0)处。
item->SetTextAlignType(0, 1);
设置文字的对齐方式为左对齐,行居中。
CLAYUI_FRAME_ITEM* fi = GetItem(GetCount() - 1)->AddFrameItem(gitem, NULL, 1, 1, 0, 1, 1, 1);
fi->SetSkinInfo(1, 0, "button");
fi->UpdateSkin();
这里,我们先加入一个皮肤对象,然后使用 SetSkinInfo将皮肤替换为皮肤库里的皮肤“button",然后把皮肤更新一下
CLAYUI_GetGItemWH(fi->m_gItem, gw, gh);
fi->FixToFrameSize(1, gw + 2, gh + 2);
fi->SetColorTrans(100, 100, 100, 100, 0, -128, -128, 128);
得到皮肤里的图形对象的高宽,然后将皮肤的大小同ITEM的高宽绑定,这样一来,当ITEM的高宽发生改变时,皮肤也会随之而变;然后设置皮肤的颜色为蓝色
FixToFrameSize函数的说明:
short bfix:是否绑定
int x, int y:当皮肤缩放系数为(1,1)时,对应的frame的高宽
CLAYUI_GetGItemWH(gitem, gw, gh);
fi = GetItem(GetCount() - 1)->AddFrameItem(gitem, NULL, 3, 2, 0, 1, 1, 1);
fi->FixToFrameSize(1, gw * m_itemwidth / m_itemheight + 6, gh + 8);
将头像也作为ITEM的皮肤加入,同样也是设置与ITEM的高宽绑定。在这里,每个ITEM有2个皮肤对象,一个是蓝色背景,一个是头像
对ITEM的自定义风格就设置完毕了,那么,接下来,再来设置动画效果,在这里,我们需要对ITEM的鼠标移入,鼠标移出消息进行处理,消息的处理需要重载UserAction函数,代码如下:
CLAYUI_FRAME* listitem = msginfo->GetFrame(this);
得到消息来源
if(listitem && listitem->GetParent() == m_sys_listbox && listitem->GetFrameID() >= CLAYUI_LISTBOXITEM_FRAMEID)
判断是否是ITEM发来的消息,判断是否满足如下2个条件:
1. 消息来源的父FRAME是m_sys_listbox
2. 消息来源的frameid大于等于CLAYUI_LISTBOXITEM_FRAMEID
提示:所有加入到LISTBOX里的ITEM的FRAMEID都大于等于 CLAYUI_LISTBOXITEM_FRAMEID
if(msg == CLAYUI_ONMOUSEIN)
判断是否是鼠标移入,在QQ2010的登录下拉框里,鼠标移动到某个ITEM时,此ITEM会放大,并且此ITEM的前一个ITEM也会放大,但会比当前 ITEM小,比其他ITEM大,此ITEM的下一个ITEM也是如此,了解了这个原理后,我们也如法炮制:
listitem = msginfo->GetFrame(this);
DWORD animid = (DWORD)listitem;
char animidname[12];
sprintf(animidname, "%d", animid);
SetAnimation(animidname, listitem, m_itemwidth * scaleft, m_itemheight * scaleft, duration);
将当前ITEM的高宽放大到原本的1.6 倍
这里用item的指针值作为了动画对象的idname,是为了防止重名
int index = GetItemIndex(listitem);
listitem = GetItem(index - 1);
if(listitem)
{
animid = (DWORD)listitem;
sprintf(animidname, "%d", animid);
SetAnimation(animidname, listitem, m_itemwidth * scaleft12, m_itemheight * scaleft12, duration);
}
将前一个ITEM的高宽放大到原本的1.2倍
listitem = GetItem(index + 1);
if(listitem)
{
animid = (DWORD)listitem;
sprintf(animidname, "%d", animid);
SetAnimation(animidname, listitem, m_itemwidth * scaleft12, m_itemheight * scaleft12, duration);
}
将下一个ITEM的高宽放大到原本的1.2倍
然后处理鼠标移出的动画,当鼠标移出时,将发送消息的ITEM,前一个ITEM,后一个ITEM的高宽还原就行了,具体代码这里就不贴了,与上面类似。
现在,当鼠标移入,鼠标移出时,ITEM的高宽都会发生变化,那么,现在有一个问题,当ITEM高宽发生变化时,他们的坐标并没有改变,那么就会发生ITEM覆盖其他ITEM的现象,为了避免这一现象,我们必须每桢去修正一个ITEM的Y坐标
需要重载Update函数,代码如下:
CLAYUI_ComboBox::Update();
先调用父类的Update,让动画对象处理完毕后,我们再来修正ITEM的Y坐标
int count = m_sys_listbox->GetCount();
if(count > 0 && GetListState())
得到ITEM个数,判断下拉框是否处于弹出状态
int i;
CLAYUI_FRAME* item = m_sys_listbox->GetItem(0);
float x, y;
item->GetTrans(x, y);
float w, h;
item->GetFrameSize(w, h);
float scalex, scaley;
item->GetScale(scalex, scaley);
得到第一个ITEM的坐标,高宽,缩放值
for(i = 1; i < count; i ++)
{
m_sys_listbox->GetItem(i)->MoveTo(x, y + h * scaley - 1);
item = m_sys_listbox->GetItem(i);
item->GetTrans(x, y);
item->GetFrameSize(w, h);
item->GetScale(scalex, scaley);
}
从第2个ITEM开始,循环到最后一个ITEM,修正ITEM的坐标。
好的,这次的教程就结束了,大家可以尝试在CMyComboBox里做一些修改,做出属于自己的CMyComboBox来。