Skip to content

列表视图控件 (SListView)

Warning

The current page still doesn't have a translation for this language.

You can read it through google translate.

SOUI中的高性能列表视图控件,基于虚拟化技术实现,支持大量数据的高效显示和滚动。适用于显示单列数据列表,每个项目可以使用自定义模板。

基本信息

  • 类名SListView
  • 控件标签listview
  • 基类SPanel
  • 功能:提供高性能单列数据列表展示

属性说明

基本属性

属性名 类型 默认值 说明
dividerSize int - 分割行高度
wantTab bool - 是否处理tab按键(0-不处理,1-处理)
vertical bool - 列表排列方式(0-水平,1-垂直)

外观属性

属性名 类型 默认值 说明
dividerSkin string - 分割行皮肤

使用示例

基础列表视图

<!-- 基础列表视图 -->
<listview pos="10,10,-10,-10" name="lv_basic"
          dividerSize="1" vertical="1" wantTab="0">
    <template itemHeight="50" colorHover="#f1f1f1"
              colorSelected="#cccccc">
        <window>
            <text pos="10,|-15" name="txt_name"/>
            <text pos="[10,|-15" name="txt_desc"/>
        </window>
    </template>
</listview>

自定义样式列表视图

<!-- 自定义样式列表视图 -->
<listview pos="10,10,-10,-10" name="lv_custom"
          dividerSize="2" dividerSkin="skin_divider"
          wantTab="1" vertical="1">
    <template>
        <window>
            <img pos="10,10,70,70" name="img_icon"/>
            <text pos="80,15" name="txt_title" font="bold:1"/>
            <text pos="80,40" name="txt_content" 
                  colorText="#666666"/>
            <button pos="-80,|-15" name="btn_action"
                    skin="skin_btn"/>
        </window>
    </template>
</listview>

水平排列列表视图

<!-- 水平排列列表视图 -->
<listview pos="10,10,-10,110" name="lv_horizontal"
          dividerSize="5" vertical="0" wantTab="0">
    <template itemHeight="100" itemWidth="150">
        <window>
            <img pos="10,10,140,90" name="img_icon"/>
        </window>
    </template>
</listview>

事件处理

列表视图控件支持以下事件:

事件名 EventID 说明
EVT_LV_SELCHANGING EventLVSelChanging::EventID 选择改变前事件
EVT_LV_SELCHANGED EventLVSelChanged::EventID 选择改变事件
EVT_LV_ITEMCLICK EventLVItemClick::EventID 项目双击事件
// 事件处理示例
EVENT_MAP_BEGIN()
    EVENT_NAME_HANDLER(L"lv_basic", EventLVSelChanged::EventID, OnLVSelChanged)
    EVENT_NAME_HANDLER(L"lv_basic", EventLVItemClick::EventID, OnLVItemClick)
EVENT_MAP_END()

void OnLVSelChanged(IEvtArgs *pEvt)
{
    EventLVSelChanged *pRealEvt = sobj_cast<EventLVSelChanged>(pEvt);
    int nOldSel = pRealEvt->nOldSel;
    int nNewSel = pRealEvt->nNewSel;
    // 处理选择改变事件
}

void OnLVItemClick(IEvtArgs *pEvt)
{
    EventLVItemClick *pRealEvt = sobj_cast<EventLVItemClick>(pEvt);
    int nItem = pRealEvt->nItem;
    // 处理项目点击事件
}

代码操作

// 查找列表视图控件
SListView *pListView = FindChildByName2<SListView>(L"lv_basic");

// 设置适配器
pListView->SetAdapter(pAdapter);

// 获取当前选中项
int nCurSel = pListView->GetSel();

// 设置选中项
pListView->SetSel(0);

// 获取项目数量
int nCount = pListView->GetCount();


// 滚动到指定项
pListView->EnsureVisible(10);

实现自定义适配器

#include <helper/SAdapterBase.h>

// 定义数据结构
struct ListItemData
{
    SStringT strName;
    SStringT strDesc;
    SStringT strIconSkin;
};

// 实现自定义适配器
class CListAdapter : public SAdapterBase
{
protected:
    SArray<ListItemData> m_dataList;

public:
    // 获取项目数量,注意 WINAPI 调用约定
    virtual int WINAPI getCount()
    {
        return m_dataList.GetCount();
    }

    // 获取视图,注意 WINAPI 调用约定
    virtual void WINAPI getView(int position, SItemPanel* pItem, SXmlNode xmlTemplate)
    {
        if(pItem->GetChildrenCount() == 0)
        {
            pItem->InitFromXml(&xmlTemplate);
        }

        if(position >= 0 && position < m_dataList.GetCount())
        {
            const ListItemData& data = m_dataList[position];

            // 设置名称
            SWindow* pName = pItem->FindChildByName(L"txt_name");
            if(pName)
            {
                pName->SetWindowText(data.strName);
            }

            // 设置描述
            SWindow* pDesc = pItem->FindChildByName(L"txt_desc");
            if(pDesc)
            {
                pDesc->SetWindowText(data.strDesc);
            }

            // 设置图标
            SWindow* pIcon = pItem->FindChildByName(L"img_icon");
            if(pIcon)
            {
                pIcon->SetAttribute(L"skin", data.strIconSkin);
            }
        }
    }

    // 添加数据
    void AddItem(const ListItemData& item)
    {
        m_dataList.Add(item);
        notifyDataSetChanged(); // 通知视图更新
    }

    // 获取指定位置的数据
    const ListItemData& GetItem(int position)
    {
        return m_dataList[position];
    }
};

// 使用示例
void InitListView()
{
    SListView* pListView = FindChildByName2<SListView>(L"lv_basic");
    if(pListView)
    {
        CListAdapter* pAdapter = new CListAdapter();

        // 添加测试数据
        for(int i = 0; i < 20; i++)
        {
            ListItemData item;
            item.strName.Format(L"项目 %d", i+1);
            item.strDesc.Format(L"这是项目 %d 的描述", i+1);
            item.strIconSkin = L"skin_icon";
            pAdapter->AddItem(item);
        }

        // 设置适配器
        pListView->SetAdapter(pAdapter);
        pAdapter->Release(); // 释放引用计数
    }
}

最佳实践

  1. 性能优化:使用虚拟化技术,只渲染可见区域的项目
  2. 交互设计:根据需要设置 wantTab 属性处理Tab键导航
  3. 视觉效果:通过 dividerSkindividerSize 属性美化项目分隔
  4. 布局方向:使用 vertical 属性选择合适的排列方向

常见问题

Q: 数据修改后如何刷新视图?

A: 如果要刷新全部视图,通过调用 adapter->notifyDataSetChanged() 方法刷新;如果只需要刷新指定的项目,可以通过调用 adapter->notifyItemChanged(position) 方法。

Q: 列表中的edit不响应回车等按键怎么办?

A: 确保设置了 wantTab 为 1,默认wantTab为0, 确保tab可以跳转,wantTab为0时,listview会将所有键盘事件转发给第一个可获取焦点的子窗口,这样里面的edit才可以响应这些功能键,其它mvc view也适用这个规则。

Q: 列表项显示不完整怎么办?

A: 检查 template 中的 itemHeight 是否设置正确,确保与实际内容高度匹配。

Q: 分隔线显示异常怎么办?

A: 检查 dividerSkindividerSize 是否正确设置。

相关控件