【问题标题】:The tab added in JTabbedPane doesn't show upJTabbedPane 中添加的选项卡不显示
【发布时间】:2012-10-18 03:52:51
【问题描述】:

我有 VTreePanel 类,它从 CPanel 扩展而来,而 CPanel 又从 JPanel 扩展而来。该类有一个 JSplitPane 对象,分为两个区域:左和右。左侧包含树形菜单选择对象。在右侧,它包含 JTabbedPane 对象。 VTreePanel 类是这样的:

public final class VTreePanel extends CPanel
    implements ActionListener
{
    private JSplitPane centerSplitPane = new JSplitPane();

    private JTabbedPane tabbedPane;

    ...

    // GET method for the tabbedPane    
    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    // Constructor
    public VTreePanel(int WindowNo, boolean hasBar, boolean editable)
    {

        ...

        tabbedPane = new JTabbedPane();
        centerSplitPane.add(treePart, JSplitPane.LEFT);
        centerSplitPane.add(tabbedPane, JSplitPane.RIGHT);  // Look at this

        ...
    }

}

在构造函数中,我将树选择 (treePart) 和 JTabbedPane 对象 (tabbedPane) 添加到 JSplitPane 对象 (centerSplitPane) 中。我还没有在 tabbedPane 中添加任何选项卡。看下面的截图:

http://i45.tinypic.com/2v3j0nl.jpg

那么当用户单击其中一个菜单时如何添加选项卡?

我有 AMenu 类,它实现了在用户单击菜单时触发 propertyChange 方法的 PropertyChangeListener:

public final class AMenu extends CFrame
    implements ActionListener, PropertyChangeListener, ChangeListener
{

    private VTreePanel treePanel = null;    // this is the VTreePanel object

    ...

    public void propertyChange(PropertyChangeEvent e)
    {
        ...

        // Here I pass the VTreePanel object as parameter to AMenuStartItem thread object
        (new AMenuStartItem(cmd, true, sta, this, treePanel)).start();
    }

}

您可以看到我有 VTreePanel 对象 (treePanel),我将 VTreePanel 对象作为参数传递给 AMenuStartItem 线程。 AMenuStartItem 包含在 JTabbedPane 中执行添加 Tab 的逻辑(请记住,JTabbedPane 对象 (tabbedPane) 在 VTreePanel 中)。

这是 AMenuStartItem 线程类:

public class AMenuStartItem extends Thread implements ActionListener
{
    private VTreePanel m_vtreePanel;

    public AMenuStartItem (int ID, boolean isMenu, String name, AMenu menu, VTreePanel vtreepanel)
    {
        ...

        m_vtreePanel = vtreepanel;  // save the VTreePanel object
    }

    // The thread method that executed when thread is started
    public void run()
    {
        ...

        startWindow(0, cmd);

        ...
    }

    private void startWindow(int AD_Workbench_ID, int AD_Window_ID)
    {
        ...

        // Here I perform adding new tab
        m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());

        ...
    }

}

所以,getTabbedPane() 返回了 JTabbedPane 对象并执行了 addTab() 方法,但根本没有显示任何选项卡。

有人知道如何解决这个问题吗?

【问题讨论】:

  • 首先,您破坏了 Swing 最重要的运行之一,您尝试从 Event Dispatching Thread 之外更新 UI 组件。阅读Concurrency in Swing 以了解解决方法。其次,你需要在 JTabbedPane 上调用 invalidate 和 repaint
  • 你的意思是我应该在 propertyChange() 方法中使用这段代码吗? javax.swing.SwingUtilities.invokeLater(new AMenuStartItem(cmd, true, sta, this, treePanel));我试过了,但仍然没有出现标签。
  • 您好 MadProgrammer,我在测试应用程序时犯了一个错误^^。结果我在错误的主选项卡中进行了测试(主选项卡是水平选项卡行,包括:会计、财务、CRM 等)。所以事实证明一切都运行良好。我将在下一小时删除我的问题。但是,我的原始代码在不使用 SwingUtilities.invokeLater() 的情况下运行良好。无论如何感谢您的建议。
  • 直到你得到一些不时随机弹出的奇怪油漆制品。很高兴知道您解决了外包问题
  • 如果 EventQueue.isDispatchingThread 返回 false,那么您在 EDT 之外。如果这个线程,你绝不能在外部创建 UI 组件或与之交互

标签: java multithreading swing tabs jtabbedpane


【解决方案1】:

与 UI 的所有交互都必须通过事件调度线程完成,没有例外...

public class AMenuStartItem extends Thread implements ActionListener
{
    private VTreePanel m_vtreePanel;

    public AMenuStartItem (int ID, boolean isMenu, String name, AMenu menu, VTreePanel vtreepanel)
    {
        ...

        m_vtreePanel = vtreepanel;  // save the VTreePanel object
    }

    // The thread method that executed when thread is started
    public void run()
    {
        ...

        startWindow(0, cmd);

        ...
    }

    private void startWindow(final int AD_Workbench_ID, final int AD_Window_ID)
    {
        ...

        if (EventQueue.isDispatchingThread()) {
            // This is safe, we're in the EDT
            m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());
            m_vtreePanel.getTabbedPane().invalidate();
            m_vtreePanel.getTabbedPane().repaint();
        } else {
            // This is unsafe, we need to resync with the EDT
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    // Here I perform adding new tab
                    m_vtreePanel.getTabbedPane().addTab(frame.getTitle(), frame.getAPanel());
                    m_vtreePanel.getTabbedPane().invalidate();
                    m_vtreePanel.getTabbedPane().repaint();

                }
            });
        }

        ...
    }

}

【讨论】:

  • 您好 MadProgrammer,我在测试应用程序时犯了一个错误^^。结果我在错误的主选项卡中进行了测试(主选项卡是水平选项卡行,包括:会计、财务、CRM 等)。所以事实证明一切都运行良好。我将在下一小时删除我的问题。但是,我的原始代码在不使用 SwingUtilities.invokeLater() 的情况下运行良好。无论如何感谢您的建议。
  • invalidate() 和 repaint() 有必要吗?我把它们排除在外,没有发现任何问题。我也有这几行:m_vtreePanel.getTabbedPane.indexOfComponent() 和 m_vtreePanel.getTabbedPane.setSelectedIndex(),我也应该把这两行放在 invokeLater() 里面吗?
  • invalidate 标记容器无效,表明容器需要布局,重绘请求重绘管理器将我们的容器添加到脏列表中,以便在下一个绘制周期更新.它们是否需要,并非总是如此,但如果您遇到组件未显示的问题,它会有所帮助。 setSelectedIndex 改变了 UI,所以是的,它必须从 EDT 中调用
  • 因此互联网上的许多 java 示例都没有显示出一个好的做法(没有从 EDT 调用),例如:java2s.com/Code/Java/Swing-JFC/CardLayoutDemo.htm 与这个 oracle java 的示例相比:docs.oracle.com/javase/tutorial/uiswing/examples/layout/…
  • 感谢您的帮助,我决定保留这个问题。谁知道它会在将来使某人受益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 2014-04-15
  • 1970-01-01
  • 2023-03-16
  • 2012-04-20
  • 1970-01-01
相关资源
最近更新 更多