DuiLib VirtualListUI 虚拟列表

思路:

修改ListBody ,重写SetPos, SetScrollPos, ProcessScrollBar, 利用 SetScrollPos 滚动条位置动态从数据接口获取数据并加载到列表, 重新设置位置;  需要增加一个数据提供者纯虚类IVirtualDataProvider;  关键点就是SetPos, SetScrollPos, ProcessScrollBar这三个函数的重写,控制需要显示哪些数据,IVirtualDataProvider 用于自定义ListItem , IVirtualDataProvider 增加CreateElement(), FillElement() , GetElementtCount() 提供给ListBody  调用,填充数据;

接口:

IVirtualDataProvider .h

#pragma once



//
// 虚拟列表数据提供者,抽象基类,由实际使用场景派生子类,实现对应的接口功能
//

/**
* @brief 虚拟列表接口类
* 提供开发者对子项数据管理个性化定制
*/
class IVirtualDataProvider
{
public:
	/**
	* @brief 创建一个子项
	* @return 返回创建后的子项指针
	*/
	virtual CControlUI* CreateElement() abstract;

	/**
	* @brief 填充指定子项
	* @param[in] control 子项控件指针
	* @param[in] index 索引
	* @return 返回创建后的子项指针
	*/
	virtual void FillElement(CControlUI *pControl, int index) abstract;

	/**
	* @brief 获取子项总数
	* @return 返回子项总数
	*/
	virtual int GetElementtCount() abstract;
};

2、VirtualListUI.h

#pragma once
#include "IVirtualDataProvider.h"
#include "VirListBodyUI.h"
#include <list>



/*class CListUI;*/
class CVirtualListUI : public CListUI
{
public:
	CVirtualListUI();
	~CVirtualListUI();

	LPCTSTR GetClass() const;
	LPVOID GetInterface(LPCTSTR pstrName);

	virtual void DoEvent(TEventUI& event);
	
	void SetItemCount(int nCount);

	/**
	* @brief 设置数据代理对象
	* @param[in] pProvider 开发者需要重写 IVirtualDataProvider 的接口来作为数据代理对象
	* @return 无
	*/
	virtual void SetDataProvider(IVirtualDataProvider * pDataProvider);

	virtual IVirtualDataProvider * GetDataProvider();

	/**
	* @brief 设置子项高度
	* @param[in] nHeight 高度值
	* @return 无
	*/
	virtual void SetElementHeight(int nHeight);

	/**
	* @brief 初始化子项
	* @param[in] nMaxItemCount 初始化数量,默认 50
	* @return 无
	*/
	virtual void InitElement(int nMaxItemCount = 50);

	/**
	* @brief 删除所有子项
	* @return 无
	*/
	virtual void RemoveAll() override;

	bool Remove(CControlUI* pControl);
	bool RemoveAt(int iIndex);

	/**
	* @brief 设置是否强制重新布局
	* @param[in] bForce 设置为 true 为强制,否则为不强制
	* @return 无
	*/
	void SetForceArrange(bool bForce);

	/**
	* @brief 获取当前所有可见控件的索引
	* @param[out] collection 索引列表
	* @return 无
	*/
	void GetDisplayCollection(std::vector<int>& collection);

	
	/**
	* @brief 获取选中的索引 (通过IVirtualDataProvider * 获取数据) 
	* @return std::vector<int> 选中索引
	*/
	std::vector<int> GetSelectIndex();
protected:
	/// 重写父类接口,提供个性化功能

	virtual void SetPos(RECT rc, bool bNeedInvalidate = true) ;

private:
	enum ScrollDirection
	{
		kScrollUp = -1,
		kScrollDown = 1
	};

	/**
	* @brief 创建一个子项
	* @return 返回创建后的子项指针
	*/
	CControlUI* CreateElement();

	/**
	* @brief 填充指定子项
	* @param[in] control 子项控件指针
	* @param[in] index 索引
	* @return 返回创建后的子项指针
	*/
	void FillElement(CControlUI *pControl, int iIndex);

	/**
	* @brief 获取元素总数
	* @return 返回元素总指数
	*/
	int GetElementCount();


private:

	void EnableScrollBar(bool bEnableVertical = true, bool bEnableHorizontal = false);


protected:
	bool SelectItem(int iIndex, bool bTakeFocus = false);
// 	bool SelectItemActivate(int iIndex);    // 双击选中
// 
 	bool SelectMultiItem(int iIndex, bool bTakeFocus = false);
// 	void SetMultiSelect(bool bMultiSel);
// 	bool IsMultiSelect() const;
// 	bool UnSelectItem(int iIndex, bool bOthers = false);
 	void SelectAllItems();
 	void UnSelectAllItems();
// 	//支持范围选择 add by ll  2019.04.02
 	virtual bool SelectRange(int iIndex, bool bTakeFocus = false);

protected:
	bool m_bIsStartBoxSel = false;//是否开始框选

	int m_iStartBoxIndex = -1; //开始框选索引
	int m_iEndBoxIndex = -1;//结束框选索引
	POINT m_BoxStartPt;//框选开始起点


	IVirtualDataProvider*  m_pDataProvider ;//OwnerData

	CVirListBodyUI* m_pList;

	int m_nOwnerElementHeight;	// 每个项的高度	
	int m_nOwnerItemCount;	// 列表真实控件数量上限  
	int m_nOldYScrollPos;
	bool m_bArrangedOnce;
	bool m_bForceArrange;	// 强制布局标记

	int m_iCurlShowBeginIndex = 0;
	int m_iCurlShowEndIndex = 0;

	bool m_bScrollProcess = false;//防止SetPos 重复调用, 导致死循环
};

3、VirListBodyUI.h


#pragma once
#include "IVirtualDataProvider.h"

class CVirListBodyUI : public CListBodyUI
{
public:
	CVirListBodyUI(CListUI* pOwner);
	~CVirListBodyUI();

	void SetVerScrollRange(int nRange);

	void SetItemCount(int nCount);

	//强制重绘,用于外部改变某些项内容
	void RedrawItems(int iItemFrom, int iItemTo);

	//void DoEvent(TEventUI& event);

	/**
	* @brief 设置数据代理对象
	* @param[in] pProvider 开发者需要重写 IVirtualDataProvider 的接口来作为数据代理对象
	* @return 无
	*/
	virtual void SetDataProvider(IVirtualDataProvider * pDataProvider);

	virtual IVirtualDataProvider * GetDataProvider();

	/**
	* @brief 设置子项高度
	* @param[in] nHeight 高度值
	* @return 无
	*/
	virtual void SetElementHeight(int nHeight);

	/**
	* @brief 初始化子项
	* @param[in] nMaxItemCount 初始化数量,默认 50
	* @return 无
	*/
	virtual void InitElement(int nMaxItemCount = 50);

	/**
	* @brief 刷新列表
	* @return 无
	*/
	virtual void Refresh();

	/**
	* @brief 删除所有子项
	* @return 无
	*/
	virtual void RemoveAll() override;

	/**
	* @brief 设置是否强制重新布局
	* @param[in] bForce 设置为 true 为强制,否则为不强制
	* @return 无
	*/
	void SetForceArrange(bool bForce);

	/**
	* @brief 获取当前所有可见控件的索引
	* @param[out] collection 索引列表
	* @return 无
	*/
	void GetDisplayCollection(std::vector<int>& collection);

	/**
	* @brief 让控件在可见范围内
	* @param[in] iIndex 控件索引
	* @param[in] bToTop 是否在最上方
	* @return 无
	*/
	void EnsureVisible(int iIndex, bool bToTop = false);

	/**
	* @brief 设置位置(子控件以及本身)
	* @param[in] rc 本身控件位置 
	* @param[in] bNeedInvalidate 是否刷新
	* @return 无
	*/
	virtual void SetPos(RECT rc, bool bNeedInvalidate = true);

	/// 重写父类接口,提供个性化功能
	/**
	* @brief 滚动条改变,设置滚动条位置,做是否刷新界面
	* @param[in] szPos 滚动条位置
	* @return 无
	*/
	virtual void SetScrollPos(SIZE szPos, bool bMsg = true);

	void EnsureVisible(int iIndex);

	/**
	* @brief 重新布局子项
	* @param[in] bForce 是否强制重新布局
	* @return 无
	*/
	void ReArrangeChild(bool bForce);
protected:

	/**
	* @brief 设置实际需要的滚动条位置
	* @param[in] rc 控件位置
	* @return 无
	*/
	void SetPosInternally(RECT rc);

	/**
	* @brief 添加一个子项
	* @param[in] iIndex 要插入的位置
	* @return 无
	*/
	void AddElement(int iIndex);//对于虚拟列表好像没什么用

	/**
	* @brief 移除一个子项
	* @param[in] iIndex 要移除的子项索引
	* @return 无
	*/
	void RemoveElement(int iIndex);

	/**
	* @brief 键盘按下通知
	* @param[in] ch 按键
	* @return 无
	*/
	virtual void OnKeyDown(TCHAR ch) {}

	/**
	* @brief 键盘弹起通知
	* @param[in] ch 按键
	* @return 无
	*/
	virtual void OnKeyUp(TCHAR ch) {}

private:
	enum ScrollDirection
	{
		kScrollUp = -1,
		kScrollDown = 1
	};

	/**
	* @brief 创建一个子项
	* @return 返回创建后的子项指针
	*/
	CControlUI* CreateElement();

	/**
	* @brief 填充指定子项
	* @param[in] control 子项控件指针
	* @param[in] index 索引
	* @return 返回创建后的子项指针
	*/
	void FillElement(CControlUI *pControl, int iIndex);

	/**
	* @brief 获取元素总数
	* @return 返回元素总指数
	*/
	int GetElementCount();

	/**
	* @brief 使用默认布局
	* @return 成功返回 true,否则返回 false
	*/
	bool UseDefaultLayout();

	/**
	* @brief 得到n个元素对应的高度和,
	* @param[in] nCount 要得到多少元素的高度,-1表示全部元素
	* @return 返回指定数量元素的高度和
	*/
	int CalcElementsHeight(int nCount);

	/**
	* @brief 得到可见范围内第一个元素的前一个元素索引
	* @param[out] bottom 返回上一个元素的 bottom 值
	* @return 返回上一个元素的索引
	*/
	int GetTopElementIndex(int &bottom);

	/**
	* @brief 判断某个元素是否在可见范围内
	* @param[in] iIndex 元素索引
	* @return 返回 true 表示可见,否则为不可见
	*/
	bool IsElementDisplay(int iIndex);

	/**
	* @brief 判断是否要重新布局
	* @param[out] direction 向上滚动还是向下滚动
	* @return true 为需要重新布局,否则为 false
	*/
	bool NeedReArrange(ScrollDirection &direction);

	//未使用
	void Arrange();

public:
	/**
	* @brief 每次重新刷新加载时 重新按照m_vSelIndex 中的索引初始化
	* @param[in] rc 位置
	* @param[in] cxRequired 水平需要的滚动条 Range 
	* @param[in] cyRequired 垂直需要的滚动条Range
	* @return无
	*/
	void ProcessScrollBar(RECT rc, int cxRequired, int cyRequired);

	/**
	* @brief 每次重新刷新加载时 重新按照m_vSelIndex 中的索引初始化
	* @return无
	*/
	void InitSelectItem();


	/**
	* @brief 单选
	* @param[in] iIndex 选中索引
	* @param[in] bTakeFocus 是否获取焦点
	* @return true 是否选中
	*/
	bool SelectItem(int iIndex, bool bTakeFocus = false);

	/**
	* @brief 多选
	* @param[in] iIndex 选中索引
	* @param[in] bTakeFocus 是否获取焦点
	* @return true 是否选中
	*/
 	bool SelectMultiItem(int iIndex, bool bTakeFocus = false);

/// 不需要重写 begin
// 	bool SelectItemActivate(int iIndex);    // 双击选中
// 	void SetMultiSelect(bool bMultiSel); 
// 	bool IsMultiSelect() const;
//	bool UnSelectItem(int iIndex, bool bOthers = false);

/// 不需要重写 end
	/**
	* @brief 全选
	* @return无
	*/
 	void SelectAllItems();

	/**
	* @brief 清空所有选中
	* @return 无
	*/
 	void UnSelectAllItems();
 	//支持范围选择 add by ll  2019.04.02
	/**
	* @brief 范围选中  m_iSelIndex ~ iIndex 区域选中
	* @param[in] iIndex 选中结束索引
	* @param[in] bTakeFocus 是否获取焦点
	* @return true 是否选中
	*/
 	virtual bool SelectRange(int iIndex, bool bTakeFocus = false);

	/**
	* @brief 获取选中的索引 (通过IVirtualDataProvider * 获取数据)
	* @return std::vector<int> 选中索引
	*/
	std::vector<int> GetSelectIndex();
private:
	bool m_bIsStartBoxSel = false;//是否开始框选

	int m_iStartBoxIndex = -1; //开始框选索引
	int m_iEndBoxIndex = -1;//结束框选索引
	POINT m_BoxStartPt = { 0,0 };//框选开始起点

					   //OwnerData
	IVirtualDataProvider*  m_pDataProvider;

	int m_nOwnerElementHeight = 0;	// 每个项的高度	
	int m_nOwnerItemCount = 0;	// 列表真实控件数量上限  
	int m_nOldYScrollPos = 0;
	bool m_bArrangedOnce = false;
	bool m_bForceArrange = false;	// 强制布局标记

	int m_iCurlShowBeginIndex = 0;
	int m_iCurlShowEndIndex = 0;

	bool m_bScrollProcess = false;//防止SetPos 重复调用, 导致死循环

	int m_iSelIndex = -1;
	std::vector<int> m_vSelIndex;//选中索引
};

 

 50万条数据显示效果:

Demo 与代码实现下载链接 https://download.csdn.net/download/qwerdf10010/11192481

 

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值