转载请说明原出处,谢谢~~: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. 基于Docker Compose构建的MySQL MHA集群

    Docker MySQL MHA 基于Docker 1.13.1之上构建的MySQL MHA Docker Compose Project 可快速启动GTID模式下的MasterHA集群, 主用于My ...

  2. mariadb BINLOG_FORMAT = STATEMENT 异常

    当在mariadb中插入数据是报 InnoDB is limited to row-logging 异常: java.sql.SQLException: Cannot execute statemen ...

  3. [leetcode-914-X of a Kind in a Deck of Cards]

    In a deck of cards, each card has an integer written on it. Return true if and only if you can choos ...

  4. 《Linux内核与分析》第五周

    20135130王川东 一.给MenuOS增加time和time-asm命令 命令:1.强制删除:rm menu -rf 2.克隆:git clone (后跟需要克隆数据所在的位置) 3.自动编译,自 ...

  5. 2018软工实践—Alpha冲刺(7)

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 学习MSI.CUDA 试运行软件并调试 ...

  6. 如何在windows下Apache环境开启htaccess伪静态功能

     以下文章来自于网络,只做学习用 很多国人习惯用windows服务器或者在windows系统下调试PHP程序,在调试货使用的时候就遇到开启伪静态的各种问题,今天在网络上搜集了一些开启伪静态需要注意 ...

  7. mysql 慢查询,查询缓存,索引,备份,水平分割

    1.开启慢查询 在mysql的配置文件my.ini最后增加如下命令 [mysqld]port=3306slow_query_log =1long_query_time = 1 2.查看慢查询记录 默认 ...

  8. 用Python实现求Fibonacci数列的第n项

    1. 背景——Fabonacci数列的介绍(摘自百度百科): 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacc ...

  9. luogu 1344 追查坏牛奶(最小割)

    第一问求最小割. 第二问求割边最小的最小割. 我们直接求出第二问就可以求出第一问了. 对于求割边最小,如果我们可以把每条边都附加一个1的权值,那么求最小割是不是会优先选择1最少的边呢. 但是如果直接把 ...

  10. python常用模块collections os random sys

    Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句. 模块让你能够有逻辑地组织你的 Python 代码段. 把相关的代码 ...