轉載請說明原出處,謝謝~~:http://blog.csdn.net/zhuhongshu/article/details/42950733html
BUG 一:padding致使其餘控件寬度計算錯誤函數
今天在寫項目的一個佈局時,用到了最經常使用的相對佈局屬性padding:在一個縱向容器裏,給其中的各個子元素設置了padding屬性來作相對佈局。可是出現了很奇怪的現象:容器的最後一個元素本應該在最底部,可是實際卻留出了一部分空白。佈局
實際上這個bug早在我寫仿酷狗時就遇到了,當時沒有很注意,就用了絕對佈局去解決了(通常狀況下用絕對佈局不是好習慣)。如今恍然大悟,原來是個bug。我這裏用仿酷狗的佈局配合截圖說明一下這個bug。ui
仿酷狗的主界面的最底部(習慣上我稱這裏成爲狀態欄),他的佈局代碼和效果圖以下:spa
<HorizontalLayout name="Main_Status" height="30" inset="77,0,0,0" bkimage="UI\statusbar\status_bk.png"><!-- 狀態欄 --> <Button name="btn_add_music" width="31" height="30" padding="0,0,0,0" normalimage="UI\statusbar\add_normal.png" hotimage="UI\statusbar\add_hover.png" pushedimage="UI\statusbar\add_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UI\statusbar\locate_normal.png" hotimage="UI\statusbar\locate_hover.png" pushedimage="UI\statusbar\locate_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UI\statusbar\search_normal.png" hotimage="UI\statusbar\search_hover.png" pushedimage="UI\statusbar\search_down.png" /> <Button width="31" height="30" padding="40,0,0,0" normalimage="UI\statusbar\setting_normal.png" hotimage="UI\statusbar\setting_hover.png" pushedimage="UI\statusbar\setting_down.png" name="MusicItem" menu="true" /> <Control /><!-- 佔位 --> <Label name="lbl_Main_Bottom_Info" text="Redrain仿酷狗音樂盒^_^~~ QQ:491646717 2014.9.9" textpadding="0,2,0,0" width="30" font="0" /> <Control width="7" height="7" padding="40,19,0,0" bkimage="UI\sizebox.png" /> </HorizontalLayout>
能夠看到,若是按照正常佈局來說,由於有倒數第三個Control的佔位效果,因此倒數第二的Label和倒數第一的Control控件,應該處於整個橫向佈局的末尾部分。而如今奇怪的是他們距離末尾還有一段距離,如圖:.net
這個bug是因爲橫向佈局的子控件,使用了padding屬性,致使倒數第三個佔位控件的寬度計算錯誤形成的。修復bug後的理想狀態以下:code
BUG 二:padding屬性的right和bottom字段致使自身寬度或者高度錯誤orm
給某一個控件的padding屬性指定了right或者bottom字段後,就會發現這個控件的寬度會自動加上padding.right的值(或者高度自動加上padding.bottom)的值,致使了控件的畸形。而padding的功能僅僅應該是控制控件的位置而不影響控件的寬高度。htm
BUG修復:blog
這個bug是因爲橫向佈局和縱向佈局在計算子控件的位置時的疏漏致使的,也就是SetPos函數的bug。如今拿橫向佈局分析一下bug產生的緣由,我在代碼中稍微作了一下注釋,方便理解。
void CHorizontalLayoutUI::SetPos(RECT rc) { CControlUI::SetPos(rc); rc = m_rcItem; rc.left += m_rcInset.left; rc.top += m_rcInset.top; rc.right -= m_rcInset.right; rc.bottom -= m_rcInset.bottom; if( m_items.GetSize() == 0) { ProcessScrollBar(rc, 0, 0); return; } if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) rc.right -= m_pVerticalScrollBar->GetFixedWidth(); if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) rc.bottom -= m_pHorizontalScrollBar->GetFixedHeight(); SIZE szAvailable = { rc.right - rc.left, rc.bottom - rc.top }; if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) szAvailable.cx += m_pHorizontalScrollBar->GetScrollRange(); int nAdjustables = 0; int cxFixed = 0; int nEstimateNum = 0; // redrain 第一輪計算獲得各類信息,不作實際佈局處理 for( int it1 = 0; it1 < m_items.GetSize(); it1++ ) { CControlUI* pControl = static_cast<CControlUI*>(m_items[it1]); if( !pControl->IsVisible() ) continue; if( pControl->IsFloat() ) continue; SIZE sz = pControl->EstimateSize(szAvailable); if( sz.cx == 0 ) { // redrain 記錄須要自動計算寬度的子控件的數量 nAdjustables++; } else { if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); } cxFixed += sz.cx + pControl->GetPadding().left + pControl->GetPadding().right; // redrain 記錄須要作相對佈局的子控件的數量 nEstimateNum++; } // redrain cxFixed保存了全部相對佈局的控件佔用的寬度(包括了padding屬性好childpadding屬性佔用的寬度) cxFixed += (nEstimateNum - 1) * m_iChildPadding; int cxExpand = 0; int cxNeeded = 0; // redrain cxExpand保存須要自動計算寬度的子控件的寬度 if( nAdjustables > 0 ) cxExpand = MAX(0, (szAvailable.cx - cxFixed) / nAdjustables); // redrain szRemaining保存除已被佈局的子控件之外的剩餘空間 SIZE szRemaining = szAvailable; int iPosX = rc.left; if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) { iPosX -= m_pHorizontalScrollBar->GetScrollPos(); } int iAdjustable = 0; // redrain cxFixedRemaining記錄當前還未被佈局過的全部子控件的總寬度 int cxFixedRemaining = cxFixed; for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]); if( !pControl->IsVisible() ) continue; if( pControl->IsFloat() ) { SetFloatPos(it2); continue; } RECT rcPadding = pControl->GetPadding(); szRemaining.cx -= rcPadding.left; SIZE sz = pControl->EstimateSize(szRemaining); if( sz.cx == 0 ) { iAdjustable++; sz.cx = cxExpand; // redrain 這裏判斷若是是最後一個須要自動計算寬度的元素則作出不一樣的處理 if( iAdjustable == nAdjustables ) { sz.cx = MAX(0, szRemaining.cx - rcPadding.right - cxFixedRemaining); } if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); } else { if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); // redrain bug出如今這裏,cxFixedRemaining只減去了被佈局的控件的寬度,而沒有減去padding屬性和childpadding屬性佔據的寬度 cxFixedRemaining -= sz.cx; } sz.cy = pControl->GetFixedHeight(); if( sz.cy == 0 ) sz.cy = rc.bottom - rc.top - rcPadding.top - rcPadding.bottom; if( sz.cy < 0 ) sz.cy = 0; if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight(); if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight(); // redrain bug2,對寬度的錯誤計算,不該該加上rcPadding.right的值 RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left + rcPadding.right, rc.top + rcPadding.top + sz.cy}; pControl->SetPos(rcCtrl); iPosX += sz.cx + m_iChildPadding + rcPadding.left + rcPadding.right; cxNeeded += sz.cx + rcPadding.left + rcPadding.right; szRemaining.cx -= sz.cx + m_iChildPadding + rcPadding.right; } cxNeeded += (nEstimateNum - 1) * m_iChildPadding; // Process the scrollbar ProcessScrollBar(rc, cxNeeded, 0); }
我以及把關鍵的變量和代碼都作了註釋,讀完代碼後很清楚的看到,最後一個子控件的位置和cxFixedRemaining變量密切相關,而cxFixedRemaining變量的計算錯誤致使了bug1。因此應該把出現bug1的代碼改成以下代碼:
for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) { // 省略 else { if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth(); if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth(); cxFixedRemaining -= sz.cx + rcPadding.left + rcPadding.right ; } cxFixedRemaining -= m_iChildPadding; // 省略
出現bug2的代碼修改成:
RECT rcCtrl = { iPosX + rcPadding.left, rc.top + rcPadding.top, iPosX + sz.cx + rcPadding.left , rc.top + rcPadding.top + sz.cy};
縱向佈局的bug代碼與這個相似,我就再也不分析了。直接給出修復代碼:
cyFixedRemaining -= sz.cy + rcPadding.top + rcPadding.bottom; } cyFixedRemaining -= m_iChildPadding;
RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top };
總結:
duilib存在不少細節上的bug,在使用過程當中經常能發覺出存在bug。這個bug的修復也已經同步到個人庫裏了。下載地址:點擊打開連接
Redrain 2015.1.21