ListView 虚拟化列表控件,仅渲染可见行,支持单选/多选、自定义渲染器、键盘导航。
ListView 是高性能虚拟化列表:
• 虚拟化:仅绘制可视区域内的行,万行数据无性能压力
• 选择模式:单选 / 多选 / Ctrl+Click / Shift 范围选
• ItemRenderer:自定义每行绘制(图标、多行文本、进度条等)
• 内建滚动:自带 Viewport + 滚动条
| 函数 | 说明 |
|---|---|
int xuiListViewCreate(xui_context pCtx, xui_widget* ppW, const xui_list_view_desc_t* pDesc) | 创建列表 |
int xuiListViewSetItems(xui_widget pW, const char** arrItems, int iCount) | 设置文本条目 |
int xuiListViewSetItemRenderer(xui_widget pW, xui_list_view_item_proc onRender, void* pUser) | 自定义渲染回调 |
int xuiListViewSetSelect(xui_widget pW, xui_list_view_select_proc onSelect, void* pUser) | 选中回调 |
int xuiListViewSetSelected(xui_widget pW, int iIndex) | 设置选中行 |
int xuiListViewGetSelected(xui_widget pW) | 获取选中行 |
int xuiListViewSetSelectionMode(xui_widget pW, int iMode) | 选择模式 |
int xuiListViewSetItemHeight(xui_widget pW, float fHeight) | 行高 |
int xuiListViewSetEnabledItems(xui_widget pW, const int* arr, int iCount) | 逐行启用/禁用 |
int xuiListViewSetMetrics(xui_widget pW, float fH, float fPad, float fBorder) | 度量 |
gcc -O2 -I. -o ch146.exe ch146_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch146.exe
xui_list_view_desc_t desc = { 0 };
xui_widget pList;
xuiListViewCreate(pCtx, &pList, &desc);
xuiWidgetSetRect(pList, (xui_rect_t){0, 0, 250.0f, 400.0f});
// 设置数据
const char* files[] = { "main.c", "app.h", "render.c", "ui.c" };
xuiListViewSetItems(pList, files, 4);
xuiListViewSetItemHeight(pList, 28.0f);
xuiListViewSetSelectionMode(pList, 1); // 单选
// 选中回调
xuiListViewSetSelect(pList, OnListSelect, NULL);
xuiWidgetAddChild(pParent, pList);
// 回调实现
static void OnListSelect(xui_widget pW, int iIndex, void* pUser) {
const char* sName = xuiListViewGetItemText(pW, iIndex);
xgeLogWrite(XGE_LOG_INFO, "选中: %s", sName);
}将以下代码保存为 ch146_main.c,编译后运行即可看到列表视图效果:
#include "xge.h"
#include "xui.h"
#include <stdio.h>
static xui_context g_pCtx;
static void onSelect(xui_widget pW, int iIndex, void* pUser) {
(void)pW; (void)pUser;
printf("选中第 %d 行\n", iIndex);
}
static int onFrame(void* pUser) {
(void)pUser;
xgeClear(0x081114FF);
float mx, my;
xgeMouseGet(&mx, &my);
xuiInputPointerMove(g_pCtx, mx, my);
if (xgeMouseDown(XGE_MOUSE_LEFT))
xuiInputPointerDown(g_pCtx, 0);
else
xuiInputPointerUp(g_pCtx, 0);
xuiUpdate(g_pCtx, xgeGetDelta());
xuiLayout(g_pCtx);
xuiRender(g_pCtx, NULL, NULL, 0);
return XGE_OK;
}
int main(void) {
xge_desc_t d = { 0 };
d.iWidth = 800; d.iHeight = 600;
d.sTitle = "ch146 - ListView";
d.iFlags = XGE_INIT_VSYNC;
xgeInit(&d);
xuiCreate(&g_pCtx);
extern const xui_proxy_t tXgeProxy;
xui_proxy_t tXgeProxy = xuiProxyXge();
xuiSetProxy(g_pCtx, &tXgeProxy);
xuiSetViewportSize(g_pCtx, 800, 600);
/* 根容器 */
xui_widget pRoot;
xuiWidgetCreate(g_pCtx, &pRoot);
xuiWidgetSetLayoutType(pRoot, XUI_LAYOUT_COLUMN);
xuiWidgetSetRect(pRoot, (xui_rect_t){0, 0, 800, 600});
xuiWidgetSetPadding(pRoot, 40, 40, 40, 40);
xuiSetRootWidget(g_pCtx, pRoot);
/* 创建列表视图 */
xui_list_view_desc_t lvd = { 0 };
xui_widget pList;
xuiListViewCreate(g_pCtx, &pList, &lvd);
xuiWidgetSetRect(pList, (xui_rect_t){0, 0, 300, 400});
xuiListViewSetItemHeight(pList, 28.0f);
xuiListViewSetSelectionMode(pList, 1);
xuiListViewSetSelect(pList, onSelect, NULL);
xuiWidgetAddChild(pRoot, pList);
/* 设置条目数据 */
const char* files[] = {
"main.c", "app.h", "render.c",
"ui.c", "audio.c", "input.c",
"scene.c", "font.c"
};
xuiListViewSetItems(pList, files, 8);
xgeRun(onFrame, NULL);
xuiDestroy(g_pCtx);
xgeUnit();
return 0;
}