Забавяне преди добавянето на манипулатори на събития JFrame?

Работя върху прост Java swing проект. Това е кодът на основния клас (името е променено):

public class MainProg
{
    private static MainProg program; 

    //mainWin is a JFrame
    private MainWindow mainWin;

    //Event handler class which extends MouseAdapter
    private TrayManager trayMgr;


    public static void main(String[] args)
    {                
        program = new MainProg();
    }

    public MainProg()
    {
        mainWin = new MainWindow();
        trayMgr = new TrayManager();

        mainWin.startBtn.addMouseListener(trayMgr);

        mainWin.setVisible(true);
    }
}

Както е ясно, когато програмата стартира, в main() тя създава нов екземпляр на класа MainProg, който след това извиква конструктора. В конструктора той създава нов екземпляр на JFrame mainWin. След това прикачва манипулатор на събитие към бутон на mainWin.

В класа на манипулатора на събития trayMgr единственият метод е mouseClicked(), който не прави нищо освен System.out.println('Clicked');

Проблемът е, че когато стартирам тази програма в Netbeans, JFrame се показва веднага, но изглежда трябва да щракна върху бутона 2-3 пъти, преди съобщението да се отпечата в конзолата.

Това само нещо специфично за Netbeans ли е или трябва да променя нещо, за да настроя манипулатора на събитието, преди прозорецът да стане видим?


person Ali    schedule 05.09.2012    source източник
comment
Има ли възможност да включите SSCCE, който демонстрира проблема?   -  person tenorsax    schedule 05.09.2012
comment
Netbeans не променя нищо относно начина на работа на програмата. Вашият код също е неправилен, тъй като извиква Swing от нишка, различна от EDT. Това може да е отговорно за проблема. Ако ни покажете SSCCE, както каза @Max, може да сме в състояние да помогнем.   -  person Gene    schedule 05.09.2012
comment
@Gene Как мога да създам SSCCE? Сигурен съм обаче, че това, което споменахте за нишка, различна от цикъла на събитията, е проблемът. Можете ли да разкажете по-подробно за това?   -  person Ali    schedule 05.09.2012
comment
Определено изглежда като проблем с нишката, например нишката за показване на Jframe и настройка на събитието се изпълняват отделно   -  person Ali    schedule 05.09.2012
comment
Вижте Главна нишка срещу UI нишка в Java.   -  person tenorsax    schedule 05.09.2012
comment
Защо все пак добавяте MouseListener към JButton? Почти никога не трябва да правите това.   -  person Hovercraft Full Of Eels    schedule 05.09.2012
comment
@HovercraftFullOfEels защо не? Как иначе бих се справила с щракване на мишката? (Това е първият път, когато правя swing приложение, така че, моля, бъдете груби и ме поправете, ако правя нещо нередно) :)   -  person Ali    schedule 05.09.2012
comment
ActionListener обработва щракването на JButton. Ако използвате MouseListener, потенциално бихте могли да объркате функционирането на бутона. Трябва да прочетете уроците, тъй като има много за учене.   -  person Hovercraft Full Of Eels    schedule 05.09.2012
comment
@HovercraftFullOfEels Ако искате да публикувате как мога да се справя с този проблем с нишките, както и с actionListener, и ще приема отговора ви   -  person Ali    schedule 05.09.2012
comment
@Click: никой не каза, че това определено е проблем с нишката и всъщност вероятно не е така. Всичко, което беше казано е, че не спазвате правилата за нишки на Swing, когато стартирате вашия Swing GUI. Поставете го в Runnable и го извикайте с помощта на SwingUtilities.invokeLater(...), както ще ви препоръчат уроците. Но е малко вероятно това да реши проблема ви. Подозирам, че имате грешка в непоказания код. Но отново се отървете от MouseListener и използвайте само ActionListener с вашия JButton.   -  person Hovercraft Full Of Eels    schedule 05.09.2012
comment
@HovercraftFullOfEels Използвам съветника за GUI на Netbeans, така че той генерира целия код за мен за настройка на компонентите на потребителския интерфейс. Така че в класа JFrame се извиква метод initComponents от конструктора на JFrame, който добавя всички контроли към JFrame. Как предлагате да го преработя?   -  person Ali    schedule 05.09.2012
comment
Отново първо и преди всичко, отървете се от този MouseListener и използвайте ActionListener. Моля, докладвайте отново, за да видите дали това води до нещо. Моето собствено пристрастие е против използването на NetBeans за генериране на GUI код за вас, докато първо не се научите да кодирате Swing на ръка. Това не е прост инструмент за насочване и щракване и може да се провали мизерно, ако първо не сте запознати с основите на Swing.   -  person Hovercraft Full Of Eels    schedule 05.09.2012
comment
@HovercraftFullOfEels Използването на ActionListener изглежда е коригирало този проблем със забавянето. Но все пак, ако правя нещо нередно с нишките, бих искал да го знам. наздраве   -  person Ali    schedule 05.09.2012


Отговори (2)


Вашият проблем с нишките вероятно не е този, който причинява текущия ви проблем, но има теоретичен потенциал за проблеми и съм виждал някои реални проблеми, свързани с някои от по-чувствителните визия и усещане. Съвсем просто трябва да поставите своя код, който стартира вашия GUI, в нишката на събитието Swing. Вие правите това, като правите:

public void main(String[] args) {
  SwingUtilities.invokeLater(new Runnable(
    public void run() {
      program = new MainProg();
    }
  ));
}

Някой друг препоръча използването на invokeAndWait(...) вместо invokeLater(...), но това може да бъде рисковано, особено ако по невнимание направите това извикване от самата нишка на събитието Swing. За вашата ситуация е по-добре да използвате invokeLater(...).

Но отново мисля, че основният проблем с кода, който показахте, беше неподходящото използване на MouseListener, където трябваше да се използва ActionListener. Да се ​​научите да кодирате всяка GUI библиотека може да бъде доста трудно и поради тази причина не можете да приемете нищо. Вижте уроците и се учете от експертите. Освен това, ако обмисляте да кодирате Swing за дълги разстояния, помислете за отказ от помощните програми за генериране на код на NetBean и се научете първо да кодирате Swing на ръка. Няма да съжалявате, ако направите това.

person Hovercraft Full Of Eels    schedule 05.09.2012
comment
Въпрос. Така че, когато този код се изпълнява, той ще върви по следния начин: MainProg() -› JFrame() -› Jframe.initComponents() (тук се настройват и добавят всички UI компоненти към Jframe). Така ли си го замислил? - person Ali; 05.09.2012
comment
Трябва да подкрепям предложението първо да научите как да кодирате Swing на ръка. Правя го значително и тъкмо започвам да се уча как да използвам NetBeans GUI Builder. Много от последните ми въпроси за Java тук, в Stackoverflow, бяха относно използването на GUI Builder. Като цяло го намирам за неудобен и най-вероятно изобщо ще се откажа от използването му за бъдещи проекти. - person Code-Apprentice; 05.09.2012
comment
@ClickUpvote Поради проблемите с нишките, стекът за повиквания е малко по-сложен от това, което заявявате. В крайна сметка се извиква run(), който след това извиква конструктора MainProg() и т.н. - person Code-Apprentice; 05.09.2012
comment
@Click: Не съм 100% сигурен какво питате, но мисля, че да, това е правилно. Всичко, което мога да кажа със сигурност е, че цялото изграждане на GUI трябва да се извършва в нишката на събитието. - person Hovercraft Full Of Eels; 05.09.2012
comment
@HovercraftFullOfEels И под нишката на събитието имате предвид новата нишка, която предоставяте на SwingUtilities.invokeLater по-горе? - person Ali; 05.09.2012
comment
@Click: не, кодът, който публикувах, не стартира директно нито една нишка. Вместо това имам предвид нишката, създадена от Swing, която отговаря за обработката на всички потребителски взаимодействия и всички графики. Swing създава тази нишка при стартиране на Swing. Кодът, който показах по-долу, поставя само Runnable в тази нишка, ако съществува. Ако все още не съществува, сигурен съм, че предлага на Swing да го стартира. Моля, прочетете за конкурентност в Swing, но не все още се тревожете за използването на SwingWorkers. - person Hovercraft Full Of Eels; 05.09.2012
comment
@HovercraftFullOfEels Благодаря за помощта, но имам още едно q. В момента използвам само един JFrame, но да кажа, че искам да заредя нов JFrame, когато потребителят щракне върху бутон (напр. той щракне върху „Показване на подробности“ и аз искам да покажа нов JFrame с подробностите). Трябва ли да направя нещо специфично за това по отношение на нишките или мога просто да настроя JFrame и да добавя компонентите към него и след това да го покажа, след като програмата се изпълнява през SwingUtilties.invokeLater в main()? - person Ali; 05.09.2012
comment
@Click: първо, няма да искате да показвате 2-ри JFrame. Ако искате да покажете втори зависим прозорец, вероятно ще използвате JDialog или ще размените изгледи чрез CardLayout. Следващата вероятност кодът за създаване и показване на диалоговия прозорец ще бъде в нишката на събитието Swing, тъй като вероятно ще бъде извикан от слушателя на събития, който винаги е в нишката на събитието. Тогава, не, няма да е необходимо да правите това. - person Hovercraft Full Of Eels; 05.09.2012
comment
@HovercraftFullOfEels Нали така, след като имам този SwingUtilities.invokeLater в main(), мога да забравя за него и да програмирам всичко останало, както обикновено? - person Ali; 05.09.2012
comment
@Click: ако не създавате или използвате фонови нишки, тогава да, вярвам, че това е правилно, но ако вашето приложение Swing е нещо повече от просто, вероятно ще трябва да направите фонови нишки в даден момент. - person Hovercraft Full Of Eels; 05.09.2012
comment
@HovercraftFullOfEels Благодаря, Последно В.. така че ако правя фонова нишка, тогава във всяка нишка ще трябва да поставя действителния код в SwingUtilities.invokeLater като в main()? - person Ali; 05.09.2012
comment
@Click: не е задължително. Ако използвате SwingWorker, той ще направи това вместо вас в определени ситуации. - person Hovercraft Full Of Eels; 05.09.2012

Тъй като попитахте, кодът, който публикувах ето това е Java SSCCE на различен тема. invokeLater е начин за провеждане на изчисления на EDT. (Има също invokeAndWait, което би работило добре тук, но при някои други условия може да доведе до блокиране.)

Всъщност този пример може би е малко прекалено консервативен. Някои препратки казват, че можете да стартирате Swing от основната нишка извикването на show() или setVisible(). Въпреки това имам програма, която се държи зле под Java 7, когато опитам това.

person Gene    schedule 05.09.2012
comment
Не, почти никога не трябва да използвате invokeAndWait(...). Много по-добре е да използвате invokeLater(...). Единственото изключение, за което знам, е може би при стартиране на JApplets, но това е всичко. OP стартира JFrame, така че тази препоръка е грешна. Моля, променете го. - person Hovercraft Full Of Eels; 05.09.2012
comment
Ако ми дадете референция в подкрепа на това твърдение, ще свърши работа. Никога не съм виждал нищо при дълго търсене на Swing документация. Тук invokeLater блокира основната нишка, на която така или иначе не е позволено да извършва по-нататъшна работа с потребителския интерфейс. В примера, който публикувах, invokeLater ще позволи на основната нишка да продължи още няколко инструкции, за да увисне в очакване EDT да излезе. Няма съществена разлика в пътищата на изпълнение. Но от друга страна, много се радвам, че греша с повече от просто изявление в удебелен шрифт. - person Gene; 06.09.2012
comment
Основният проблем, който знам, е, че можете да извикате invokeLater във всяка нишка, докато не можете да извикате invokeAndWait на EDT. Ако искате документация за това, просто проверете SwingUtilities API. - person Hovercraft Full Of Eels; 06.09.2012
comment
Съществува също така риск от причиняване на блокиране с invokeAndWait и трябва да се уверите, че нишката, която извиква invokeAndWait, не поддържа никакво заключване, което може да се изисква от други, когато се извършва повикването. За повече информация относно това, моля, прочетете Нишки и Swing от Hans Muller и Kathy Walrath, двама, които бяха част от екипа, помогнал за създаването на Swing и неговата механика за резби. - person Hovercraft Full Of Eels; 06.09.2012
comment
OP изпълняваше прост код за инициализиране на Swing в основната нишка и моето предложение за InvokeAndWait беше в този контекст. Съгласен съм, че някой може да извади това от контекста и да си навлече проблеми, така че ще го променя. Благодаря. - person Gene; 06.09.2012
comment
Човекът, за когото се притеснявам да извадя това от контекста, е оригиналният плакат и вие. Вкоренените навици умират трудно и ако сега се научи да използва invokeAndWait, лесно може да се натъкне на проблеми по-късно, когато се натъкне на един от изброените по-горе проблеми. Днес не слагате предпазни колани, защото знаете, че днес ще претърпите автомобилна катастрофа, слагате ги, защото това намалява шансовете ви да претърпите непредвидими щети. Същото е и с добрите практики за програмиране. 1+ за редакцията на вашата публикация. Благодаря. - person Hovercraft Full Of Eels; 06.09.2012
comment
И аз се тревожа за теб, така че сме квит... ;-) - person Gene; 06.09.2012