I'm trying to code a little maze runner program, and have run into some trouble relating to the paintComponent(). I have gone through the debug and for some reason my paintComponent() is never called, even with the repaint() which is being called by my timer.
private void jpanelinit() {
JPanel Background = new JPanel (new BorderLayout());
JPanel Menu = new JPanel (new BorderLayout());
JPanel Maze = new JPanel (new GridLayout(arow, acolumn));
Background.setPreferredSize(new Dimension(850,850));
Menu.setPreferredSize(new Dimension(850, 100));
Maze.setPreferredSize(new Dimension(850,750));
frame.add(Background);
comboboxinit();
Background.add(Menu, BorderLayout.NORTH);
Background.add(Maze, BorderLayout.SOUTH);
Menu.add(Startpause, BorderLayout.WEST);
Menu.add(Reset, BorderLayout.EAST);
Menu.add(Intervalpick, BorderLayout.CENTER);
Intervalpick.setVisible(true);
Intervalpick.addActionListener(this);
Startpause.setVisible(true);
Startpause.addActionListener(this);
Reset.setVisible(true);
Reset.addActionListener(this);
Maze.setVisible(true);
}
private static void frameinit() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setSize(850,850);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
Those are my frame and jpanel init methods.
#Override
public void paintComponent(Graphics g){
System.out.println("Entered Graphics");
super.paintComponent(g);
g.drawImage(biWall,0,100,850,750, this );
}
This is my paintComponent, the image is indeed buffered and has been stored.
public void actionPerformed(ActionEvent e) {
if(e.getSource()==Intervalpick)
timecheck(); //Checks if the time was changed
if(e.getSource()==Startpause||e.getSource()==Reset)
buttoncheck(e); //Checks if the buttons were pressed
if(t.isRunning())
mazeupdate();
}
private void mazeupdate() {
repaint();
}
That is my actionPerformed which is called with my timer which is set to a 5 sec interval at default.
public class mazerunner extends JPanel implements ActionListener {
static JButton Startpause = new JButton("Start");
static JButton Reset = new JButton("Reset");
static JComboBox Intervalpick= new JComboBox();
static JFrame frame=new JFrame ("Maze Runner");
static int arow=0, acolumn=0, icurrenttime=5000;
static boolean gameplaying=false;
static Timer t;
static BufferedImage biWall, biFloor, biMouse, biCheese;
public static void main(String[] args) {
mazerunner mr= new mazerunner();
filereader(); //Reads the file
imagebuffer(); //Buffers the images
mr.jpanelinit(); //Inits the gui
frameinit(); //Inits the frame
mr.timerinit(); //Inits the timer
}
private static void imagebuffer() {
try{
biWall=ImageIO.read(new File("cobblewall.jpg"));
}
catch(IOException e){}
try{
biFloor=ImageIO.read(new File("woodenfloor.jpg"));
}
catch(IOException e){}
try{
biCheese=ImageIO.read(new File("chest_cheese.jpg"));
}
catch(IOException e){}
try{
biMouse=ImageIO.read(new File("lara_mouse.jpg"));
}
catch(IOException e){}
}
private void timerinit() {
t=new Timer(icurrenttime,this);
}
private void jpanelinit() {
JPanel Background = new JPanel (new BorderLayout()); //Inits all the JPanels
JPanel Menu = new JPanel (new BorderLayout());
JPanel Maze = new JPanel (new GridLayout(arow, acolumn));
Background.setPreferredSize(new Dimension(850,850)); //Sets the size of the panels
Menu.setPreferredSize(new Dimension(850, 100));
Maze.setPreferredSize(new Dimension(850,750));
frame.add(Background); //Adds background into the frame
comboboxinit();
Background.add(Menu, BorderLayout.NORTH); //Adds the other panels into the background
Background.add(Maze, BorderLayout.SOUTH);
Menu.add(Startpause, BorderLayout.WEST); //Adds the menu's components into the menu panel
Menu.add(Reset, BorderLayout.EAST);
Menu.add(Intervalpick, BorderLayout.CENTER);
Intervalpick.setVisible(true); //Sets the components to visible and adds actionlistener
Intervalpick.addActionListener(this);
Startpause.setVisible(true);
Startpause.addActionListener(this);
Reset.setVisible(true);
Reset.addActionListener(this);
Maze.setVisible(true);
}
private static void comboboxinit() {
for(int a=5;a<=30;a=a+5){ //Sets the text inside the combobox
Intervalpick.addItem(a+" Seconds");
}
DefaultListCellRenderer dlcr = new DefaultListCellRenderer(); //Centers the text inside the combobox
dlcr.setHorizontalAlignment(DefaultListCellRenderer.CENTER);
Intervalpick.setRenderer(dlcr);
}
private static void frameinit() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Inits the jframe
frame.setResizable(false);
frame.setSize(850,850);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
private static void filereader() {
try{
FileReader fr=new FileReader("maze.txt");
BufferedReader br= new BufferedReader(fr);
String LineIn=br.readLine();
int num, row=0;
arow=Integer.parseInt(LineIn);
LineIn=br.readLine();
acolumn=Integer.parseInt(LineIn);
int Maze[][]=new int[arow][acolumn]; //inits the maze itself
LineIn=br.readLine();
do{ //stores the maze from the file into the arrray
int collumn=0;
StringTokenizer tokens=new StringTokenizer(LineIn);
while (tokens.hasMoreTokens()){
num=Integer.parseInt(tokens.nextToken());
Maze[row][collumn]=num;
collumn++;
}
LineIn=br.readLine();
row++;
}while(LineIn!=null);
br.close();
fr.close();
}
catch(FileNotFoundException e){
System.err.println("file not found");
}
catch(IOException e){
System.err.println("read failed");
}
}
#Override
public void paintComponent(Graphics g){
System.out.println("Entered Graphics");
super.paintComponent(g);
g.drawImage(biWall,0,100,850,750, this );
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==Intervalpick)
timecheck(); //Checks if the time was changed
if(e.getSource()==Startpause||e.getSource()==Reset)
buttoncheck(e); //Checks if the buttons were pressed
if(t.isRunning())
mazeupdate();
}
private void mazeupdate() {
repaint();
}
private void buttoncheck(ActionEvent e) {
if(e.getSource()==Startpause){ //Starts and pauses the simulation
if(gameplaying==false){
gameplaying=true;
t.start();
Startpause.setText("Pause");
}
else if(gameplaying==true){
gameplaying=false;
t.stop();
Startpause.setText("Start");
}
}
else if(e.getSource()==Reset){ //Resets the maze to the original maze
if(t.isRunning())
t.stop();
gameplaying=false;
Startpause.setText("Start");
filereader();
// t.repaint();
}
}
private void timecheck() {
int boxtime= Integer.parseInt(((String) Intervalpick.getSelectedItem()).split(" ")[0])*1000; //Turns Intervalpick into a milisecond amount
if(boxtime!=icurrenttime){ //Checks if the selected delay is equal to the actual delay
boolean gamerunning=false;
icurrenttime=boxtime;
if(t.isRunning()){ //Stops timer if running
t.stop();
gamerunning=true;
}
t.setDelay(icurrenttime); //Changes delay
if(gamerunning==true) //If timer was running it turns it back on
t.start();
}
}
}
That is my full code if you're interested. My question is why I can't get the image to draw onto the Maze panel.
paintComponent won't be called for three basic reasons.
The component isn't attached to a valid native peer (ie, it's not attached to a window directly or indirectly that is visible on the screen)
or it doesn't have a size greater than 0x0
or it's not visible...
Skimming through your code, I can't find anywhere an instance of mazerunner is actually added to the frame...
You might like to have a read through Code Conventions for the Java TM Programming Language, it will make it easier for people to read your code and for you to read others
Related
I am new to graphics in java and I am currently working on a game. Essentially, there are rising bubbles, and the user has to pop them by moving the mouse over them.
I have already made an animation on the JFrame and I need to add a JPanel for a MouseMotionListener on top. However, when I add the JPanel on top of the JFrame (even with the setOpaque to false) it still does not let me see my animation underneath. You can see my code below. If you find coding errors, please let me know.
I have two solutions in mind, either animate in JPanel (which I don't know how to do), or make the JPanel transparent.
Game Class:
public class Game extends JPanel{
public static final int WINDOW_WIDTH = 600;
public static final int WINDOW_HEIGHT = 400;
private boolean insideCircle = false;
private static boolean ifPaused = false;
private static JPanel mainPanel;
private static JLabel statusbar;
private static int clicks = 0;
//Creates a Bubbles object Array
Bubbles[] BubblesArray = new Bubbles[5];
public Game() {
//initializes bubble objects
for (int i = 0; i < BubblesArray.length; i++)
BubblesArray[i] = new Bubbles();
}
public void paint(Graphics graphics) {
//makes background white
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
//paints square objects to the screen
for (Bubbles aBubblesArray : BubblesArray) {
aBubblesArray.paint(graphics);
}
}
public void update() {
//calls the Square class update method on the square objects
for (Bubbles aBubblesArray : BubblesArray) aBubblesArray.update();
}
public static void main(String[] args) throws InterruptedException {
statusbar = new JLabel("Default");
mainPanel = new JPanel();
mainPanel.setOpaque(false);
mainPanel.setBackground(new Color(0,0,0,0));
mainPanel.setVisible(true);
mainPanel.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
statusbar.setText(String.format("Your mouse is at %d, %d", e.getX(), e.getY()));
}
public void mouseExited(MouseEvent e){
ifPaused = true;
}
public void mouseEntered(MouseEvent e){
ifPaused = false;
}
});
Game game = new Game();
JFrame frame = new JFrame();
frame.add(game);
frame.add(mainPanel);
frame.add(statusbar, BorderLayout.SOUTH);
frame.setVisible(true);
frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setTitle("Bubble Burst");
frame.setResizable(false);
frame.setLocationRelativeTo(null);
while (true) {
if (!ifPaused){
game.update();
game.repaint();
Thread.sleep(5);
}
}
}
}
Your game panel is not showing up because you are adding two components to the same location of a BorderLayout (at BorderLayout.CENTER). Therefore mainPanel is not being added "on top of" game, it is replacing it. That being said:
It doesn't seem like your mainPanel is actually doing anything except listening for mouse motion. Could you not just add the MouseAdapter to your game object since it extends JPanel like this:
game.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
statusbar.setText(String.format("Your mouse is at %d, %d", e.getX(), e.getY()));
}
public void mouseExited(MouseEvent e){
ifPaused = true;
}
public void mouseEntered(MouseEvent e){
ifPaused = false;
}
});
I have a JFrame and 2 JPanels. One panel is the start screen with buttons. The other panel is the game screen. When I remove the start screen and display the game screen nothing happens to the JFrame. The start screen is persistent but not usable and the game runs in the background without updating the screen. When the game completes the start screen is usable again. I've searched everywhere online for a solution. They all say removeAll, revalidate, repaint, pack, or getContentPane.removeAll. I've not been able to get any of these solutions to work. Here is the driver class:
public class BallDriver extends JFrame {
private static final long serialVersionUID = 1L;
int width = 500;
int height = 500;
Container cont;
StartScreen start;
BallGame ballGame;
public BallDriver() {
cont = getContentPane();
startScreen();
}
private void startScreen() {
setTitle("BallGame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(500,500));
start = new StartScreen();
JButton[] startButtons = start.getButtons();
setupButtons(startButtons);
add(start, BorderLayout.CENTER);
}
private void startGame(String difficulty) throws InterruptedException {
removeAll();
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(width, height));
ballGame = new BallGame(width, height);
ballGame.setPreferredSize(new Dimension(width, height));
add(ballGame, BorderLayout.CENTER);
revalidate();
repaint();
while(!ballGame.endGame()) {
ballGame.moveBall();
ballGame.repaint();
try {
Thread.sleep(2);
} catch (InterruptedException e) { e.printStackTrace(); }
}
removeAll();
add(start, BorderLayout.CENTER);
revalidate();
repaint();
}
private void setupButtons(final JButton[] buttons) {
for (JButton button : buttons) {
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object object = e.getSource();
try {
if (object == buttons[0])
startGame(StartScreen.MODE_EASY);
if (object == buttons[1])
startGame(StartScreen.MODE_NORMAL);
if (object == buttons[2])
startGame(StartScreen.MODE_HARD);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BallDriver ballDriver = new BallDriver();
ballDriver.setVisible(true);
}
});
}
}
EDIT:
I've updated the class to use cardlayout, but the same thing occurs.
public class BallDriver extends JFrame {
private static final long serialVersionUID = 1L;
int width = 500;
int height = 500;
JPanel cardPanel = new JPanel();
Container cont;
StartScreen start;
BallGame ballGame;
CardLayout cardLayout = new CardLayout();
public BallDriver() {
setTitle("BallGame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(new Dimension(width, height));
setPreferredSize(new Dimension(width, height));
cardPanel.setLayout(cardLayout);
cont = getContentPane();
add(cardPanel, BorderLayout.CENTER);
startScreen();
}
private void startScreen() {
start = new StartScreen();
JButton[] startButtons = start.getButtons();
setupButtons(startButtons);
cardPanel.add(start, "start");
cardLayout.show(cardPanel, "start");
}
private void startGame(String difficulty) throws InterruptedException {
ballGame = new BallGame(width, height);
cardPanel.add(ballGame, "game");
cardLayout.show(cardPanel, "game");
cardPanel.revalidate();
cardPanel.repaint();
while(!ballGame.endGame()) {
System.out.println("Running");
ballGame.moveBall();
ballGame.repaint();
try {
Thread.sleep(2);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
private void setupButtons(final JButton[] buttons) {
for (JButton button : buttons) {
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object object = e.getSource();
try {
if (object == buttons[0])
startGame(StartScreen.MODE_EASY);
if (object == buttons[1])
startGame(StartScreen.MODE_NORMAL);
if (object == buttons[2])
startGame(StartScreen.MODE_HARD);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
BallDriver ballDriver = new BallDriver();
ballDriver.setVisible(true);
}
});
}
}
CardLayout allows you to add multiple panels to a Container, displaying only 1 panel at a time, with the ability to switch between panels. You set the parent's layout to CardLayout, add panels to your frame specifying a name as the constraints:
CardLayout layout = new CardLayout();
JFrame frame = new JFrame();
frame.setLayout(layout);
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
frame.add(panel1, "first");
frame.add(panel2, "second");
The first panel added is the first tk be displayed. To switch between panels, call CardLayout#show(Container, String), passing in the parent of the panels (considered the "deck") and the name of the specific panel you want (considered the "card").
layout.show(frame, "second");
A common problem people have with CardLayout is having the parent resize to the current panel's size. This can be achieved by extending upon CardLayout
I'm wanting the panel to expand and display buttons and stuff when the mouse is hovered over, then apon mouse exiting the panel must return top original size.
So far I can only print a message:
public JPanel GUI()
{
final JPanel totalGUI = new JPanel();
totalGUI.setBackground(Color.blue);
totalGUI.setLayout(null);
//+++++++++++++++
// - - - - - - PANEL 1!
//+++++++++++++++
JPanel SearchPanel = new JPanel(); //Create new grid bag layout
SearchPanel.setLocation(5, 5);
SearchPanel.setSize(420, 120);
totalGUI.add(SearchPanel);
SearchPanel.addMouseListener(this);
return totalGUI;
}
public void mouseEntered(MouseEvent e) {
System.out.print("Mouse entered");
}
public void mouseExited(MouseEvent e) {
System.out.print("Mouse exited");
}
private static void createAndShowGUI()
{
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("RWB");
gay3 demo = new gay3();
frame.setContentPane(demo.GUI());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setSize(650, 500);
frame.setVisible(true);
}
public static void main(String[] args)
{
createAndShowGUI();
}
You could do this inside the mouseEntered and mouseExited methods:
JPanel source = (JPanel)e.getSource();
//Edit the searchPanel here
If searchPanel isn't the only component that uses this MouseListener, you may first have to check if the source really is an instance of JPanel:
Object o = e.getSource();
if(o instanceof JPanel) {
JPanel source = (JPanel)o;
//Edit the searchPanel here
}
This isn't necessary if you know the source is a JPanel.
hi I'm basically give up. Ok so this is what I am trying to do. So far I have written code to create a JFrame which contains a text field and combo box. The code is supposed to calculate the area of an input in the text field depending on what shape is selected from the combo box and output the result on the JFrame!
Here's is what the output should look like
And here is my code so far. it's a bit messed up but any help would be much appreciated. Thanks in advance
import javax.swing. *;
import java.awt.event. *;
import java.awt.FlowLayout;
import java.lang.Math;
public class AreaFrame3 extends JFrame
{
double Area;
double input;
public static void main(String[]args)
{
//Create array containing shapes
String[] shapes ={"(no shape selected)","Circle","Equilateral Triangle","Square"};
//Use combobox to create drop down menu
JComboBox comboBox=new JComboBox(shapes);
JLabel label1 = new JLabel("Select shape:");
JPanel panel1 = new JPanel(new FlowLayout()); //set frame layout
JLabel label2 = new JLabel("(select shape first)");
JTextField text = new JTextField(10); //create text field
text.setEnabled(false);
panel1.add(label1);
panel1.add(comboBox);
panel1.add(label2);
panel1.add(text);
JFrame frame=new JFrame("Area Calculator Window");//create a JFrame to put combobox
frame.setLayout(new FlowLayout()); //set layout
frame.add(panel1);
frame.add(text);
//JButton button = new JButton("GO"); //create GO button
//frame.add(button);
//set default close operation for JFrame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
//set JFrame ssize
frame.setSize(400,250);
//make JFrame visible. So we can see it
frame.setVisible(true);
// public void actionPerformed(ActionEvent e)
//{
}
public void AreaCalc()
{
JButton button = new JButton("GO"); //create GO button
frame.add(button);
button.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e)
{
int input = double.parseDouble(text.getText());
if(e.getSource() == button)
{
String shape = (String).comboBox.getSelectedItem();
if(shape == "(no shape selected)")
{
text.setEnabled(false);
}
else{
text.setEnabled(true);
}
if(input > 1 && shape == "Circle")
{
// label2.getText() = "Enter the radius of the circle: ";
Area = (Math.PI * (input * input));
}
}
else{}
}
}
);
}
}
I try to understand what you did here:
panel1.add(label1);
panel1.add(comboBox);
panel1.add(label2);
panel1.add(text); // <---
JFrame frame=new JFrame("Area Calculator Window");//create a JFrame to put combobox
frame.setLayout(new FlowLayout()); //set layout
frame.add(panel1);
frame.add(text); // <---
Especially frame.add(text); and panel1.add(text);. Don't add text to JFrame. Use JPanel.
Further,
public class AreaFrame3 extends Frame
Use public class AreaFrame3 extends JFrame so you don't need create additional JFrame:
JFrame frame=new JFrame("Area Calculator Window");
Something like:
super.setLayout(new FlowLayout()); //set layout
super.add(panel1);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.pack();
//set JFrame ssize
super.setSize(400,250);
//make JFrame visible. So we can see it
super.setVisible(true);
At last Ill give you some tamplate to start with (that will help you):
public class FrameExmpl extends JFrame{
private static final long serialVersionUID = 1L;
private JTabbedPane tabbedPane;
private JPanel topPanel;
private JTextField txtf_loadDS_;
public static int valueInt = 0; // responsible for Task status updating
public static Boolean isFinish = false;
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException{
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
FrameExmpl UI_L = new FrameExmpl();
UI_L.buildGUI();
UI_L.setVisible(true);
}
public void buildGUI(){
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
}
});
setSize(435, 225);
setLocation(285, 105);
setResizable(false);
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);
txtf_loadDS_ = new JTextField();
txtf_loadDS_.setBounds(22, 22, 382, 25);
topPanel.add(txtf_loadDS_);
finishBuildGUI();
}
public void finishBuildGUI(){
tabbedPane = new JTabbedPane();
topPanel.add(tabbedPane, BorderLayout.CENTER);
}
}
There are multiple issues with this application such as extending from Frame rather than JFrame & attempting to assign an int from Double.parseDouble. I would recommend that you start again building a small but working application and incrementally add functionality, this way errors are easier to fix.
I have a button in a java frame that when pressed it reads a value from a text field and uses that string as a port name attempting to connect to a serial device.
If this connection is successful the method returns true if not it returns false. If it returns true I want the frame to disappear. A series of other frames specifed in other classes will then appear with options to control the serial device.
My problem is: the button is connected to an action listener, when pressed this method is invoked. If I try to use the frame.setVisible(true); method java throws a abstract button error because I'm effectively telling it to disappear the frame containing the button before the button press method has exited. Removing the frame.setVisible(true); allow the program to run correctly however I am left with a lingering connection frame that is no longer any use.
How to I get the frame to disappear upon pressing a the button?
package newimplementation1;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
*
* #author Zac
*/
public class ConnectionFrame extends JPanel implements ActionListener {
private JTextField textField;
private JFrame frame;
private JButton connectButton;
private final static String newline = "\n";
public ConnectionFrame(){
super(new GridBagLayout());
textField = new JTextField(14);
textField.addActionListener(this);
textField.setText("/dev/ttyUSB0");
connectButton = new JButton("Connect");
//Add Components to this panel.
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(connectButton, c);
connectButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
boolean success = Main.mySerialTest.initialize(textField.getText());
if (success == false) {System.out.println("Could not connect"); return;}
frame.setVisible(false); // THIS DOES NOT WORK!!
JTextInputArea myInputArea = new JTextInputArea();
myInputArea.createAndShowGUI();
System.out.println("Connected");
}
});
}
public void actionPerformed(ActionEvent evt) {
// Unimplemented required for JPanel
}
public void createAndShowGUI() {
//Create and set up the window.
frame = new JFrame("Serial Port Query");
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
//Add contents to the window.
frame.add(new ConnectionFrame());
frame.setLocation(300, 0);
//Display the window.
frame.pack();
frame.setVisible(true);
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent e) {
System.out.println("Exiting Gracefully");
Main.mySerialTest.close();
((JFrame)(e.getComponent())).dispose();
System.exit(0);
}
});
}
}
Running your snippet (after removing/tweaking around the custom classes), throws an NPE. Reason is that the frame you'r accessing is null. And that's because it's never set. Better not rely on any field, let the button find its toplevel ancestor and hide that, like in
public void actionPerformed(final ActionEvent e) {
boolean success = true;
if (success == false) {
System.out.println("Could not connect");
return;
}
Window frame = SwingUtilities.windowForComponent((Component) e
.getSource());
frame.setVisible(false); //no problem :-)
}
Your problem is with this line:
frame.add(new ConnectionFrame());
You're creating a new ConnectionFrame object, and so the frame that your button tries to close on is not the same as the one being displayed, and this is the source of your problem.
If you change it to,
//!! frame.add(new ConnectionFrame());
frame.add(this);
so that the two JFrames are one and the same, things may work more smoothly.
But having said that, your whole design smells bad and I'd rethink it in a more OOP and less static fashion. Also, use dialogs where dialogs are needed, not frames, and rather than dialogs consider swapping views (JPanels) via CardLayout as a better option still.
Myself, I'd create a "dumb" GUI for this, one that creates a JPanel (here in my example it extends a JPanel for simplicity, but I'd avoid extending if not necessary), and I'd let whoever is calling this code decide what to do with the information via some control. For e.g.,
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ConnectionPanel extends JPanel {
private JTextField textField;
private JButton connectButton;
private ConnectionPanelControl control;
public ConnectionPanel(final ConnectionPanelControl control) {
super(new GridBagLayout());
this.control = control;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.connectButtonAction();
}
}
};
textField = new JTextField(14);
textField.addActionListener(listener);
textField.setText("/dev/ttyUSB0");
connectButton = new JButton("Connect");
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
add(textField, c);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(connectButton, c);
connectButton.addActionListener(listener);
}
public String getFieldText() {
return textField.getText();
}
}
Again, something outside of the simple GUI would make decisions on what to do with the text that the textfield contains and what to do with the GUI that is displaying this JPanel:
public interface ConnectionPanelControl {
void connectButtonAction();
}
Also, you will likely do any connecting in a background thread so as to not freeze your GUI, probably a SwingWorker. Perhaps something like this:
import java.awt.event.ActionEvent;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyMain extends JPanel {
public MyMain() {
add(new JButton(new ConnectionAction("Connect", this)));
}
private static void createAndShowGui() {
JFrame frame = new JFrame("My Main");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyMain());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class ConnectionAction extends AbstractAction {
private MyMain myMain;
private ConnectionPanel cPanel = null;
private JDialog dialog = null;
public ConnectionAction(String title, MyMain myMain) {
super(title);
this.myMain = myMain;
}
#Override
public void actionPerformed(ActionEvent e) {
if (dialog == null) {
dialog = new JDialog(SwingUtilities.getWindowAncestor(myMain));
dialog.setTitle("Connect");
dialog.setModal(true);
cPanel = new ConnectionPanel(new ConnectionPanelControl() {
#Override
public void connectButtonAction() {
final String connectStr = cPanel.getFieldText();
new MySwingWorker(connectStr).execute();
}
});
dialog.getContentPane().add(cPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
}
dialog.setVisible(true);
}
private class MySwingWorker extends SwingWorker<Boolean, Void> {
private String connectStr = "";
public MySwingWorker(String connectStr) {
this.connectStr = connectStr;
}
#Override
protected Boolean doInBackground() throws Exception {
// TODO: make connection and then return a result
// right now making true if any text in the field
if (!connectStr.isEmpty()) {
return true;
}
return false;
}
#Override
protected void done() {
try {
boolean result = get();
if (result) {
System.out.println("connection successful");
dialog.dispose();
} else {
System.out.println("connection not successful");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
Your code would be much more readable if you named JFrame instances xxxFrame, and JPanel instances xxxPanel. Naming JPanel instances xxxFrame makes things very confusing.
It would also help if you pasted the stack trace of the exception.
I suspect the problem comes from the fact that frame is null. This is due to the fact that the frame field is only initialized in the createAndShowGUI method, but this method doesn't display the current connection panel, but a new one, which thus have a null frame field:
ConnectionFrame firstPanel = new ConnectionFrame();
// The firstPanel's frame field is null
firstPanel.createAndShowGUI();
// the firstPanel's frame field is now not null, but
// the above call opens a JFrame containing another, new ConnectionFrame,
// which has a null frame field
The code of createAndShowGUI should contain
frame.add(this);
rather than
frame.add(new ConnectionFrame());
for Swing GUI is better create only once JFrame and another Top-Level Containers would be JDialog or JWindow(un-decorated by default),
simple example here
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SuperConstructor extends JFrame {
private static final long serialVersionUID = 1L;
public SuperConstructor() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(300, 300));
setTitle("Super constructor");
Container cp = getContentPane();
JButton b = new JButton("Show dialog");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
FirstDialog firstDialog = new FirstDialog(SuperConstructor.this);
}
});
cp.add(b, BorderLayout.SOUTH);
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
});
add(bClose, BorderLayout.NORTH);
pack();
setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
SuperConstructor superConstructor = new SuperConstructor();
}
});
}
private class FirstDialog extends JDialog {
private static final long serialVersionUID = 1L;
FirstDialog(final Frame parent) {
super(parent, "FirstDialog");
setPreferredSize(new Dimension(200, 200));
setLocationRelativeTo(parent);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
JButton bNext = new JButton("Show next dialog");
bNext.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
SecondDialog secondDialog = new SecondDialog(parent, false);
}
});
add(bNext, BorderLayout.NORTH);
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
setVisible(false);
}
});
add(bClose, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
private int i;
private class SecondDialog extends JDialog {
private static final long serialVersionUID = 1L;
SecondDialog(final Frame parent, boolean modal) {
//super(parent); // Makes this dialog unfocusable as long as FirstDialog is visible
setPreferredSize(new Dimension(200, 200));
setLocation(300, 50);
setModal(modal);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setTitle("SecondDialog " + (i++));
JButton bClose = new JButton("Close");
bClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
setVisible(false);
}
});
add(bClose, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
}
better would be re-use Top-Level Containers, as create lots of Top-Level Containers on Runtime (possible memory lack)