duilib List控件,横向滚动时列表项不移动或者显示错位的bug的修复-阿里云开发者社区

开发者社区> 云原生> 正文

duilib List控件,横向滚动时列表项不移动或者显示错位的bug的修复

简介: 转载请说明出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42264673         关于这个bug的修复我之前写过一篇博客,连接为:http://blog.csdn.net/zhuhongshu/article/details/40622705         本以为已经修复好,但是后来有网友私聊我,反映到还存在bug。

转载请说明出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42264673


        关于这个bug的修复我之前写过一篇博客,连接为:http://blog.csdn.net/zhuhongshu/article/details/40622705


        本以为已经修复好,但是后来有网友私聊我,反映到还存在bug。原本已经解决的bug如下:

        1.当List出现滚动条后,拖动滚动条,这时ListContainElementUI控件内部的子控件跟随Header自动调节位置会有差错,明显看到偏移不正确

        2.当List出现滚动条后,拖动滚动条到任意位置让ListContainElementUI控件内部的子控件位移,然后最小化窗体再恢复。发现本应该发生偏移的子控件这时却跑到了原位。


        新的bug是:当List出现滚动条后,拖动滚动条到任意位置让ListContainElementUI控件内部的子控件位移,然后最大化窗体,就发现列表项的偏移不正确,再次恢复后的列表项位置也有问题。


        尝试从ListContainElementUI控件的SetPos函数解决问题,发现总是无法十全十美,没办法既顾及到最小化又顾及最大化,同时还要计算偏移。尝试修复几个小时都不成功。


        回头想想,前面的三个问题的根源都在于,List容器先重新计算列表项的位置,再去计算表头的位置。而列表项要根据表头的位置去自适应。之前的做法一直是在列表项调整位置时去尝试计算表头的位置,而这样做的致命缺点就是,当最小化和最大化后表头的位置会有突然的变化而知道计算失误。


       换个思路,只要让列表先计算表头的位置,再去计算列表项的位置,自然就没问题了。······,感觉以前的思路好傻。


       通过这个思路可以简单有效的修复所有bug,之前的文章的修复过程全部作废,bug的修复过程如下:


修复过程:


       一、List控件的SetPos函数源码如下:


void CListUI::SetPos(RECT rc)
{
	CVerticalLayoutUI::SetPos(rc);

    if( m_pHeader == NULL ) return;
    // Determine general list information and the size of header columns
    m_ListInfo.nColumns = MIN(m_pHeader->GetCount(), UILIST_MAX_COLUMNS);
    // The header/columns may or may not be visible at runtime. In either case
    // we should determine the correct dimensions...

    if( !m_pHeader->IsVisible() ) {
        for( int it = 0; it < m_pHeader->GetCount(); it++ ) {
            static_cast<CControlUI*>(m_pHeader->GetItemAt(it))->SetInternVisible(true);
        }
        m_pHeader->SetPos(CDuiRect(rc.left, 0, rc.right, 0));
    }
    int iOffset = m_pList->GetScrollPos().cx;
    for( int i = 0; i < m_ListInfo.nColumns; i++ ) {
        CControlUI* pControl = static_cast<CControlUI*>(m_pHeader->GetItemAt(i));
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) continue;

        RECT rcPos = pControl->GetPos();
        if( iOffset > 0 ) {
            rcPos.left -= iOffset;
            rcPos.right -= iOffset;
            pControl->SetPos(rcPos);
        }
        m_ListInfo.rcColumn[i] = pControl->GetPos();
    }
    if( !m_pHeader->IsVisible() ) {
        for( int it = 0; it < m_pHeader->GetCount(); it++ ) {
            static_cast<CControlUI*>(m_pHeader->GetItemAt(it))->SetInternVisible(false);
        }
    }

}

       代码里首先调用父类的SetPos函数,在父类的SetPos里面会初始化表头和列表项的位置,然后可以看到List的SetPos根据滚动条的位置重新调整了表头的位置。


       首先要在函数的最后加入这句代码来重新计算列表项的位置:


	m_pList->SetPos(m_pList->GetPos());

       二、当横向滚动条控件移动时,会通知父控件已经移动来让父控件做出响应。也就是列表滚动条控件会调用List的SetScrollPos函数,而List控件的SetScrollPos函数会调用CListBodyUI控件的SetScrollPos函数,CListBodyUI控件是所有列表项的容器。在CListBodyUI控件的SetScrollPos函数里,先计算了列表项的位置,后计算表头的位置。所以修改对应代码的位置就可以了,修改后的代码如下:


void CListBodyUI::SetScrollPos(SIZE szPos)
{
    int cx = 0;
    int cy = 0;
    if( m_pVerticalScrollBar && m_pVerticalScrollBar->IsVisible() ) {
        int iLastScrollPos = m_pVerticalScrollBar->GetScrollPos();
        m_pVerticalScrollBar->SetScrollPos(szPos.cy);
        cy = m_pVerticalScrollBar->GetScrollPos() - iLastScrollPos;
    }

    if( m_pHorizontalScrollBar && m_pHorizontalScrollBar->IsVisible() ) {
        int iLastScrollPos = m_pHorizontalScrollBar->GetScrollPos();
        m_pHorizontalScrollBar->SetScrollPos(szPos.cx);
        cx = m_pHorizontalScrollBar->GetScrollPos() - iLastScrollPos;
    }

    if( cx == 0 && cy == 0 ) return;

	if( cx != 0 && m_pOwner ) {
		CListHeaderUI* pHeader = m_pOwner->GetHeader();
		if( pHeader == NULL ) return;
		TListInfoUI* pInfo = m_pOwner->GetListInfo();
		pInfo->nColumns = MIN(pHeader->GetCount(), UILIST_MAX_COLUMNS);

		if( !pHeader->IsVisible() ) {
			for( int it = 0; it < pHeader->GetCount(); it++ ) {
				static_cast<CControlUI*>(pHeader->GetItemAt(it))->SetInternVisible(true);
			}
		}
		for( int i = 0; i < pInfo->nColumns; i++ ) {
			CControlUI* pControl = static_cast<CControlUI*>(pHeader->GetItemAt(i));
			if( !pControl->IsVisible() ) continue;
			if( pControl->IsFloat() ) continue;

			RECT rcPos = pControl->GetPos();
			rcPos.left -= cx;
			rcPos.right -= cx;
			pControl->SetPos(rcPos);
			pInfo->rcColumn[i] = pControl->GetPos();
		}
		if( !pHeader->IsVisible() ) {
			for( int it = 0; it < pHeader->GetCount(); it++ ) {
				static_cast<CControlUI*>(pHeader->GetItemAt(it))->SetInternVisible(false);
			}
		}
	}

    RECT rcPos;
    for( int it2 = 0; it2 < m_items.GetSize(); it2++ ) {
        CControlUI* pControl = static_cast<CControlUI*>(m_items[it2]);
        if( !pControl->IsVisible() ) continue;
        if( pControl->IsFloat() ) continue;

        rcPos = pControl->GetPos();
        rcPos.left -= cx;
        rcPos.right -= cx;
        rcPos.top -= cy;
        rcPos.bottom -= cy;
        pControl->SetPos(rcPos);
    }

    Invalidate();


}


       三、当表头的位置计算好后,最后修改ListContainElementUI的SetPos函数去计算他的子控件的位置,代码比以前的版本简单了很多:


void CListContainerElementUI::SetPos(RECT rc)
{	
	CHorizontalLayoutUI::SetPos(rc);
	if( m_pOwner == NULL ) return;		

	CListUI* pList = static_cast<CListUI*>(m_pOwner);
	if (pList == NULL) return;

	CListHeaderUI *pHeader = pList->GetHeader();
	if (pHeader == NULL || !pHeader->IsVisible())
		return;
	
	int nCount = m_items.GetSize();
	for (int i = 0; i < nCount; i++)
	{
		CControlUI *pListItem = static_cast<CControlUI*>(m_items[i]);

		CControlUI *pHeaderItem = pHeader->GetItemAt(i);
		if (pHeaderItem == NULL)
			return;

		RECT rcHeaderItem = pHeaderItem->GetPos();
		if (pListItem != NULL && !(rcHeaderItem.left ==0 && rcHeaderItem.right ==0) )
		{
			RECT rt = pListItem->GetPos();
			rt.left =rcHeaderItem.left;
			rt.right = rcHeaderItem.right;
			pListItem->SetPos(rt);
		}

	}

}


总结:


      有这个修复代码后,以前的文章的修复代码就作废了。

      如果要修改ListContainElementUI的朋友可以根据我上面给出的代码修改,也可以直接下载我自己的Duilib库。

      我的Duilib库代码下载地址:点击打开链接



  Redrain  2014.12.30


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
云原生
使用钉钉扫一扫加入圈子
+ 订阅

云原生时代,是开发者最好的时代

其他文章