Java Swing:当右键单击菜单在未进行选择的情况下被*取消*时,如何触发事件?

Java Swing:当右键单击菜单在未进行选择的情况下被*取消*时,如何触发事件?,java,swing,Java,Swing,我已经浏览谷歌两天了,有点不知所措 当右键单击被触发时,我可以轻松触发事件。这不是问题。我的问题是,如果/当右键菜单在没有进行任何选择的情况下被取消时,我还需要触发类似的事件 这归结为一个业务需求:表中有一个项目列表,该表最左边的列中有复选框,以显示已选择的行。由于这些复选框是确定要处理哪些行的UI键,因此任何处理操作都会查看复选框的状态,而不是已选择哪些行 用户可以直接选择复选框对一行或多行执行操作(通过窗口页脚中的按钮或通过右键单击菜单)。但是,还需要通过右键单击事件本身进行单个活动选择,而

我已经浏览谷歌两天了,有点不知所措

当右键单击被触发时,我可以轻松触发事件。这不是问题。我的问题是,如果/当右键菜单在没有进行任何选择的情况下被取消时,我还需要触发类似的事件

这归结为一个业务需求:表中有一个项目列表,该表最左边的列中有复选框,以显示已选择的行。由于这些复选框是确定要处理哪些行的UI键,因此任何处理操作都会查看复选框的状态,而不是已选择哪些行

用户可以直接选择复选框对一行或多行执行操作(通过窗口页脚中的按钮或通过右键单击菜单)。但是,还需要通过右键单击事件本身进行单个活动选择,而无需显式选中复选框

在这种情况下,触发右键单击条目将自动选中该复选框(无论是否已选中)。但是,如果用户决定不对该行执行任何操作,则需要将该复选框恢复到其先前的状态(选中或未选中,通过右键单击触发)

通过只使用原始状态,我不必关心原始状态,只需要在放弃右键单击时重新应用它。这允许成功恢复选中和未选中的原始状态。从本质上说,这允许取消意外的右键单击,而无需每次都在表格行中勾选复选框,这将是不希望出现的行为

不幸的是,我在互联网上还没有找到任何关于取消右键菜单的例子,以及如何将一项行动与该事件挂钩

到目前为止,我的代码是这样的:

private void setListenerForItemsTable() {
    tblItems.addMouseListener( new MouseAdapter() {
        public void mousePressed( MouseEvent evt ) {
            if ( view.showMaybePopup( evt ) ) {
                rightClickEvent(); // Fires needed code on right-click popup appearance.
            }
        }

        public void mouseReleased( MouseEvent evt ) {
            if ( view.showMaybePopup( evt ) ) {
                rightClickEvent();
            }
        }
    } );
}
这在创建右键单击弹出窗口方面非常有效。它工作得很好

仅供参考,该代码还为右键单击菜单本身提供了一个侦听器,允许处理该右键单击菜单中任何项目的选择:

private void setListenerForRightClickMenu() {
    // Preview
    mnuPreviewItem.addActionListener( (e)
            -> {
        previewItem();
    } );

    // Resend Fax
    mnuResendItem.addActionListener( (e)
            -> {
        resendItem();
    } );

    /// etc...
}
但是,任何尝试在右键单击菜单上使用类似于
addFocusListener
的内容的行为都会导致空指针异常,其方式与将
addMouseListener
附加到表的方式相同;大概是因为在实际触发右键单击之前,右键单击菜单不可用


建议?

简而言之,我的解决方案是向上面的
setListenerForItemsTable()
方法添加第二个侦听器。特别是,
addPopupMenuListener()
侦听器:

tblItems.addPopupMenuListener( new PopupMenuListener() {
    @Override
    public void popupMenuWillBecomeVisible( PopupMenuEvent evt ) {
        // set checkbox
        if( rightClickRow >= 0 ) {
            mdlItems.setValueAt( true, rightClickRow, model.COL_CHECK );
        }
    }
    @Override
    public void popupMenuCanceled( PopupMenuEvent evt ) {
        // return checkbox to prior value
        if( rightClickRow >= 0 ) {
            mdlItems.setValueAt( rightClickValue, rightClickRow, model.COL_CHECK );
        }
    }
} );
但是,有一个问题:如果表小于包含它的窗口,则右键单击可能实际上不涉及活动选定的行(如果在该区域内右键单击,但不涉及表本身的任何行)。因此,您需要以实际右键单击的任何行(如果存在)为目标,然后保存该行号及其复选框的当前状态(如果放弃右键单击,则为反转)

因此,在类的根目录下,我们设置了一些默认容器来保存这些值:

private Boolean rightClickValue = false;
private int rightClickRow = -1;
然后以
addMouseListener()
为目标,查看我们是否实际单击了一行,如果是,则记录该行及其复选框的状态:

tblItems.addMouseListener( new MouseAdapter() {
    @Override
    public void mouseClicked( MouseEvent evt ) {
        rightClickRow = tblItems.rowAtPoint( evt.getPoint() );
        rightClickValue = mdlItems.getValueAt(rightClickRow, model.COL_CHECK) == Boolean.TRUE;
    }
}
最后,我们有以下几点:

    /**
     * Listener to decide between showing right-click menu OR calling Preview Item after double clicking a row
     */
    private void setListenerForItemsTable() {

        tblItems.addMouseListener( new MouseAdapter() {
            @Override
            public void mouseClicked( MouseEvent evt ) {
                isPreview( evt );
            }

            @Override
            public void mousePressed( MouseEvent evt ) {
                processRightClick( evt );
            }

            @Override
            public void mouseReleased( MouseEvent evt ) {
                processRightClick( evt );
            }
        } );
        itmMenu.addPopupMenuListener( new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible( PopupMenuEvent evt ) {
                processCheckbox( true );
            }

            @Override
            public void popupMenuWillBecomeInvisible( PopupMenuEvent evt ) {
                // do nothing
            }

            @Override
            public void popupMenuCanceled( PopupMenuEvent evt ) {
                processCheckbox( rightClickValue );
            }
        } );
    }

    /**
     * Checks whether a preview of the line item is warranted, and provides it
     *
     * @param evt
     */
    private void isPreview( MouseEvent evt ) {
        if ( isRow( evt ) && isDblClick( evt ) ) {
            previewItem();
        }
    }

    /**
     * Checks if the item that was clicked on was a viable table row or not
     *
     * @param evt the MouseEvent
     *
     * @return Boolean
     */
    private Boolean isRow( MouseEvent evt ) {
        return getClickedRow( evt ) >= 0;
    }

    /**
     * Checks if the click action was a double click or not
     *
     * @param evt the MouseEvent
     *
     * @return Boolean
     */
    private Boolean isDblClick( MouseEvent evt ) {
        return evt.getClickCount() == 2;
    }

    /**
     * Gets the index of the table row that was clicked on
     *
     * @param evt the MouseEvent
     *
     * @return Integer
     */
    private Integer getClickedRow( MouseEvent evt ) {
        return tblItems.rowAtPoint( evt.getPoint() );
    }

    /**
     * Bringing everything together: storing the row index and value of the checkbox, and triggering the pop-up menu
     *
     * @param evt the MouseEvent
     */
    private void processRightClick( MouseEvent evt ) {
        int row = getClickedRow( evt );
        if ( row >= 0 ) {
            rightClickRow = row;
            rightClickValue = ( Boolean.TRUE ).equals( mdlItems.getValueAt( row, model.COL_CHECK ) );
        }
        view.showMaybePopup( evt );
    }

    /**
     * Setting or unsetting the checkbox in the active row
     *
     * @param value the desired value to apply to the checkbox
     */
    private void processCheckbox( Boolean value ) {
        if ( rightClickRow >= 0 ) {
            mdlItems.setValueAt( value, rightClickRow, model.COL_CHECK );
        }
    }

简而言之,我的解决方案是向上面的
setListenerForItemsTable()
方法添加第二个侦听器。特别是,
addPopupMenuListener()
侦听器:

tblItems.addPopupMenuListener( new PopupMenuListener() {
    @Override
    public void popupMenuWillBecomeVisible( PopupMenuEvent evt ) {
        // set checkbox
        if( rightClickRow >= 0 ) {
            mdlItems.setValueAt( true, rightClickRow, model.COL_CHECK );
        }
    }
    @Override
    public void popupMenuCanceled( PopupMenuEvent evt ) {
        // return checkbox to prior value
        if( rightClickRow >= 0 ) {
            mdlItems.setValueAt( rightClickValue, rightClickRow, model.COL_CHECK );
        }
    }
} );
但是,有一个问题:如果表小于包含它的窗口,则右键单击可能实际上不涉及活动选定的行(如果在该区域内右键单击,但不涉及表本身的任何行)。因此,您需要以实际右键单击的任何行(如果存在)为目标,然后保存该行号及其复选框的当前状态(如果放弃右键单击,则为反转)

因此,在类的根目录下,我们设置了一些默认容器来保存这些值:

private Boolean rightClickValue = false;
private int rightClickRow = -1;
然后以
addMouseListener()
为目标,查看我们是否实际单击了一行,如果是,则记录该行及其复选框的状态:

tblItems.addMouseListener( new MouseAdapter() {
    @Override
    public void mouseClicked( MouseEvent evt ) {
        rightClickRow = tblItems.rowAtPoint( evt.getPoint() );
        rightClickValue = mdlItems.getValueAt(rightClickRow, model.COL_CHECK) == Boolean.TRUE;
    }
}
最后,我们有以下几点:

    /**
     * Listener to decide between showing right-click menu OR calling Preview Item after double clicking a row
     */
    private void setListenerForItemsTable() {

        tblItems.addMouseListener( new MouseAdapter() {
            @Override
            public void mouseClicked( MouseEvent evt ) {
                isPreview( evt );
            }

            @Override
            public void mousePressed( MouseEvent evt ) {
                processRightClick( evt );
            }

            @Override
            public void mouseReleased( MouseEvent evt ) {
                processRightClick( evt );
            }
        } );
        itmMenu.addPopupMenuListener( new PopupMenuListener() {
            @Override
            public void popupMenuWillBecomeVisible( PopupMenuEvent evt ) {
                processCheckbox( true );
            }

            @Override
            public void popupMenuWillBecomeInvisible( PopupMenuEvent evt ) {
                // do nothing
            }

            @Override
            public void popupMenuCanceled( PopupMenuEvent evt ) {
                processCheckbox( rightClickValue );
            }
        } );
    }

    /**
     * Checks whether a preview of the line item is warranted, and provides it
     *
     * @param evt
     */
    private void isPreview( MouseEvent evt ) {
        if ( isRow( evt ) && isDblClick( evt ) ) {
            previewItem();
        }
    }

    /**
     * Checks if the item that was clicked on was a viable table row or not
     *
     * @param evt the MouseEvent
     *
     * @return Boolean
     */
    private Boolean isRow( MouseEvent evt ) {
        return getClickedRow( evt ) >= 0;
    }

    /**
     * Checks if the click action was a double click or not
     *
     * @param evt the MouseEvent
     *
     * @return Boolean
     */
    private Boolean isDblClick( MouseEvent evt ) {
        return evt.getClickCount() == 2;
    }

    /**
     * Gets the index of the table row that was clicked on
     *
     * @param evt the MouseEvent
     *
     * @return Integer
     */
    private Integer getClickedRow( MouseEvent evt ) {
        return tblItems.rowAtPoint( evt.getPoint() );
    }

    /**
     * Bringing everything together: storing the row index and value of the checkbox, and triggering the pop-up menu
     *
     * @param evt the MouseEvent
     */
    private void processRightClick( MouseEvent evt ) {
        int row = getClickedRow( evt );
        if ( row >= 0 ) {
            rightClickRow = row;
            rightClickValue = ( Boolean.TRUE ).equals( mdlItems.getValueAt( row, model.COL_CHECK ) );
        }
        view.showMaybePopup( evt );
    }

    /**
     * Setting or unsetting the checkbox in the active row
     *
     * @param value the desired value to apply to the checkbox
     */
    private void processCheckbox( Boolean value ) {
        if ( rightClickRow >= 0 ) {
            mdlItems.setValueAt( value, rightClickRow, model.COL_CHECK );
        }
    }

可能的副本我不认为它是副本。我的问题甚至比这个问题更为根本:首先,我试图将某种侦听器附加到右键单击菜单,但发现自己无法(和/或不知道如何)这样做。可能的重复我不认为它是重复的。我的问题甚至比这个问题更为根本:首先,我试图在右键菜单上附加某种侦听器,但发现自己无法(和/或不知道如何)这样做。