如何创建具有任意/通用类别节点的可变的,可变的jtree?


12

请注意:我不想在这里进行编码帮助,Programmers原因是我在这里。我想提高我的程序计划/编写技能,而不仅仅是(对)我对Java的理解

我正在根据此处针对LARP游戏列出的技能,试图找出如何制作具有任意类别系统的树。我先前的尝试对技能是否也是一类产品感到很失望。尝试对此进行编码很乱。画出我的树时,我注意到只有我的“叶子”才是技能,我将其他人标记为类别。

我所追求的是一种制作树的方法,该树尝试将模型和视图分开,并允许将任意类型的子节点(具有独立的编辑/渲染方式)添加到任意父节点。

上面链接中列出了各种技能的树

注意:这里的所有东西都是作为技能购买的,即使看起来像是房产。最终用户会将其视为购买技能(他们在纸上atm上做的事情),因此应将其呈现在同一页面上。

树的解释:树是“出生的”,具有一组硬编码的最高级别类别(武器,身体和心理,医疗等)。由此,用户需要能够增加一项技能。最终,他们希望增加例如“单手剑专精” 技能而非物品)。为此,理想情况下,请单击“添加”并Weapons选中,然后One-handed从出现在该子节点上的组合框节点中进行选择,然后再次单击“添加” ,然后在出现的子节点上的文本字段中输入名称。然后再次单击添加以为该叶子添加/指定“级别”或“层”;首先是熟练程度,然后是专业化(例如)。

当然,如果您想购买其他技能,那是完全不同的途径。您可能不需要像使用武器示例那样在树下同一级别的组合框,也不需要其后的其他逻辑。这就是我难以理解的地方,更不用说编程了。如何制作一组类,而不指定将它们连接在一起的顺序,但是仍然使它们都适合。

用代码描述这种树的好的系统是什么?我见过的所有其他JTree示例都具有一些可预测的模式,而我的则没有。我不想在“文字”中编写全部代码,而在父级上应允许列出子节点的类型(组合框,文本字段等)的长列表。我应该使用抽象类吗?接口?

当我添加上面未列出的其他行为不同的技能时,如何使这类对象集群可扩展?

如果没有一个好的系统可以使用,那么是否有一个好的过程可以弄清楚该怎么做呢?

我脑海中的齿轮正在转动:

我总是需要:

  • 检查父母
  • 提供基于父项的选项

由于这种共性,我开始思考,我需要某种抽象/接口skill类来定义/概述技能和类别的通用方法。我可以(希望)将规则和选项放入数据库中并从中读取。现在我想的问题是,在抽象或接口方法与如何实现该方法之间。


我已经开始(与朋友交谈之后)为数据中的SkillComponents散列一个抽象类,然后通过扩展基本类来指定个别情况。该树将仅查看数据并适当地绘制自身。我会回答是否可行。
Pureferret 2012年

问题:生命是一种具有“等级”属性的技能,可以增加。这是生活所特有的,还是其他技能也可以提高水平?我可以想象一个抽象类和您的解决方案中的多个接口的组合,例如一个抽象的“ Skill”或“ Category”或“ SkillNode”类,以便构建您的树,以及许多接口,例如“ Specializable” ”或“ Levelable”,以混合使用树状结构不需要的各种功能。评论只是为了好玩...希望您找到想要的东西!
大卫·卡钦斯基

您在问题中指定jTree。您是在问如何使文本字段和组合框显示为节点,还是在寻找非Java设计?
psr 2012年

@DavidKaczynski,您打到了头上的钉子。我将尽快实现这种事情。
Pureferret 2012年

@psr我正在尝试查看是否存在类的编码模式(不一定是“设计模式”),或者以此为基础。DavidKaczynski非常接近这一点。
Pureferret 2012年

Answers:


3

简单的可变JTree示例

在此处输入图片说明

代码(与Java 7兼容并经过测试以制作上图):

import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;

public class SimpleTree extends JFrame {
    public static void main(String[] args) {
        new SimpleTree();
    }

    public SimpleTree() {
        super("Mutable Varied JTree");
        Container content = getContentPane();
        N root = new N("Root");

        N weapons = new N("Weapons").add(
            new N("One-handed").add(
                new N("Sword").add("Proficiency", "Specialization"), 
                new N("Mace").add("Proficiency")),
            new N("Bow").add("Proficiency"));
        root.add(weapons);

        N phys = new N("Physical & Mental").add(
            new N("Life"),
            new N("Strength").add(
                "Double", "Triple", "Quadruple"));

        root.add(phys);
        N med = new N("Medical");
        med.add(new N("Bind Wounds"));
        med.add(new N("Set Broken Bones"));
        root.add(med);

        JTree tree = new JTree(root);
        content.add(new JScrollPane(tree), BorderLayout.CENTER);
        setSize(275, 300);
        setVisible(true);
    }

    private class N extends DefaultMutableTreeNode {
        public N(String s) { super(s); }
        public N add(String... strs) {
            for (String s : strs) {
                super.add(new N(s));
            }
            return this;
        }
        public N add(N... ns) {
            for (N n : ns) {
                super.add(n);
            }
            return this;
        }
    }
}

特别感谢本JTree教程

更新:讨论可能的解决方案

有一个关于制作动态树教程,使用框架底部的按钮添加/删除/清除节点。

对于组合框等,您想要获取适当的swing类的对象,并使它们像实现J.ComboBox的对象,该对象实现java.swing.tree.MutableTreeNode。Java Swing教程具有控件列表

您仍然需要制作一个用户界面元素,以使人们可以选择要添加的节点类型并进行编辑。如果他们添加一个组合框,他们将需要能够定义该框可用的选择。

您需要一个基础数据模型来保存数据。您可以使用所有Java对象中内置的默认序列化方案,但这仅用于原型制作-如果您更改程序代码,它将被破坏。您将需要使用具有JDBC或JPA的数据库,或者需要使用JSON或XML作为存储格式编写自己的序列化例程(或使用为您处理序列化的库)。

更新:带有项目概述的建议解决方案

最简单的解决方案可能是先忘记数据库,然后首先提出数据的XML表达式,然后编辑XML文件,然后将结果显示为JTree,而无需特殊的组合框或任何其他内容。我认为这就是Chibueze Opata在他的回答中所暗示的。该树的部分XML表示可能类似于以下内容:

<folder name="Physical &amp; Mental">
    <int name="Life">12</int>
    <drop-down name="Strength">
        <option name="Single" />
        <option name="Double" />
        <option name="Triple" />
        <option name="Quadruple" />
    </drop-down>
</folder>
<folder name="Medical">
    <text name="Bind Wounds" />
    <text name="Set Broken Bones" />
</folder>

以下是您的一些潜在里程碑:

  • 组成XML或JSON数据格式,然后编写一个程序,该程序读取该格式的文件并将其显示为JTree,而没有任何组合框,仅包含文件夹和文件。
  • 将摆动控件添加到JTree显示
  • 扩展程序以写入相同的文件
  • 创建一个用户界面,以便用户可以使用GUI添加和编辑节点

确保每次在每个里程碑结束时工作时都保存程序的备份。为此,将源控制系统安装在另一台机器上非常理想。如果您不介意共享代码并且不想在服务器上设置带有源代码控制的服务器,则可以使用github,sourceforge或其他一些公共源代码控制。

这些解决方案中的任何一个都需要大量工作,并且肯定比初学者项目还要大。这意味着您比学习LARP花费更多的时间来学习Java Swing和XML或JSON,这就是为什么我首先探索与现有工具结合的选择的原因。但是学习任何事物的最佳方法是最有动力学习的方法。因此,这对您来说可能是一个完美的项目。

希望对您有所帮助。


嗨,我只是在手机上,所以我无法完全查看它,但看起来像是我要的东西,但是正如我在问题中指出的那样,我需要能够交换各种类型的节点。+1,但步入正轨!
Pureferret

1
“DefaultMutableTreeNode提供的作业可检查和修改节点的父母和孩子们:” docs.oracle.com/javase/7/docs/api/javax/swing/tree/...
GlenPeterson

我的问题是,格伦(Glen),虽然这代表一棵潜在的树,但可能不代表角色的技能。例如,他们需要能够在文本字段中输入武器的类型,并从下拉组合框中选择一些选项。从这个意义上讲,它是可变的,允许任意子代。你只有一种类型的子节点的位置,ñ。而且,我并不是很在意代码(这是一个很好的例子),更不是这种事/树/数据模型的通用设计模式。
Pureferret 2012年

1
好的,因此您需要一个让人们制作树木的程序-像轮廓。Microsoft Word中已经有一个大纲视图。OpenOffice / LibreOffice Write具有一些类似的功能。你还需要什么?
GlenPeterson 2012年

1
我知道我可以用我所有的问题以错误的方式摩擦别人,为此我深表歉意。但是我需要这样做才能理解问题。我已经在上面的解决方案(在“ Update”大标题下)的末尾更新了我认为可能是您的问题的答案,或者至少是有关如何自己回答问题的指南。希望对您有所帮助。
GlenPeterson 2012年

2

我不认为JTree是您要解决的问题的正确代表。我查看了您的网站,并看到了清单。这是一个很好的开始,但是我认为您可能具有一个潜在的数据设计,但是我没有看到。

生命和力量对我来说就像角色的属性。Double,Triple和Quadruple看起来像“强度”属性的值。然后我看到技能分为武器和医疗。我将武器视为财产(可以购买,找到或丢弃),并且我认为该技能是使您能够使用给定武器有效的技能。但是从我的角度来看,武器和药物没有技能,角色却有。实际上,我强烈怀疑角色是数据设计中的中心人物。

到目前为止,以下三个数据对象在您的设计中对我来说很突出:

Character:
    Life (a value: e.g. 12)
    Strength (a value: e.g. double, triple, quadruple)
    Skills (list or set of weapon-skills, healing-skills)
    Possessions (list of weapons, armor and other possessions)
    WornArmor (maybe only wear one you possess at a time?)
    WieldedWeapon (one or two of your posessions)

Skill:
    Skill-Type (healing, manufacture, or weapon)
    RelevantWeapon (for this skill)

Weapon:
    IsOneHanded (boolean)
    IsBow (boolean)

现在,角色可以拥有武器和技能,但是要获得真正好的攻击,他们需要特定于所使用武器的技能。无论如何,这里存在一对多的关系(一个角色可以具有许多技能)。但是我还没有看到树。

拿一张纸,在首页上将角色放在页面中间,并列出周围5-10个最重要的游戏世界对象。找出数据仅仅是另一个对象的属性(例如,生命和力量是角色的不可分割的部分),以及对象之间最重要的关系是什么,而无需考虑树,文本字段或组合框。在对象之间画线以表示关系,然后找出哪些关系是1:1,哪些关系是1:很多,哪些是许多:很多并标记它们。也可能有“具有”和“具有”以及其他类型的关系。

您可能会决定需要分别表示WeaponSkill与HealingSkill与ManufactureSkill。或者,您可以确定它们是如此相似,以至于它们属于一个“技能”表(在上面的示例中),其中包含一个字段来确定该技能类型。

这是一个很好的信号,表明您到目前为止对尝试还不满意-这意味着您愿意在选择一个之前考虑很多可能性。您考虑的草图越多,最终设计就越好。最终,最好的将成为您的数据图。当这一切都很好解决后,您可以回到考虑“组合框”或“文本字段”的问题,但是我将UI的决定留到最后。

建议您不要研究JTrees,而应该研究数据结构,数据库设计以及数据建模等主题。 编辑->或更好的实体关系映射图,如David Kaczynski所建议的!<-EDIT 如果您正确地进行了数据设计,那么Java和UI显然会自然而然地从中产生。但是,如果您弄错了,那么几乎没有Java功夫或UI-beauty可以解决它。

祝好运!


2

我将专注于对象模型。

我认为,为了获得所需的灵活性,您可能希望将您的类分为知识层(在这种情况下,“定义”在这种情况下可能是个更好的词)和事务层。我所说的“层”实际上是指在脑海中将它们逻辑上分开。知识层包含您对树的布局方式的定义,树中的数据来自游戏设计师,数据层存储特定角色的特定选择。

您的知识水平至少会有些混乱,因为您正在尝试制作一个很棒的游戏,而不是一个数学上干净的模型。

尝试整理到目前为止的内容,我认为您拥有一个SkillDefinition类,其Parent属性类型为SkillDefinition。您还具有SkillDefinition的子类(可能成为数据库DEF_SkillType表中的条目),以涵盖一些情况。

“武器”,“身心”和“医疗”似乎只不过是头衔(和儿童技能)。

“单手”,“力量”和“绑定伤口”似乎具有关联的组合框(这意味着数据库上的DEF_SkillChoice表类似,带有DEF_Skill的外键)。Sword and Bow似乎是用户定义的字符串(因此在知识级别上没有关联的表,但是数据将存储在事务层中)。

“生活”似乎有一个与之相关的整数。它可能无法与使用整数的任何将来特征共享此类,因为在暗示如何增加整数的行为上。无论如何,它当前确实需要自己的类。

在树中,“剑”,“弓”,“力量”和“绑定伤口”似乎都具有与之相关的进步技能水平。在“ Sword”和“ Bow”的情况下,这些级别确实与其父级技能相关联。我认为您需要一个带有合法值的有序集合的ProgressiveSkillDefinition类(带有DEF_Skill外键的DEF_SkillLevel表。在知识级别上,尚不清楚您要建模的规则。如果“熟练”和“专业化”始终可用对于所有武器,您可能都有一个DEF_SkillLevel表,其中的“熟练程度”和“专业化”记录具有“武器”记录的外键。

这涵盖了您在知识水平上所掌握的知识。事务级别(也许“字符级别”会更合适?)仅指向相应的知识级别。因此,在您的示例中,您将拥有一个Character对象和一个Skills对象,该对象具有一系列技能-只是他实际拥有的技能。

因此,角色将具有“熟练”技能,其父代是“剑”技能,其类型为Skill_Level。在数据库中,TRANS_SkillDefinition记录具有事务性“剑”技能记录的外键以及名称为“熟练程度”的知识水平DEF_SkillLevel记录。

熟练技能应具有“剑”技能的父对象,其类型为Skill_UserNamed。在数据库中,它没有知识级别的外键,因为没有为“剑”(它是用户名)定义任何特殊的东西。但是,它也具有父事务记录的外键,因此可以通过它获得更多信息。

“剑”技能对象的父对象为“单手”,因为用户选择将“剑”置于“单手”类别中。这是SkillCategory类型的对象。在数据库中,它具有记录“单手”的DEF_SkillChoice表的外键,并且没有事务性父级,因为此技能的所有其他数据都存储在知识级别。

在为新角色构造初始树时,仅需要查询知识水平。要填充对角色所做的选择,需要交易级别。

您将需要代码将对象模型转换为树,然后再转换为树。我希望应该清楚地知道您需要做些什么-知识层上的每种类型在树上都会有一组关联的控件,这些控件会获取适合该类型的数据。

稍后,您可能希望在SkillCategory记录中为每个选择使用类(如果“ Specialization”具有与之相关的行为,则代码必须放在某个地方),但是对于此树,您不需要该类,并且此设计将兼容接着就,随即。您只需要拥有一个使用知识水平信息来构建正确对象的工厂即可。


如果不清楚或无法回答问题,请告诉我。这是一个很大的话题,在某些时候我不得不停下来。
psr 2012年

实际上,这是最接近我想要的答案,对于我正在仿真的系统编写代码感到自然而然,并且可能会起作用。现在,我的技能全部来自抽象的SkillComponent(AbSkillComponent,因为我找不到抽象类的良好命名约定),并且我有一个RootSkillComponent,SkillComponent和一些用于执行下拉框和UserNamed部分的接口。我必须感谢您添加了它与数据库的关系,这是我暂时推迟的,但值得考虑。这个答案非常好,当然还有新鲜空气。
Pureferret 2012年

@Pureferret-我想我会照原样留下答案-我不确定我知道你在找什么。
psr 2012年

1

毕竟,我们总是以管理层次结构为最终目标-无论是程序的类层次结构,还是程序本身是由将您连接到“内部世界”(GUI,DB连接,数据绑定的业务逻辑等)的不同模块构建的。 。但这是哲学,在您的情况下应该如何工作?

您在此树中有节点,每个项目都是一个“对象实例”;而它们的行为(可以放置它们,允许的子节点列表等)在某些集合(例如“类”)中很常见。是的,这就像程序代码一样。如果您足够了解Java反射,您甚至可以使用POJO类和继承层次结构来构建此组件,并使用IoC容器或工厂机制来构建实际实例。但是我想你不想要那样,因为那是沉重的语言黑客行为,所以...

首先,创建一个“类”对象,该对象将描述项目的行为。其中包含类标识符,“字段”名称以及允许的项目类型和数量等。例如:“根”类为“玩家属性”,具有“物理/心理”,“医疗”,“武器”字段。此类型为“最终”:现在,您不想拥有其他类型的玩家。提示:稍后,您可以使用相同的工具来处理“非人类怪物” ... :-)

“医疗”字段是

  • “单身”:玩家不能拥有多个“医疗”信息
  • 子类型是固定的,此处只能使用“医疗”类型的对象
  • 必需:创建播放器时,它必须具有“医疗”字段

相反:不需要武器成员,应该在将第一个武器添加到玩家时创建。这意味着:当您“编辑”您的“对象”(现在是播放器),并希望允许向其中添加新项目时,您不应通过实例的实际属性来限制选择(该实例现在不包含“武器” ”),但显示类定义的所有字段,并在首次使用时懒惰地创建新字段(子节点)。这将创建一个可扩展的环境。以后,您可以添加具有多个播放器...“ er”引用的“朋友”字段(请参阅稍后)。

按照实际的“类”进行操作。它将帮助您完善自己的想法,例如:分离武器类型(剑,匕首,弓等)似乎更好,以某种方式处理弹药(知道玩家可能没有弓但可以收集箭,但是可以使用某些武器需要弹药并减少弹药,其中一些可以找到,另一些则丢失了……)在“武器”类下:更容易进行调查,并显示您的玩家是否仅携带该物品或也可以使用它(有技能)。另一方面:您肯定会在以后开发游戏时更改此结构。

创建一个全局可用的“类存储”,在游戏开始时进行初始化,并在任何人引用其名称时提供类定义。在这种情况下,类def对象必须是不可变的。

“对象”更容易:它们可以从相同的根类派生而来,该根类包含对类定义的引用以及对字段的通用访问。也许使用HashMap包含由字段名称标识的那些子代。请记住:其中一些字段包含一个对象,其他字段则包含一组对象。这些实例当然是可变的。

现在为界面。首先,您应该检查JTree教程...,现在它应该很明显了。每个“对象”都可以由DefaultMutableTreeNode表示,该节点的“ userObject”应该是您的对象(我认为该节点的toString()显示为该节点的标签,但是您可以创建自定义单元格格式化程序)。每当打开节点时,都将浏览对象的字段,并在父节点下创建子节点(如果要动态地进行操作)-或浏览整棵树并在显示JTree时构建TreeNode结构(提示:是具有TreeNode参数的JTree构造函数。

当您在树中选择一个节点时,便有了对象实例。根据其类,您可以显示一个适当的编辑器面板,或由类定义生成的通用面板(例如:带有字段的选项卡面板,通过字段声明来显示实际字段的编辑器:创建适当类型的子代的按钮可以让您选择可用的类别之一;以及该实例的编辑器字段,例如武器属性。修改结构后,您必须刷新树本身-TreeModel及其fire ... change函数是您的朋友。

看起来不错?还是太复杂了?好吧,这只是故事的一小部分。我没说过

  • 类定义层次结构/或分类。当您支持将不同的类添加到同一字段(如不同种类的剑)时,最好使用其中之一。一个分类专家:一个类可以有多个类别,而不是固定的继承树,这是一件好事,如果您想在技能改进上花费XP时收集玩家的“所有技能” ... :-)
  • 持久性支持:您必须创建一个通用解决方案以将对象层次结构外部存储在属性或JSON文件中。如果您想让大脑保持良好状态,请使用人类可读的格式,不要二进制序列化。
  • 实例存储:您很快就会意识到,最好将游戏的“世界”保存在一个地方。一把剑不是玩家的财产,而是游戏中世界上的一个实体(玩家可能会失去它,有人可以找到它,等等),因此树中的许多“对象”实际上是对实体的引用(而其他玩家,例如玩家的健康状况实际上只是属性)。您将必须将两种类型分开,并为可以解析引用的实例提供全局存储。当序列化游戏状态时,这还将为您节省很多时间,并且多个实体引用相同的另一个实体(例如,多个玩家指向同一庙宇的“位置”)。
  • (并提到真正的大脑磨床:类存储和实例存储的相似性;类型声明本身可以是对象实例的事实,其方式与程序组件,活动窗口,用户会话等相同。这就是领域像我这样的疯子。)

无论如何,我希望您可以在此热身会议中使用一些功能。


1

我不会给您一个很长的答案,但是坦率地说,我曾经遇到过这种情况,我放弃了尝试使用任何现有数据结构的尝试。我只是简单地创建了自己的对象,该对象可以接受类似于XML节点的新父级和子级。

但是对于您的情况,我认为使用Xml类会有所帮助。然后,您可以为每个节点具有属性,这些属性可以告诉您类是否是一项技能,并且可以轻松地获取要在组合框或其他框中使用的任何节点的父级和子级。

另外,我想补充一点,您不应该想到拥有大量文本/字符串的想法。游戏通常涉及AI,而AI通常涉及大量文本。


该游戏不是通过程序进行的,它只是提供了持久性数据和一些逻辑。我当然会研究XML。谢谢!
Pureferret 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.