列表控件是客户端应用最常用的控件之一。列表控件通常只负责显示数据,最多通知一下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 ;
} SStringT getSizeText(DWORD dwSize)
{
int num1=dwSize/(<<);
dwSize -= num1 *(<<);
int num2 = dwSize*/(<<);
return SStringT().Format(_T("%d.%02dM"),num1,num2);
} virtual void getView(int position, SWindow * pItem,pugi::xml_node xmlTemplate)
{
if(pItem->GetChildrenCount()==)
{
pItem->InitFromXml(xmlTemplate);
}
int dataSize = ;
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/);
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+);
} struct SORTCTX
{
int iCol;
SHDSORTFLAG stFlag;
}; bool OnSort(int iCol,SHDSORTFLAG * stFlags,int nCols)
{
if(iCol==) //最后一列“操作”不支持排序
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=;i<nCols;i++)
{
stFlags[i]=ST_NULL;
}
stFlags[iCol]=stFlag; SORTCTX ctx={iCol,stFlag};
qsort_s(s_info,,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 =;
switch(pctx->iCol)
{
case ://name
nRet = wcscmp(pSI1->pszName,pSI2->pszName);
break;
case ://score
{
float fCmp = (pSI1->fScore - pSI2->fScore);
if(fabs(fCmp)<0.0000001) nRet = ;
else if(fCmp>0.0f) nRet = ;
else nRet = -;
}
break;
case ://size
nRet = (int)(pSI1->dwSize - pSI2->dwSize);
break;
case ://install time
nRet = wcscmp(pSI1->pszInstallTime,pSI2->pszInstallTime);
break;
case ://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,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon2",
L"PhotoShop",
L"强大的图片处理工具",
9.0f,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon3",
L"QQ7.0",
L"腾讯公司出品的即时聊天工具",
8.0f,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon4",
L"Visual Studio 2008",
L"Microsoft公司的程序开发套件",
9.0f,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon5",
L"YY8",
L"YY语音",
9.0f,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon6",
L"火狐浏览器",
L"速度最快的浏览器",
8.5f,
*(<<),
L"2015-8-5",
L"今天"
},
{
L"skin_icon7",
L"迅雷",
L"迅雷下载软件",
7.3f,
*(<<),
L"2015-8-5",
L"今天"
}
};

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

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

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

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

        int dataSize = ;
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/);
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来处理表头点击事件,以确实如何对数据排序。

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

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

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

    在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件 ...

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

    Win32编程中,用户需要一个新控件时,需要向系统注册一个新的控件类型.注册以后,调用::CreateWindow时才能根据标识控件类型的字符串创建出一个新的控件窗口对象. 为了能够从XML描述的字符 ...

  3. 第二十九篇、UICollectionView瀑布流

    1.实现思路 >第一种方案:UIScrollView 镶嵌三个UITableView (不推荐使用) >第二种方案:UIScrollView 镶嵌UIImageView (需要解决循环利用 ...

  4. 第二十九篇、CoreAnimation的使用

    使用的的三个步骤 1.初始化演员 2.设置好剧情 3.播放 主要类: CALayer // 绘图部分 CABaseAnimation // 基本动画(缩放,移动) CAKeyframeAnimatio ...

  5. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  6. 第二十九篇-Fragment动态用法

    效果图: 上节学习了静态添加Fragment的方法,这节学习动态添加方法. 主页面 layout.xml Fragment页面 layout2.xml 实现功能,当点击主页面的button时,将Fra ...

  7. flask第二十九篇——一个例子+【更新内容通知】

    请关注公众号:自动化测试实战 大家先自己写一下,船长写这个花了半个小时,因为我和大家一样,也是新手: 写一个页面如下,点击书名以后跳转到书的详情页 书的信息如下: books = [ { 'id': ...

  8. Android UI开发第二十九篇——Android中五种常用的menu(菜单)

    Android Menu在手机的应用中起着导航的作用,作者总结了5种常用的Menu. 1.左右推出的Menu 前段时间比较流行,我最早是在海豚浏览器中看到的,当时耳目一新.最早使用左右推出菜单的,听说 ...

  9. Python之路【第二十九篇】:django ORM模型层

    ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的 ...

随机推荐

  1. C#重构之道

    定义 重构的定义:在不改变软件可观察行为的前提下,改善其内部结构. 其中,不改变软件行为,是重构最基本的要求.要想真正发挥威力,就必须做到“不需了解软件行为”. 如果一段代码能让你容易了解其行为,说明 ...

  2. <<< 怎么开启服务器的3398端口

    拿到shell之后对方没有开启3389端口... 2000的话,需要传3389.exe运行才行. 2003的话方法就多了,可以实现一句话开3389: reg add "HKEY_LOCAL_ ...

  3. oracle---plsql---示例laobai

    select * from scott.emp; --1 列出emp表中各部门的部门号,最高工资,最低工资 select deptno,max(sal),min(sal) from scott.emp ...

  4. Android开发笔记之《Window下安装Ubuntu双系统,Grub无法显示Window选项》

    解决方法是: 在terminal里面输入: sudo update-grub 会找到windows的grub 重启电脑就可以了.

  5. CFBundleVersion与CFBundleShortVersionString,上架注意事项

    CFBundleVersion,标识(发布或未发布)的内部版本号.这是一个单调增加的字符串,包括一个或多个时期分隔的整数. CFBundleShortVersionString  标识应用程序的发布版 ...

  6. UEditor百度编辑器,工具栏上自定义添加一个普通按钮

    添加一个名叫“hougelou”的普通按钮在工具栏上: 第一步:找到ueditor.config.js文件中的toolbars数组,增加一个“hougelou”字符串,然后找到labelMap数组,对 ...

  7. log4net各种Filter使用【转】

    log4net各种Filter使用[转] log4net里面的filter类常用的为:      1.DenyAllFilter         拒绝所用的日志输出         <filte ...

  8. 深入理解javascript原型和闭包(5)——instanceof

    又介绍一个老朋友——instanceof. 对于值类型,你可以通过typeof判断,string/number/boolean都很清楚,但是typeof在判断到引用类型的时候,返回值只有object/ ...

  9. Android JSON、GSON、FastJson的封装与解析

    声明: 1.本帖只提供代码,不深入讲解原理.如果读者想要深入了解,那就不要在这个帖子上浪费时间了 2.客户端用的是Google官方的Volley访问服务器,具体了解Volley请戳 这里 3.本帖三种 ...

  10. 记 Mac Pro 系统升级后,编译安装 PHP-5.6.28 / PHP-7.0 报错修复过程

    买 Mac Pro 的时候,系统为 OS X 10.11.5,编译 PHP-5.6.21 的时候,也遇到一些坑,安装过程记录如下: Mac Pro 编译安装 PHP 5.6.21 及 问题汇总 后来, ...