【问题标题】:Best way to stop a JTree selection change from happening?阻止 JTree 选择更改发生的最佳方法是什么?
【发布时间】:2010-09-20 15:47:50
【问题描述】:

我有一个对话框,其中jtree中的每个条目都有其在不同面板中的相应选项,在选择变化时会更新。如果其中一个条目的选项设置为无效状态,当用户尝试更改为树中的不同条目时,我希望出现一个错误对话框并且不更改选择。

我尝试在 JTree 上使用 valueChangeListener 来执行此操作,但目前如果出现错误,则必须让 valueChanged 方法对旧选择调用“setSelectionRow”。为了避免 StackOverflow,我在执行此操作之前将布尔值“isError”设置为 true,以便忽略新的 valueChanged 事件。不知何故,我有直觉认为这不是最好的解决方案。 ;-)

我该怎么做呢?对于这种情况,有没有好的设计模式?

【问题讨论】:

    标签: java swing design-patterns error-handling


    【解决方案1】:

    我没有找到更好的方法,但这种方法对我来说很好用。 我知道在 Delphi 中这是一个非常方便的事件:“在更改选择之前”,您可以非常轻松地停止更改选择。

    这是我的防止无限递归问题的java代码

        navTree.addTreeSelectionListener(new TreeSelectionListener() {
    
            boolean treeSelectionListenerEnabled = true;
    
            public void valueChanged(TreeSelectionEvent e) {
                if (treeSelectionListenerEnabled) {
                    if (ok to change selection...) {
                        ...
                    } else {
                        TreePath treePath = e.getOldLeadSelectionPath();
                        treeSelectionListenerEnabled = false;
                        try {
                            // prevent from leaving the last visited node
                            navTree.setSelectionPath(treePath);
                        } finally {
                            treeSelectionListenerEnabled = true;
                        }
                    }
                }
            }
        });
    

    始终记得删除您添加的所有侦听器,以防止内存泄漏。

    这是另一种方法:

    private class VetoableTreeSelectionModel extends DefaultTreeSelectionModel {
        public void setSelectionPath(TreePath path){
            if (allow selection change?) {
                super.setSelectionPath(path);
            }
        }
    }
    {
        navTree.setSelectionModel(new VetoableTreeSelectionModel());
    }
    

    【讨论】:

      【解决方案2】:

      不确定这是最佳实践,但也许您可以在要验证的组件上放置一个 FocusListener...在调用事件时调用您的验证,然后如果您不想要焦点,则使用该事件因为验证失败而被移动?

      稍后编辑:

      至少对于 java 8(我没有检查早期版本),这个解决方案不起作用,因为 FocusEvent 似乎不是一个低级事件。因此不能食用。参见方法 AWTEvent.consume()

      【讨论】:

      • 这是理想的方法,IMO
      【解决方案3】:

      这是我的解决方案。

      在 JTree 子类中:

      protected void processMouseEvent(MouseEvent e) {
              TreePath selPath = getPathForLocation(e.getX(), e.getY());
              try {
                  fireVetoableChange(LEAD_SELECTION_PATH_PROPERTY, getLeadSelectionPath(), selPath);
              }
              catch (PropertyVetoException ex) {
                  // OK, we do not want change to happen
                  return;
              }
      
              super.processMouseEvent(e);
      }
      

      然后在树中使用类:

      VetoableChangeListener vcl = new VetoableChangeListener() {
      
              public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
                  if ( evt.getPropertyName().equals(JTree.LEAD_SELECTION_PATH_PROPERTY) ) {
                      try {
                          <some code logic that has to be satisfied>
                      } catch (InvalidInputException e) {
                          throw new PropertyVetoException("", evt);
                      }
      
                  }
              }
          };
          tree.addVetoableChangeListener(vcl);
      

      该机制在尽可能早的地方开始。鼠标动作被截获,将要选择的路径通告给 VetoableChangeListeners。在具体的 VCL 中,检查更改属性,如果是前导选择,则检查否决逻辑。如果需要否决,VCL 会抛出 PropertyVetoException,否则鼠标事件处理将照常进行并进行选择。 简而言之,这使得潜在客户选择属性成为约束属性。

      【讨论】:

        【解决方案4】:

        设置一个实现适当语义的 TreeSelectionModel。

        【讨论】:

          【解决方案5】:

          下面是一个实现 TreeSelectionModel 的示例,它包装了另一个 TreeSelectionModel 但允许否决选择:

          public class VetoableTreeSelectionModel implements TreeSelectionModel
          {
             private final ListenerList<VetoableTreeSelectionListener> m_vetoableTreeSelectionListeners = new ListenerList<VetoableTreeSelectionListener>();
          
             private final DefaultTreeSelectionModel m_treeSelectionModel = new DefaultTreeSelectionModel();
          
             /**
              * {@inheritDoc}
              */
             public void addTreeSelectionListener(final TreeSelectionListener listener)
             {
                m_treeSelectionModel.addTreeSelectionListener(listener);
             }
          
             /**
              * {@inheritDoc}
              */
             public void removeTreeSelectionListener(final TreeSelectionListener listener)
             {
                m_treeSelectionModel.removeTreeSelectionListener(listener);
             }
          
             /**
              * Add a vetoable tree selection listener
              *
              * @param listener the listener
              */
             public void addVetoableTreeSelectionListener(final VetoableTreeSelectionListener listener)
             {
                m_vetoableTreeSelectionListeners.addListener(listener);
             }
          
             /**
              * Remove a vetoable tree selection listener
              *
              * @param listener the listener
              */
             public void removeVetoableTreeSelectionListener(final VetoableTreeSelectionListener listener)
             {
                m_vetoableTreeSelectionListeners.removeListener(listener);
             }
          
             /**
              * {@inheritDoc}
              */
             public void addPropertyChangeListener(final PropertyChangeListener listener)
             {
                m_treeSelectionModel.addPropertyChangeListener(listener);
             }
          
             /**
              * {@inheritDoc}
              */
             public void removePropertyChangeListener(final PropertyChangeListener listener)
             {
                m_treeSelectionModel.removePropertyChangeListener(listener);
             }
          
             /**
              * {@inheritDoc}
              */
             public void addSelectionPath(final TreePath path)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToAddSelectionPath(path);
                      }});
          
                   m_treeSelectionModel.addSelectionPath(path);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void addSelectionPaths(final TreePath[] paths)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToAddSelectionPaths(paths);
                      }});
          
                   m_treeSelectionModel.addSelectionPaths(paths);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void clearSelection()
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToClearSelection();
                      }});
          
                   m_treeSelectionModel.clearSelection();
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public TreePath getLeadSelectionPath()
             {
                return m_treeSelectionModel.getLeadSelectionPath();
             }
          
             /**
              * {@inheritDoc}
              */
             public int getLeadSelectionRow()
             {
                return m_treeSelectionModel.getLeadSelectionRow();
             }
          
             /**
              * {@inheritDoc}
              */
             public int getMaxSelectionRow()
             {
                return m_treeSelectionModel.getMaxSelectionRow();
             }
          
             /**
              * {@inheritDoc}
              */
             public int getMinSelectionRow()
             {
                return m_treeSelectionModel.getMinSelectionRow();
             }
          
             /**
              * {@inheritDoc}
              */
             public RowMapper getRowMapper()
             {
                return m_treeSelectionModel.getRowMapper();
             }
          
             /**
              * {@inheritDoc}
              */
             public int getSelectionCount()
             {
                return m_treeSelectionModel.getSelectionCount();
             }
          
             public int getSelectionMode()
             {
                return m_treeSelectionModel.getSelectionMode();
             }
          
             /**
              * {@inheritDoc}
              */
             public TreePath getSelectionPath()
             {
                return m_treeSelectionModel.getSelectionPath();
             }
          
             /**
              * {@inheritDoc}
              */
             public TreePath[] getSelectionPaths()
             {
                return m_treeSelectionModel.getSelectionPaths();
             }
          
             /**
              * {@inheritDoc}
              */
             public int[] getSelectionRows()
             {
                return m_treeSelectionModel.getSelectionRows();
             }
          
             /**
              * {@inheritDoc}
              */
             public boolean isPathSelected(final TreePath path)
             {
                return m_treeSelectionModel.isPathSelected(path);
             }
          
             /**
              * {@inheritDoc}
              */
             public boolean isRowSelected(final int row)
             {
                return m_treeSelectionModel.isRowSelected(row);
             }
          
             /**
              * {@inheritDoc}
              */
             public boolean isSelectionEmpty()
             {
                return m_treeSelectionModel.isSelectionEmpty();
             }
          
             /**
              * {@inheritDoc}
              */
             public void removeSelectionPath(final TreePath path)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutRemoveSelectionPath(path);
                      }});
          
                   m_treeSelectionModel.removeSelectionPath(path);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void removeSelectionPaths(final TreePath[] paths)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutRemoveSelectionPaths(paths);
                      }});
          
                   m_treeSelectionModel.removeSelectionPaths(paths);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void resetRowSelection()
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToResetRowSelection();
                      }});
          
                   m_treeSelectionModel.resetRowSelection();
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void setRowMapper(final RowMapper newMapper)
             {
                m_treeSelectionModel.setRowMapper(newMapper);
             }
          
             /**
              * {@inheritDoc}
              */
             public void setSelectionMode(final int mode)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToSetSelectionMode(mode);
                      }});
          
                   m_treeSelectionModel.setSelectionMode(mode);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void setSelectionPath(final TreePath path)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToSetSelectionPath(path);
                      }});
          
                   m_treeSelectionModel.setSelectionPath(path);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             public void setSelectionPaths(final TreePath[] paths)
             {
                try
                {
                   m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
                      public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
                      {
                         listener.aboutToSetSelectionPaths(paths);
                      }});
          
                   m_treeSelectionModel.setSelectionPaths(paths);
                }
                catch (final EventVetoedException e)
                {
                   return;
                }
             }
          
             /**
              * {@inheritDoc}
              */
             @Override
             public String toString()
             {
                return m_treeSelectionModel.toString();
             }
          

          }

          下面是听者:

          public interface VetoableTreeSelectionListener
          {
             /**
              * About to add a path to the selection
              *
              * @param path the path to add
              *
              * @throws EventVetoedException
              */
             void aboutToAddSelectionPath(TreePath path) throws EventVetoedException;
          
             /**
              * About to add paths to the selection
              *
              * @param paths the paths to add
              *
              * @throws EventVetoedException
              */
             void aboutToAddSelectionPaths(TreePath[] paths) throws EventVetoedException;
          
             /**
              * About to clear selection
              *
              * @throws EventVetoedException
              */
             void aboutToClearSelection() throws EventVetoedException;
          
             /**
              * About to remove a selection path
              *
              * @param path the path
              *
              * @throws EventVetoedException
              */
             void aboutRemoveSelectionPath(TreePath path) throws EventVetoedException;
          
             /**
              * About to remove multiple selection paths
              *
              * @param paths the paths
              *
              * @throws EventVetoedException
              */
             void aboutRemoveSelectionPaths(TreePath[] paths) throws EventVetoedException;
          
             /**
              * About to reset the row selection
              *
              * @throws EventVetoedException
              */
             void aboutToResetRowSelection() throws EventVetoedException;
          
             /**
              * About to set the selection mode
              *
              * @param mode the selection mode
              *
              * @throws EventVetoedException
              */
             void aboutToSetSelectionMode(int mode) throws EventVetoedException;
          
             /**
              * About to set the selection path
              *
              * @param path the path
              *
              * @throws EventVetoedException
              */
             void aboutToSetSelectionPath(TreePath path) throws EventVetoedException;
          
             /**
              * About to set the selection paths
              *
              * @param paths the paths
              *
              * @throws EventVetoedException
              */
             void aboutToSetSelectionPaths(TreePath[] paths) throws EventVetoedException;
          }
          

          您可以使用自己的 ListenerList 实现,但您明白了……

          【讨论】:

            【解决方案6】:

            为了防止选择,我只是继承了 DefaultTreeSelectionModel 并覆盖了所有方法来检查我不想被选择的对象(下面我的示例中的“DisplayRepoOwner”实例)。如果对象可以被选中,我调用 super 方法;否则我没有。我将 JTree 的选择模型设置为该子类的一个实例。

            public class MainTreeSelectionModel extends DefaultTreeSelectionModel {
            public void addSelectionPath(TreePath path) {
                if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
                    return;
                }
                super.addSelectionPath(path);
            }
            public void addSelectionPaths(TreePath[] paths) {
                for (TreePath tp : paths) {
                    if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
                        return;
                    }
                }
                super.addSelectionPaths(paths);
            }
            public void setSelectionPath(TreePath path) {
                if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
                    return;
                }
                super.setSelectionPath(path);
            }
            public void setSelectionPaths(TreePath[] paths) {
                for (TreePath tp : paths) {
                    if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
                        return;
                    }
                }
                super.setSelectionPaths(paths);
            }
            

            }

            【讨论】:

              【解决方案7】:

              在调查同一问题的解决方案时偶然发现了此线程。首先,让我告诉你一些没有用的事情。我试图用树注册 MouseListeners 和所有这些。问题是 TreeUI 的鼠标侦听器在我的 JTree 之前就开始处理事件,这意味着设置标志或类似的东西为时已晚。除此之外,这个解决方案产生了一些丑陋的代码,我通常会避免它。

              现在是实际解决方案!
              在使用几个 Thread.dumpStack() 调用来获取堆栈转储后,我找到了我想要覆盖的方法。我扩展了 BasicTreeUI 并覆盖了“受保护的 void selectPathForEvent(TreePath 路径,MouseEvent 事件)”。

              这将使您能够在选择实际发生之前访问导致选择的鼠标事件。然后,您可以使用 event.consume() 所需的任何逻辑,如果要停止选择则返回,执行所需的任何选择或通过调用 super.selectPathForEvent(path, event); 将其传递给默认处理;

              只需记住设置您在 JTree 中创建的 UI。这个错误浪费了我生命中的几分钟;-)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-10-26
                相关资源
                最近更新 更多