第一次接触customPlus这个库的时候,我正被一个客户项目折磨得焦头烂额。客户要求做一个文件管理器的界面,需要支持动态图标、悬停高亮、选中状态这些现代化交互效果。如果用传统方式一个个控件去堆,光是处理各种状态切换就能把人逼疯。直到发现了customPlus这个神器,我才真正体会到什么叫"声明式开发"的爽快。
customPlus是基于aardio的plus组件和GDI+绘图技术开发的自绘组件库。它的核心思想很简单:你只需要定义"要画什么",而不用操心"怎么画"。比如要做一个带图标的按钮,传统方式可能需要处理鼠标事件、重绘逻辑,而用customPlus只需要定义好正常状态、悬停状态、按下状态分别显示什么内容就行了。
这个库有几个特别实用的特点:
在开始实战之前,我们需要准备好开发环境。这里我分享一个快速配置的小技巧,可以避免很多新手容易踩的坑。
首先去官网下载两个必要的库文件:
下载完成后,把它们解压到aardio安装目录下的lib/godking/文件夹中。这里有个细节要注意:一定要确保两个库的版本匹配,否则可能会出现奇怪的绘图错误。我建议直接使用作者提供的最新版本组合。
接下来新建一个aardio项目,在代码开头引用这些库:
aardio复制import win.ui;
import godking.paint;
import godking.customPlus;
为了验证环境是否配置正确,可以尝试运行一个最简单的例子:
aardio复制winform = win.form(text="测试窗口";right=400;bottom=300)
winform.add(plus={cls="plus";left=10;top=10;right=390;bottom=290})
winform.show()
itemModel = {
{type="rect";rectf={x=0;y=0;width=0;height=0};fillcolor=0xFFFF0000}
}
p = godking.customPlus(winform.plus,itemModel)
win.loopMessage()
如果能看到一个红色矩形填满整个plus组件,说明环境配置成功了。这个小测试虽然简单,但能排除90%的环境问题,建议新手一定要先走通这个流程。
现在我们来实战一个更复杂的例子——开发一个现代化的设置面板。这个面板需要包含图标、文本标签、开关控件等元素,并且要支持完整的交互状态反馈。
项目模板(itemModel)是customPlus的核心概念,它定义了每个界面元素的绘制规则。我们先来设计一个包含三个部分的模板:
对应的模板定义如下:
aardio复制itemModel = {
{ // 背景矩形
type="rect",
round=8,
rectf={x=5;y=5;width=-5;height=-5},
width=1,
fillcolor=0xFFF5F5F5,
itemhoveredfillcolor=0xFFEEEEEE,
itemselectedfillcolor=0xFFE0E0E0
},
{ // 图标
name="icon",
type="img",
rectf={x=15;y=15;width=24;height=24},
scale=0 // 原尺寸居中
},
{ // 标题文本
name="title",
type="text",
rectf={x=50;y=15;width=-80;height=24},
font={name="微软雅黑";h=14;color=0xFF333333},
itemhoveredfont={color=0xFF000000}
},
{ // 开关背景
name="switchBg",
type="rect",
rectf={x=-60;y=15;width=50;height=24},
round=12,
fillcolor=0xFFCCCCCC,
checkedfillcolor=0xFF4CAF50
},
{ // 开关滑块
name="switchThumb",
type="rect",
rectf={x=-55;y=17;width=20;height=20},
round=10,
fillcolor=0xFFFFFFFF,
checkedrectf={x=-25;y=17;width=20;height=20}
}
}
这个模板有几个设计亮点:
有了模板之后,我们需要准备实际要显示的数据。这里模拟一个典型的设置项列表:
aardio复制itemList = {
{
icon="res/wifi.png",
title="Wi-Fi设置",
switchBg={progress=0} // 开关关闭状态
},
{
icon="res/bluetooth.png",
title="蓝牙",
switchBg={progress=1} // 开关打开状态
},
{
icon="res/display.png",
title="显示设置",
switchBg={progress=0}
},
{
icon="res/sound.png",
title="声音",
switchBg={progress=1},
disabled=true // 禁用状态
}
}
每个项目对应模板中定义的name属性,可以灵活地设置各种状态值。注意到最后一个项目设置了disabled=true,这会触发模板中定义的禁用状态样式。
现在我们把模板和数据结合起来,创建一个完整的customPlus实例:
aardio复制p = godking.customPlus(winform.plus, itemModel, itemList, {
itemWidth=300,
itemHeight=50,
colnum=1,
padLeft=10,
padTop=10,
padRight=10,
padBottom=10
})
p.onClick = function(itemIndex, elemIndex, elemID, elemName){
if(elemName == "switchBg"){
// 切换开关状态
local item = itemList[itemIndex]
item.switchBg.progress = 1 - item.switchBg.progress
p.update(itemIndex)
}
else if(elemName == "title"){
win.msgbox("你点击了:" + itemList[itemIndex].title)
}
}
这段代码实现了两个交互功能:
update()方法会触发局部重绘,保证界面响应既流畅又高效。
接下来我们挑战一个更复杂的案例——文件管理器界面。这个界面需要显示文件图标、名称、大小等信息,并支持多选操作。
文件管理器通常采用网格布局,customPlus通过colnum参数可以轻松实现:
aardio复制p = godking.customPlus(winform.plus, itemModel, itemList, {
itemWidth=120,
itemHeight=150,
colnum=0, // 自动计算列数
autoSizeWidth=true, // 自动调整项目宽度填满容器
padLeft=10,
padTop=10,
padRight=10,
padBottom=10
})
设置colnum=0会让库自动根据itemWidth和容器宽度计算合适的列数。autoSizeWidth=true则会让项目均匀分布,填满整个宽度。
customPlus内置了多选支持,只需要在配置表中设置singleCheck参数:
aardio复制{
singleCheck=2, // 0:多选 1:项目内单选 2:整个列表单选
// 其他配置...
}
对于文件管理器,我们可能想要实现类似Windows的Ctrl+多选效果。这需要处理鼠标事件和键盘状态:
aardio复制p.onClick = function(itemIndex, elemIndex, elemID, elemName){
local keyState = win.getKeyState()
if(keyState.ctrl){
// Ctrl+点击:切换选中状态
itemList[itemIndex].selected = not itemList[itemIndex].selected
}
else {
// 普通点击:单选
for(i=1;#itemList;1){
itemList[i].selected = (i == itemIndex)
}
}
p.update()
}
一个完整的文件管理器还需要右键菜单功能。我们可以结合aardio的plus组件原生事件来实现:
aardio复制winform.plus.onContextMenu = function(hwnd, x, y){
local itemIndex = p.hitTest(x, y)
if(itemIndex){
local menu = win.ui.popmenu(winform)
menu.add("打开", function(){
openFile(itemList[itemIndex].path)
})
menu.add("删除", function(){
deleteFile(itemList[itemIndex].path)
})
menu.popup(x, y)
}
return true
}
hitTest()方法可以获取鼠标位置对应的项目索引,这是实现精准交互的关键。
在实际项目中,当列表项很多时,性能就可能成为问题。经过多个项目的实战,我总结出几个特别有效的优化方法:
对于超长列表,可以使用分页加载+虚拟滚动技术:
aardio复制p = godking.customPlus(winform.plus, itemModel, nil, {
itemWidth=100,
itemHeight=100,
colnum=5,
virtualMode=true // 启用虚拟模式
})
// 动态加载数据
p.onNeedData = function(startIndex, count){
local items = {}
for(i=startIndex; startIndex+count-1; 1){
table.push(items, createItem(i))
}
return items
}
虚拟模式下,customPlus只会保留可视区域附近的少量DOM元素,大幅降低内存占用。
频繁加载图片会影响性能,我们可以实现一个简单的缓存机制:
aardio复制local imgCache = {}
function getCachedImage(path){
if(not imgCache[path]){
imgCache[path] = gdip.image(path)
}
return imgCache[path]
}
itemList = {
{icon=getCachedImage("res/icon1.png")},
{icon=getCachedImage("res/icon2.png")}
// ...
}
当需要修改多个项目时,应该使用批量更新而不是单个更新:
aardio复制// 不推荐的做法(性能差)
for(i=1; #itemList; 1){
itemList[i].title = "新标题" + i
p.update(i)
}
// 推荐的做法(高性能)
for(i=1; #itemList; 1){
itemList[i].title = "新标题" + i
}
p.update() // 一次性重绘
在最近的一个项目中,通过这几种优化方法,我们将一个包含5000项的列表滚动性能提升了近10倍。