Chapter 144 · XUI 容器与导航

ScrollView 滚动视图

ScrollView 提供可滚动视口,支持惯性滚动、弹性回弹、内容拖拽、滚动条策略配置。

144

概念

ScrollView 由三层结构组成:
• Frame:外框(含滚动条)
• Viewport:可视窗口,裁剪超出内容
• Content:实际内容容器,可大于视口
滚动偏移 (OffsetX, OffsetY) 控制 Content 在 Viewport 中的位置。支持鼠标滚轮、拖拽内容、滚动条三种交互方式。

API 速查

函数说明
int xuiScrollViewCreate(xui_context pCtx, xui_widget* ppW, const xui_scroll_view_desc_t* pDesc)创建滚动视图
xui_widget xuiScrollViewGetContentWidget(xui_widget pW)获取内容容器
int xuiScrollViewSetContentSize(xui_widget pW, float fW, float fH)设置内容尺寸
int xuiScrollViewSetOffset(xui_widget pW, float fX, float fY)设置滚动偏移
int xuiScrollViewScrollBy(xui_widget pW, float fDX, float fDY)增量滚动
int xuiScrollViewEnsureChildVisible(xui_widget pW, xui_widget pChild)确保子控件可见
int xuiScrollViewSetScrollbarPolicy(xui_widget pW, int iPX, int iPY)滚动条策略
int xuiScrollViewSetScrollbarMode(xui_widget pW, int iMode)滚动条模式
int xuiScrollViewSetWheelStep(xui_widget pW, float fStep)滚轮步长
int xuiScrollViewSetContentDragEnabled(xui_widget pW, int bEnabled)内容拖拽

编译本章示例

gcc -O2 -I. -o ch144.exe ch144_main.c src\xui_*.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch144.exe

示例:长列表滚动

xui_scroll_view_desc_t desc = { 0 };

xui_widget pScroll;
xuiScrollViewCreate(pCtx, &pScroll, &desc);
xuiWidgetSetRect(pScroll, (xui_rect_t){0, 0, 300.0f, 400.0f});

// 设置内容高度(100 个 40px 高的条目)
xuiScrollViewSetContentSize(pScroll, 300.0f, 4000.0f);
xuiScrollViewSetWheelStep(pScroll, 40.0f);
xuiScrollViewSetContentDragEnabled(pScroll, 1);

// 滚动条策略:Y 轴自动显示,X 轴隐藏
xuiScrollViewSetScrollbarPolicy(pScroll, 0, 1);

// 向内容区添加子控件
xui_widget pContent = xuiScrollViewGetContentWidget(pScroll);
xuiWidgetSetLayout(pContent, XUI_LAYOUT_COLUMN);

xuiWidgetAddChild(pParent, pScroll);

完整代码

将以下代码保存为 ch144_main.c,编译后运行即可看到滚动视图效果:

#include "xge.h"
#include "xui.h"
#include <stdio.h>

static xui_context g_pCtx;

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);
    /* 注入鼠标输入以支持滚轮/拖拽 */
    float mx, my;
    xgeMouseGet(&mx, &my);
    xuiInputPointerMove(g_pCtx, mx, my);
    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 = "ch144 - ScrollView";
    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_scroll_view_desc_t svd = { 0 };
    xui_widget pScroll;
    xuiScrollViewCreate(g_pCtx, &pScroll, &svd);
    xuiWidgetSetRect(pScroll, (xui_rect_t){0, 0, 400, 500});
    xuiScrollViewSetContentSize(pScroll, 400, 2000);
    xuiScrollViewSetWheelStep(pScroll, 40.0f);
    xuiScrollViewSetContentDragEnabled(pScroll, 1);
    xuiScrollViewSetScrollbarPolicy(pScroll, 0, 1);
    xuiWidgetAddChild(pRoot, pScroll);

    /* 向内容区添加 50 个标签 */
    xui_widget pContent = xuiScrollViewGetContentWidget(pScroll);
    xuiWidgetSetLayout(pContent, XUI_LAYOUT_COLUMN);
    static char buf[50][32];
    for (int i = 0; i < 50; i++) {
        sprintf(buf[i], "Item #%d", i + 1);
        xui_label_desc_t lb = { 0 };
        lb.sText = buf[i];
        xui_widget pLb;
        xuiLabelCreate(g_pCtx, &pLb, &lb);
        xuiWidgetSetRect(pLb, (xui_rect_t){0, 0, 380, 36});
        xuiWidgetAddChild(pContent, pLb);
    }

    xgeRun(onFrame, NULL);
    xuiDestroy(g_pCtx);
    xgeUnit();
    return 0;
}

代码讲解

  • ContentSize:设置内容区总高度 2000px,超出视口 500px 产生滚动。
  • WheelStep:滚轮每次滚动 40px,与行高匹配。
  • ContentDrag:启用拖拽内容滚动,触屏友好。
  • ScrollbarPolicy:X 轴隐藏(0),Y 轴自动(1),内容溢出时显示滚动条。
  • GetContentWidget:获取内容容器,向其中添加子控件。

要点与陷阱

  • ContentSize:必须手动设置或让布局自动计算,否则无滚动效果。
  • EnsureChildVisible:自动滚动使指定子控件进入视口。
  • ContentDrag:启用后用户可直接拖拽内容区滚动(触屏友好)。
  • WheelAxis:可设置滚轮映射到 X 或 Y 轴。
  • CornerMode:双滚动条交汇角处的处理模式。

进阶练习

  1. 实现图片画廊:内容区放置网格图片,双向滚动。
  2. 滚动到指定位置:按钮点击后调用 SetOffset 平滑定位。
ScrollView 滚动视图效果
图:ScrollView 滚动视图渲染效果