初识 Aardio
挺洋气的名字,乍看神似arduino的重新排序。不过这是一种国人开发的脚本编程语言,自带基于win32 api的界面框架,可以和其他编程语言沟通,比如调用java或者python代码。
官网下载下来6.5MB的压缩包,解压后,打开集成开发环境,它还会远程下载组件,比如粘贴了vlist的示例代码后,点击硕大的运行按钮,会要求安装vlist组件,确认即可。
也可以从网上下载 .aardio 文件,自己放到lib文件夹中,作为开源库使用。这对于开源很友好,也可以魔改、借此入门,aardio上层都是开源的。
aardio 项目编译之迅速,可以让你暂时忍受其编辑器界面与错误提示界面之简单……
aardio 制作的小工具很赞,可谓精悍,比如Gif123录屏软件,如果你有制作gif动图教程的打算,不妨试试。Gif123可以在多显示器环境下工作,跨越扩展显示屏进行录屏。用SPY++探测了一下,发现界面不是directui,拖动条等控件都有自己的窗口句柄。那么,aardio 显示无限列表的性能如何呢?
使用虚表和适配器
aardio 的 vlist 有 createTableAdapter 方法,里面封装了 getCount,getItem等类似于安卓listview适配器的接口,令人欣慰。但是网上的vlist demo无法正常运行:
而且这个命令行窗口也无法复制错误信息,令人烦躁。
好在aardio还有其他更好的虚表示例程序,如: aardio - 【库】虚表增强版。增强了vlist,是一个新的控件了,需要从他博客下载 vlistEx.rar、_.rar,解压出 .aardio 文件放入lib/godking文件夹中。这样就可以运行他博客中的代码了。
但是示例程序中的虚表,与我想象中的不同,仍像是普通的列表,需要提前加装一行行的数据。其实参考库文件中的setTable方法,可以自己写适配器的!代码如下:
import win.ui;
import godking.vlistEx;
import godking
/*DSG{{*/
mainForm = win.form(text="vlistEx - table adapter";right=849;bottom=578)
mainForm.add(
vlist={cls="vlistEx";left=10;top=10;right=840;bottom=570;db=1;dl=1;dr=1;dt=1;edge=1;transparent=1;z=1}
)
/*}}*/
var t = { fields={"序号","姓名","年龄","地址","身份证"} }
math.randomize()
for(i=1;100;1){
var tt={}
tt["序号"]="[@rowindex]" // 行序号标记是不允许编辑修改的
tt["姓名"]=math.random(1000,9999)+"姓名"
tt["年龄"]=math.random(10,99)
tt["地址"]=math.random(1000,9999)+"地址"
tt["身份证"]=math.random(1000,9999)+"身份证"
..table.push(t,tt)
}
// 直接把 setTable 方法复制到demo中。将this.改成lv.
setTable = function(lv, dataSource,fields,colwidth,colstyle,toArray=false,toUtf8=false){
lv.setRedraw(false);
..table.clear(lv.dataAdapter._dataSource);
dataSource = ..table.clone(dataSource);
// 初始化标题栏
if(!fields){ //如果没有指定标题栏,则自动生成标题栏。
if (dataSource[["fields"]]) fields=dataSource[["fields"]] //如果指定了fields字段
else fields = ..table.keys(dataSource[[1]]);// 如果没指定fields字段,且数据为字典型,则字典关键字为字段。
}
if (type(fields)="string") fields=..string.split(fields,",;");//将字符串转换为“表”
if (type(fields)!="table" or !#fields) fields=null; //取消非法字段指定
// 填充数据
if (#fields){ //如果是字典,有了字段
// 先按字段刷新标题栏。
var curfields = lv.getColumnText();
if (#fields!=#curfields or ..table.tostring(curfields)!=..table.tostring(fields)) {
lv.setColumns(fields,colwidth,colstyle);
}
// 判断是否为字典
if #..table.keys(dataSource[1]){
if !dataSource["fields"] dataSource.fields=fields;
// 字典转数组
if toArray dataSource=lv.dictToArray(dataSource);
} else {
// 如果既不是字典,也不是空,则确定为数组。则取消fields字段。否则保持原dataSource的fields设置。
if #dataSource[1] dataSource.fields = null;
}
}else{ //如果是数组,则按照数组1的成员数,生成列数。
if type(dataSource[[1]])!=type.table {
lv.setColumns();
return lv.setRedraw(true);
}
if (lv.columnCount!=#(dataSource[[1]])){
lv.setColumns(..string.split(..string.repeat(#(dataSource[[1]]),' ')),colwidth,colstyle);
}
}
if toUtf8 {
if dataSource.fields {
for(i=1;#dataSource;1){
for(k,v in dataSource[i]){
if type(v)=type.string dataSource[i][k] = ..string.fromto(v,0,65001);
}
}
} else {
for(i=1;#dataSource;1){
for(n=1;#dataSource[i];1){
if type(dataSource[i][n])=type.string dataSource[i][n] = ..string.fromto(dataSource[i][n],0,65001);
}
}
}
}
tableAdapter(lv/*listview*/,dataSource).update();
lv.setRedraw(true);
}
//自己写适配器!爽歪歪
class tableAdapter {
ctor (listview,dataSource) {
this._listview = listview;
this._dataSource = dataSource;
if( listview.dataAdapter ) listview.dataAdapter.__close();
listview.dataAdapter = this;
this.update = function(){
::SendMessageInt(listview.hwnd, 0x102F/*_LVM_SETITEMCOUNT*/,this.count(), 0x2/*_LVSICF_NOSCROLL*/);
}
this.__close = function(){ this._listview.dataAdapter = null; };
this._columns = dataSource.fields;
};
count = function () {
return 100000000; // 一个小目标的数据量,惊不惊喜,
};
getItem = function (row, col) {
return row +','+ col; // 意不意外?
}
}
//mainForm.vlist.setTable(t,,{80,100,100,0,200},0x2/*_LVCFMT_CENTER*/)
//tableAdapter(mainForm.vlist/*listview*/).update();
//tableAdapter(mainForm.vlist/*listview*/, t).update();
setTable(mainForm.vlist, t,,{80,100,100,0,200},0x2/*_LVCFMT_CENTER*/)
mainForm.vlist.setCheckBox(true)
mainForm.vlist.textColor = 0xFF0000;
mainForm.vlist.bkColor = 0xEEEEEE;
mainForm.vlist.setHeaderHeight(50);
mainForm.vlist.lineColorH = 0x008800;
mainForm.vlist.lineColorV = 0xBB9999;
mainForm.vlist.fillParent(4)
mainForm.vlist.onEditBegin = true;
mainForm.vlist.onEditEnd = function(row/*行*/,col/*列*/,text/*内容*/){
/*单元格编辑完毕,是否允许单元格内容改变。返回false不允许,返回true允许,返回文本则改为新文本*/
/*不定义此事件,则默认允许改变。注意:只有onEditBegin事件返回true,此事件才会触发。*/
// 确保第三列输入数值!
if col==3 {
var r = ..tostring((..tonumber(text)):0);
if r!=text {
..win.msgbox(f(`"{text}" 将转为:"{r}"`));
return r;
}
}
return true;
}
mainForm.vlist.onSortColumn = function(col,desc){
/*点击列标题进行排序。col:列号,从1开始。desc:是否倒序。返回true重置标题栏排序图标*/
..table.sortEx(owner.getTable(),col,desc,/*数据转换*/)
owner.update()
return true;
}
mainForm.vlist.onRClick = function(row/*行*/,col/*列*/){
/*鼠标右键点击项目事件*/
..win.msgbox(f("您鼠标右键点击了:{row}行,{col}列"))
}
mainForm.show();
win.loopMessage();
可以看到适配器接口class tableAdapter
很简单,但是setTable那一坨东西是在干啥,暂时还没搞明白……
直接运行会出错,因为vListEx.aardio第201行访问了原_dataSource列表数据,注释掉即可。
//if this.dataAdapter._dataSource[item.iItem].checked { …
运行效果:
aardio 的虚表似乎还是基于win32原生的列表控件,但是能够实现虚表,令人眼前一亮。aardio 语法像js或者java,比autohotkey脚本的语法好写易懂。
虽然有着悠久的历史,但是对我而言,这是一门新技术、新思路。祝愿这门自带界面框架的脚本语言,引擎越来越快、开源的生态越来越好!
调用 Java 包,由Java层提供列表数据
-
IDEA新建纯Java项目,写一些类和方法。然后管理项目结构(文件夹+三个小方块的那个图标),在Artifact区域新建Artifact,选择导出编译结果为jar,最后build Artifact,可以在磁盘上找到jar。将生成的
mdict-java-core.jar
复制到项目的Java文件夹中 -
本例测试的是词典库Mdict-Java。脚本代码如下:
import java;
var jvm = java();
Mdict = jvm.import("com.knziha.plod.dictionary.mdict");
var md = Mdict("D:/assets/mdicts/OED2.mdx");
…… 修改适配器代码 ……
getItem = function (row, col) {
return md.getEntryAt(row); // 获取词条名称
}
效果图:
不知道怎么样才能正确导入绝对路径中的class文件,试了好久没成功,最后直接把jar复制到项目java文件夹了,不去管。
列表项目数量超过一亿显示空白,不知是哪里的问题。
感觉性能还可以,快速拖拽滚动条的时候有轻微卡顿,可忽略。资源占用有所增加,但也不是很大。导出exe失败:不能发布,说生成失败,添加资源失败,然而没有资源,纯代码项目而已。