This is the strangest bug I've ever encountered.
Whenever I start my program, in about 1 of 2 times, the gui is successfully created and pop-ups. In the other 1 out of 2 times, I get no gui, I get no error and all the code is executed in the same order as whenever the gui is successfully created.
What is causing this bug? And how could I resolve this? I'm clueless..
The view class :
package view;
import controller.GameController;
import lombok.Data;
import model.GameModel;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
#Data public class View extends JFrame {
GameInterface gameInterface = new GameInterface();
MainMenuScreen menuScreen = new MainMenuScreen();
public View() {
super("View");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
displayMenuScreen();
setVisible(true);
}
public void displayMenuScreen(){
menuScreen.getSTARTTHEGAMEButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GameModel.getInstance().newGame();
displayGameInterface();
GameController.startGame();
}
});
setContentPane(menuScreen.getContentPane());
}
public void displayGameInterface(){
setContentPane(gameInterface.getContentPane());
}
}
The Main Menu class:
package view;
import lombok.Data;
import javax.swing.*;
#Data public class MainMenuScreen extends JFrame{
private JButton STARTTHEGAMEButton;
private JPanel rootPanelMainMenu;
private JTextPane crazyBananaRepublicTycoonTextPane;
public MainMenuScreen() {
super("MY GAME'S NAME");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setContentPane(rootPanelMainMenu);
System.out.println("HEYHEY3");
}
}
The class where the view is created:
import model.GameModel;
import model.GameModelObserver;
import view.View;
public class CrazyBananaRepublicTycoon {
public static final void main(String args[]){
View view = new View();
view.setVisible(true);
GameModelObserver gameModelObserver = new GameModelObserver(view.getGameInterface());
GameModel.getInstance().addObserver(gameModelObserver);
GameModel.getInstance().setGameModelObserver(gameModelObserver);
}
}
You are not properly using the Event Dispatch Thread...
You must only interact with most Swing/AWT objects via the EDT.
Change your main method:
public static final void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
View view = new View();
view.setVisible(true);
GameModelObserver gameModelObserver = new GameModelObserver(view.getGameInterface());
GameModel.getInstance().addObserver(gameModelObserver);
GameModel.getInstance().setGameModelObserver(gameModelObserver);
}
}
}
Related
I have a pet project which is a 2D game engine.
After creating all the backend functionality, I want to implement a UI for it.
My current plan is to do this via MVC because it strikes me as the most feasible way of doing this (logic first, then UI).
Now, I am unsure how to design/implement this with either Swing or JavaFX, as I do not yet fully understand what the underlying concepts of either are.
Can someone describe to me how to implement MVC with Swing or JavaFX?
You can have a model that is a general one can be used by different views.
To demonstrate it let's first introduce a interface that can be used to listen to such model:
//Interface implemented by SwingView and used by Model
interface Observer {
void observableChanged();
}
Consider a very simple model, with one attribute only: an integer value between 0 and a certain max:
//Generic model. Not dependent on the GUI tool kit. Use by Swing as well as JAvaFX
class Model {
private int value;
private static final int MAX_VALUE = 100;
private Observer observer;
int getValue(){
return value;
}
void setValue(int value){
this.value = Math.min(MAX_VALUE, Math.abs(value));
notifyObserver();
}
int getMaxValue() {
return MAX_VALUE;
}
//-- handle observers
void setObserver(Observer observer) {
this.observer = observer;
}
private void notifyObserver() {
if(observer != null) {
observer.observableChanged();
}
}
}
Note that the model calls observer.observableChanged() when value changes it.
Now let's use this model with a Swing gui that displays a random number :
import java.awt.*;
import java.util.Random;
import javax.swing.*;
//Swing app, using a generic model
public class SwingMVC {
public static void main(String[] args) {
new SwingController();
}
}
//SwingController of the MVC pattern."wires" model and view (and in this case also worker)
class SwingController{
public SwingController() {
Model model = new Model();
SwingView swingView = new SwingView(model);
model.setObserver(swingView); //register view as an observer to model
update(model);
}
//change model
private void update(Model model) {
Random rnd = new Random();
//use swing timer so the change is performed on the Event Dispatch Thread
new Timer(1000,(e)-> model.setValue(1+rnd.nextInt(model.getMaxValue()))).start();
}
}
//view of the MVC pattern. Implements observer to respond to model changes
class SwingView implements Observer{
private final Model model;
private final JLabel label;
public SwingView(Model model) {
this.model = model;
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setLayout(new GridBagLayout());
label = new JLabel(" - ");
label.setFont(new Font(label.getFont().getName(), Font.PLAIN, 48));
label.setHorizontalTextPosition(SwingConstants.CENTER);
frame.add(label);
frame.pack();
frame.setVisible(true);
}
#Override
public void observableChanged() {
//update text in response to change in model
label.setText(String.format("%d",model.getValue()));
}
}
We can use the very same model and achieve the same functionality with a JavaFx gui:
import java.util.Random;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Duration;
//JavaFa app, using a generic model
public class FxMVC extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
FxController fxController = new FxController();
Scene scene = new Scene(fxController.getParent());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}
class FxController{
private final FxView view;
FxController() {
Model model = new Model();
view = new FxView(model);
model.setObserver(view); //register fxView as an observer to model
update(model);
}
//change model
private void update(Model model) {
Random rnd = new Random();
//use javafx animation tools so the change is performed on the JvaxFx application thread
PauseTransition pt = new PauseTransition(Duration.seconds(1));
pt.play();
pt.setOnFinished(e->{
model.setValue(1+rnd.nextInt(model.getMaxValue()));
pt.play();
});
}
Parent getParent(){
return view;
}
}
//View of the MVC pattern. Implements observer to respond to model changes
class FxView extends StackPane implements Observer{
private final Model model;
private final Label label;
public FxView(Model model) {
this.model = model;
label = new Label(" - ");
label.setFont(new Font(label.getFont().getName(), 48));
getChildren().add(label);
}
#Override
public void observableChanged() { //update text in response to change in model
//update text in response to change in model
label.setText(String.format("%d",model.getValue()));
}
}
On the other hand, you can have a model that is more specific, intended to be used with a specific tool-kit, and gain some advantages by using some tool of that tool kit.
For example a model made using JavaFx properties, in this example SimpleIntegerProperty, which simplifies the listening to model changes (does not make use of the Observer interface):
import java.util.Random;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Duration;
//JavaFa app, using a JavaFx model
public class FxApp extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
FxController fxController = new FxController();
Scene scene = new Scene(fxController.getParent());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}
class FxAppController{
private final FxAppView view;
FxAppController() {
FxAppModel model = new FxAppModel();
view = new FxAppView(model);
update(model);
}
//change model
private void update(FxAppModel model) {
Random rnd = new Random();
//use javafx animation tools so the change is performed on the JvaxFx application thread
PauseTransition pt = new PauseTransition(Duration.seconds(1));
pt.play();
pt.setOnFinished(e->{
model.setValue(1+rnd.nextInt(model.getMaxValue()));
pt.play();
});
}
Parent getParent(){
return view;
}
}
//View does not need to implement listener
class FxAppView extends StackPane{
public FxAppView(FxAppModel model) {
Label label = new Label(" - ");
label.setFont(new Font(label.getFont().getName(), 48));
getChildren().add(label);
model.getValue().addListener((ChangeListener<Number>) (obs, oldV, newV) -> label.setText(String.format("%d",model.getValue())));
}
}
//Model that uses JavaFx tools
class FxAppModel {
private SimpleIntegerProperty valueProperty;
private static final int MAX_VALUE = 100;
SimpleIntegerProperty getValue(){
return valueProperty;
}
void setValue(int value){
valueProperty.set(value);
}
int getMaxValue() {
return MAX_VALUE;
}
}
A Swing gui and a JavaFx gui, each uses a different instance of the same Model:
I use this class for my school app projects. It is how I set the application up and it extends JFrame and implements Runnable. Now whenever I use this in school on a Windows computer and everything works and the screen updates, but at home on a Mac it doesn't. I use Eclipse neon with JDK 1.8.0_101
Please help me out, I can't test any projects at home cause of this.
import java.awt.Graphics;
import javax.swing.JFrame;
public abstract class GUIApplication extends JFrame implements Runnable{
private Screen currentScreen;
//no main, cant instentiate an abstract class
public GUIApplication(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int x=40;
int y=40;
int width=1000;
int height=640;
setBounds(x,y,width,height);
initScreen();
setVisible(true);
}
//this is a method for creating the starting screen
protected abstract void initScreen();
public void setScreen(Screen screen){
//stop controls from previous screen
removeListeners();
setCurrentScreen(screen);
//add new controls
addListeners();
}
private void removeListeners(){
if(getCurrentScreen() != null){
if(getCurrentScreen().getMouseListener() != null) removeMouseListener(getCurrentScreen().getMouseListener());
if(getCurrentScreen().getMouseMotionListener() != null) removeMouseMotionListener(getCurrentScreen().getMouseMotionListener());
if(getCurrentScreen().getKeyListener() != null) removeKeyListener(getCurrentScreen().getKeyListener());
// if(currentScreen.getMouseWheelListener() != null) removeMouseWheelListener(currentScreen.getMouseWheelListener());
}
}
private void addListeners(){
if(getCurrentScreen() != null){
if(getCurrentScreen().getMouseListener() != null)addMouseListener(getCurrentScreen().getMouseListener());
if(getCurrentScreen().getMouseMotionListener() != null) addMouseMotionListener(getCurrentScreen().getMouseMotionListener());
if(getCurrentScreen().getKeyListener() != null){
addKeyListener(getCurrentScreen().getKeyListener());
}
// if(currentScreen.getMouseWheelListener() != null) addMouseWheelListener(currentScreen.getMouseWheelListener());
}
}
public void paint(Graphics g){
g.drawImage(getCurrentScreen().getImage(), 0, 0, null);
}
public void run(){
while(true){
getCurrentScreen().update();
repaint();
try {
Thread.sleep(30);
repaint();
revalidate();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public Screen getCurrentScreen() {
return currentScreen;
}
public void setCurrentScreen(Screen currentScreen) {
this.currentScreen = currentScreen;
}
}
This is how a game would start:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.util.ArrayList;
import javax.swing.JFrame;
import game.mainScreenTeam.Dragon;
import game.mainScreenTeam.HomeScreen;
import game.miniGameTeam.GameInstructions;
import game.miniGameTeam.GameScreen;
import game.miniGameTeam.HighScoreScreen;
import game.shopScreen.BuyScreenWendy;
import game.shopScreen.HomeShopScreen;
import game.shopScreen.SellShopZheng;
import guiPractice.GUIApplication;
import guiPractice.Screen;
import guiPractice.components.AnimatedComponent;
/**
* #author Kat
*
*/
public class DragonLand extends GUIApplication {
public static DragonLand game;
public static int coins = 1500;
public static HomeScreen homeScreen;
public static Screen shopMain; // shop 1
public static Screen sellScreen; // shop 2
public static Screen buyScreen; // shop 3
public static Screen highscoreScreen; // high score
public static GameScreen miniGameScreen; // minigame
public static Screen gameInstructionsScreen;
public static Screen HelpScreen;
public static Color NAVY;
public static Color BRIGHT_PINK;
public static Color LIGHT_PINK;
public static Color LIGHT_NUDE;
public static Color DARKER_NUDE;
/**
*
*/
// public static void addDragon(AnimatedComponent a){
// dragonList.add(a);
// }
public DragonLand() {
}
/* (non-Javadoc)
* #see guiPractice.GUIApplication#initScreen()
*/
#Override
protected void initScreen() {
initColors();
miniGameScreen = new GameScreen(getWidth(),getHeight());
shopMain = new HomeShopScreen(getWidth(),getHeight());
sellScreen = new SellShopZheng(getWidth(),getHeight());
homeScreen = new HomeScreen(getWidth(),getHeight());
buyScreen = new BuyScreenWendy(getWidth(),getHeight());
highscoreScreen = new HighScoreScreen(getWidth(),getHeight());
HomeScreen.jenCode = new game.mainScreenTeam.HomeJenniber();
gameInstructionsScreen = new GameInstructions(getWidth(), getHeight());
setScreen(homeScreen);
}
private void initColors() {
NAVY = new Color(62,74,99);
BRIGHT_PINK = new Color(224,102,102);
LIGHT_PINK = new Color(248,186,182);
LIGHT_NUDE = new Color(244,215,183);
DARKER_NUDE = new Color(230,195,147);
}
/**
* #param args
*/
public static void main(String[] args) {
game = new DragonLand();
Thread go = new Thread(game);
go.start();
}
//public coin getter + setter
public void setCoins(int x){
coins = x;
}
public int getCoins(){
return coins;
}
}
This is the home screen
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import game.DragonLand;
import guiPractice.ClickableScreen;
import guiPractice.components.Action;
import guiPractice.components.AnimatedComponent;
import guiPractice.components.Button;
import guiPractice.components.Graphic;
import guiPractice.components.TextLabel;
import guiPractice.components.Visible;
import guiPractice.sampleGames.MouseFollower;
/**
* #author Kat
* #author Jenniber
*
*/
public class HomeScreen extends ClickableScreen implements Runnable{
private Graphic background;
public static HomeJenniber jenCode;
public HomeScreen(int width, int height) {
super(width, height);
Thread play = new Thread(this);
play.start();
}
#Override
public void initAllObjects(ArrayList<Visible> viewObjects) {
background=new Graphic(0,0,getWidth(),getHeight(),"img/Grassland.png");
viewObjects.add(background);
HomeKat katCode=new HomeKat(viewObjects, getWidth(), getHeight());
}
#Override
public void run() {
}
}
katCode adds buttons to the screen and image annimations
public void paint(Graphics g){
g.drawImage(getCurrentScreen().getImage(), 0, 0, null);
}
Don't override paint() on a JFrame.
The proper way to do custom painting is to override paintComponent(...) on a JPanel (or JComponent) and then you can set the content pane of the frame to this panel. And don't forget to invoke super.paintComponent(...) as the first statement in the method. Read the section from the Swing tutorial on Custom Painting for more information and working examples.
However if you do get lazy, then at minimum you need to invoke super.paint(...) as the first statement in the paint(...) method.
Also, I doubt you need the revalidate(), since you don't appear to be adding/removing components from the frame.
But in general the order should be:
revalidate(); // to invoke the layout manager
repaint(); // paint components in new location.
I also don't know why you are invoking the update() method. That seems like old AWT code which you don't use in Swing. I suggest you take a look at the tutorial link I gave you and look at the table of contents for other Swing basics.
Here is the code:
I know by default this code below overrides the default behavier of the Button as Mock object. So it won't work. I stuck on that.
What's the best practice to pass the Test with an already existing view like that ? How can I run that small part of view within an anonymous inner class like that ? How can I simulate the click event but at the same time keep the code like it is.
I don't want to have a seperate getter, just to invoke button.click() method
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwtmockito.GwtMock;
import com.google.gwtmockito.GwtMockitoTestRunner;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
#RunWith(GwtMockitoTestRunner.class)
public class ExceptionTest {
#GwtMock
private ClickHandler clickHandler;
#GwtMock
private Button btn;
private GUI gui;
#Before
public void setUp() {
when(btn.addClickHandler(any(ClickHandler.class))).thenAnswer(new Answer() {
public Object answer(InvocationOnMock aInvocation) throws Throwable {
clickHandler = (ClickHandler) aInvocation.getArguments()[0];
return null;
}
});
gui = new GUI();
}
#Test
public void runner() {
clickHandler.onClick(new ClickEvent(){});
Assert.assertEquals(1, gui.getSize());
}
}
GUI.java
class GUI extends FlowPanel {
private final Button button = new Button("OK");
private int size;
public GUI() {
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent clickEvent) {
size += 1;
}
});
}
public int getSize(){
return this.size;
}
}
I'm creating a "simple" 2D game engine for the block game Collapse for my college class. I am trying to make it such that the JFrame is resized any time a new screen is introduced, and I have made two attempts at it, one that resizes correctly, but doesn't show anything, and one that shows SOMETHING, but doesn't resize.
For this post, I will be showing the code for the second game engine that I have created, as my first attempt was a monolithic version that I ended up having trouble following myself.
First off, for some reason the window will not close with the X button, even though the setDefaultCLoseOperation(JFrame.EXIT_ON_CLOSE) is enacted.
Second (for the second attempt) the JFrame will not resize, but will show a sliver of the test StartMenu
(sorry for links, editor said I have to have rep of 10 before being able to post pictures... smart move, but still annoying)
http://i1148.photobucket.com/albums/o577/FoxnEagle/GraphicsWindow1_zpscqdgsjqh.png
but upon resizing;
http://i1148.photobucket.com/albums/o577/FoxnEagle/GraphicsWindow2_zpsckr4eohs.png
I guess the question of the post is what am I doing wrong to have the window become unresponsive, and be able to make modern art with it?
Collapse.java
import gameEngine.*;
import screens.*;
import javax.swing.*;
public class Collapse{
private Game game;
public Collapse(){
this.game = new Game("Test", null);
game.getScrMan().setScreen(new StartMenu(game.getScrMan()));
}
public static void main(String[] args){
new Collapse();
}
}
:||package screens||:
StartMenu.java
package screens;
import gameEngine.*;
import java.awt.*;
import javax.swing.*;
public class StartMenu extends Screen{
private final ScreenManager scrMan;
public StartMenu(ScreenManager scrMan){
super(scrMan);
this.scrMan = scrMan;
}
public void onCreate(){
JButton b1 = new JButton();
scrMan.getScrMan().setSize(200, 200);
scrMan.getScrMan().add(b1);
System.out.println("got here");
}
public void onUpdate(){
}
public void onDraw(Graphics2D g2d){
g2d.setColor(Color.BLACK);
g2d.fillOval(10, 10, 10, 10);
}
public void paintComponent(Graphics g){
g.setColor(Color.BLACK);
g.fillOval(10,10,10,10);
}
}
:||package gameEngine||:
Game.java
package gameEngine;
import javax.swing.*;
import java.awt.*;
public class Game{
private final ImageIcon image;
private final String title;
private final GameLoop gameLoop;
private final ScreenManager scrMan;
private final InputManager inpMan;
private final EntityManager entMan;
private JFrame window;
private boolean running;
//constructor and initializer for gameLoop
public Game(String title, ImageIcon image){
this.title = title;
this.image = image;
window = new JFrame();
scrMan = new ScreenManager(this);
inpMan = new InputManager();
entMan = new EntityManager();
gameLoop = new GameLoop(this);
initialize();
SwingUtilities.invokeLater(gameLoop);
}
private void initialize(){
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //close on exit
window.setTitle(title); //set the title
if(image != null){} //set the image icon NOT IMPLEMENTED YET
//window.setResizable(false); //user cannot resize
window.setFocusable(true); //
window.setLocationRelativeTo(null); //put in center of screen
window.add(scrMan); //add the
window.pack();
window.setVisible(true);
running = true;
}
public JFrame getWindow(){return window;}
public boolean getRunning(){return running;}
public ScreenManager getScrMan(){return scrMan;}
public InputManager getInput(){return inpMan;}
public EntityManager getEntMan(){return entMan;}
public void paused(){running = false;}
public void resume(){running = true;}
}
GameLoop.java
package gameEngine;
import java.lang.Runnable;
public class GameLoop implements Runnable{
private final Game game;
private long time;
private final int fps = 30;
public GameLoop(Game game){this.game = game;}
public void run(){
while(true){
while(game.getRunning()){
//System.out.println("running");//debugging
if(game.getScrMan().getScreen() != null){
game.getScrMan().getScreen().onUpdate();
//System.out.println("screen is updating");//debugging
}
//fps clock, commenting out does not fix problem
time = System.currentTimeMillis();
time = (1000/fps) - (System.currentTimeMillis() - time);
if(time > 0){try{Thread.sleep(time);}catch(Exception e){}}
}
}
}
}
ScreenManager.java
package gameEngine;
import javax.swing.JPanel;
import java.awt.*;
public class ScreenManager extends JPanel{
private final Game game;
private Screen screen;
public ScreenManager(Game game){this.game = game;}
public void setScreen(Screen screen){
this.screen = screen;
this.removeAll();
screen.onCreate();
game.getWindow().revalidate();
//game.getWindow().pack();
}
public Screen getScreen(){return screen;}
public Game getGame(){return game;}
public ScreenManager getScrMan(){return this;}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(0,0, 10, 10);
}
}
Screen.java (StartMenu extends and overrides all functions)
package gameEngine;
import java.awt.Graphics;
import java.awt.Graphics2D;
public abstract class Screen{
private final ScreenManager scrMan;
public Screen(ScreenManager scrMan){this.scrMan = scrMan;}
public abstract void onCreate();
public abstract void onUpdate();
public abstract void onDraw(Graphics2D g2d);
}
InputManager.java (nothing implemented yet, just a placeholder)
package gameEngine;
import java.awt.event.*;
public class InputManager implements KeyListener, MouseListener{
public void mouseClicked(MouseEvent event){
}
public void mouseEntered(MouseEvent event){
}
public void mouseExited(MouseEvent event){
}
public void mousePressed(MouseEvent event){
}
public void mouseReleased(MouseEvent event){
}
public void keyPressed(KeyEvent event){
}
public void keyReleased(KeyEvent event){
}
public void keyTyped(KeyEvent event){
}
}
EntityManager.java and Entity.java are just blank classes
This,
SwingUtilities.invokeLater(gameLoop);
runs your game loop in the event dispatch thread, blocking it. So the repaint manager never has a change to draw the contents. You should instead create the GUI in the event dispatch thread, as modifying or creating swing components from other threads is not safe.
The game loop's timing needs to be done in a way that does not block the EDT. The easiest is using a swing Timer; if that proves later to be insufficient, you can switch to another solution, but then you need to pay close attention to thread safety. Swing timer runs the action listener code in the EDT so it's easy to be thread safe that way.
I have 3 clasess : Loader, MyDialog and TEST(with main method). (for code see below)
Everything I want to achieve is create simple dialog with JLabel and JProgressBar, which will notify user about how much time remains to show MyDialog. MyDialog is Jdialog with time consuming operation in constructor (loading data from database etc.).
In code below is model situation. When "MyDialog" is created by main (constant BY_USER is false), everything working exactly i want to. But when i make dialog with button , and instance of MyDialog is created after button press (constant BY_USER is true), Loader is blank white form. It looks like is not completed.
Loader is extending Thread, so i suppose that problem will be in threading (event dispatch thread)? I dont know, what is wrong and how fix it. Please help.
Thanks and sorry for my English.
CLASS TEST :
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class TEST {
public static final boolean BY_USER = false;
public static void main(String[] args) {
if (BY_USER) {
JFrame mainDialog = new JFrame("Main");
JButton show = new JButton("Show MyDialog");
show.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MyDialog dialog = new MyDialog();
}
});
mainDialog.add(show);
mainDialog.setLocationRelativeTo(null);
mainDialog.setMinimumSize(new Dimension(160, 80));
mainDialog.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainDialog.setVisible(true);
} else {
MyDialog dialog = new MyDialog();
}
}
}
CLASS MyDialog :
package test;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class MyDialog extends JFrame{
public MyDialog() {
super();
// making loader with title, first message and count of steps of operation
Loader loader = new Loader("Loader", "First showed message", 100);
loader.ShowLoader();
// time-consuming operation (loading data from database etc.).
// for clarity replaced with for statement
int j=0;
for(int i=0; i<Integer.MAX_VALUE; i++)
{
j++;
if(j==Integer.MAX_VALUE/100){
// updating loader message and progress bar value
loader.NewAction(Integer.MAX_VALUE - i+"");
j=0;
}
}
// closing loader
loader.DestroyLoader();
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
CLASS Loader:
package test;
import java.awt.BorderLayout;
import java.awt.Dialog;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
public class Loader extends Thread{
private JDialog dialog;
private JLabel message = new JLabel("", SwingConstants.CENTER);
private JProgressBar progressBar = new JProgressBar(0, 100);
private String newMessage;
private double percentForStep;
private int remainingSteps;
public Loader(String taskName, String firstMessage, int steps) {
this.remainingSteps = steps-1;
dialog = new JDialog((Dialog) null, taskName);
dialog.setLayout(new BorderLayout(15, 15));
dialog.add(message, BorderLayout.CENTER);
dialog.add(progressBar, BorderLayout.SOUTH);
message.setText(firstMessage);
percentForStep = 100 / steps;
}
public void ShowLoader()
{
dialog.setMinimumSize(new Dimension(400,120));
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
this.start();
}
public void DestroyLoader(){
dialog.dispose();
this.interrupt();
}
public void NewAction(String newMessage){
this.newMessage = newMessage;
this.remainingSteps--;
Lock.changed = true;
}
public int RemainingStepsCount()
{
return remainingSteps;
}
#Override
#SuppressWarnings({"CallToThreadYield", "SleepWhileInLoop"})
public void run() {
do{
synchronized (Lock.class) {
if (Lock.changed) {
Lock.changed = false;
this.message.setText(newMessage);
this.progressBar.setValue((int)(100-(remainingSteps*percentForStep)));
dialog.repaint();
}
dialog.repaint();
}
}while(true);
}
}
class Lock{
static boolean changed = false;
}
Look to SwingWorker and his use; I think it can help you to solve the problem.