`
blogfeifei
  • 浏览: 1188173 次
文章分类
社区版块
存档分类
最新评论

让UI开发轻松而快乐,用SonicUI引擎实现常见UI效果

 
阅读更多

摘要:作为windows工程师,UI开发是无可避免的工作,无论你是写一个供销存系统,还是一款聊天IM,UI开发总是会占据你大量的时间。接下来展示的将是一种windows下的非常轻松快捷的UI开发解决方案,实现大家实际工作中经常需要实现的UI特效,力争将你从繁复的UI工作中解放出来,将注意力投入到更有挑战性的工作中去。

关键字:UI引擎 异形窗体 动画按钮 gdi引擎 自绘控件 轻便

作为windows工程师,UI开发是无可避免的工作,无论你是写一个供销存系统,还是一款聊天IM,UI开发总是会占据你大量的时间。前段时间在公司开发项目中,带着些许私心实现了一个构想了较长时间的UI引擎,自已在使用过程中感觉极大的加快了UI开发的效率,希望与大家分享,并用大家的建议来不断完善。

接下来将以几个在实际工作中常见的UI开发问题为例,介绍实现方法及效果,相信这几个问题能引起客户端UI开发同仁的共鸣。
1.多格式图片支持
2.文字和超链接
3.自绘按钮
4.脏处理与区域刷新
5.异形窗体(包括像素级透明异形窗体)

1.多格式图片支持
UI开发离不开图片,windows的api提供了一些加载图片的方法,如常用的LoadImage,使用很简单。但其功能也跟其用法一样简单,只能加载bmp,ico等几种格式。众所周知,bmp是不带alpha通道的,一旦需要实现阴影等alpha渐变的效果,系统提供的api就有些捉襟见肘了。当然很多人会想到大名鼎鼎的CxImage,这也是个不错的选择。我在内部也是封装了CxImage帮忙加载和保存多格式的图片,但加载之后的图像数据处理都是自处理的了,因为CxImage在处理RGB转hsl,旋转等特效时大量使用了浮点运算,效率不能使人十分满意。我把所有的浮点运算都转为整形运算,并大量使用了SSE2指令进行优化,实测证明在旋转,HSL转换,灰化等特效时,效率可以提高4-10倍(CPU为T2330 1.6GHz)。图片加载支持三种方式:从文件;从资源;从dc。需要说明的是从资源加载时请将资源类型命名为IMAGE。
演示代码如下:

//GetSonicUI是引擎导出的唯一函数,是类厂和引擎总控,负责创建对象和销毁对象等。
ISonicImage * pImg = GetSonicUI()->CreateImage();
pImg->Load("C:\\1.png");
pImg->Draw(hdc, 10, 10);
GetSonicUI()->DestroyObject(pImg);

OK,一个带透明通道的png图片绘制就完成了,是不是轻松惬意。

2.文字和超链接
UI开发过程中经常最麻烦的是绘制文字,需要你不停的初始化字体,设定字体属性,如果产品人员要求文字按一定的格式排版或输出彩色文字,那简直就是我们的噩梦了。而在自己的界面加入超链接,网上已经有不少演示代码了,但我相信ISonicString是一个更简单的实现方案。ISonicString是一个可以进行消息交互的UI组件对象。只需要像html语言一样加入一些类似的控制符,你就可以随心所欲的控制字体的大小颜色,超链接等属性,非常方便。
ISonicString * pStr = GetSonicUI()->CreateString();

pStr->Format("/c=%x, a='http://hi.csdn.net/zskof', font, font_height=16/点我打开链接", RGB(0, 0, 255));



LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

	PAINTSTRUCT ps;

	HDC hdc;

	switch (message) 

	{

	case WM_PAINT:

		{

			hdc = BeginPaint(hWnd, &ps);

			pStr->TextOut(hdc, 0, 0, hWnd);

			EndPaint(hWnd, &ps);

		}

		break;

	}

	.

	.

	.

}

如何,只需要创建,然后像CString的Format一样格式化一个字符串,在WM_PAINT响应中输出即可,只需要三步,你就得到了一行蓝色的功能完整的超链接,是不是很方便。通过控制字符,你还可以设定下划线的样式,鼠标形状,响应鼠标时变色等细节,具体参看ISonicUI.h中的注释即可。
ISonicString也可以将文字和图片混合输出,或使图片带有超链接属性,需要用'p'控制符指定一个ISonicImage的id:
ISonicImage * pImg = GetSonicUI()->CreateImage();

pImg->Load("C:\\1.png");

ISonicString * pStr = GetSonicUI()->CreateString();

pStr->Format("/c=%x/你好吗,朋友/p=%d, a='http://hi.csdn.net/zskof'/", RGB(0, 0, 255), pImg->GetObjectId());

这样就可以像写网页一样在你的界面上进行文字和图片的混合排版输出了。

3.自绘按钮
自绘按钮恐怕是UI编写中最常见也是重复度最高的工作,通常是继承自CButton然后ownerdraw出来。我的实现是不使用窗体的纯自绘。其实超链接也可以理解为按钮的一种,所以我自绘按钮的使用方式跟超链接也大同小异。
void WINAPI OnMove(ISonicString * pStr, LPVOID)

{

	g_pEffect->MoveGently(0, 0);

}



// 加载三态图片资源

ISonicImage * pImgNormal = GetSonicUI()->CreateImage();

pImgNormal->Load(BMP_NORMAL);

pImgNormal->SetColorKey(RGB(255, 0, 255));



ISonicImage * pImgHover = GetSonicUI()->CreateImage();

pImgHover->Load(BMP_HOVER);

pImgHover->SetColorKey(RGB(255, 0, 255));



ISonicImage * pImgClick = GetSonicUI()->CreateImage();

pImgClick->Load(BMP_CLICK);

pImgClick->SetColorKey(RGB(255, 0, 255));



// ISonicString * g_pTest[10]

g_pTest[10]->Format("/a, p=%d, ph=%d, pc=%d, linkt='点我移动'/", pImgNormal->GetObjectId(), pImgHover->GetObjectId(), 

	pImgClick->GetObjectId());

g_pTest[10]->Delegate(DELEGATE_EVENT_CLICK, NULL, NULL, OnMove);

同样的,格式化好后的ISonicString在OnPaint的时候输出即可,这样你就拥有一个具有三态变换的漂亮按钮,其中'p'关键字代表normal态,'ph'代表hover态,'pc'代表click态。如果从美工那里得到的源图是一张图片三态平铺的也不要紧,只需要将ph,pc都指向同一张img即可,内部会自动进行源区域裁剪。另外用过QQ2009的人可能会发现,2009的很多按钮三态变换是渐变的,体验很好,ISonicString一样可以做,只需要格式化时稍稍修改一下,
g_pTest[10]->Format("/a, p=%d, ph=%d, pc=%d, linkt='点我移动', animation=40/", pImgNormal->GetObjectId(), 

	pImgHover->GetObjectId(), pImgClick->GetObjectId());

增加一个'animation=40'的控制符(40是渐变速度),就可以得到一个QQ2009一样漂亮的三态渐变按钮了。按钮的点击响应是用“委托”的方式,你需要向按钮委托一个形如void WINAPI Func(ISonicBase *, LPVOID)的全局函数或类的成员函数,以供引擎在按钮被点击时回调。

图一:自绘按钮

4.脏处理与区域刷新
我们都知道gdi的绘制效率是不高的,无法像DDraw直接操作显存buffer那么爽快,所以InvalidateRect才提供了局部刷新的参数,而局部刷新也是gdi下进行优化的关键所在。可在实际操作中,我不常看见有人做这么精细的切割,都是一个InvalidateRect(hwnd, NULL, TRUE)了事。这也难怪,我随便TextOut一个字符串,我如果要去关心它占据了多少区域,区域之间的交叉裁剪等等,未免就太繁琐了。所以我的引擎提供了一个ISonicPaint对象,意如其名,就是一块画布。创建这块画布时,你可以指定其拥有自己的memDC,而出于节省gdi对象的考虑,你也可以指定其是一块无memDC的画布,如何取舍根据实际情况。
创建一个画布
ISonicPaint * pPaint = GetSonicUI()->CreatePaint();

pPaint->Create(FALSE/*是否需要memDC*/, m_rtString.Width()/*宽*/, m_rtString.Height()/*高*/);


画布创建之后,只需要在WM_PAINT中调用画面的Draw方法即可,很简单。 如果你想在这个画布上做画,就需要像自绘按钮一样,向画布委托一个你自己的绘制过程,以便在每次重绘时调用。示例代码如下:
class CTest

{

public:

	void RenderImage(ISonicPaint * pPaint, LPVOID);

};



void CTest::RenderImage(ISonicPaint * pPaint, LPVOID)

{

	if(pPaint->GetCurrentPaint() == NULL)

	{

		return;

	}

	HDC hdc = pPaint->GetCurrentPaint()->hdc;

	int x = pPaint->GetCurrentPaint()->x;

	int y = pPaint->GetCurrentPaint()->y;

	// draw here

	...

}



CTest test;

pPaint->Delegate(DELEGATE_EVENT_PAINT, NULL, &test, CSonicString::RenderImage);

如此每次只需要调用pPaint->Redraw()便会进行画布的区域重绘。这里需要说明的是,ISonicString,包括接下来要介绍的几个对象都是基于画布的,也就是说引擎的所有对象都是具有脏处理检查和区域自绘制优化的,可以极大的提高运行效率。
除了委托绘制以外,你还可以向画布上直接添加UI对象,画布支持的对象有:ISonicImage, ISonicString, ISonicPaint

5.异形窗体(包括像素级透明异形窗体)
异形窗体也是UI特效中经常需要使用的技术,常见的有两种实现方法。一种方法是根据图片裁剪出一个rgn,然后调用SetWindowRgn,另一个方法是将窗体设为WS_EX_LAYERED属性,调用SetLayeredWindowAttributes或UpdateLayeredWindow实现透明裁剪。前一种方法效率较低,而且拖动窗体时会出现难看的残影,后一种方法表现效果更好,拖动时可以避免残影出现,但不能作用于WS_CHILD属性的窗体。二者各有优劣。SonicUI同时提供了这两种实现方法,可以根据情况选择。
方法1:
...

// ISonicImage * pImg

SetWindowRgn(hWnd, pImg->CreateRgn());

方法2:
...

// ISonicImage * pImg

// ISonicWndEffect * pEffect

pEffect->Attach(hWnd, TRUE);	// 使用像素级alpha模式attach

pEffect->SetShapeByImage(pImg);


图二:异形窗体效果图
值得一提的是,如果使用UpdateLayeredWindow做窗体的像素级alpha特效,文字输出就成为了一个麻烦,因为gdi的文字输出函数是不带alpha通道的,直接TextOut上去无法正常表现。不过ISonicString可以帮你解决这一难题,我在内部已经为文字增加了alpha通道,可以很好的适应背景。

其它UI软件

http://topic.csdn.net/u/20100306/00/ad4496f4-7d82-459b-b523-e18b42675580.html

SkinSE(全称:skin so easy)

文档也很全。

SkinSB

http://topic.csdn.net/u/20100127/12/491a7a32-9231-4227-bc33-fb9a2f629457.html

http://www.skinse.com/update.html

Codejock Xtreme Toolkit

VS 2008 Feature Pack

http://blog.csdn.net/barech/archive/2009/07/02/4315157.aspx


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics