Swing问题/ JTree /自定义树模型

时间:2022-03-29 12:33:11

I'm having a problem and hope, someone knows what's going wrong and why and is able to give me the explanation of what I'm missing out right now to make that thing work as suggested.

我有一个问题和希望,有人知道哪里出了问题,为什么,并能给我解释我现在错过了什么,使事情按照建议进行。

I have a JTree which is build upon a custom TreeModel ("WRTreeModel", see below). The data structure this model shall be used for is build of an root object which contains some fields and furthermore a list which is backed by the "ArrayListModel" shown below. The tree looks fine when I build it using the WRTreeModel. I'm able to expand and collapse the nodes which represent the lists and fields contained in the objects. I can expand and collapse these lists and see their contents as well and so on.

我有一个JTree构建在自定义TreeModel(“WRTreeModel”,请参见下面)之上。这个模型应使用的数据结构是由一个根对象构建的,该对象包含一些字段,而且还包含一个列表,该列表由下面显示的“ArrayListModel”支持。当我使用WRTreeModel构建树时,它看起来很不错。我可以展开和折叠代表对象中包含的列表和字段的节点。我可以展开和折叠这些列表并查看它们的内容等等。

Now I want to remove a child of one of the lists and - as I already know - do it by removing it from the model calling the remove method of the ArrayListModel. To make the WRTreeModel aware of that remove, the first thing is to call its fireIntervalRemoved method is called, so far so good.

现在,我想要删除其中一个列表的子元素,正如我已经知道的那样,通过从调用ArrayListModel的remove方法的模型中删除它。要使WRTreeModel意识到删除操作,首先要调用它的fireIntervalRemoved方法,到目前为止还不错。

In the WRTreeModels inner class ArrayModelListener the intervalRemoved method prepares the call of fireTreeNodesRemoved which then builds a TreeEvent which is forwarded to all registered TreeModelListeners (and therefore the JTree which registers itself automaticall when it's connected to the model).

在WRTreeModels内部类ArrayModelListener中,intervalRemoved方法准备firetreenodesremove的调用,然后构建一个TreeEvent,该TreeEvent被转发给所有已注册的treemodellistener(因此JTree在连接到模型时自动注册自己的自动调用)。

Now I would expect that the tree reflects the change and updates it's internal and visual representation to show the new state. Unfortunately this doesn't seem to work that way. Something happens. But when I click on the node I just have changed some EventHandler-Exceptions are thrown. Obviously something got really confused.

现在我希望树反映了变化和更新,它的内部和可视化表示显示了新的状态。不幸的是,这似乎行不通。会发生的事情。但当我单击节点时,我只是更改了一些事件-异常。很明显,有些东西弄混了。

I know it's not easy to answer such a question on the fly but I would really appreciate a fast answer. It would also be of help, if someone knew websites explaining the use of custom tree models (not on DefaultMutableTreeNode or any given implementation based class) and how the event handling and updating of the JTree works.

我知道回答这样一个问题并不容易,但我真的希望能有一个快速的答案。如果有人知道网站解释自定义树模型的使用(不是在DefaultMutableTreeNode或任何给定的基于实现的类上)以及JTree的事件处理和更新是如何工作的,那么这也会有帮助。

With best regards,

此致敬礼,

Thomas Arts

托马斯艺术


public class ArrayListModel<E> extends ArrayList<E> implements ListModel {

...

public E remove(int index) {
    fireIntervalRemoved(index, index);
    E removedElement = super.remove(index);
    return removedElement;
  }

...

}

public class WRTreeModel extends LogAndMark implements TreeModel {


  class ArrayModelListener implements ListDataListener {

  ...

    @Override
    public void intervalRemoved(ListDataEvent e) {
      int[] indices = new int[e.getIndex1() - e.getIndex0() + 1];
      for (int i = e.getIndex0(); i < e.getIndex1(); i++)
        indices[i - e.getIndex0()] = i;
        fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,     ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray());
    }

  ...

  }

  public Object[] getPathToRoot(Object child) {
    ArrayList<Object> ret = new ArrayList<Object>();
    if (child == null)
      return ret.toArray();
    ret.add(root);
    if (child == root)
      return ret.toArray();
    int childType = 0;
    if (child instanceof List<?> && ((List) child).get(0) instanceof Einleitungsstelle) {
      childType = 1;
    }
    if (child instanceof Einleitungsstelle) {
      childType = 2;
    }
    if (child instanceof List<?> && ((List) child).get(0) instanceof Messstelle) {
      childType = 3;
    }
    if (child instanceof Messstelle) {
      childType = 4;
    }
    if (child instanceof List<?> && ((List) child).get(0) instanceof     Ueberwachungswert) {
      childType = 5;
    }
    if (child instanceof Ueberwachungswert) {
      childType = 6;
    }
    if (child instanceof List<?> && ((List) child).get(0) instanceof     Selbstueberwachungswert) {
      childType = 7;
    }
    if (child instanceof Selbstueberwachungswert) {
      childType = 8;
    }
    switch (childType) {
    // List of ESTs
    case 1: {
      ret.add(child);
      break;
    }
    // EST
    case 2: {
      List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
      ret.add(listOfEST);
      ret.add(child);
      break;
    }
    // List of MSTs
    case 3: {
      List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
      ret.add(listOfEST);
      // Find the EST containing the List of MSTs the child referes to
      for (Einleitungsstelle einleitungsstelle : listOfEST) {
        if (child == einleitungsstelle.getListOfMST()) {
          ret.add(einleitungsstelle);
          break;
        }
      }
      ret.add(child);
      break;
    }
    // MST
    case 4: {
       List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
       ret.add(listOfEST);
       // Find the EST containing the List of MSTs the child referes to
       for (Einleitungsstelle einleitungsstelle : listOfEST) {
          if (child == einleitungsstelle.getListOfMST()) {
            ret.add(einleitungsstelle.getListOfMST());
            break;
          }
       }
       ret.add(child);
       break;
    }
    // List of UEWs
    case 5: {
        List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
        ret.add(listOfEST);
        // Find the EST containing the List of MSTs the child referes to
       for (Einleitungsstelle einleitungsstelle : listOfEST) {
         ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST();
         if (child == listOfMST) {
           ret.add(listOfMST);
           for (Messstelle messstelle : listOfMST) {
             if (child == messstelle.getListOfUEW()) {
               ret.add(messstelle.getListOfUEW());
               break;
             }
           }
          break;
        }
      }
     break;
    }
    // UEW
    case 6: {
      List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
      ret.add(listOfEST);
      // Find the EST containing the List of MSTs the child referes to
      for (Einleitungsstelle einleitungsstelle : listOfEST) {
        ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST();
        if (child == listOfMST) {
          ret.add(listOfMST);
          for (Messstelle messstelle : listOfMST) {
            if (child == messstelle.getListOfUEW()) {
              ret.add(messstelle.getListOfUEW());
              break;
            }
          }
          break;
        }
      }
      ret.add(child);
      break;
    }
    // List of SUEWs
    case 7: {
      List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
      ret.add(listOfEST);
      // Find the EST containing the List of MSTs the child referes to
      for (Einleitungsstelle einleitungsstelle : listOfEST) {
        ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST();
        if (child == listOfMST) {
          ret.add(listOfMST);
          for (Messstelle messstelle : listOfMST) {
            if (child == messstelle.getListOfSUEW()) {
              ret.add(messstelle.getListOfSUEW());
              break;
            }
          }
          break;
        }
      }
      break;
    }
    // SUEW
    case 8: {
       List<Einleitungsstelle> listOfEST = ((Wasserrecht) (root)).getListOfEST();
       ret.add(listOfEST);
       // Find the EST containing the List of MSTs the child referes to
       for (Einleitungsstelle einleitungsstelle : listOfEST) {
          ArrayListModel<Messstelle> listOfMST = einleitungsstelle.getListOfMST();
          if (child == listOfMST) {
          ret.add(listOfMST);
          for (Messstelle messstelle : listOfMST) {
             if (child == messstelle.getListOfSUEW()) {
               ret.add(messstelle.getListOfSUEW());
               break;
             }
           }
           break;
         }
       }
       ret.add(child);
       break;
      }
      default:
      ret = null;
    }
    return ret.toArray();
    }
  }

...

    protected void fireTreeNodesRemoved(Object changed, Object path[], int     childIndecies[], Object children[]) {
      TreeModelEvent event = new TreeModelEvent(this, path, childIndecies, children);
      synchronized (listeners) {
      for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
        TreeModelListener tml = (TreeModelListener) e.nextElement();
        tml.treeNodesRemoved(event);
      }
      }
    }

...

}

4 个解决方案

#1


2  

You need to perform the node delete and the subsequent TreeModelListener.treeNodesRemoved event firing on the Event Dispatch Thread.

您需要执行节点删除和随后的TreeModelListener。在事件分派线程上启动treenodesremove事件。

To do this use:

这样使用:

SwingUtilities.invokeLater(
  new Runnable() 
  {
    public void run() 
    {
      //Delete and event firing logic goes here.
      ...
    }
  }
);

Doing this prevents Swing using the EDT to update the tree in the middle of your delete and the event firing tells the JTree control (which has added listeners) that the model has changed.

这样做可以防止Swing使用EDT更新删除中间的树,事件触发会告诉JTree控件(它添加了侦听器)模型已经更改。

#2


0  

Because we're in a hurry I haven't looked at your code yet. Your description sounds as if you're doing everything correctly and have thought of what's needed.

因为我们赶时间,我还没看过你的代码。你的描述听起来好像你做的每一件事都是正确的,并且已经想到了需要什么。

My guess at something you may not have considered: Is this tree model change happening in the Event Dispatch Thread (alias Swing worker thread)? If the change comes from another thread then chances are it won't be properly processed.

我对您可能没有考虑过的事情的猜测是:这种树模型更改发生在事件分派线程(别名Swing工作线程)中吗?如果更改来自另一个线程,那么它很可能不会被正确处理。

Just a stab in the dark, of course.

当然,只是在黑暗中捅了一刀。

#3


0  

Looks like you have an off-by-one bug in intervalRemoved.

看起来你在间隔中有一个一个一个的bug。

You aren't initializing the last value in indices array. It will autoinitialized to 0.

您没有初始化索引数组中的最后一个值。它将自动初始化为0。

@Override
public void intervalRemoved(ListDataEvent e) {
  int[] indices = new int[e.getIndex1() - e.getIndex0() + 1];
  for (int i = e.getIndex0(); i < e.getIndex1(); i++)
    indices[i - e.getIndex0()] = i;
    fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,     ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray());
}

Try instead "i <= e.getIndex1()":

试试“i <= e.getIndex1()”:

for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
    indices[i - e.getIndex0()] = i;
}

#4


0  

The method name is fireIntervalRemoved, so try calling it after removing:

方法名为fireintervalremove,请在删除后调用:

public E remove(int index) {
    E removedElement = super.remove(index);
    fireIntervalRemoved(index, index);
    return removedElement;
}

It's the way I've done, and always worked (maybe added some checking).
(sorry if I missed something, did'nt get time to analise/test your code...)

这是我的工作方式,而且一直都很有效(也许还增加了一些检查)。(抱歉,如果我漏掉了什么,没有时间对你的代码进行分析/测试…)

#1


2  

You need to perform the node delete and the subsequent TreeModelListener.treeNodesRemoved event firing on the Event Dispatch Thread.

您需要执行节点删除和随后的TreeModelListener。在事件分派线程上启动treenodesremove事件。

To do this use:

这样使用:

SwingUtilities.invokeLater(
  new Runnable() 
  {
    public void run() 
    {
      //Delete and event firing logic goes here.
      ...
    }
  }
);

Doing this prevents Swing using the EDT to update the tree in the middle of your delete and the event firing tells the JTree control (which has added listeners) that the model has changed.

这样做可以防止Swing使用EDT更新删除中间的树,事件触发会告诉JTree控件(它添加了侦听器)模型已经更改。

#2


0  

Because we're in a hurry I haven't looked at your code yet. Your description sounds as if you're doing everything correctly and have thought of what's needed.

因为我们赶时间,我还没看过你的代码。你的描述听起来好像你做的每一件事都是正确的,并且已经想到了需要什么。

My guess at something you may not have considered: Is this tree model change happening in the Event Dispatch Thread (alias Swing worker thread)? If the change comes from another thread then chances are it won't be properly processed.

我对您可能没有考虑过的事情的猜测是:这种树模型更改发生在事件分派线程(别名Swing工作线程)中吗?如果更改来自另一个线程,那么它很可能不会被正确处理。

Just a stab in the dark, of course.

当然,只是在黑暗中捅了一刀。

#3


0  

Looks like you have an off-by-one bug in intervalRemoved.

看起来你在间隔中有一个一个一个的bug。

You aren't initializing the last value in indices array. It will autoinitialized to 0.

您没有初始化索引数组中的最后一个值。它将自动初始化为0。

@Override
public void intervalRemoved(ListDataEvent e) {
  int[] indices = new int[e.getIndex1() - e.getIndex0() + 1];
  for (int i = e.getIndex0(); i < e.getIndex1(); i++)
    indices[i - e.getIndex0()] = i;
    fireTreeNodesRemoved(e.getSource(), getPathToRoot(e.getSource()), indices,     ((ArrayListModel<?>)e.getSource()).subList(e.getIndex0(), e.getIndex1()+1).toArray());
}

Try instead "i <= e.getIndex1()":

试试“i <= e.getIndex1()”:

for (int i = e.getIndex0(); i <= e.getIndex1(); i++) {
    indices[i - e.getIndex0()] = i;
}

#4


0  

The method name is fireIntervalRemoved, so try calling it after removing:

方法名为fireintervalremove,请在删除后调用:

public E remove(int index) {
    E removedElement = super.remove(index);
    fireIntervalRemoved(index, index);
    return removedElement;
}

It's the way I've done, and always worked (maybe added some checking).
(sorry if I missed something, did'nt get time to analise/test your code...)

这是我的工作方式,而且一直都很有效(也许还增加了一些检查)。(抱歉,如果我漏掉了什么,没有时间对你的代码进行分析/测试…)