列表控件是客户端应用最常用的控件之一。列表控件通常只负责显示数据,最多通知一下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. 【CityHunter】游戏进度总控,及需求设计

    需求列表 序号 标题 描述 进度 更新日期 1 游戏主界面 游戏进入的主操作界面,     2 基础定位功能 实现自身定位功能,     3 特殊地点的Marker 搜索周边银行(资产保护).医院(状 ...

  2. mybatis 批量更新

    <update id="batchUpdate" parameterType="java.util.List"> <foreach colle ...

  3. String类的功能

    String类              标红的为较少出现的 1.判断功能 boolean equals(Object obj) :比较字符串内容是否相同,区分大小写 boolean equalsIg ...

  4. WinForm------GridControl单元格内容修改外表样式

    private void gridView1_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDra ...

  5. Google Maps API V3 之 路线服务

    Google官方教程: Google 地图 API V3 使用入门 Google 地图 API V3 针对移动设备进行开发 Google 地图 API V3 之事件 Google 地图 API V3 ...

  6. 如何在ASP.NET MVC和EF中使用AngularJS

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) AngularJS作为一个越来越流行的前端框架,在使用ASP.NET MVC和实体框架开发W ...

  7. JavaScript 快速排序(Quicksort)

    "快速排序"的思想很简单,整个排序过程只需要三步: (1)在数据集之中,选择一个元素作为"基准"(pivot). (2)所有小于"基准"的元 ...

  8. SQL 的坑1 除法“”不可用“”

    今天工作中遇见 一问题,有5各部分,现要求5个部分各自的比例,SQL语句没有问题,后来还试了"加","减","乘","Round& ...

  9. PHP计算一年有多少周,每周开始日期和结束日期

    一年有多个周,每周的开始日期和结束日期 参考代码一:[正在使用的版本] <?php header("Content-type:text/html;charset=utf-8" ...

  10. Daily Scrum Meeting ——ThirdDay

    一.Daily Scrum Meeting照片 二.Burndown Chart 三.项目进展 1.完成了github上的文档整理 Transcend/ActivityHelper 2.主界面侧滑框的 ...