第二十九篇:使用SOUI的SMCListView控件

列表控件是客户端应用最常用的控件之一。列表控件通常只负责显示数据,最多通知一下APP列表行的选中状态变化。

现在的UI经常要求程序猿在列表控件里不光显示内容,还要能和用户交互,显示动画等等,传统的列表控件对于这样的需求基本是无能为力了。

Android开发中很多界面都直接采用ListView实现,ListView中每一个Item中都可以容纳其它控件,这样的设计使得在表项中的交互和在主面板上交互一样简单。

虽然在列表项中容纳其它控件并不是什么新的思想,考虑到列表中的数据量是不确定的,如果给每一个表项的分配一个容器窗口,系统的内存占用及效率都成问题。

还好Andriod开源,简单看一下Android里ListView控件的源代码就可以发现,Android实现的ListView关键在于容器窗口的重用。

借鉴Andriod ListView控件的思想,在SOUI中也实现了对应的ListView控件。但是ListView只有一列,显示显示复杂内容问题不大,但是不能调整列宽等,和一个ListCtrl的功能还是有些差距。

多列列表和单列表最大的区别在于多了一个表头,核心的东西并没有区别,经过近3天的编码调试,终于完成了这个革命性的控件(至少我认为是Windows UI上革命性的)。

先看下效果:

SMCListView控件的使用:

XML配置:

要使用这个列表控件,首先应该在XML中定义该控件的位置及属性,参考下面摘自demo的代码:

          <mclistview name="mclv_test" colorBkgnd="#ffffff" pos="10,10,-10,-10" headerHeight="30">
            <header align="center"  sortSkin="skin_lcex_header_arrow" itemSkin="skin_lcex_header" itemSwapEnable="1" fixWidth="0" font="underline:0,adding:-3" sortHeader="1" colorBkgnd="#ffffff" >
              <items>
                <item width="480">软件名称</item>
                <item width="95">软件评分</item>
                <item width="100">大小</item>
                <item width="100">安装时间</item>
                <item width="100">使用频率</item>
                <item width="100">操作</item>
              </items>
            </header>
            <template itemHeight="80" colorHover="#cccccc" colorSelected="#0000ff" id="30000">
              <window name="col1">
                <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">
                <ratingbar name="rating_score" starSkin="skin_star1" starNum="5" value="3.5" pos="10,16"  />
                <text name="txt_score" pos="15,36,50,-16" font="adding:-5"  >8.5分</text>
                <link pos="[5,36,@30,-16" cursor="hand" colorText="#1e78d5" href="www.163.com" font="adding:-5" >投票</link>
              </window>
              <window name="col3">
                <text name="txt_size" pos="0,26,-0,-26" font="adding:-4" align="center" >85.92M</text>
              </window>
              <window  name="col4">
                <text name="txt_installtime" pos="0,26,-0,-26" font="adding:-4" align="center" >2015-01-09</text>
              </window>
              <window name="col5">
                <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">
                <imgbtn animate="1"  pos="|-35,|-14" font="adding:-3" align="center" skin="skin_install" name="btn_uninstall">卸载</imgbtn>
              </window>
            </template>

          </mclistview>

mclistview有一个属性headerHeight,该属性定义表头的显示高度。

节点下有一个header控件,用来定义表头控件的样式,都很简单,自己看XML。

最关键的在于下面的template(模板)节点,该XML节点用来定义如何显示列表项。

模板内样式的定义其实并没有特别的规定,因为最后如何解析这个模板是由APP决定的,但推荐使用上面的样式:template节点下为每一列定义一个window节点,只需要指定一个name属性(当然也可以指定其它的窗口属性,布局属性无效)。在该window节点下可以定义任意的其它控件。

代码编写:

和listview控件一样,mclistview也需要XML和代码配合才能正确显示数据。

要使用mclistview,首先需要实现一个数据适配器(IMcAdapter,继承自SListView中实现的IAdapter),还是先看demo中的实现:

class CTestMcAdapterFix : public SMcAdapterBase
{
public:
struct SOFTINFO
{
    wchar_t * pszSkinName;
    wchar_t * pszName;
    wchar_t * pszDesc;
    float     fScore;
    DWORD     dwSize;
    wchar_t * pszInstallTime;
    wchar_t * pszUseTime;
};

static SOFTINFO s_info[];

public:
    CTestMcAdapterFix()
    {

    }

    virtual int getCount()
    {
        return 12340;
    }   

    SStringT getSizeText(DWORD dwSize)
    {
        int num1=dwSize/(1<<20);
        dwSize -= num1 *(1<<20);
        int num2 = dwSize*100/(1<<20);
        return SStringT().Format(_T("%d.%02dM"),num1,num2);
    }

    virtual void getView(int position, SWindow * pItem,pugi::xml_node xmlTemplate)
    {
        if(pItem->GetChildrenCount()==0)
        {
            pItem->InitFromXml(xmlTemplate);
        }
        int dataSize = 7;
        SOFTINFO *psi = s_info+position%dataSize;
        pItem->FindChildByName(L"img_icon")->SetAttribute(L"skin",psi->pszSkinName);
        pItem->FindChildByName(L"txt_name")->SetWindowText(S_CW2T(psi->pszName));
        pItem->FindChildByName(L"txt_desc")->SetWindowText(S_CW2T(psi->pszDesc));
        pItem->FindChildByName(L"txt_score")->SetWindowText(SStringT().Format(_T("%1.2f 分"),psi->fScore));
        pItem->FindChildByName(L"txt_installtime")->SetWindowText(S_CW2T(psi->pszInstallTime));
        pItem->FindChildByName(L"txt_usetime")->SetWindowText(S_CW2T(psi->pszUseTime));
        pItem->FindChildByName(L"txt_size")->SetWindowText(getSizeText(psi->dwSize));
        pItem->FindChildByName2<SRatingBar>(L"rating_score")->SetValue(psi->fScore/2);
        pItem->FindChildByName(L"txt_index")->SetWindowText(SStringT().Format(_T("第%d行"),position));

        SButton *pBtnUninstall = pItem->FindChildByName2<SButton>(L"btn_uninstall");
        pBtnUninstall->SetUserData(position);
        pBtnUninstall->GetEventSet()->subscribeEvent(EVT_CMD,Subscriber(&CTestMcAdapterFix::OnButtonClick,this));
    }

    bool OnButtonClick(EventArgs *pEvt)
    {
        SButton *pBtn = sobj_cast<SButton>(pEvt->sender);
        int iItem = pBtn->GetUserData();
        SMessageBox(NULL,SStringT().Format(_T("button of %d item was clicked"),iItem),_T("uninstall"),MB_OK);
        return true;
    }

    SStringW GetColumnName(int iCol) const{
        return SStringW().Format(L"col%d",iCol+1);
    }

    struct SORTCTX
    {
        int iCol;
        SHDSORTFLAG stFlag;
    };

    bool OnSort(int iCol,SHDSORTFLAG * stFlags,int nCols)
    {
        if(iCol==5) //最后一列“操作”不支持排序
            return false;

        SHDSORTFLAG stFlag = stFlags[iCol];
        switch(stFlag)
        {
            case ST_NULL:stFlag = ST_UP;break;
            case ST_DOWN:stFlag = ST_UP;break;
            case ST_UP:stFlag = ST_DOWN;break;
        }
        for(int i=0;i<nCols;i++)
        {
            stFlags[i]=ST_NULL;
        }
        stFlags[iCol]=stFlag;

        SORTCTX ctx={iCol,stFlag};
        qsort_s(s_info,7,sizeof(SOFTINFO),SortCmp,&ctx);
        return true;
    }

    static int __cdecl SortCmp(void *context,const void * p1,const void * p2)
    {
        SORTCTX *pctx = (SORTCTX*)context;
        const SOFTINFO *pSI1=(const SOFTINFO*)p1;
        const SOFTINFO *pSI2=(const SOFTINFO*)p2;
        int nRet =0;
        switch(pctx->iCol)
        {
            case 0://name
                nRet = wcscmp(pSI1->pszName,pSI2->pszName);
                break;
            case 1://score
                {
                    float fCmp = (pSI1->fScore - pSI2->fScore);
                    if(fabs(fCmp)<0.0000001) nRet = 0;
                    else if(fCmp>0.0f) nRet = 1;
                    else nRet = -1;
                }
                break;
            case 2://size
                nRet = (int)(pSI1->dwSize - pSI2->dwSize);
                break;
            case 3://install time
                nRet = wcscmp(pSI1->pszInstallTime,pSI2->pszInstallTime);
                break;
            case 4://user time
                nRet = wcscmp(pSI1->pszUseTime,pSI2->pszUseTime);
                break;

        }
        if(pctx->stFlag == ST_UP)
            nRet = -nRet;
        return nRet;
    }
};

CTestMcAdapterFix::SOFTINFO CTestMcAdapterFix::s_info[] =
{
    {
        L"skin_icon1",
        L"鲁大师",
        L"鲁大师是一款专业的硬件检测,驱动安装工具",
        5.4f,
        15*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon2",
        L"PhotoShop",
        L"强大的图片处理工具",
        9.0f,
        150*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon3",
        L"QQ7.0",
        L"腾讯公司出品的即时聊天工具",
        8.0f,
        40*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon4",
        L"Visual Studio 2008",
        L"Microsoft公司的程序开发套件",
        9.0f,
        40*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon5",
        L"YY8",
        L"YY语音",
        9.0f,
        20*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon6",
        L"火狐浏览器",
        L"速度最快的浏览器",
        8.5f,
        35*(1<<20),
        L"2015-8-5",
        L"今天"
    },
    {
        L"skin_icon7",
        L"迅雷",
        L"迅雷下载软件",
        7.3f,
        17*(1<<20),
        L"2015-8-5",
        L"今天"
    }
};

注意CTestMcAdapterFix::getView虚函数,上面提到的template会通过该函数的参数 pugi::xml_node xmlTemplate 传递过来。

在getView中,首先需要判断表项容器的子窗口是不是已经被初始化过,如果没有就执行InitFromXml如下:

        if(pItem->GetChildrenCount()==0)
        {
            pItem->InitFromXml(xmlTemplate);
        }

在子窗口初始化完成后,还需要从数据表中获取对应项的数据填充到控件中显示:

        int dataSize = 7;
        SOFTINFO *psi = s_info+position%dataSize;
        pItem->FindChildByName(L"img_icon")->SetAttribute(L"skin",psi->pszSkinName);
        pItem->FindChildByName(L"txt_name")->SetWindowText(S_CW2T(psi->pszName));
        pItem->FindChildByName(L"txt_desc")->SetWindowText(S_CW2T(psi->pszDesc));
        pItem->FindChildByName(L"txt_score")->SetWindowText(SStringT().Format(_T("%1.2f 分"),psi->fScore));
        pItem->FindChildByName(L"txt_installtime")->SetWindowText(S_CW2T(psi->pszInstallTime));
        pItem->FindChildByName(L"txt_usetime")->SetWindowText(S_CW2T(psi->pszUseTime));
        pItem->FindChildByName(L"txt_size")->SetWindowText(getSizeText(psi->dwSize));
        pItem->FindChildByName2<SRatingBar>(L"rating_score")->SetValue(psi->fScore/2);
        pItem->FindChildByName(L"txt_index")->SetWindowText(SStringT().Format(_T("第%d行"),position));

和主面板上的控件响应不同,要响应表项中控件的事件,没有事件映射表可以使用,可能在IMcAdapter的实现中使用控件的GetEventSet()->subscribeEvent方法来响应:

        SButton *pBtnUninstall = pItem->FindChildByName2<SButton>(L"btn_uninstall");
        pBtnUninstall->SetUserData(position);
        pBtnUninstall->GetEventSet()->subscribeEvent(EVT_CMD,Subscriber(&CTestMcAdapterFix::OnButtonClick,this));

除了getView这个方法外,相对于IAdapter,IMcAdapter还需要实现另外两个非常重要的方法:

    interface IMcAdapter : public IAdapter
    {
        //获取列名
        virtual SStringW GetColumnName(int iCol) const PURE;
        //排序接口
        // int iCol:排序列
        // SHDSORTFLAG * stFlags [in, out]:当前列排序标志
        // int nCols:总列数,stFlags数组长度
        virtual bool OnSort(int iCol,SHDSORTFLAG * stFlags,int nCols) PURE;
    };

实现GetColumnName方法来获取每一列对应的子窗口名称,SMcListView通过它来确实template中的子窗口哪一个应该显示在什么位置,返回在template中定义的子节点的name即可。

实现OnSort来处理表头点击事件,以确实如何对数据排序。

至此,这个超级列表控件的使用就完成了。

时间: 2024-10-08 12:06:07

第二十九篇:使用SOUI的SMCListView控件的相关文章

第二十八篇:SOUI中自定义控件开发过程

在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件和自定义控件的开发流程是完全一样的,因此只需要打开SOUI的源代码,随便找一个控件看一下就大体差不多了. 下面我以controls.extend目录下的的SRadioBox2控件为例对控件开发过程需要注意的地方做一点说明. 要开发一个控件,首先要确定的是应该从哪个控件来继承.选择一个合适的基类是正确

第二十一篇:SOUI中的控件注册机制

Win32编程中,用户需要一个新控件时,需要向系统注册一个新的控件类型.注册以后,调用::CreateWindow时才能根据标识控件类型的字符串创建出一个新的控件窗口对象. 为了能够从XML描述的字符串中创建出需要的控件对象,和Win32类似,在SOUI中要创建一个新的控件也同样需要向SOUI系统注册新的控件类. 从demo.cpp的main中我们可以看到类似如下的控件注册控件的代码: //向SApplication系统中注册由外部扩展的控件及SkinObj类 SWkeLoader wkeLoa

第二十九篇:浅谈一线经理的管理策略

今天看到一条朋友圈中的内容:好单位与坏单位 文中列举了好与坏的各种分别: 好公司: 牛人很多,比你更谦卑,更努力,你不努力都不好意思呆下去! 员工都有目标有理想有希望 每个人脸上都写着"价值与目标" 坏公司: 扯皮,推诿,不承担,办事效率低,发牢骚的人很多, 不仅不干活,而且党同伐异,想把干活的人全干掉! 员工以混日子,不承担责任,得过且过去生存. 每个人的脸上都写着"自我与个性" 具体到一个公司,都是好公司与坏公司的结合体. 刚刚起步的STARTUP,大家都是志同

我的第二十九篇博客---正则进阶

re.match() 从头找一个re.search() 找一个re.findall() 找所有返回一个列表,没有就是空列表re.findall('\d','chuan1zhi2') 结果为['1','2'] re.sub() 替换 re.sub('\d','_','chuan1zhi2') 结果为['chuan_zhi_'] re.compile(编译)返回一个模型p,具有和re一样的方法,但是传递的参数不同匹配模式需要传到compile中如果不使用re.S参数,则只在每一行内进行匹配,如果一行

第二十九篇、CoreAnimation的使用

使用的的三个步骤 1.初始化演员 2.设置好剧情 3.播放 附录:一个把商品添加到购物车的抛物线动画 coreAnimation // 1.初始化演员 CALayer *layer = [[CALayer alloc]init]; layer.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height); layer.position = C

第二十九篇:USER MODE DLL开发过程中的调试技巧心得分享

最近由于项目需要,被老板安排开发一个WINDOWS USER MODE DLL, 即WINDOWS用户模式下的动态链接库. 写代码,实现具体的功能这类初等工作,对于大多数软件开发工程师来讲,只是时间与工作量上的问题,具体的WINDOWS DLL的知识点,本人参考了Jeffrey Richter的<WINDOWS核心编程>中的DLL几章,不再熬述. 这里谈一下关于USER MODE DLL在调试过程中所涉及到的一些知识点,以及它们彼此之间的关系: 1. DebugBreak / int 3; 2

小刘同学的第二十九篇博文

很遗憾今天没有干货分享出来,不过明天就考试了,衷心希望SQL Server数据库可以过啊!!! 白天状态都不怎么好,也不知道是快考试了还是怎么,反正都不是很好. 下午自己找了很多鸡汤读,还打印出来了,希望能对自己有点帮助吧. 发现跟录播真是会漏掉很多知识点啊,上次那个小球随机移动的,老师给了一段很风骚的代码,大家可以看看. <!DOCTYPE html> <html lang="en"> <head> <meta charset="U

开始写游戏 --- 第二十九篇

今天: 1.修复昨天物品显示组件文字超出的问题 2.物品显示组件新增拖拽.双击关闭功能 3.退出战斗.显示战利品按钮 4.物品奖励添加 领取.丢弃 菜单功能 5.添加UI窗口.修复UI窗口数据和人物属性不一致的问题 上图: 1.修复昨天物品显示组件文字超出的问题 { 昨天的BUG图: 现在已经修复: 主要是多个富文本改成了一个富文本的形式,之前的是这样的: 现在弄成了这样: 然后我要在代码里编辑文本格式: } 2.物品显示组件新增拖拽.双击关闭功能 { 效果: 代码: } 3.退出战斗.显示战利

NeHe OpenGL教程 第二十九课:Blt函数

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第二十九课:Blt函数 Blitter 函数: 类似于DirectDraw的blit函数,过时的技术,我们有实现了它.它非常的简单,就是把一块纹理贴到另一块纹理上. 这篇文章是有Andreas Lffler所写的,它写了一份原始的教