Visual C++ 開發環境為控件提供的自繪制功能使程序員能夠充分發揮自己的創造性來設計比較漂亮的程序界面。所謂AVI按鈕是指每當鼠標從按鈕上經過時就播放一段按鈕提示的AVI,在許多的游戲程序以及三維動畫軟件中(如摩托英豪、Cool 3D等)都廣泛的采用了這種AVI按鈕。它使得程序的用戶界面很具有動感,也使得我們的程序至少看上去更專業,本實例借助Visual C++強大的控件自繪制功能來實現這種AVI按鈕的原理及實現。 程序編譯運行後的界面效果如圖一所示:
圖一、播放AVI視頻流的按鈕
一、實現方法
為了實現能夠播放AVI視頻流的動畫按鈕,還是需要利用控件的重載功能,這部分內容本書已經在《實例:實現XP風格的按鈕》中介紹了,這裡就不再贅述了,讀者可以參考該實例查閱相關內容。本實例主要探討如何使用MFC的CanimateCtrl動畫類播放AVI格式的視頻流,視頻流可以來自一個AVI文件,也可以來自資源。在程序中合理地使用動畫控件,可以使程序的界面更加形象生動。
MFC的CAnimateCtrl類封裝了動畫控件,該類的Create()成員函數負責創建動畫控件,其聲明為:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
函數中的參數dwStyle是如表一所示的控件風格的組合,參數rect指定了控件的尺寸,pParentWnd指向父窗口,nID是控件的ID。若創建成功則函數返回TRUE。
風格 含義 ACS_CENTER 使動畫片居於控件中央,並使動畫片打開後控件窗口的尺寸和位置保持不變。如果不指定該風格,則控件的尺寸會自動調整來適應動畫片的大小。 ACS_TRANSPARENT 使動畫片的背景透明(不輸出動畫片的背景色)。 ACS_AUTOPLAY 一旦打開動畫片後就一直重復播放。表一、動畫控件的風格
除表中的風格外,一般還要為動畫控件指定WS_CHILD、WS_VISIBLE和WS_BORDER窗口風格。例如,要創建一個能自動播放的動畫控件,應該指定其風格為WS_CHILD|WS_VISIBLE|WS_BORDER|ACS_AUTOPLAY。
CAnimateCtrl類主要的成員函數包括:BOOL Open( LPCTSTR lpszFileName )、BOOL Open( UINT nID ) 、BOOL Play( UINT nFrom, UINT nTo, UINT nRep ) 、BOOL Seek( UINT nTo )、BOOL Stop( )、BOOL Close( )等。
上述的Open()函數從AVI文件或資源中打開視頻流,如果參數lpszFileName或nID為NULL,則系統將關閉以前打開的視頻流。Play()函數用來播放選定的視頻流,參數nFrom指定了播放的開始幀的索引,索引值必須小於65536,若為0則從頭開始播放。nTo指定了結束幀的索引,它的值必須小於65536,若為-1則表示播放到視頻流的末尾。nRep是播放的重復次數,若為-1則無限重復播放。Seek()函數用來靜態地顯示視頻流的某一幀。參數nTo是幀的索引,其值必須小於65536,若為0則顯示第一幀,若為-1則顯示最後一幀,若成功則函數返回TRUE。Stop()函數用來停止動畫片的播放。Close()函數用來關閉並從內存中清除視頻流。上述所有函數都是調用成功返回TRUE,否則返回FALSE。
讀者朋友們在學習的過程中要注意,動畫控件並不能播放所有的AVI文件,只有滿足下列條件的AVI文件才能被播放:1)AVI文件必須是無聲的,不能有聲道;2)AVI文件必須是未壓縮的,或是用RLE算法壓縮的;3)AVI的調色板必須保持不變。另外,動畫控件最大的一個局限性在於它只能顯示系統調色板中缺省的顏色,因此如果用動畫控件來播放一個256色的AVI文件,那麼播放效果看起來就象一個16色的動畫一樣,很不理想。總之,動畫控件只能播放一些簡單的,顏色數較少的AVI動畫。如果要較滿意地播放256色的AVI文件,就要利用MCI接口,這部分內容請讀者朋友參閱有關資料。
二、編程步驟
1、啟動Visual C++6.0,生成一個基於對話框的應用程序,取其項目名為 TestAviButton, 然後按下 Finish 按鈕來完成工程的創建;
2、使用Class Wizard菜單命令以CButton 為基類創建CAviButton 類,生成類的頭文件 AviButton.h 和實現文件 AviButton.cpp,同時在類中重載 Create()、DrawItem()函數和 WM_MOUSEMOVE的消息映射;
3、打開工程中ID值為IDD_TESTAVIBUTTON_DIALOG 的對話框進行編輯,該對話框的提示文本為"將鼠標移至按鈕上:"刪除"取消"按鈕,將"確定"按鈕的屬性 Styles 改為 OwnerDraw ,並去掉其提示文本 "確定";另外執行 Insert | Resource… 命令,在彈出的對話框中按下 Custom… 按鈕,然後輸入"AVI",向程序中添加"AVI"格式的資源,然後選擇AVI類型的文件向項目中添加"AVI"格式的資源IDR_AVI;
4、添加代碼,編譯運行程序,此時每當我們的鼠標經過按鈕時,一個漂亮的AVI按鈕就產生了。
三、實現代碼
/////////////////////////////// AviButton.h : header file
#if !defined(AFX_AVIBUTTON_H__5E20D4EF_864E_11D7_886E_F16C81CD642B__INCLUDED_)
#define AFX_AVIBUTTON_H__5E20D4EF_864E_11D7_886E_F16C81CD642B__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CAviButton : public CButton
{
// Construction
public:
CAviButton();
public:
UINT m_nAviID;
CAnimateCtrl AnimateCtrl;
BOOL bPlaying;
void LoadAvi(UINT nAviID);
void DrawButton(CDC* pDC, UINT nState, CRect rect);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAviButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD
dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
CCreateContext* pContext = NULL);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CAviButton();
// Generated message map functions
protected:
//{{AFX_MSG(CAviButton)
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#endif
////////////////////////////CAviButton.CPP文件;
#include "stdafx.h"
#include "TestAviButton.h"
#include "AviButton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CAviButton::CAviButton()
{
m_nAviID = 0;
bPlaying = FALSE;
}
CAviButton::~CAviButton()
{}
void CAviButton::LoadAvi(UINT nAviID)
{
m_nAviID =nAviID;
}
BEGIN_MESSAGE_MAP(CAviButton, CButton)
//{{AFX_MSG_MAP(CAviButton)
ON_WM_MOUSEMOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
///////////////////// CAviButton message handlers
void CAviButton::DrawButton(CDC *pDC, UINT nState, CRect rect)
{
COLORREF upCol,downCol,edgeCol;
edgeCol=RGB(0,0,0); //設置按鈕邊緣的初始化顏色;
if ((nState & ODS_SELECTED) == ODS_SELECTED)
{
//設置按鈕被按下時按鈕的顏色
upCol=RGB(0,0,0);
edgeCol=RGB(128,128,128);
downCol=RGB(0,0,0);
}
else
{
//設置按鈕正常時按鈕的顏色
upCol=RGB(255,255,255);
downCol=RGB(128,128,128);
}
CPen* pOldPen = NULL;
BOOL pen1Created;
CPen pen1;
BOOL pen2Created;
CPen pen2;
if (pen1Created = pen1.CreatePen(PS_SOLID, 1, upCol))
pOldPen = pDC->SelectObject( &pen1 );
//畫左上邊緣
pDC->MoveTo(1,rect.Height()-1);
pDC->LineTo(1,1);
pDC->LineTo(rect.Width()-1,1);
pDC->MoveTo(0,rect.Height()-1);
pDC->LineTo(0,0);
pDC->LineTo(rect.Width()-1,0);
if (pen2Created = pen2.CreatePen(PS_SOLID, 1, downCol))
pDC->SelectObject( &pen2 );
if (pen1Created)
{
pen1.DeleteObject();
pen1Created = FALSE;
}
//畫右下邊緣
pDC->MoveTo(rect.Width()-1,0);
pDC->LineTo(rect.Width()-1,rect.Height()-1);
pDC->LineTo(0,rect.Height()-1);
pDC->MoveTo(rect.Width()-2,1);
pDC->LineTo(rect.Width()-2,rect.Height()-2);
pDC->LineTo(0,rect.Height()-2);
if (pen2Created)//刪除"pen2"畫筆對象
{
pen2.DeleteObject();
pen2Created = FALSE;
}
if (pen1Created = pen1.CreatePen(PS_SOLID, 1, edgeCol))
pOldPen = pDC->SelectObject( &pen1 );
if (pen1Created)
{
pen1.DeleteObject();
pen1Created = FALSE;
}
if (pOldPen != NULL)
pDC->SelectObject( pOldPen );
}
void CAviButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
CRect rect;
GetClientRect(rect);
if (!::IsWindow(AnimateCtrl))
{
//在按鈕上生成一個動畫控件
AnimateCtrl.Create(WS_CHILD |WS_VISIBLE,rect,this,0);
//打開avi文件並顯示第一幀
AnimateCtrl.Open(m_nAviID);
AnimateCtrl.GetClientRect(rect);
}
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
UINT nState = lpDrawItemStruct->itemState;
CRect buttonRect;
GetClientRect(buttonRect);
//繪制按鈕
DrawButton(pDC, nState, buttonRect);
}
BOOL CAviButton::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD
dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
// TODO: Add your specialized code here and/or call the base class
return CWnd::Create(lpszClassName, lpszWindowName, dwStyle,
rect, pParentWnd, nID, pContext);
}
void CAviButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
ClientToScreen(&point);
CRect rcWindow;
GetWindowRect(rcWindow);
//判斷鼠標是否經過按鈕
BOOL bNewMouseOverButton = rcWindow.PtInRect(point);
if (bNewMouseOverButton && IsWindowEnabled() )
{
if (::IsWindow(AnimateCtrl) && !bPlaying)
{
AnimateCtrl.Play(0,-1,1);
bPlaying = TRUE;
SetCapture();
}
}
else
{
bPlaying = FALSE;
ReleaseCapture();
}
CButton::OnMouseMove(nFlags, point);
}
/////////////////////////////////
BOOL CTestAviButtonDlg::OnInitDialog()
{
CDialog::OnInitDialog();
…………………//此處代碼省略;
m_AviButton.LoadAvi(IDR_AVI);
return TRUE; // return TRUE unless you set the focus to a control
}
四、小結
通過CAnimateCtrl類和按鈕控件的自畫功能的結合,本實例實現了動畫按鈕,如果該類和工具條、狀態條等控件結合,還可以實現在上述控件上播放動畫視頻流的效果。