Java Swing : how to fire an event when a right-click menu gets *dismissed* without a selection having been made?

标签 java swing

我已经在 Google 上搜索了两天了,有点迷失了。

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

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

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

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

通过仅引入原始状态,我不必关心原始状态,只需要在放弃右键单击时重新应用它即可。这使得选中和未选中的原始状态都可以成功恢复。从本质上讲,这允许意外的右键单击被取消,而不会每次都在该表格行中勾选复选框 - 这将是不受欢迎的行为。

不幸的是,我在互联网上没有找到任何示例来讨论针对右键单击菜单的解除以及如何将操作 Hook 到该事件中。

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

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 );
        }
    }

关于Java Swing : how to fire an event when a right-click menu gets *dismissed* without a selection having been made?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55009202/

相关文章:

Java Swing 编程结构 : are listeners supposed to be the source of almost all Swing components?

java - 系统托盘文本而不是图标

java - 如何解析时间范围输入?

java - 使用埃拉托色尼筛法寻找第 n 个素数

Java - 使用 fireTableDataChanged() 更新 JTable;

java - 尝试更新 JLabel 的内容

Java Swing : trouble understanding adding image to JLabel

java - 比较数组列表中的元素

java - 异步 pig 作业提交

java - 为我的 webapp 自动添加 "www"子域