我有一个对话框,其中JTree中的每个条目在不同的面板中都有相应的选项,当选择更改时会更新。如果其中一个条目的选项设置为无效状态,当用户尝试更改为树中的其他条目时,我希望有一个错误对话框并且选择不会更改。
我尝试在JTree上使用valueChangeListener执行此操作,但是如果出现错误,则当前必须将valueChanged方法调用“setSelectionRow”到旧选择。所以我没有获得StackOverflow,我在执行此操作之前将布尔“isError”设置为true,以便我可以忽略新的valueChanged事件。不知怎的,我有直觉,这不是最好的解决方案。 ; - )
我该怎么做呢?对于这样的情况,有一个好的设计模式吗?
答案 0 :(得分:6)
我没有找到更好的方法,但这种方法对我来说很好。 我知道在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());
}
答案 1 :(得分:3)
不确定这是最佳做法,但也许您可以将FocusListener放在要验证的组件上...在调用事件时调用验证然后如果您不想要焦点则使用then then事件要移动,因为验证失败了吗?
稍后编辑:
至少使用java 8(我没有检查早期版本),这个解决方案不起作用,因为FocusEvent似乎不是一个低级事件。因此它无法消费。请参见方法AWTEvent.consume()
答案 2 :(得分: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);
}
然后在树中使用class:
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,否则鼠标事件处理将照常进行并进行选择。 简而言之,这使得潜在客户选择属性成为约束属性。
答案 3 :(得分:0)
设置一个实现相应语义的TreeSelectionModel。
答案 4 :(得分:0)
以下是实现TreeSelectionModel的示例,该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实现,但你明白了......
答案 5 :(得分:0)
为了防止选择,我只是将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);
}
}
答案 6 :(得分:-1)
在调查同一问题的解决方案时偶然发现了这个问题。首先,让我告诉你一些不起作用的事情。我试图用树注册MouseListeners和所有这些。问题是TreeUI的鼠标监听器在我的JTree之前进入事件的过程,这意味着设置一个标志或类似的东西为时已晚。除此之外,这个解决方案产生了一些丑陋的代码,我通常会避免它。
现在为实际解决方案!
在使用一些Thread.dumpStack()调用来获取堆栈转储后,我找到了我想要覆盖的方法。我扩展了BasicTreeUI并覆盖了“protected void selectPathForEvent(TreePath path,MouseEvent event)”。
这将使您能够访问在选择实际发生之前导致选择的鼠标事件。然后,您可以使用event.consume()所需的任何逻辑,如果要停止选择,返回所需的任何选择,或通过调用super.selectPathForEvent(path,event)将其传递给默认处理;
请记住设置您在JTree中创建的UI。这个错误浪费了我生命中的几分钟; - )