【问题标题】:Sharing Components with multiple containers与多个容器共享组件
【发布时间】:2026-01-28 07:20:05
【问题描述】:

我有 JTabbedPane,每个选项卡都有一个 JTextPane。

每个 JTextPane 都有一个弹出菜单,但我希望它们都共享完全相同的弹出菜单。为什么?因为当我切换标签时,我希望在每个弹出窗口上突出显示相同的选项。

我该怎么做?我尝试向每个窗格添加一个静态 PopupMenu 实例,但是当我将它添加到一个窗格时,它会从其他窗格中消失..

编辑:在以下 SSCCE 中,当在 TabOne 上选中复选框时,在 TabTwo 上未选中。我希望在两个选项卡上都检查它。除该选项卡外,所有其他选项对于每个选项卡都可以是唯一的。

SSCCE:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;

import javax.swing.*;


public class Main {

    public static void main(String[] Args) {
        final Main M = new Main();

        try {
            SwingUtilities.invokeAndWait(new Runnable() {

                @Override
                public void run() {
                    JFrame F = new JFrame("SSCCE");
                    JTabbedPane Pane = new JTabbedPane();
                    Pane.addTab("TabOne", M.new DebugBox(500, 500));
                    Pane.addTab("TabTwo", M.new DebugBox(500, 500));

                    F.setLayout(new BorderLayout());
                    F.add(Pane, BorderLayout.NORTH);
                    F.pack();
                    F.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    F.setVisible(true);
                }

            });
        } catch (InvocationTargetException | InterruptedException e) {
            e.printStackTrace();
        }
    }


    public class DebugBox extends JTextPane {

        private JScrollPane ScrollPane = null;
        private final JPopupMenu Menu = new JPopupMenu();
        private static final long serialVersionUID = 7731036968185936516L;

        public DebugBox(int Width, int Height) {
            this.ScrollPane = new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            this.setPreferredSize(new Dimension(Width, Height));

            JCheckBoxMenuItem Debug = new JCheckBoxMenuItem(new AbstractAction() {
                private static final long serialVersionUID = -336209978671944858L;

                @Override
                public void actionPerformed(ActionEvent e) {
                                 //DO something here that affects ALL the DebugBoxes because they all share this Menu Option somehow :S
                                 //Change name of this menu option, all instances have their names changed too.
                }
            });

            JMenuItem Copy = new JMenuItem(new AbstractAction() {
                private static final long serialVersionUID = -6774461986513304498L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    DebugBox.this.copy();
                }
            });

            JMenuItem Clear = new JMenuItem(new AbstractAction() {
                private static final long serialVersionUID = -5567371173360543484L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    DebugBox.this.setText(null);
                }
            });

            JMenuItem SelectAll = new JMenuItem(new AbstractAction() {
                private static final long serialVersionUID = -8792250195980016624L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    DebugBox.this.selectAll();
                }
            });

            this.Menu.add(Copy);
            this.Menu.add(Clear);
            this.Menu.add(SelectAll);
            this.Menu.add(Debug);

            Copy.setText("Copy");
            Clear.setText("Clear");
            SelectAll.setText("Select All");
            Debug.setText("Show Debug Box");
            this.setEditable(false);
            this.add(this.Menu);

            this.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    if (e.isPopupTrigger()) {
                        DebugBox.this.Menu.show(DebugBox.this, e.getX(), e.getY());
                    }
                }
            });
        }
    }
}

【问题讨论】:

  • 您能否提供SSCCE您的工作流程...
  • 可能重复:*.com/questions/4620601/…。您能否根据@MadProgrammer 展示您如何处理弹出菜单
  • 写完一个又快又脏的 SSCCE。我刚刚编辑了帖子。
  • 如果您希望其他人阅读您的代码,请使用标准 Java 命名约定。变量名不应以大写字符开头。
  • 嗯.. 对不起。我使用 C、C++、ASM 和所有其他语言进行编程。当许多其他语言使用与 Java 完全不同的约定时,很难仅仅“切换”约定。我会试试的。

标签: java swing jtabbedpane jpopupmenu


【解决方案1】:

如果您想共享一个对象,那么您需要创建该对象并将该对象作为参数传递给您的其他类。比如:

JPopupMenu popup = createSharedPopupMenu();
Pane.addTab("TabOne", M.new DebugBox(500, 500, popup));
Pane.addTab("TabTwo", M.new DebugBox(500, 500, popup));

然后您需要创建适用于任何文本组件的通用操作。 DefaultEditorKit 为您提供了其中一些操作:

public JPopupMenu createSharedPopupMenu()
{
    JPopupMenu popup = new JPopupMenu()

    JMenuItem copy = new JMenuItem( new DefaultEditorKit.CopyAction() );
    popup.add( copy );
    ...

    return popup.
}

如果编辑器工具包没有为您提供 Action,那么您需要创建自己的 Action,并且您应该扩展 TextAction 而不是 AbstractAction。 TextAction 类有一个方法可以返回具有焦点的文本组件。因此,您可以以通用方式实现 Action。

您也不需要使用 MouseListener 来调用弹出窗口。您可以只使用以下 JComponent 方法:

JComponent.setComponentPopupMenu(JPopupMenu popup)

【讨论】:

  • 我一定会改用 TextAction。我不能将 Object 作为参数传递,因为 Object 一次只能包含在一个容器中。当添加到第二个容器时,它会从第一个容器中删除。 :(
  • 没错,但弹出菜单一次只显示在一个组件上。它不会永久添加到容器中。您传递的只是对组件的引用,因此可以根据需要显示/隐藏弹出窗口。
  • 最佳答案!我没有考虑到这一点。起初我制作了一个静态的最终 PopUpMenu 并为每个 JTextArea 添加了一个侦听器,但这个答案是最好的。我将重写它以适应这个答案。