前情提要:Duilib源碼中的ListDemo,給我們提供了一個(gè)可以左右拖拉h(huán)eaderItem,下面的listitem也跟著變化。但實(shí)際工作中,往往HeaderItem和listitem都比較復(fù)雜,不可能只是一個(gè)text。這就要求他是個(gè)容器,可以放option,button,image等。 類似這樣的效果:
1 首先改進(jìn)CListHeaderItemUI 原來(lái)CListHeaderItemUI繼承的事CControlUI,讓他改成繼承CContainerUI就好了,另外需要重寫void SetPos(RECT rc);否則其子控件的有效作用范圍給headerItem的大小,拖拉的觸發(fā)事件失效。 void SetPos(RECT rc); 參考void CHorizontalLayoutUI::SetPos(RECT rc) 拷貝過(guò)來(lái)就可以了。
另外xml中的寫法有變化 <ListHeader height="30" menu="true"> <ListHeaderItem text="" font="1" width="60" hotimage="file='list_bg.png' source='1,0,2,58'" pushedimage="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="5"> <Option float="true" pos="5,0,0,0" width="50" height="30" name="OptionOfListApplication" normalimage="file='checkbox.png' dest='23,8,37,22' source='0,0,14,14'" hotimage="file='checkbox.png' dest='23,8,37,22' source='14,0,28,14'" pushedimage="file='checkbox.png' dest='23,8,37,22' source='28,0,42,14'" disabledimage="file='checkbox.png' dest='23,8,37,22' source='42,0,56,14'" hottextcolor="#FF007AFF" selectedforeimage="file='check.png' dest='23,8,37,22'"/> </ListHeaderItem> <ListHeaderItem text="Domain" font="1" width="260" hotimage="file='list_bg.png' source='1,0,2,58'" pushedimage="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="1">
</ListHeaderItem> <ListHeaderItem text="Description" font="1" width="240" hotimage="file='list_bg.png' source='1,0,2,58'" pushedimage="file='list_header_pushed.png'" sepimage="file='list_header_sepline.png'" sepwidth="1"> </ListHeaderItem> </ListHeader>
注意Option 要設(shè)置 float="true" pos="5,0,0,0" 兩個(gè)屬性
2 改進(jìn)CListContainerElementUI 雖然他是個(gè)容器,但他對(duì)headeritem的拖拉無(wú)響應(yīng),不能隨之改變。
源碼中void CListContainerElementUI::DrawItemText(HDC hDC, const RECT& rcItem) 這個(gè)方法直接return了。需要實(shí)現(xiàn)。
void CListContainerElementUI::DrawItemText(HDC hDC, const RECT& rcItem) {
if( m_pOwner == NULL ) return; TListInfoUI* pInfo = m_pOwner->GetListInfo(); DWORD iTextColor = pInfo->dwTextColor;
if( (m_uButtonState & UISTATE_HOT) != 0 ) { iTextColor = pInfo->dwHotTextColor; } if( IsSelected() ) { iTextColor = pInfo->dwSelectedTextColor; } if( !IsEnabled() ) { iTextColor = pInfo->dwDisabledTextColor; }
for( int i = 0; i < pInfo->nColumns; i++ ) { RECT rcItem = { pInfo->rcColumn[i].left, m_rcItem.top, pInfo->rcColumn[i].right, m_rcItem.bottom }; rcItem.left += pInfo->rcTextPadding.left; rcItem.right -= pInfo->rcTextPadding.right; rcItem.top += pInfo->rcTextPadding.top; rcItem.bottom -= pInfo->rcTextPadding.bottom;
CControlUI* pControl = GetItemAt(i); pControl->SetPos(rcItem); } }
而且需要在DoPaint中調(diào)用 void CListContainerElementUI::DoPaint(HDC hDC, const RECT& rcPaint) { if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
DrawItemText(hDC, m_rcItem);
DrawItemBk(hDC, m_rcItem); CContainerUI::DoPaint(hDC, rcPaint); }
另外xml的寫法有所改變,不能像以前那樣隨便寫了,CListContainerElementUI下面的一級(jí)子控件為每一列的容器,也可以不是容器,但如果此列包含多個(gè)控件,就必須是個(gè)容器,可參考如下寫法: <ListContainerElement height="58"> <HorizontalLayout float="true" pos="0,0,60,58"> <Option float="true" pos="5,0,0,0" width="50" height="30" name="OptionOfListApplication" normalimage="file='checkbox.png' dest='23,8,37,22' source='0,0,14,14'" hotimage="file='checkbox.png' dest='23,8,37,22' source='14,0,28,14'" pushedimage="file='checkbox.png' dest='23,8,37,22' source='28,0,42,14'" disabledimage="file='checkbox.png' dest='23,8,37,22' source='42,0,56,14'" hottextcolor="#FF007AFF" selectedforeimage="file='check.png' dest='23,8,37,22'"/> </HorizontalLayout> <Label float="true" pos="60,0,320,58" name="docsize" text="2222" align="center" textcolor="#FF333333" width="260"/> <Label float="true" pos="320,0,560,58" name="appversion" text="3333" align="center" textcolor="#FF333333" /> </ListContainerElement>
上篇博客寫完,以為改進(jìn)List達(dá)到了項(xiàng)目要求,可后來(lái)發(fā)現(xiàn)誘發(fā)了其他的問(wèn)題,如滾動(dòng)條部分功能失效,還有程序在運(yùn)行一段時(shí)間后進(jìn)入了無(wú)響應(yīng)狀態(tài)。 后來(lái)在以下三個(gè)方向進(jìn)行了探索: 1 主要改進(jìn)還是在DrawItemText 函數(shù)中,試圖解決由此引起的Bug 結(jié)果:不能解決掉,但證明duilib是可以實(shí)現(xiàn)的,只是思路和方法還沒(méi)有找對(duì)。 DrawItemText 本來(lái)的作用是重繪Text文本,在這里調(diào)用setpos,會(huì)引起其父控件重繪,父控件重繪又會(huì)調(diào)用DoPaint,有循環(huán)調(diào)用,程序很容易崩掉。思路不對(duì)。 2 重寫DoPaint函數(shù) 結(jié)果:只能對(duì)其一級(jí)子控件進(jìn)行背景,文本的重繪,沒(méi)有誘發(fā)其他Bug ,但很難實(shí)現(xiàn)一級(jí)子控件及它的子控件一起重繪。 思路: 由于CListContainerElementUI繼承于CContainerUI,原來(lái)只是在最后調(diào)用了父類的DoPaint,根據(jù)CContainerUI::DoPaint(hDC, rcPaint);源碼進(jìn)行修改,每列的寬度可以得到,所以子控件的可以根據(jù)頭部的寬度重繪。 3 重寫SetPos函數(shù) 結(jié)果:這次終于完美解決,上張圖,呵呵!
思路:以前兩種方案,仍舊掉在CListTextElementUI實(shí)現(xiàn)思路的坑里。整理思路CListContainerElementUI中的每一列可以是一個(gè)簡(jiǎn)單控件,也可以是一個(gè)容器控件,所以只是在DoPaint里做文章,無(wú)法滿足要求。SetPos既然可以實(shí)現(xiàn)容器控件的位置,寬高的改變,那為什么不重寫SetPos呢,拿來(lái)CContainerUI的SetPos,進(jìn)行重寫。 上代碼
void CListContainerElementUI::SetPos(RECT rc) { if( m_pOwner == NULL ) return; TListInfoUI* pInfo = m_pOwner->GetListInfo(); int iChangeIndex=0; LONG cx = 0; for( int i = 0; i < pInfo->nColumns; i++ ) { CControlUI* pControl = GetItemAt(i); if(!pControl) break; RECT rcOldItem = pControl->GetPos(); if(pInfo->rcColumn[i].right-rcOldItem.right!=0){ iChangeIndex =i; cx=pInfo->rcColumn[i].right-rcOldItem.right; break;
}
} RECT rcNew = {rc.left,rc.top,rc.right+cx,rc.bottom}; CControlUI::SetPos(rcNew); if( m_items.IsEmpty() ) return; rcNew.left += m_rcInset.left; rcNew.top += m_rcInset.top; rcNew.right -= m_rcInset.right; rcNew.bottom -= m_rcInset.bottom;
for( int it = 0; it < m_items.GetSize(); it++ ) { CControlUI* pControl = static_cast<CControlUI*>(m_items[it]); if( !pControl->IsVisible() ) continue; if( pControl->IsFloat() ) { if(it>=iChangeIndex){ RECT rcItem = { pInfo->rcColumn[it].left, m_rcItem.top, pInfo->rcColumn[it].right, m_rcItem.bottom }; pControl->SetPos(rcItem); } } else { pControl->SetPos(rcNew); // 所有非float子控件放大到整個(gè)客戶區(qū) } } }
|