Chapter 146 · XUI 容器与导航

ListView 列表

ListView 虚拟化列表控件,仅渲染可见行,支持单选/多选、自定义渲染器、键盘导航。

146

概念

ListView 是高性能虚拟化列表:
• 虚拟化:仅绘制可视区域内的行,万行数据无性能压力
• 选择模式:单选 / 多选 / Ctrl+Click / Shift 范围选
• ItemRenderer:自定义每行绘制(图标、多行文本、进度条等)
• 内建滚动:自带 Viewport + 滚动条

API 速查

函数说明
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;
}

代码讲解

  • SetItems:传入字符串指针数组,不复制内容,确保生命周期。
  • ItemHeight:统一行高 28px,用于虚拟化计算可见行。
  • SelectionMode=1:单选模式,点击一行高亮显示。
  • onSelect 回调:用户点击行时触发,获取选中索引。

要点与陷阱

  • 虚拟化:SetItems 仅存储指针数组,不复制字符串,确保生命周期。
  • ItemRenderer:回调中通过 Surface 绘制自定义内容,行高需预设。
  • SelectionBuffer:多选模式下用 SetSelectionBuffer 获取所有选中索引。
  • EnabledItems:禁用行不可选中,呈灰色显示。

进阶练习

  1. 带图标的文件列表:ItemRenderer 中绘制文件类型图标。
  2. 多选 + 右键菜单:配合 Menu 实现批量操作。
ListView 列表效果
图:ListView 列表渲染效果