在GUI编程中,Tree是最常用的控件之一。如果TreeItem比较多,比如数百上千个,在程序启动时全部进行初始化,会影响程序启动效率,从而降低用户的体验。我原来就遇到过这个问题,一直没有好的解决办法。
在Java swt Jface中,这个问题得到了较好的解决,就是使用ILazyTreeContentProvider,TreeItem在展开时才创建。
但是如果Tree的数据如果从数据库读取,ILazyTreeContentProvider也会有问题。因为IO是非常耗时的操作。
举例来说,有10个根TreeItem, 每个TreeItem下又有15个子TreeItem。
程序启动时,读取要创建10个根TreeItem所需要的数据。Jface在更新每个TreeItem时,又会请求每个根TreeItem的childCount, 于是又要读取一次数据库。这样在程序启动时就要读取11次数据库,很是影响速度。
解决办法:
1. 在数据库表中增加chidCount列,在程序启动第一次读取数据库时,根据这个列值是否大于0,就知道了每个根TreeItem是否有子TreeItem
2. 同时还要使用ILazyTreePathContentProvider
于是找ILazyTreePathContentProvider的sample, 奇怪的是Jface这么红,却没有在Google搜索到使用的例子。只好去读源代码自己研究。。。。于是就有了下面示例代码。要注意下面只是示例用法,代码并不全,有些函数没有给出实现代码,理解其用意就可以了。
经过这样的改进,程序启动时,TreeItem项再多,程序启动的速度也很快了。
java swt, jface 虽然比较复杂。复杂不要紧,关键是需要的功能能否实现。
复制内容到剪贴板
代码:
// Written in the D programming language.
/*******************************************************************************
Copyright: Copyright (c) 2009 (dyuyan gmail at com) All rights reserved
License: BSD style: $(LICENSE)
Version: Initial release: May 2009
Authors: 紫气东来 ( D Programming Language China : http://www.d-programming-language-china.org/ )
*******************************************************************************/
import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
import tango.util.Convert;
debug import tango.io.Stdout;
class TreeContentProvider : ILazyTreePathContentProvider {
public static GuiNode rootNode;
private TreeViewer viewer;
private static TreeContentProvider instance;
public static TreeContentProvider getInstance() { return instance; }
public this(TreeViewer viewer) {
this.viewer = viewer;
this.instance = this;
}
public static GuiNode getRootNode() { return rootNode; }
//IContentProvider
public void dispose() { }
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.rootNode = cast(GuiNode) newInput;
}
//ILazyTreePathContentProvider
//#2 调用
public void updateElement(TreePath parentPath, int index)
{
GuiNode node = (parentPath.getSegmentCount == 0) ?
rootNode :
cast(GuiNode) parentPath.getLastSegment();
debug Stdout.formatln("upadeElement in: {} {}", node.toString, index);
if(index < node.getChildren(true).length) // getChildren(true) to read data from database if necessary
{
TreePath element = parentPath.createChildPath(node.getChildren(false)[index]);
viewer.replace(parentPath, index, node.getChildren(false)[index]);
updateHasChildren(element); //important statement
//updateChildCount(element, -1); //
}
else
{ assert(0); }
debug Stdout.formatln("updateElement out: {} {}", node.toString, index);
}
//#1 setInput referesh,click + to expand TreeItem
//
public void updateChildCount(TreePath treePath, int currentChildCount) {
GuiNode node = (treePath.getSegmentCount == 0) ?
rootNode :
cast(GuiNode) treePath.getLastSegment();
debug Stdout.formatln("updateChildCount in: {}", node.toString);
int childCount = node.childCount(); //read childCount from database if necessary
if(childCount != currentChildCount)
//viewer.setChildCount(treePath, childCount); //dwt2 bug here?
viewer.setChildCount(node, childCount);
debug Stdout.formatln("updateChildCount out: {}", node.toString);
}
public void updateHasChildren(TreePath path)
{
auto node = cast(GuiNode) path.getLastSegment();
debug Stdout.formatln("updateHasChildren in: {}", node.toString);
int postCount = to!(int)(nodeGetChildrenCount(node), 0); //read a column value from memory
if(postCount > 0) //we know the item has children
{
//debug Stdout.formatln("{} hasChildren {}", node.toString, postCount);
viewer.setHasChildren(path, true);
}
else
{
viewer.setHasChildren(path, node.childCount() > 0); // read from database
//debug Stdout.formatln("{} childCount {}", node.toString, node.childCount());
}
debug Stdout.formatln("updateHasChildren out: {}", node.toString);
}
public TreePath[] getParents(Object element) {
debug Stdout.formatln("getParents in");
TreePath[] result;
if(auto node = cast(GuiNode)element)
{
GuiNode[] nodes;
while(node.parent !is null)
{
node = node.parent;
nodes ~= node;
}
if(nodes.length > 1) //TreePath not contains rootNode
result = [new TreePath(nodes.reverse[1..$])];
}
debug Stdout.formatln("updateParents out");
return result;
}
}