DuiLib实现仿微信聊天界面(二)——解决RichEdit自适应高度问题

前言

在上一篇文章中:

我们实现了仿微信的布局和简单的聊天列表:
在这里插入图片描述
但是,因为网易开源 NimDuiLib 的版本落后于其提供的Demo,同样的写法在开源DuiLib中就会出现问题,比如RichEdit在同时设置width、height为auto时,会断错误。所以需要我们自己进行修复。

至于为何要设置RichEdit的width和height属性为auto?因为文本的宽度和高度都是动态的,根据文本的内容需要自行计算。

网易DuiLib中控件如何计算宽高?

我们从网易DuiLib中自带的"doc\duilib属性列表.xml",查看RichEdit是继承自Box,而Box又是继承自Control,Control是顶级控件。由此查看Control.cpp的源码,发现如下代码:

	/**
	 * @brief 计算控件大小
	 * @param[in] szAvailable 暂无意义
	 * @return szAvailable 控件实际大小(如果设置了图片并设置 width 或 height 任意一项为 auto,将根据图片来计算最终大小)
	 */
	virtual CSize EstimateSize(CSize szAvailable);

所以我们可以确定,高度和宽度应该是各个子控件重写了EstimateSize函数动态计算而成

CSize Control::EstimateSize(CSize szAvailable) {
     CSize imageSize = m_cxyFixed;

     if (GetFixedWidth() == DUI_LENGTH_AUTO || GetFixedHeight() == DUI_LENGTH_AUTO) {
         if (!m_bReEstimateSize) {
             return m_szEstimateSize;
         }

         Image* image = GetEstimateImage();

         if (image) {
             auto imageAttribute = image->imageAttribute;

             if (imageAttribute.rcSource.left != DUI_NOSET_VALUE && imageAttribute.rcSource.top != DUI_NOSET_VALUE
                     && imageAttribute.rcSource.right != DUI_NOSET_VALUE && imageAttribute.rcSource.bottom != DUI_NOSET_VALUE) {
                 if ((GetFixedWidth() != imageAttribute.rcSource.right - imageAttribute.rcSource.left)) {
                     SetFixedWidth(imageAttribute.rcSource.right - imageAttribute.rcSource.left);
                 }

                 if ((GetFixedHeight() != imageAttribute.rcSource.bottom - imageAttribute.rcSource.top)) {
                     SetFixedHeight(imageAttribute.rcSource.bottom - imageAttribute.rcSource.top);
                 }

                 return m_cxyFixed;
             }

             GetImage(*image);

             if (image->imageCache) {
                 if (GetFixedWidth() == DUI_LENGTH_AUTO) {
                     int image_width = image->imageCache->nX;
                     DpiManager::GetInstance()->ScaleInt(image_width);
                     imageSize.cx = image_width;
                 }

                 if (GetFixedHeight() == DUI_LENGTH_AUTO) {
                     int image_height = image->imageCache->nY;
                     DpiManager::GetInstance()->ScaleInt(image_height);
                     imageSize.cy = image_height;
                 }
             }
         }

         m_bReEstimateSize = false;
         CSize textSize = EstimateText(szAvailable, m_bReEstimateSize);

         // 宽度为Auto时,自动计算
         if (GetFixedWidth() == DUI_LENGTH_AUTO && imageSize.cx < textSize.cx) {
             imageSize.cx = textSize.cx;
         }

         // 高度为Auto时,自动计算
         if (GetFixedHeight() == DUI_LENGTH_AUTO && imageSize.cy < textSize.cy) {
             imageSize.cy = textSize.cy;
         }

         m_szEstimateSize = imageSize;
     }

     return imageSize;
 }

解决RichEdit无法同时设置宽高为Auto的BUG

思路

  1. 调试 RichEdit::EstimateSize(),打断点。
  2. 发现 m_pTwh->GetTextServices()->TxGetNaturalSize() 函数会根据文本计算高度,其中iWidth非常关键,如果是0或负数,则无法计算文本高度

再看这句代码:

LONG iWidth = size.cx;
LONG iHeight = size.cy;
if (size.cx == DUI_LENGTH_AUTO) {
	ASSERT(size.cy != DUI_LENGTH_AUTO);
	iWidth = 0;
}
else if (size.cy == DUI_LENGTH_AUTO) {
	ASSERT(size.cx != DUI_LENGTH_AUTO);
	iHeight = 0;
}

本身就没有处理iWidth和iiHeight 同时为DUI_LENGTH_AUTO的情况。

修复

因为 TxGetNaturalSize() 需要知道显示区域的宽度,才能计算出文本的高度。故我们用了一个取巧的办法,设置控件的 MaxWidth熟悉 ,这样当文本需要换行的时候,**TxGetNaturalSize()**函数会返回文本的总高度,然后我们更新一下RichEdit的高度即可。

修复后的关键代码如下:

CSize RichEdit::EstimateSize(CSize szAvailable) {
        CSize size(GetFixedWidth(), GetFixedHeight());

        if (size.cx == DUI_LENGTH_AUTO || size.cy == DUI_LENGTH_AUTO) {
            LONG iWidth = size.cx;
            LONG iHeight = size.cy;

            if (size.cx == DUI_LENGTH_AUTO && size.cy == DUI_LENGTH_AUTO) {
                // fixed_by xmcy0011@sina.com 2021-04-22 设置width=auto && height=auto时,RichEdit无法自动计算宽高的问题
                // 此时要设置最大宽度,否则无法计算。
                ASSERT(GetMaxWidth() != 9999999);
                iWidth = GetMaxWidth();

            } else if (size.cx == DUI_LENGTH_AUTO) {
                iWidth = 0;

            } else if (size.cy == DUI_LENGTH_AUTO) {
                iHeight = 0;
            }

            SIZEL szExtent = { -1, -1 };
            m_pTwh->GetTextServices()->TxGetNaturalSize(
                DVASPECT_CONTENT,
                GetWindow()->GetPaintDC(),
                NULL,
                NULL,
                TXTNS_FITTOCONTENT,
                &szExtent,
                &iWidth,
                &iHeight);

            if (size.cx == DUI_LENGTH_AUTO) {
                size.cx = iWidth + m_pLayout->GetPadding().left + m_pLayout->GetPadding().right;
            }

            if (size.cy == DUI_LENGTH_AUTO) {
                size.cy = iHeight + m_pLayout->GetPadding().top + m_pLayout->GetPadding().bottom;
            }
        }

        return size;
    }

效果:
在这里插入图片描述
完整代码在:GitHub

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值