2

我们想在 MVVM 模式中创建一棵树。

这棵树应该有 2 个通过参数传递的目录。目标是创建一个目录浏览器,我们希望在打开一个子项时有一个“按需加载”以获得更好的性能。

目前,我们在文档中找到了示例,但它不完整:

public class TreeSelectionVM {

    private TreeModel<TreeNode<String>> itemTree;
    private String pickedItem;
    //omit getter and setter for brevity
}

<window apply="org.zkoss.bind.BindComposer"
    viewModel="@id('vm') @init('org.zkoss.reference.developer.mvvm.collection.TreeSelectionVM')">
    <tree id="tree" model="@bind(vm.itemTree) " width="600px"
    selectedItem="@bind(vm.pickedItem)">
        <treecols>
            <treecol label="name" />
            <treecol label="index" />
        </treecols>
        <template name="model" var="node" status="s">
            <treeitem open="@bind(node.open)">
                <treerow>
                    <treecell label="@bind(node.data)" />
                    <treecell label="@bind(s.index)" />
                </treerow>
            </treeitem>
        </template>
    </tree>
</window>

我们应该管理 onOpen 事件吗?还是我们应该实现 TreeModel 和 TreeNode 方法(getChild & co)?

谢谢你。

4

3 回答 3

3

这是一个更完整的 zk mvvm 树示例,它类似于按需加载的目录资源管理器:

https://github.com/simbo1905/zktreemvvm

只需使用“git clone”检查它,然后运行它:

mvn -Djetty.port=8080 package jetty:run

该代码将在给定端口上启动自己的码头服务器。

它使用Apache Commons VFS作为文件系统的抽象,而不是使用更难模拟和单元测试的原始 java.io.File。默认情况下,代码显示一个 ZK 屏幕来浏览 commons-vfs2-2.0.jar 的内部,就好像它是常规文件和文件夹一样。只需将 ViewModel 类中的 FILE_SYSTEM_URI uri 更改为文件位置,如“file:///some/path/”,即可查看常规文件系统。

zul 页面包含一棵树并将其模型绑定到 vm.treeModel 并将 selectedItem 事件绑定到 vm.pickedItem 方法:

<?xml version="1.0" encoding="UTF-8"?>
<zk>
    <window apply="org.zkoss.bind.BindComposer"
        viewModel="@id('vm') @init('org.github.simbo1905.zktreemvvm.CommonsVfs220ViewModel')">
        <tree model="@load(vm.treeModel)" selectedItem="@bind(vm.pickedItem)">
            <treecols>
                <treecol label="name" />
                <treecol label="index" />
            </treecols>
            <template name="model" var="node" status="s">
                <treeitem>
                    <treerow>
                        <treecell label="@bind(node) @converter('org.github.simbo1905.zktreemvvm.NodeConverter')" />
                        <treecell label="@bind(s.index)" />
                    </treerow>
                </treeitem>
            </template>
        </tree>
    </window>
</zk>

@Converter 对保存在树模型中的 FileObject 数据进行友好渲染。

实际的 ViewModel 并没有做太多的事情,只是保存了 selectedItem 事件处理程序(它只做日志)并提供对 TreeModel 的访问:

public TreeModel<FileObject> getTreeModel() {
    if (treeModel == null) {
        try {
            FileSystemManager fsManager = VFS.getManager();
            FileObject fo = fsManager.resolveFile( FILE_SYSTEM_URI );
            treeModel = new CachingVfsTreeModel(fo);
        } catch (FileSystemException e) {
            throw new IllegalArgumentException(String.format("Could not open VFS uri: %s",FILE_SYSTEM_URI),e);
        }
        }
    return treeModel;
}

主要工作是在扩展 AbstractTreeModel 并定义强制方法的 VfsTreeModel 类中

public class VfsTreeModel extends AbstractTreeModel<FileObject> {

// __ snip __

@Override
public FileObject getChild(FileObject parent, int index) {
    log.info(String.format("%s getChild on %s with index %s", level(parent), innerName(parent), index));
    FileObject child = null;
    try {
        FileObject[] children = parent.getChildren();
        child = children[index];
    } catch (FileSystemException e) {
        throw new IllegalArgumentException(e);
    }
    return child;
}

@Override
public int getChildCount(FileObject node) {
    int childCount = 0;
    try {
        FileType type = node.getType();
        if( type == FileType.FOLDER ){
            childCount = node.getChildren().length;
        }
    } catch (FileSystemException e) {
        throw new IllegalArgumentException(e);
    }
    log.info(String.format("%s getChildCount on %s returning %s",level(node),innerName(node), childCount));
    return childCount;
}

@Override
public boolean isLeaf(FileObject node) {
    boolean isLeaf = false;
    try {
        FileType type = node.getType();
        isLeaf = (type == FileType.FILE );
    } catch (FileSystemException e) {
        throw new IllegalArgumentException(e);
    }
    log.info(String.format("%s isLeaf on %s returning %s", level(node),innerName(node), isLeaf));
    return isLeaf;
}

在这个错误跟踪器中有一个建议覆盖 getPath 以防止框架进行大量其他调用来找出该信息:

http://tracker.zkoss.org/browse/ZK-1278

该方法实现为步行到根:

@Override
public int[] getPath(FileObject node) {
    List<Integer> paths = new ArrayList<Integer>();
    try {
        FileObject parent = node.getParent();
        while (parent != null && parent.getType().equals(FileType.FOLDER)) {
            FileObject[] children = parent.getChildren();
            for( int index = 0; index < children.length; index++){
                FileObject c = children[index];
                if( node.equals(c)){
                    paths.add(index);
                    break;
                }
            }
            node = parent;
            parent = node.getParent();
        }
    } catch (FileSystemException e) {
        throw new IllegalArgumentException(e);
    }
    int[] p = new int[paths.size()];
    for( int index = 0; index < paths.size(); index++){
        p[index] = paths.get(p.length - 1 - index); // reverse
    }
    log.info(String.format("%s getPath on %s",level(node),innerName(node)));
    return p;
}

在以下树模型文档和“海量数据”文档中,它建议缓存:

http://books.zkoss.org/wiki/ZK_Developer%27s_Reference/MVC/Model/Tree_Model

http://books.zkoss.org/wiki/ZK%20Developer%27s%20Reference/Performance%20Tips/Listbox,%20Grid%20and%20Tree%20for%20Huge%20Data/Implement%20ListModel%20and%20TreeModel

因此,在示例代码中,ViewModel 创建了 VfsViewModel 的缓存子类。刷新页面将创建新对象并让缓存被垃圾收集,这对于本示例来说可能已经足够了。

于 2012-12-26T17:39:52.967 回答
1

该文档告诉您实现自己的TreeModel,我也这样做;)。
请阅读内容以获取更多信息。

编辑

这是一般的示例TreeModel
请记住,这只是一个一般示例,您必须实现。缓存
和部分加载你自己。

回复第一条评论:

ZK 的 Data-Models for MeshElements 独立于 MVVM 或 MVC,
因为model="xxx"只是调用setModel()for ,Component
而 ZK 并不关心xxx表达式,只要它 eval
到 ZK 在 java 中可以找到的东西。并且模板替换了渲染器

一个例子:

.zul 文件

<window title="new page title" border="normal" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('web.zk.controller.ListboxMVVM')">
        <listbox model="@load(vm.list)">
            <listhead>
                <listheader />
            </listhead>
            <template name="model">
                <listitem>
                    <listcell label="@load(each.name)" />
                </listitem>
            </template>
        </listbox>
    </window>
</zk>

列表框MVVM

public class ListboxMVVM {

    public UsersListModel getList() {
        return new UsersListModel(10, 0);
    }
}

UsersListModel 是一个实现。的AbstractListModel

回复MychaL的第二条评论

正如我之前提到的,ZK 文档的这一部分讨论了如何实现。一棵树的模型。
简而言之,

public Object getElementAt(int index);

被调用以获取预期的内容,其中逻辑的责任,
加载或卸载以及如何缓冲您需要的东西的方式,是你的。 当读取为返回类型时
,它返回的对象可以是您喜欢的任何对象,正如您所期望的那样。 要从您的数据中生成树节点,请使用 impl。一个渲染器,它更像是 MVC 方式 ,但也适用于 MVVM,或者像上面那样编写一个模板, 你从.
Object

each
getElementAt(int index)

于 2012-12-03T14:34:41.247 回答
1

我尝试实现 RODTreeModel 和 RODTreeNode 以分离模型/节点端和数据端,因此如果数据 bean 实现 RODTreeNodeData 接口(其中包含两个方法 getChildren 和 getChildCount),则可以按需加载任何数据 bean

基本概念/规则是让数据bean来做,用node.getChildCount()替换node.getChildren().size(),并根据需要调用dataBean.getChildCount。尽可能晚地推迟 getChildren()。

相关文件:

树 ROD 类和示例文件

tree_rod_load_on_demand.zul

于 2013-01-02T17:28:57.103 回答