多列列表视图控件 (SMCListView)¶
Warning
The current page still doesn't have a translation for this language.
You can read it through google translate.
SOUI中的多列列表视图控件,类似于 Windows 的列表控件,支持多列数据显示,具有表头、排序、列宽调整等功能。基于虚拟化技术实现,适用于大量数据的表格形式展示。
基本信息¶
- 类名:
SMCListView
- 控件标签:
mclistview
- 基类:
SListView
- 功能:提供多列数据表格形式展示
属性说明¶
基本属性¶
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
headerHeight | int | - | 表头高度 |
gridLines | bool | 1 | 是否显示网格线 |
使用示例¶
基础多列列表视图¶
<!-- 基础多列列表视图 -->
<mclistview pos="10,10,-10,-10" name="mclist_basic"
headerHeight="30">
<header align="center" itemSwapEnable="1"
fixWidth="0" sortHeader="1">
<items>
<item width="100">名称</item>
<item width="80">大小</item>
<item width="150">修改日期</item>
<item width="100">类型</item>
</items>
</header>
</mclistview>
自定义样式多列列表视图¶
<!-- 自定义样式多列列表视图 -->
<mclistview pos="10,10,-10,-10" name="mclist_custom"
headerHeight="40" colorBkgnd="#FFFFFF">
<header align="left" sortSkin="skin_sort"
colorText="#333333" colorBkgnd="#F5F5F5">
<items>
<item width="200">文件名</item>
<item width="100">大小</item>
<item width="150">类型</item>
<item width="200">路径</item>
</items>
</header>
</mclistview>
带复选框的多列列表视图¶
<!-- 带复选框的多列列表视图 -->
<mclistview name="mclv_test" colorBkgnd="@color/white" pos="10,10,-10,-10" headerHeight="30" colorGrid="@color/red">
<headerStyle wndclass="header" align="center" name="mclv_test_header" sortSkin="skin_lcex_header_arrow" itemSkin="skin_lcex_header" itemSwapEnable="1" fixWidth="0" font="underline:0,adding:-3" sortHeader="1" colorBkgnd="#ffffff">
<items>
<item weight="480" align="left">@string/mccol_1</item>
<item weight="95" >@string/mccol_2</item>
<item weight="100" >@string/mccol_3</item>
<item weight="100" align="right">@string/mccol_4</item>
<item weight="100">@string/mccol_5</item>
<item weight="100">@string/mccol_6</item>
</items>
<check name="chk_mclv_sel" text="全选" />
</headerStyle>
<template itemHeight="80" colorHover="#cccccc" colorSelected="#0000ff">
<window name="col1" clipClient="1">
<img name="img_icon" skin="skin_icon6" pos="10,8,@64,@64" />
<text name="txt_name" pos="[5,16" font="bold:1,adding:-1">火狐浏览器</text>
<text name="txt_desc" pos="{0,36,-10,-10" font="bold:1,adding:-4" dotted="1">速度最快的浏览器</text>
<text name="txt_index" pos="|0,|0" offset="-0.5,-0.5" font="adding:10" colorText="#ff000088">10</text>
</window>
<window name="col2" clipClient="1">
<ratingbar name="rating_score" starSkin="skin_star1" starNum="5" value="3.5" pos="10,16" />
<text name="txt_score" pos="10,36" font="adding:-5">8.5分</text>
<link pos="[5,36" cursor="hand" colorText="#1e78d5" href="www.163.com" font="adding:-5">投票</link>
</window>
<window name="col3" clipClient="1">
<text name="txt_size" pos="0,26,-0,-26" font="adding:-4" align="center">85.92M</text>
</window>
<window name="col4" clipClient="1">
<text name="txt_installtime" pos="0,26,-0,-26" font="adding:-4" align="center">2015-01-09</text>
</window>
<window name="col5" clipClient="1">
<text name="txt_usetime" pos="0,26,-0,-26" font="adding:-4" align="center">今天</text>
<animateimg pos="|0,|0" offset="-0.5,-0.5" skin="skin_busy" name="ani_test" tip="animateimg is used here" msgTransparent="0" />
</window>
<window name="col6" clipClient="1">
<imgbtn animate="1" pos="|-35,|-14" font="adding:-3" align="center" skin="skin_install" name="btn_uninstall">卸载</imgbtn>
</window>
</template>
</mclistview>
事件处理¶
多列列表视图控件支持以下事件:
事件名 | EventID | 说明 |
---|---|---|
EVT_LV_SELCHANGING | EventLVSelChanging::EventID | 选择改变前事件 |
EVT_LV_SELCHANGED | EventLVSelChanged::EventID | 选择改变事件 |
EVT_LV_ITEMCLICK | EventLVItemClick::EventID | 项目双击事件 |
// 事件处理示例
EVENT_MAP_BEGIN()
EVENT_NAME_HANDLER(L"mclist_basic", EventLVSelChanged::EventID, OnMCLVSelChanged)
EVENT_MAP_END()
void OnMCLVSelChanged(IEvtArgs *pEvt)
{
EventLVSelChanged *pRealEvt = sobj_cast<EventLVSelChanged>(pEvt);
int nOldSel = pRealEvt->nOldSel;
int nNewSel = pRealEvt->nNewSel;
// 处理选择改变事件
}
代码操作¶
// 查找多列列表视图控件
SMCListView *pMCListView = FindChildByName2<SMCListView>(L"mclist_basic");
// 设置适配器
pMCListView->SetAdapter(pAdapter);
// 获取当前选中项
int nCurSel = pMCListView->GetSel();
// 设置选中项
pMCListView->SetSel(0);
// 获取项目数量
int nCount = pMCListView->GetCount();
// 获取列数
int nColCount = pMCListView->GetHeaderCtrl()->GetItemCount();
// 获取选中项数量(多选模式下)
int nSelCount = pMCListView->GetSelCount();
// 获取所有选中项索引(多选模式下)
SArray<int> selItems;
pMCListView->GetSelItems(selItems);
实现自定义适配器¶
#include <helper/SAdapterBase.h>
// 文件项数据结构
struct FileItemData
{
SStringT strName; // 文件名
SStringT strPath; // 完整路径
SStringT strType; // 文件类型
ULONGLONG nSize; // 文件大小
FILETIME tModified; // 修改时间
BOOL bIsFolder; // 是否为文件夹
};
// 高级多列列表适配器
class CFileListAdapter : public SMcAdapterBase
{
protected:
SArray<FileItemData> m_items;
int m_nSortCol;
BOOL m_bAscending;
public:
CFileListAdapter() :
m_nSortCol(-1), m_bAscending(TRUE)
{
}
// 获取项目数量,注意 WINAPI 调用约定
virtual int WINAPI getCount()
{
return m_items.GetCount();
}
SStringW WINAPI GetColumnName(int iCol) const {
return SStringW().Format(L"col%d", iCol + 1);
}
// 执行排序,注意 WINAPI 调用约定
virtual void WINAPI Sort(int columnId, BOOL ascending)
{
m_nSortCol = columnId;
m_bAscending = ascending;
// 使用快速排序
qsort_s(m_items.GetData(), m_items.GetCount(),
sizeof(FileItemData),
CompareFunc, this);
notifyDataSetChanged();
}
// 排序比较函数
static int __cdecl CompareFunc(void *context,
const void *elem1,
const void *elem2)
{
CFileListAdapter *pThis = (CFileListAdapter*)context;
const FileItemData *p1 = (const FileItemData*)elem1;
const FileItemData *p2 = (const FileItemData*)elem2;
int nResult = 0;
switch(pThis->m_nSortCol)
{
case 0: // 按名称排序
nResult = p1->strName.CompareNoCase(p2->strName);
break;
case 1: // 按大小排序
if(p1->bIsFolder && !p2->bIsFolder)
nResult = -1;
else if(!p1->bIsFolder && p2->bIsFolder)
nResult = 1;
else
nResult = (p1->nSize > p2->nSize) ? 1 :
(p1->nSize < p2->nSize) ? -1 : 0;
break;
case 2: // 按修改时间排序
nResult = CompareFileTime(&p1->tModified, &p2->tModified);
break;
case 3: // 按类型排序
nResult = p1->strType.CompareNoCase(p2->strType);
break;
}
return pThis->m_bAscending ? nResult : -nResult;
}
// 获取视图,注意 WINAPI 调用约定
virtual void WINAPI getView(int position, SItemPanel* pItem, SXmlNode xmlTemplate)
{
if(pItem->GetChildrenCount() == 0)
{
pItem->InitFromXml(&xmlTemplate);
}
if(position >= 0 && position < m_items.GetCount())
{
const FileItemData &item = m_items[position];
// 设置图标
SWindow* pIcon = pItem->FindChildByName(L"img_icon");
if(pIcon)
{
pIcon->SetAttribute(L"skin",
item.bIsFolder ? L"skin_folder" : L"skin_file");
}
// 设置名称
SWindow* pName = pItem->FindChildByName(L"txt_name");
if(pName)
{
pName->SetWindowText(item.strName);
}
// 设置大小
SWindow* pSize = pItem->FindChildByName(L"txt_size");
if(pSize)
{
pSize->SetWindowText(FormatFileSize(item.nSize));
}
// 设置修改时间
SWindow* pDate = pItem->FindChildByName(L"txt_date");
if(pDate)
{
SYSTEMTIME st;
FileTimeToSystemTime(&item.tModified, &st);
SStringT strDate;
strDate.Format(L"%04d-%02d-%02d %02d:%02d",
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute);
pDate->SetWindowText(strDate);
}
}
}
// 添加文件项
void AddFile(const FileItemData &item)
{
m_items.Add(item);
// 如果已经排序,则重新排序
if(m_nSortCol >= 0)
{
Sort(m_nSortCol, m_bAscending);
}
else
{
notifyDataSetChanged();
}
}
// 移除文件项
void RemoveFile(int position)
{
if(position >= 0 && position < m_items.GetCount())
{
m_items.RemoveAt(position);
notifyDataSetChanged();
}
}
// 获取文件项数据
const FileItemData* GetItemData(int position)
{
if(position >= 0 && position < m_items.GetCount())
return &m_items[position];
return NULL;
}
// 格式化文件大小
SStringT FormatFileSize(ULONGLONG nSize)
{
if(nSize < 1024)
return SStringT().Format(L"%d B", (int)nSize);
else if(nSize < 1024 * 1024)
return SStringT().Format(L"%.1f KB", (float)nSize / 1024);
else if(nSize < 1024 * 1024 * 1024)
return SStringT().Format(L"%.1f MB", (float)nSize / (1024 * 1024));
else
return SStringT().Format(L"%.1f GB", (float)nSize / (1024 * 1024 * 1024));
}
};
// 使用示例
void InitFileList()
{
SMCListView *pMCListView = FindChildByName2<SMCListView>(L"mclist_basic");
if(pMCListView)
{
CFileListAdapter *pAdapter = new CFileListAdapter();
// 添加测试数据
for(int i = 0; i < 50; i++)
{
FileItemData item;
item.strName.Format(L"文件%d.txt", i+1);
item.strPath.Format(L"C:\\测试文件夹\\文件%d.txt", i+1);
item.strType = L"文本文件";
item.nSize = (i + 1) * 1024; // 模拟不同大小
item.bIsFolder = (i % 5 == 0); // 每5个文件有一个是文件夹
// 设置修改时间
GetSystemTimeAsFileTime(&item.tModified);
pAdapter->AddFile(item);
}
// 设置适配器
pMCListView->SetAdapter(pAdapter);
pAdapter->Release();
}
}
最佳实践¶
- 表头设计:合理设置 headerHeight 和列宽度,确保表头内容清晰可见
- 数据展示:使用合适的模板设计项目视图,确保数据展示清晰易读
- 交互功能:根据需要启用复选框和多选功能,提供灵活的数据操作方式
常见问题¶
Q: 数据修改后如何刷新视图?¶
A: 如果要刷新全部视图,通过调用 adapter->notifyDataSetChanged() 方法刷新;如果只需要刷新指定的项目,可以通过调用 adapter->notifyItemChanged(position) 方法。
Q: 表头显示不完整怎么办?¶
A: 检查 headerHeight 是否设置合适,确保表头内容完整显示。
Q: 处理排序功能异常怎么办?¶
A: 确保实现了 compareItem 方法并正确处理了排序逻辑。
Q: 数据显示异常怎么办?¶
A: 检查适配器实现是否正确,确保 adatper->GetColumnName 方法返回正确的数据, adatper->GetColumnName是获取列名,mclistview使用这个返回的列名,从XML模板中获取name为该列名的子窗口,并匹配header的索引号,在当前行中布局这一列的显示。
代码示例解析¶
让我们看一个更复杂的多列列表视图示例:
// 文件项数据结构
struct FileItemData
{
SStringT strName; // 文件名
SStringT strPath; // 完整路径
SStringT strType; // 文件类型
ULONGLONG nSize; // 文件大小
FILETIME tCreated; // 创建时间
FILETIME tModified; // 修改时间
FILETIME tAccessed; // 访问时间
BOOL bEnabled; // 是否可用
};
// 高级多列列表适配器
class CAdvancedMcListAdapter : public SMcAdapterBase
{
protected:
SArray<FileItemData> m_items;
int m_nSortCol;
BOOL m_bAscending;
public:
CAdvancedMcListAdapter() :
m_nSortCol(-1), m_bAscending(TRUE)
{
}
// 获取项目数量,注意 WINAPI 调用约定
virtual int WINAPI getCount()
{
return m_items.GetCount();
}
// 获取列文本,注意 WINAPI 调用约定
virtual SStringT WINAPI getColumnText(int position, int column)
{
if(position < 0 || position >= m_items.GetCount())
return SStringT();
const FileItemData &item = m_items[position];
SStringT strText;
switch(column)
{
case 0: // 文件名
strText = item.strName;
break;
case 1: // 大小
strText = FormatFileSize(item.nSize);
break;
case 2: // 类型
strText = item.strType;
break;
case 3: // 修改日期
SYSTEMTIME st;
FileTimeToSystemTime(&item.tModified, &st);
strText.Format(L"%04d-%02d-%02d %02d:%02d:%02d",
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
break;
}
return strText;
}
// 执行排序,注意 WINAPI 调用约定
virtual void WINAPI Sort(int columnId, BOOL ascending)
{
m_nSortCol = columnId;
m_bAscending = ascending;
// 使用快速排序
qsort_s(m_items.GetData(), m_items.GetCount(),
sizeof(FileItemData),
CompareFunc, this);
notifyDataSetChanged();
}
// 获取视图,注意 WINAPI 调用约定
virtual void WINAPI getView(int position, SItemPanel* pItem, SXmlNode xmlTemplate)
{
if(pItem->GetChildrenCount() == 0)
{
pItem->InitFromXml(&xmlTemplate);
}
if(position >= 0 && position < m_items.GetCount())
{
const FileItemData &item = m_items[position];
// 更新各个列的内容
SWindow *pCol1 = pItem->FindChildByName(L"col1");
if(pCol1) pCol1->SetWindowText(item.strName);
SWindow *pCol2 = pItem->FindChildByName(L"col2");
if(pCol2) pCol2->SetWindowText(FormatFileSize(item.nSize));
SWindow *pCol3 = pItem->FindChildByName(L"col3");
if(pCol3)
{
SYSTEMTIME st;
FileTimeToSystemTime(&item.tModified, &st);
SStringT strDate;
strDate.Format(_T("%04d-%02d-%02d %02d:%02d"),
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute);
pCol3->SetWindowText(strDate);
}
SWindow *pCol4 = pItem->FindChildByName(L"col4");
if(pCol4) pCol4->SetWindowText(item.strType);
// 设置选中状态
if(item.bSelected)
{
pItem->ModifyState(WndState_Check, 0);
}
else
{
pItem->ModifyState(0, WndState_Check);
}
}
}
static int __cdecl CompareFunc(void *context,
const void *elem1,
const void *elem2)
{
CAdvancedMcListAdapter *pThis =
(CAdvancedMcListAdapter*)context;
const FileItemData *p1 = (const FileItemData*)elem1;
const FileItemData *p2 = (const FileItemData*)elem2;
int nResult = 0;
switch(pThis->m_nSortCol)
{
case 0: // 按名称
nResult = p1->strName.Compare(p2->strName);
break;
case 1: // 按大小
nResult = p1->nSize > p2->nSize ? 1 :
p1->nSize < p2->nSize ? -1 : 0;
break;
case 2: // 按类型
nResult = p1->strType.Compare(p2->strType);
break;
case 3: // 按日期
nResult = CompareFileTime(&p1->tModified,
&p2->tModified);
break;
}
return pThis->m_bAscending ? nResult : -nResult;
}
// 添加文件项
void AddFile(const FileItemData &item)
{
m_items.Add(item);
// 如果已经排序,则重新排序
if(m_nSortCol >= 0)
{
Sort(m_nSortCol, m_bAscending);
}
else
{
notifyDataSetChanged();
}
}
// 移除文件项
void RemoveFile(int position)
{
if(position >= 0 && position < m_items.GetCount())
{
m_items.RemoveAt(position);
notifyDataSetChanged();
}
}
// 获取文件项数据
const FileItemData* GetItemData(int position)
{
if(position >= 0 && position < m_items.GetCount())
return &m_items[position];
return NULL;
}
};
这个示例展示了: 1. 如何实现复杂的数据结构 2. 如何处理文件相关信息 3. 如何实现高效的排序功能 4. 如何管理大量数据