最近刚刚看完MFC中各种控件的重绘,试着模仿QQ音乐做了个界面,不过没有播放音乐的功能。最近为了C++大作业,把功能加了上去,不过界面是临时重写的,十分粗糙,很多图片没有处理,直接放上去了,不过对于我们新手来说,一步步来,过一段时间一定能再填完整的。
好了,直接总结。
核心:播放代码(直接使用Bass库,不过只是小小的用了下,不知道的可以到http://www.un4seen.com/files/bass24.zip去下载,很强大的库)
BOOL CTestBassDlg::OnInitDialog() { //初始化代码 BASS_Init(-1,44100,0,this->m_hWnd,NULL); m_volum.SetRange(0,100); m_volum.SetPos(20); //设置音量 float pp=10/100.0f; BASS_SetVolume(pp); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CTestBassDlg::OnBnClickedButton1() { //打开文件 m_play=BASS_StreamCreateFile(FALSE, "E:\\歌曲\\:北极雪 ....mp3",0,0,BASS_SAMPLE_MONO); //开始播放 BASS_ChannelPlay(m_play,FALSE); } void CTestBassDlg::OnBnClickedButton2() { // TODO: 在此添加控件通知处理程序代码 CFileDialog read(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT|OFN_NOCHANGEDIR, _T("媒体文件(*.avi,*.mp3,*.wma)|*.avi;*.mp3;*.wma||")); POSITION pos=read.GetStartPosition(); if (IDOK==read.DoModal()) { int count=m_list.GetCount(); while(pos!=NULL) { CString path; path=read.GetNextPathName(pos); m_list.AddString(path); count++; } } } void CTestBassDlg::OnLbnDblclkList1() { // TODO: 在此添加控件通知处理程序代码 int nIndex=m_list.GetCurSel(); CString path; if (nIndex!=-1) m_list.GetText(nIndex,path); //释放 if (m_play!=NULL) BASS_StreamFree(m_play); m_play=BASS_StreamCreateFile(FALSE, path,0,0,BASS_SAMPLE_MONO); initpro(); //播放 BASS_ChannelPlay(m_play,FALSE); //设置回调函数 BASS_ChannelSetSync(m_play, BASS_SYNC_END, (QWORD)MAKELONG(10,0), &CTestBassDlg::MySyncProc, 0); } void CTestBassDlg::OnBnClickedButton3() { // TODO: 在此添加控件通知处理程序代码 //停止 BASS_ChannelStop(m_play); } void CTestBassDlg::OnBnClickedButton4() { // TODO: 在此添加控件通知处理程序代码 //播放 if (m_play!=NULL) BASS_ChannelPlay(m_play,FALSE); } void CTestBassDlg::OnBnClickedButton5() { // TODO: 在此添加控件通知处理程序代码 //暂停 BASS_ChannelPause(m_play); } void CTestBassDlg::OnTRBNThumbPosChangingSlider1(NMHDR *pNMHDR, LRESULT *pResult) { NMTRBTHUMBPOSCHANGING *pNMTPC = reinterpret_cast<NMTRBTHUMBPOSCHANGING *>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 int pos=m_volum.GetPos(); BASS_SetVolume(pos/100.0f); *pResult = 0; } void CTestBassDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: 在此添加消息处理程序代码和/或调用默认值 int pos=m_volum.GetPos(); BASS_SetVolume(pos/100.0f); pos=m_progress.GetPos(); //获取播放的位置 QWORD post=BASS_ChannelSeconds2Bytes(m_play,pos); //重新设置 BASS_ChannelSetPosition(m_play,post,BASS_POS_BYTE); CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); } void CTestBassDlg::initpro() { double time=BASS_ChannelBytes2Seconds(m_play, BASS_ChannelGetLength(m_play, BASS_POS_BYTE));
//这里获取歌曲的相关信息 具体的结构可以去看帮助文档的内容 TAG_ID3 *pTag = (TAG_ID3 *)BASS_ChannelGetTags(m_play,BASS_TAG_ID3); CString show; show.Format(("%lf %s %s %s"),time,pTag->title,pTag->artist,pTag->album); GetDlgItem(IDC_SHOW)->SetWindowText(show); m_progress.SetRange(0,(int)time); m_progress.SetPos(0); KillTimer(1); SetTimer(1,1000,NULL); } void CTestBassDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 //获取时间 以秒计算 double time=BASS_ChannelBytes2Seconds(m_play, BASS_ChannelGetPosition(m_play, BASS_POS_BYTE)); m_progress.SetPos((int)time); CDialogEx::OnTimer(nIDEvent); }
//回调函数 可以用来设置 播放的模式 void CALLBACK CTestBassDlg::MySyncProc(HSYNC handle, DWORD channel, DWORD data, void *user) { DWORD dwActive = BASS_ChannelIsActive(channel); if( dwActive == BASS_ACTIVE_STOPPED ) { AfxMessageBox("111"); } }
上面是Bass的使用 详细的大家可以直接去http://www.cnblogs.com/del/archive/2008/08/17/1269958.html 虽然是VB的方法,但是看函数用法就行了
2.界面的绘制
先看下效果图(图怎么传不上去啊,算了)
很垃圾的界面,我只说下大概的思路(刚学的新手看看,应该会有帮助吧)
1.滚动条(重绘都是在OnPaint中,其实麻烦的是轨道区,滑块的坐标计算)
我只给出部分代码(新手写的,有错误请直接留言)
CPaintDC dc(this); CRect rc; GetClientRect(&rc); CRect bkrect; bkrect.SetRect(rc.left,rc.top,rc.right,rc.bottom); CPoint tmp(0,0); if (!m_back.IsNull()) m_back.StretchBlt(dc.m_hDC,bkrect,SRCCOPY); if (!m_tunner.IsNull()) m_tunner.BitBlt(dc.m_hDC,m_ctunner,tmp,SRCCOPY); if (isdown) m_dowbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY); else if (ishover) m_hovbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY); else m_norbutton.StretchBlt(dc.m_hDC,m_cbutton,SRCCOPY);
说白了,就是计算坐标,然后贴图片,整个的代码下面给出,主要用这个实现进度变化,音量设置,以及一个小小的功能(设置透明,不过我不会裁剪区,做不到像QQ音乐那样)
2.CButton的绘制(这个就简单了,就是设置自绘属性,所有的变量在DrawItem这个虚函数中都有)
CDC *pdc=CDC::FromHandle(lpDrawItemStruct->hDC); SetStretchBltMode(pdc->m_hDC,STRETCH_HALFTONE); if (!m_pic.IsNull()) { CRect rc=lpDrawItemStruct->rcItem; BOOL issel=(lpDrawItemStruct->itemState&ODS_SELECTED?TRUE:FALSE); int nIndex=0; if (issel) nIndex=2; else if (ishover) nIndex=1; CRect picrect; picrect.SetRect(nIndex*GetWidth(),0,(nIndex+1)*GetWidth(),GetHeight()); //设置去背景 m_pic.TransDrawImage(pdc,rc.left,rc.top,rc.right,rc.bottom,picrect.left,picrect.top,RGB(255,255,255)); }
至于鼠标移进,离开的图片变换就是几个BOOL变量的判断,请看具体代码。
3CListBox的重绘(这个重绘不怎么成功,我想实现透明,但是一直出错,没办法只能先写个简单的了)
if (GetCount()<=0) return; CRect rcItem=lpDrawItemStruct->rcItem; CDC * pDCControl=CDC::FromHandle(lpDrawItemStruct->hDC); CDC BufferDC; CBitmap ImageBuffer; BufferDC.CreateCompatibleDC(pDCControl); ImageBuffer.CreateCompatibleBitmap(pDCControl,rcItem.Width(),rcItem.Height()); BufferDC.SelectObject(&ImageBuffer); CString strString; GetText(lpDrawItemStruct->itemID,strString); CRect rcString; rcString.SetRect(4,0,rcItem.Width()-8,rcItem.Height()); COLORREF crTextColor=RGB(84,84,84); BufferDC.FillSolidRect(0,0,rcItem.Width(),rcItem.Height(),RGB(251,243,235)); BOOL isselect=(lpDrawItemStruct->itemState&ODS_SELECTED?TRUE:FALSE); CRect tmprect(0,0,rcItem.Width(),rcItem.Height()); if (isselect) { m_sel.StretchBlt(BufferDC.m_hDC,tmprect,SRCCOPY); } else if ( hovenindex==lpDrawItemStruct->itemID ) { if ( m_sel!= NULL && !m_sel.IsNull()) { m_sel.StretchBlt(BufferDC.m_hDC,tmprect,SRCCOPY); } } BufferDC.SetBkMode(TRANSPARENT); BufferDC.SetTextColor(crTextColor); BufferDC.DrawText(strString,&rcString,DT_VCENTER|DT_SINGLELINE); pDCControl->BitBlt(rcItem.left,rcItem.top,rcItem.Width(),rcItem.Height(),&BufferDC,0,0,SRCCOPY); BufferDC.DeleteDC(); ImageBuffer.DeleteObject();
上面使用双缓冲来是为了减少闪屏(让我头痛啊,有时候还是会闪屏,看来我还是要再学学这方面啊)
4抛开重绘,主界面就是播放的代码。大家可以直接去看代码
上面可能写的简单了,不过大家可以直接去下代码,但是写的比较急(给别人交作业),代码重复的很多,不过我会加上注释,希望能对新手有所帮助,当然有错误或者不懂得的可
以给我留言,大家一起讨论,解决这些问题,一起加油!!!