列表视图控件 (SListView)¶
SOUI 中的高性能列表视图控件,基于虚拟化(adapter)技术实现,支持大量数据的高效显示和滚动。适用于显示单列数据列表,每个项目可以使用自定义模板(<template>)。
基本信息¶
- 类名:
SListView - 控件标签:
listview - 基类:
SPanel(通过TPanelProxy<IListView>+SViewBase) - 功能:提供高性能单列数据列表展示
属性说明¶
SListView 的自身属性在 SListView::SOUI_ATTRS_BEGIN 中声明,并通过 ATTR_CHAIN_CLASS(SViewBase) 继承 SViewBase 的选择相关属性。
自身属性¶
| 属性名 | 类型 | 默认值 | 是否重绘 | 说明 |
|---|---|---|---|---|
dividerSkin |
skin | 无 | 是 | 列表项之间的分割线皮肤 |
dividerSize |
layoutsize | 未设置(0) | 否 | 分割线尺寸 |
vertical |
bool | false |
否 | 排列方向:true=垂直排列,false=水平排列 |
wantTab |
bool | false |
否 | 是否响应 Tab 键在项之间切换焦点 |
multiSel |
bool | false |
否 | 是否允许多选(由 SViewBase 提供) |
说明:
SOUI_ATTRS_BEGIN的第三个参数allredraw表示“属性变更后是否重绘”,不是默认值。上表默认值来自构造函数成员初始化(m_bVertical(FALSE)、SViewBase中m_bWantTab(FALSE)、m_bMultiSel(FALSE))。列表项高度、悬停/选中背景等通过
<template>内的子控件与<style>设置,不是listview的独立 XML 属性。
使用示例¶
MVC 虚表控件的核心是通过 数据适配器(Adapter) 把数据与界面解耦:listview 只负责按模板渲染“可见项”,数据由 ILvAdapter 提供。SOUI 提供了便捷基类 SAdapterBase(helper/SAdapterBase.h),继承它并实现 getCount() 与 getView() 即可。完整可运行示例见 demos/wxDemo/CLvMessageAdapter.h。
1. XML 模板¶
<listview pos="10,10,-10,-10" name="lv_msg"
vertical="1" dividerSize="1">
<template itemHeight="64" colorHover="#f1f1f1" colorSelected="#cccccc">
<window>
<img name="item_avatar" pos="10,|-16" skin="skin_default_personal32"/>
<text name="item_name" pos="[60,15" font="bold:1"/>
<text name="item_content" pos="[60,40" colorText="#666666"/>
<text name="item_time" pos="-10,15" align="right"/>
</window>
</template>
</listview>
模板内的子控件通过
name属性命名,适配器在getView()中用FindChildByName2<T>()找到它们并填入数据。itemHeight既可在 XML 模板上声明,也可由适配器的getViewDesiredSize()动态计算(控件默认按模板itemHeight/defHeight定位)。
2. 实现适配器(C++)¶
#include <helper/SAdapterBase.h>
#include <control/SListView.h>
struct MsgItem
{
SStringW strName;
SStringW strContent;
SStringW strTime;
SStringW strAvatar;
};
class CMsgListAdapter : public SAdapterBase
{
public:
void AddMsg(const MsgItem &item)
{
m_items.Add(item);
notifyDataSetChanged();
}
protected:
virtual int WINAPI getCount() override
{
return m_items.GetCount();
}
virtual void WINAPI getView(int position, SItemPanel *pItem, SXmlNode xmlTemplate) override
{
if (pItem->GetChildrenCount() == 0)
pItem->InitFromXml(&xmlTemplate);
const MsgItem &data = m_items[position];
pItem->FindChildByName2<SImageWnd>(L"item_avatar")
->SetAttribute(L"skin", data.strAvatar);
pItem->FindChildByName2<SStatic>(L"item_name")->SetWindowText(S_CW2T(data.strName));
pItem->FindChildByName2<SStatic>(L"item_content")->SetWindowText(S_CW2T(data.strContent));
pItem->FindChildByName2<SStatic>(L"item_time")->SetWindowText(S_CW2T(data.strTime));
}
private:
SArray<MsgItem> m_items;
};
3. 绑定与使用¶
void InitList(SListView *pList)
{
CMsgListAdapter *pAdapter = new CMsgListAdapter;
pAdapter->AddMsg({L"张三", L"在吗?", L"10:24", L"skin_default_personal32"});
pAdapter->AddMsg({L"李四", L"文件已发", L"10:30", L"skin_default_personal32"});
pList->SetAdapter(pAdapter);
pAdapter->Release();
pList->SetSel(0, TRUE);
pList->SetMultiSel(TRUE);
}
SetAdapter内部会对适配器AddRef,因此传入后本地应Release()一次,避免内存泄漏。SAdapterBase已通过TObjRefImpl实现引用计数,无需手动delete。