转载请说明原出处,谢谢~~: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的更多相关文章

  1. 2013 duilib入门简明教程 -- 自绘控件 (15)

        在[2013 duilib入门简明教程 -- 复杂控件介绍 (13)]中虽然介绍了界面设计器上的所有控件,但是还有一些控件并没有被放到界面设计器上,还有一些常用控件duilib并没有提供(比如 ...

  2. 演练:使用属性自定义 DataGrid 控件

    演练:使用属性自定义 DataGrid 控件 Silverlight   此主题尚未评级 - 评价此主题   Silverlight DataGrid 控件支持常用表格式设置选项,例如交替显示不同的行 ...

  3. WPF 10天修炼 第六天- 系统属性和常用控件

    WPF系统属性和常用控件 渐变的背景色 WPF中的前景色和背景色不同于传统Winform的设置,这些属性都是Brush类型的值.在XAML中,当为这些属性设置指定的颜色后将被转换为SolidColor ...

  4. UWP &WP8.1 依赖属性和用户控件 依赖属性简单使用 uwp添加UserControl

    上面说 附加属性.这章节说依赖属性. 所谓依赖属性.白话讲就是添加一个公开的属性. 同样,依赖属性的用法和附加属性的用法差不多. 依赖属性是具有一个get,set的属性,以及反调函数. 首先是声明依赖 ...

  5. winform控件大小改变是防止背景重绘导致的闪烁(转载)

    在工作中需要做一个伸缩控件,这个自定义控件继承于Panel.这个伸缩控件分为两个部分,头部是一个自定义组件,伸缩控件的背景为灰色,头部背景要求白色.伸缩控件在点击按钮时会重绘,同时他内部的控件也会重绘 ...

  6. .NET Framework的属性类对控件的支持功能

     ToolBoxItem 此属性为类特性.属于工具箱属性,可以设置当前控件是否在工具箱中显示,以及所在工具箱项的类型名称等信息.默认生成的控件都显示在工具箱中. 更多设计时属性介绍: 4.3 属性的 ...

  7. .NET混合开发解决方案8 WinForm程序中通过设置固定版本运行时的BrowserExecutableFolder属性集成WebView2控件

    系列目录     [已更新最新开发文章,点击查看详细] 在我的博客<.NET混合开发解决方案7 WinForm程序中通过NuGet管理器引用集成WebView2控件>中介绍了WinForm ...

  8. VC++ 两种动态调整控件位置的方法(CButton设置为Radio形式会出现错误)

    ((CButton*)GetDlgItem(IDC_CHECK1))->MoveWindow(, cy - , , ); ((CButton*)GetDlgItem(IDC_CHECK2))-& ...

  9. Duilib学习笔记《03》— 控件使用

    在前面已经对duilib有个一个基本的了解,并且创建了简单的空白窗体.这仅仅只是一个开始,如何去创建一个绚丽多彩的界面呢?这就需要一些控件元素(按钮.文本框.列表框等等)来完善. 一. Duilib控 ...

随机推荐

  1. 15 分钟用 ML 破解一个验证码系统

    人人都恨验证码——那些恼人的图片,显示着你在登陆某网站前得输入的文本.设计验证码的目的是,通过验证你是真实的人来避免电脑自动填充表格.但是随着深度学习和计算机视觉的兴起,现在验证码常常易被攻破. 我拜 ...

  2. activemq 持久化

    转自: http://blog.csdn.net/kobejayandy/article/details/50736479 消息持久性的原理很简单,就是在发送者将消息发送出去后,消息中心首先将消息存储 ...

  3. 王者荣耀交流协会scrum立会20171111

    1.立会照片 成员王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐. master:高远博 2.时间跨度: 2017年11月10日 18:00 - 18:33 ,总计33分钟. 3.地 点: 一 ...

  4. 博弈---ZOJ 3057 Beans Game(DP博弈)

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3057 有豆类三个桩.TT和DD挑选任意数量的豆子从任何两堆轮流任何桩或相同 ...

  5. 青岛 2016ICPC 区域现场赛题目

    A. Relic Discovery B. Pocket Cube C. Pocky D. Lucky Coins E. Fibonacci F. Lambda Calculus G. Coding ...

  6. 【Leetcode】322. Coin Change

    You are given coins of different denominations and a total amount of money amount. Write a function ...

  7. 将oracle数据库表使用命令的形式导入到excle文件中 亲测可用!

    main.sql 中的代码 set markup html on entmap ON spool on preformat off spool D:\新建文件夹\mick\tables.xls @ge ...

  8. Linux 下安装 java 环境(jdk + mysql + tomcat)

    Linux选用的是 centOS 6.8 64位 ,最先要将 centOS 中自带的 jdk 和 myqsql 卸载掉. 首先安装 了 SSH,通过 SSH 将 jdk,mysql,tomcat 的压 ...

  9. MVC与MVP简单对比

    在Java平台,基于Spring等技术的MVC框架已经走向成熟:在.NET平台,微软也推出了MVC.MVP Framework,MVP不同于MVC的地方,关键在于,View不再显示的依赖于Busine ...

  10. php 随机密码和盐 来自wordpress

    生成随机密码或盐. Generate keys and salts using secure CSPRNG $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJ ...