Разделът, добавен в JTabbedPane, не се показва

Имам клас VTreePanel, който се разширява от 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). Все още не добавям никакъв Tab в tabbedPane. Вижте екранната снимка по-долу:

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

Тогава как да добавя раздела, когато потребителят щракне върху едно от менютата?

Имам клас AMenu, където е имплементирал PropertyChangeListener, който задейства метода propertyChange, когато потребителят кликне върху меню:

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 съдържа логика, която извършва добавяне на Tab в JTabbedPane (не забравяйте, че обектът 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() се изпълнява, но изобщо не се показва раздел.

Някой знае ли как да поправя този проблем?


person null    schedule 18.10.2012    source източник
comment
Първо, прекъснахте едно от най-важните изпълнения на Swing, опитвате се да актуализирате компонентите на потребителския интерфейс извън Нишка за изпращане на събития. Прочетете Concurrency в Swing за начини за заобикаляне то. Второ, много трябва да извикате invalidate и прерисувате на JTabbedPane   -  person MadProgrammer    schedule 18.10.2012
comment
Имахте предвид, че трябва да използвам този код в метода propertyChange()? javax.swing.SwingUtilities.invokeLater(нов AMenuStartItem(cmd, true, sta, this, treePanel)); Опитах, но пак не се появи раздел.   -  person null    schedule 18.10.2012
comment
Здравей MadProgrammer, направих грешка при тестването на приложението ^^. Оказа се, че тествах в грешен главен раздел (Основният раздел е редът с хоризонтални раздели, който се състои от: Счетоводство, Финанси, CRM и т.н.). Така се оказа, че всичко върви добре. Ще изтрия въпроса си в следващия час. Моят оригинален код обаче работи добре, без да използвам SwingUtilities.invokeLater(). Благодаря все пак за съвета.   -  person null    schedule 18.10.2012
comment
Докато не получите някакъв странен артефакт от боя, който произволно изскача от време на време. Радвам се да знам, че сте работили с проблема с аутсорсинга   -  person MadProgrammer    schedule 18.10.2012
comment
Как определяме дали една нишка трябва да бъде извикана от invokeLater()? Всъщност, вътре в нишката AMenuStartItem, той също създаде обект AWindow (име на променлива: рамка) и извика frame.initWindow(), който извършва добавяне на дъщерен компонент (лента с инструменти, меню, лента на състоянието и т.н.). Вярвам, че сте знаели от предишния ми въпрос, че AWindow е подклас на JFrame. Авторът на кода обаче не използва SwingUtilities.invokeLater за изпълнение на нишката AMenuStartItem.   -  person null    schedule 18.10.2012
comment
Ако EventQueue.isDispatchingThread върне false, значи сте извън EDT. НИКОГА не трябва да създавате или да взаимодействате с UI компонент извън тази нишка   -  person MadProgrammer    schedule 18.10.2012
comment
къде трябва да поставя EventQueue.isDispatchThread()? Вътре в нишката?   -  person null    schedule 18.10.2012
comment
Трябва да EventQueue.isDispatchThread() навсякъде, където не сте сигурни дали методът се извиква от контекста на EDT и искате да актуализирате потребителския интерфейс   -  person MadProgrammer    schedule 18.10.2012
comment
Актуализирах отговора си с пример   -  person MadProgrammer    schedule 18.10.2012


Отговори (1)


Всички взаимодействия с потребителския интерфейс ТРЯБВА да се извършват чрез нишката за изпращане на събития, без изключения...

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

                }
            });
        }

        ...
    }

}
person MadProgrammer    schedule 18.10.2012
comment
Здравей MadProgrammer, направих грешка при тестването на приложението ^^. Оказа се, че тествах в грешен главен раздел (Основният раздел е редът с хоризонтални раздели, който се състои от: Счетоводство, Финанси, CRM и т.н.). Така се оказа, че всичко върви добре. Ще изтрия въпроса си в следващия час. Моят оригинален код обаче работи добре, без да използвам SwingUtilities.invokeLater(). Благодаря все пак за съвета. - person null; 18.10.2012
comment
Необходими ли са invalidate() и repaint()? Оставих ги и не открих проблем. Имах и тези редове: m_vtreePanel.getTabbedPane.indexOfComponent() и m_vtreePanel.getTabbedPane.setSelectedIndex(), трябва ли да поставя тези два реда и в invokeLater()? - person null; 18.10.2012
comment
invalidate маркира контейнер е невалиден, което показва, че контейнерът трябва да бъде разположен, repaint изисква от мениджъра на repaint да добави нашия контейнер към мръсния списък за актуализиране по време на следващия цикъл на рисуване. Задължителни ли са, не винаги, но ако имате проблеми с компонентите, които не се показват, помага. setSelectedIndex променя потребителския интерфейс, така че да, трябва да се извика от EDT - person MadProgrammer; 18.10.2012
comment
Така че много примери за Java в интернет не показват добра практика (без извикване от EDT), напр.: java2s.com/Code/Java/Swing-JFC/CardLayoutDemo.htm в сравнение с този пример на Oracle Java: docs.oracle.com/javase/tutorial/uiswing/examples/layout/ - person null; 18.10.2012
comment
благодаря за помощта, реших да запазя този въпрос. Кой знае, че ще бъде от полза на някого в бъдеще. - person null; 18.10.2012