转自博客:http://blog.csdn.net/damingg/article/details/41149037

首先看一段xml代码

[html] view
plain
copy

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Window size="300,200" caption="0,0,300,20">
  3. <HorizontalLayout name="aaaa" bkcolor="#FFEEEEEE" >
  4. <VerticalLayout name="xxxxxx" float="true" pos="50,20,250,195" hscrollbar="true" vscrollbar="true" sepheight="4" >
  5. <Label name="child1" text="text1" float="false" pos="0,0,300,200" bordercolor="#FFEE00EE" bkcolor="#FF0000EE" textcolor="#FF010101"/>
  6. <Label name="child2" text="text2" float="false" pos="0,0,200,200" bordercolor="#FFEE00EE" bkcolor="#FF0000EE" textcolor="#FF01FF01"/>
  7. </VerticalLayout>
  8. </HorizontalLayout>
  9. </Window>

这是一个窗口,它包含一个竖的布局,显示出来初始状态是这样的:

在布局中,2个子控件所需要的长和宽,比布局本身的大小要大,所以需要2个滚动条来拉动显示。

我们可以看到横竖两个滚动条。

不过,我们拉动滚动条,却不能完全展示子控件。如下图:

虽然横向滚动条拉到了最右边,但Label控件child1的右边没有展示出来。

检查代码,发现CVerticalLayoutUI的SetPos方法如下:

  1. void CVerticalLayoutUI::SetPos(RECT rc)
  2. {
  3. ...省略若干代码
  4. // Process the scrollbar
  5. ProcessScrollBar(rc, 0, cyNeeded);
  6. }

在最后面,调用了基类CContainerUI的ProcessScrollBar方法,来设置滚动条信息,ProcessScrollBar函数的第二个参数是设置横向滚动条信息,第三个参数是竖向滚动条。此时设置横向的参数为0,竖向的是cyNeeded。显然,这里忽略了横向的,所以在竖向布局VerticalLayout中,横向滚动条不能正常显示。

在CVerticalLayoutUI::SetPos中,所有涉及到cyNeeded的代码如下:

  1. int cyNeeded = 0;
  2. int cyExpand = 0;
  3. if( nAdjustables > 0 ) cyExpand = MAX(0, (szAvailable.cy - cyFixed) / nAdjustables);
  4. // Position the elements
  5. SIZE szRemaining = szAvailable;
  6. int iPosY = rc.top;
  7. if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
  8. iPosY -= m_pVerticalScrollBar->GetScrollPos();
  9. }
  10. int iPosX = rc.left;
  11. if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
  12. iPosX -= m_pHorizontalScrollBar->GetScrollPos();
  13. }
  14. int iAdjustable = 0;
  15. int cyFixedRemaining = cyFixed;
  16. for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
  17. CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
  18. if( !pControl->IsVisible() ) continue;
  19. if( pControl->IsFloat() ) {
  20. SetFloatPos(it2);
  21. continue;
  22. }
  23. RECT rcPadding = pControl->GetPadding();
  24. szRemaining.cy -= rcPadding.top;
  25. SIZE sz = pControl->EstimateSize(szRemaining);
  26. if( sz.cy == 0 ) {
  27. iAdjustable++;
  28. sz.cy = cyExpand;
  29. // Distribute remaining to last element (usually round-off left-overs)
  30. if( iAdjustable == nAdjustables ) {
  31. sz.cy = MAX(0, szRemaining.cy - rcPadding.bottom - cyFixedRemaining);
  32. }
  33. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  34. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  35. }
  36. else {
  37. if( sz.cy < pControl->GetMinHeight() ) sz.cy = pControl->GetMinHeight();
  38. if( sz.cy > pControl->GetMaxHeight() ) sz.cy = pControl->GetMaxHeight();
  39. cyFixedRemaining -= sz.cy;
  40. }
  41. sz.cx = pControl->GetFixedWidth();
  42. if( sz.cx == 0 ) sz.cx = szAvailable.cx - rcPadding.left - rcPadding.right;
  43. if( sz.cx < 0 ) sz.cx = 0;
  44. if( sz.cx < pControl->GetMinWidth() ) sz.cx = pControl->GetMinWidth();
  45. if( sz.cx > pControl->GetMaxWidth() ) sz.cx = pControl->GetMaxWidth();
  46. RECT rcCtrl = { iPosX + rcPadding.left, iPosY + rcPadding.top, iPosX + rcPadding.left + sz.cx, iPosY + sz.cy + rcPadding.top + rcPadding.bottom };
  47. pControl->SetPos(rcCtrl);
  48. iPosY += sz.cy + m_iChildPadding + rcPadding.top + rcPadding.bottom;
  49. cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;
  50. szRemaining.cy -= sz.cy + m_iChildPadding + rcPadding.bottom;
  51. }
  52. cyNeeded += (nEstimateNum - 1) * m_iChildPadding;

在for循环里,会统计所有非float子控件的高度

在  cyNeeded += sz.cy + rcPadding.top + rcPadding.bottom;  里,sz.cy是1个子控件的高度,而rcPadding = pControl->GetPadding(); 是控件的padding属性,也就是外边距。

最后yNeeded += (nEstimateNum - 1) * m_iChildPadding;  m_iChildPadding是布局的childpadding属性,也就是子控件之间的额外距离。

我们可以仿照cyNeeded的计算方式,来算出cxNeeded。因为是竖向布局,所以 cyNeeded += sz.cy 这里是累加,但是横向值,应该取最宽的控件的值,

所以for循环中的代码应该是这样:

int tmp = sz.cx + rcPadding.left + rcPadding.right;

cxNeeded = (tmp > cxNeeded) ? tmp: cxNeeded;

最后cxNeeded += (nEstimateNum - 1) * m_iChildPadding;

此时通过对代码的了解,可以得知:布局在计算容量面积时,只计算非float类型的子控件,忽略了float类型的子控件。

算出了布局的宽度后,就应用起来

修改原来的代码为:ProcessScrollBar(rc, cxNeeded, cyNeeded);

改完后,发现问题依然。于是,检查了下ProcessScrollBar的代码,如下

  1. void CContainerUI::ProcessScrollBar(RECT rc, int cxRequired, int cyRequired)
  2. {
  3. if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
  4. RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()};
  5. m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
  6. }
  7. if( m_pVerticalScrollBar == NULL ) return;
  8. if( cyRequired > rc.bottom - rc.top && !m_pVerticalScrollBar->IsVisible() ) {
  9. m_pVerticalScrollBar->SetVisible(true);
  10. m_pVerticalScrollBar->SetScrollRange(cyRequired - (rc.bottom - rc.top));
  11. m_pVerticalScrollBar->SetScrollPos(0);
  12. m_bScrollProcess = true;
  13. SetPos(m_rcItem);
  14. m_bScrollProcess = false;
  15. return;
  16. }
  17. // No scrollbar required
  18. if( !m_pVerticalScrollBar->IsVisible() ) return;
  19. // Scroll not needed anymore?
  20. int cyScroll = cyRequired - (rc.bottom - rc.top);
  21. if( cyScroll <= 0 && !m_bScrollProcess) {
  22. m_pVerticalScrollBar->SetVisible(false);
  23. m_pVerticalScrollBar->SetScrollPos(0);
  24. m_pVerticalScrollBar->SetScrollRange(0);
  25. SetPos(m_rcItem);
  26. }
  27. else
  28. {
  29. RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom };
  30. m_pVerticalScrollBar->SetPos(rcScrollBarPos);
  31. if( m_pVerticalScrollBar->GetScrollRange() != cyScroll ) {
  32. int iScrollPos = m_pVerticalScrollBar->GetScrollPos();
  33. m_pVerticalScrollBar->SetScrollRange(::abs(cyScroll));
  34. if( m_pVerticalScrollBar->GetScrollRange() == 0 ) {
  35. m_pVerticalScrollBar->SetVisible(false);
  36. m_pVerticalScrollBar->SetScrollPos(0);
  37. }
  38. if( iScrollPos > m_pVerticalScrollBar->GetScrollPos() ) {
  39. SetPos(m_rcItem);
  40. }
  41. }
  42. }
  43. }

很明显,cxRequired根本没有用到。

对于横向滚动条,只使用了这3行代码:

  1. if( m_pHorizontalScrollBar != NULL && m_pHorizontalScrollBar->IsVisible() ) {
  2. RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight()};
  3. m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
  4. }

于是,我们需要仿照设置竖向滚动条的方式,来设置横向滚动条:

  1. void CContainerUI::ProcessScrollBar(RECT rc, int cxRequired, int cyRequired)
  2. {
  3. while(m_pHorizontalScrollBar)
  4. {
  5. if (cxRequired > rc.right - rc.left && !m_pHorizontalScrollBar->IsVisible())
  6. {
  7. m_pHorizontalScrollBar->SetVisible(true);
  8. m_pHorizontalScrollBar->SetScrollRange(cxRequired - (rc.right - rc.left));
  9. m_pHorizontalScrollBar->SetScrollPos(0);
  10. m_bScrollProcess = true;
  11. SetPos(m_rcItem);
  12. m_bScrollProcess = false;
  13. break;
  14. }
  15. if( !m_pHorizontalScrollBar->IsVisible() ) break;
  16. int cxScroll = cxRequired - (rc.right - rc.left);
  17. if (cxScroll <= 0 && !m_bScrollProcess)
  18. {
  19. m_pHorizontalScrollBar->SetVisible(false);
  20. m_pHorizontalScrollBar->SetScrollPos(0);
  21. m_pHorizontalScrollBar->SetScrollRange(0);
  22. SetPos(m_rcItem);
  23. }
  24. else
  25. {
  26. RECT rcScrollBarPos = { rc.left, rc.bottom, rc.right, rc.bottom + m_pHorizontalScrollBar->GetFixedHeight() };
  27. m_pHorizontalScrollBar->SetPos(rcScrollBarPos);
  28. if( m_pHorizontalScrollBar->GetScrollRange() != cxScroll ) {
  29. int iScrollPos = m_pHorizontalScrollBar->GetScrollPos();
  30. m_pHorizontalScrollBar->SetScrollRange(::abs(cxScroll));
  31. if( m_pHorizontalScrollBar->GetScrollRange() == 0 ) {
  32. m_pHorizontalScrollBar->SetVisible(false);
  33. m_pHorizontalScrollBar->SetScrollPos(0);
  34. }
  35. if( iScrollPos > m_pHorizontalScrollBar->GetScrollPos() ) {
  36. SetPos(m_rcItem);
  37. }
  38. }
  39. }
  40. break;
  41. }
  42. while(m_pVerticalScrollBar)
  43. {
  44. if( cyRequired > rc.bottom - rc.top && !m_pVerticalScrollBar->IsVisible() ) {
  45. m_pVerticalScrollBar->SetVisible(true);
  46. m_pVerticalScrollBar->SetScrollRange(cyRequired - (rc.bottom - rc.top));
  47. m_pVerticalScrollBar->SetScrollPos(0);
  48. m_bScrollProcess = true;
  49. SetPos(m_rcItem);
  50. m_bScrollProcess = false;
  51. break;
  52. }
  53. // No scrollbar required
  54. if( !m_pVerticalScrollBar->IsVisible() ) break;
  55. // Scroll not needed anymore?
  56. int cyScroll = cyRequired - (rc.bottom - rc.top);
  57. if( cyScroll <= 0 && !m_bScrollProcess) {
  58. m_pVerticalScrollBar->SetVisible(false);
  59. m_pVerticalScrollBar->SetScrollPos(0);
  60. m_pVerticalScrollBar->SetScrollRange(0);
  61. SetPos(m_rcItem);
  62. }
  63. else
  64. {
  65. RECT rcScrollBarPos = { rc.right, rc.top, rc.right + m_pVerticalScrollBar->GetFixedWidth(), rc.bottom };
  66. m_pVerticalScrollBar->SetPos(rcScrollBarPos);
  67. if( m_pVerticalScrollBar->GetScrollRange() != cyScroll ) {
  68. int iScrollPos = m_pVerticalScrollBar->GetScrollPos();
  69. m_pVerticalScrollBar->SetScrollRange(::abs(cyScroll));
  70. if( m_pVerticalScrollBar->GetScrollRange() == 0 ) {
  71. m_pVerticalScrollBar->SetVisible(false);
  72. m_pVerticalScrollBar->SetScrollPos(0);
  73. }
  74. if( iScrollPos > m_pVerticalScrollBar->GetScrollPos() ) {
  75. SetPos(m_rcItem);
  76. }
  77. }
  78. }
  79. break;
  80. }
  81. }

看看效果

可以看到控件child1的右边框。

HorizontalLayout也有同样的问题,可类似修改。

duilib中的V和H布局中滚动条问题的更多相关文章

  1. ios中fixed元素在滚动布局中的延时渲染问题

    在之前做的一个demo中,有个视图是内滚动的,里边有个bar用了fixed,不是fixed在最外层视图的顶部和底部,在微信/safari/chrome/其他浏览器app上都没出现问题. 然后今天,我把 ...

  2. 【转】在Android布局中使用include和merge标签

    内容转自:http://fengweipeng1208.blog.163.com/blog/static/21277318020138229754135/ 在我们开发android布局时,经常会有很多 ...

  3. 在网页布局中合理使用inline formating context(IFC)

    引子:给大家出一个小小的考题,如何使用css来实现类似下面的在指定区域内,内容自适应的垂直居中.

  4. 使用ViewSwitcher和ViewFlipper在不同布局中切换

    xml布局: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:androi ...

  5. 摘:C/C++中时间类time.h

    摘要:本文从介绍基础概念入手,探讨了在C/C++中对日期和时间操作所用到的数据结构和函数,并对计时.时间的获取.时间的计算和显示格式等方面进行了阐述.本文还通过大量的实例向你展示了time.h头文件中 ...

  6. vivo前端智能化实践:机器学习在自动网页布局中的应用

    作者:vivo 互联网前端团队- Su Ning 在设计稿转网页中运用基于self-attention机制设计的机器学习模型进行设计稿的布局,能够结合dom节点的上下文得出合理的方案. 一.背景 切图 ...

  7. 在布局中使用android.support.v4.app.Fragment的注意事项

    1.Activity必须继承android.support.v4.app.FragmentActivity 2.fragment标签的name属性必须是完全限定包名,如下: <LinearLay ...

  8. DIV+CSS布局中主要CSS属性介绍

    Float: Float属性是DIV+CSS布局中最基本也是最常用的属性,用于实现多列功能,我们知道<div>标签默认一行只能显示一个,而使用Float属性可以实现一行显示多个div的功能 ...

  9. 响应式web布局中iframe的自适应

    困境 在响应式布局中,我们应该小心对待iframe元素,iframe元素的width和height属性设置了其宽度和高度,但是当包含块的宽度或高度小于iframe的宽度或高度时,会出现iframe元素 ...

随机推荐

  1. C# Regex类用法

    使用Regex类需要引用命名空间:using System.Text.RegularExpressions; 利用Regex类实现全部匹配输出 string str = "test43232 ...

  2. hadoop集群基本配置

    最近在学习hadoop.网上具体过程很多,我就说说简单过程和注意问题. 环境:宿主机(windows64),虚拟机(centos64). 准备软件: 1.Vmware——虚拟机 2.centos镜像文 ...

  3. Android 虚线分割Shape

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  4. pinyin4j

    最近在倒腾与搜索相关的拼音检查技术,顺便看了一下中文转拼音开源插件pinyin4j的源码,参考资料:http://blog.csdn.net/hfhwfw/archive/2010/11/23/603 ...

  5. eclipse运行emulator时,PANIC:Could not open emulator的解决办法

    使用eclipse启动emulator的时候,出现PANIC:Could not open emulator,模拟器无法正常的运行. 经过搜索得知,因为我的SDK的环境变量出问题,需要重新配置下环境变 ...

  6. C++ STL之排序算法

    排序算法和查找算法差不多,也涉及到迭代器区间问题,关于该问题的注意事项就不在啰嗦了 一.全部排序sort.stable_sort sort是一种不稳定排序,使用时需要包含头文件algorithm 默认 ...

  7. Effective C++学习笔记 条款06:如不想使用编译器自动生成的函数,就该明确拒绝

    一.为驳回编译器自动提供的机能,可将相应成员函数声明为private并且不予实现.(如果你仅仅是自己不实现的话,编译器会帮你实现) 如: class A { public: A(const strin ...

  8. topcoder srm 628 div2 250 500

    做了一道题,对了,但是还是掉分了. 第二道题也做了,但是没有交上,不知道对错. 后来交上以后发现少判断了一个条件,改过之后就对了. 第一道题爆搜的,有点麻烦了,其实几行代码就行. 250贴代码: #i ...

  9. poj 1195 Mobile phones(二维树状数组)

    树状数组支持两种操作: Add(x, d)操作:   让a[x]增加d. Query(L,R): 计算 a[L]+a[L+1]……a[R]. 当要频繁的对数组元素进行修改,同时又要频繁的查询数组内任一 ...

  10. JAVA中获取工程路径的方法

    在jsp和class文件中调用的相对路径不同.在jsp里,根目录是WebRoot 在class文件中,根目录是WebRoot/WEB-INF/classes 当然你也可以用System.getProp ...