duilib 修复padding属性导致其他控件自动计算宽高度错误的bug和导致自己宽高度错误的bug
转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42950733
BUG 一:padding导致其他控件宽度计算错误
今天在写项目的一个布局时,用到了最常用的相对布局属性padding:在一个纵向容器里,给其中的各个子元素设置了padding属性来做相对布局。但是出现了很奇怪的现象:容器的最后一个元素本应该在最底部,但是实际却留出了一部分空白。
实际上这个bug早在我写仿酷狗时就遇到了,当时没有很注意,就用了绝对布局去解决了(一般情况下用绝对布局不是好习惯)。现在恍然大悟,原来是个bug。我这里用仿酷狗的布局配合截图说明一下这个bug。
仿酷狗的主界面的最底部(习惯上我称这里成为状态栏),他的布局代码和效果图如下:
<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控件,应该处于整个横向布局的末尾部分。而现在奇怪的是他们距离末尾还有一段距离,如图:
这个bug是由于横向布局的子控件,使用了padding属性,导致倒数第三个占位控件的宽度计算错误造成的。修复bug后的理想状态如下:
BUG 二:padding属性的right和bottom字段导致自身宽度或者高度错误
给某一个控件的padding属性指定了right或者bottom字段后,就会发现这个控件的宽度会自动加上padding.right的值(或者高度自动加上padding.bottom)的值,导致了控件的畸形。而padding的功能仅仅应该是控制控件的位置而不影响控件的宽高度。
BUG修复:
这个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
duilib 修复padding属性导致其他控件自动计算宽高度错误的bug和导致自己宽高度错误的bug的更多相关文章
- 2013 duilib入门简明教程 -- 自绘控件 (15)
在[2013 duilib入门简明教程 -- 复杂控件介绍 (13)]中虽然介绍了界面设计器上的所有控件,但是还有一些控件并没有被放到界面设计器上,还有一些常用控件duilib并没有提供(比如 ...
- 演练:使用属性自定义 DataGrid 控件
演练:使用属性自定义 DataGrid 控件 Silverlight 此主题尚未评级 - 评价此主题 Silverlight DataGrid 控件支持常用表格式设置选项,例如交替显示不同的行 ...
- WPF 10天修炼 第六天- 系统属性和常用控件
WPF系统属性和常用控件 渐变的背景色 WPF中的前景色和背景色不同于传统Winform的设置,这些属性都是Brush类型的值.在XAML中,当为这些属性设置指定的颜色后将被转换为SolidColor ...
- UWP &WP8.1 依赖属性和用户控件 依赖属性简单使用 uwp添加UserControl
上面说 附加属性.这章节说依赖属性. 所谓依赖属性.白话讲就是添加一个公开的属性. 同样,依赖属性的用法和附加属性的用法差不多. 依赖属性是具有一个get,set的属性,以及反调函数. 首先是声明依赖 ...
- winform控件大小改变是防止背景重绘导致的闪烁(转载)
在工作中需要做一个伸缩控件,这个自定义控件继承于Panel.这个伸缩控件分为两个部分,头部是一个自定义组件,伸缩控件的背景为灰色,头部背景要求白色.伸缩控件在点击按钮时会重绘,同时他内部的控件也会重绘 ...
- .NET Framework的属性类对控件的支持功能
ToolBoxItem 此属性为类特性.属于工具箱属性,可以设置当前控件是否在工具箱中显示,以及所在工具箱项的类型名称等信息.默认生成的控件都显示在工具箱中. 更多设计时属性介绍: 4.3 属性的 ...
- .NET混合开发解决方案8 WinForm程序中通过设置固定版本运行时的BrowserExecutableFolder属性集成WebView2控件
系列目录 [已更新最新开发文章,点击查看详细] 在我的博客<.NET混合开发解决方案7 WinForm程序中通过NuGet管理器引用集成WebView2控件>中介绍了WinForm ...
- VC++ 两种动态调整控件位置的方法(CButton设置为Radio形式会出现错误)
((CButton*)GetDlgItem(IDC_CHECK1))->MoveWindow(, cy - , , ); ((CButton*)GetDlgItem(IDC_CHECK2))-& ...
- Duilib学习笔记《03》— 控件使用
在前面已经对duilib有个一个基本的了解,并且创建了简单的空白窗体.这仅仅只是一个开始,如何去创建一个绚丽多彩的界面呢?这就需要一些控件元素(按钮.文本框.列表框等等)来完善. 一. Duilib控 ...
随机推荐
- 简单主机批量管理工具(这里实现了paramiko 用su切换到root用户)
项目名:简单主机批量管理工具 一.需求 1.主机分组 2.可批量执行命令.发送文件,结果实时返回,执行格式如下 batch_run -h h1,h2,h3 -g web_clusters,db_ ...
- 如何让QT程序以管理员权限运行(UAC)
方案一:(仅适用于使用msvc编译器) 在PRO文件中添加一行指令即可, QMAKE_LFLAGS += /MANIFESTUAC:"level='requireAdministrator' ...
- 兰亭集势收购美国社交购物网站Ador,收购的是人才
1 月 6 日消息,外贸电商公司兰亭集势(LightInTheBox)今日宣布,已经完成对美国社交电商网站 Ador 公司的收购.Ador 公司总部位于西雅图.这项资产收购通过现金完成,但未披露交易金 ...
- __autoload 与spl_autoload_register()
PHP __autoload函数(自动载入类文件)的使用方法 作者: 字体:[增加 减小] 类型:转载 时间:2012-02-04 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都 ...
- Thunder团队第七周 - Scrum会议2
Scrum会议2 小组名称:Thunder 项目名称:i阅app Scrum Master:王航 工作照片: 参会成员: 王航(Master):http://www.cnblogs.com/wangh ...
- Mysql中``和‘’的区别
两者在linux下和windows下不同,linux下不区分,windows下区分. 在windows下主要区别就是 单引号( ' )或双引号主要用于 字符串的引用符号 如: mysql> SE ...
- saltstack进阶
查看minion端的文件内容 [root@linux-node2 ~]# cat /etc/resolv.conf # Generated by NetworkManager nameserver 1 ...
- Spring boot整合shiro框架
ShiroConfiguration package com.energy.common.config; import java.util.LinkedHashMap; import java.uti ...
- h5端提示下载app
// app下载提示 if (!sessionStorage.getItem("appDownloadTipClosed") && isAndroidOrIphon ...
- CF464C-Substitutes in Number
题意 开始给出一个长为\(n\)的数字串,有\(m\)次操作按顺序执行,每次把当前数字串中的某一个数码替换成一个数字串\(t\)(可以为空或多位),最后问操作结束后的数字串十进制下模\(10^9+7\ ...