I am having a bit of problem regarding Swing. I have a JFrame called FrameMain. Inside it is a JPanel called panelChoices.
When FrameMain is called/created, it fills up the panelChoices object with a number of PanelEntries objects, which is a JPanel with a number of JButtons in it (it is a different class that I wrote).
What I want to do is when I click one of the buttons inside the PanelEntries object, I want to destroy/remove FrameMain, along with the rest of it components (including the PanelEntries object that contains the JButton).
I've tried using super but it returns the JPanel (the PanelEntries object) that holds the JButton and not FrameMain that holds them all together. How can I achieve this?
EDIT: It seems that I am not clear enough, so here's a bit more information from my work. I don't have the actual code right now because I am on a different machine but I hope this will help elaborate my question.
public class FrameMain() {
private JFrame frameMain;
private JPanel panelChoices;
public FrameMain(args) {
createGUI();
loadData();
}
private void createGUI() {
JFrame frameMain = new JFrame();
JPanel panelChoices = new JPanel(new GridLayout(1,1));
frameMain.add(panel);
// removed formatting and other design codes since they are not important.
pack();
}
private void loadData() {
boolean available;
for (int i = 1; i <= 10; i++) {
// do some if/else and give value to boolean available
PanelEntries panel = new PanelEntries(i, available);
frameMain.add(panel);
// more code here to handle data.
}
}
}
public class PanelEntries() extends JPanel {
public PanelEntries(int num, boolean avb) {
JButton button = new JButton("Button Number " + num);
button.setEnabled(avb);
add(button);
// add action listener to created button so that it calls 'nextScreen()' when clicked.
// more code
pack();
}
private void nextScreen() {
// destroy/dispose MainFrame here.
// See Notes.
AnotherFrame anotherFrame = new AnotherFrame();
}
}
Notes:
All classes are inside their own .java file.
I need to know how to dispose FrameMain from the button inside the PanelEntries object, not just disposing a JFrame.
As per the given information,
If you want to exit the application, its not a big deal use System.exit(0); :)
If you mean to dispose the frame, jframe.dispose();
If you want to remove a componet / all components you can use .remove(Component) / .removeAll() etc
If this did not help, please re-write your question with more information.
Related
I've been researching communication, event handling and listening across JPanels for a while. I'm going to try and describe my issue without code first, because I feel it's more a design pattern roadblock.
So I have three custom JPanels inside a custom JFrame, each with their own instance variables and Actionlisteners. The ActionListeners at the moment update variables whenever a change happens within them.
But the catch is, I want the ActionListener in one panel to pay attention to elements in other panels. So if I've a box in Panel B and I change its value, I want the label in Panel C to change too.
I've researched a lot on the topic, from stackoverflow answers to documentation on the topic. But I'm having a hard time putting it all together. Especially when I've divided my custom panels into different classes. Can anyone help sum up how it should look?
Ultimately what you need here is to register an ActionListener on Panel B's text box which updates Panel C's label. The fact that Panel B and Panel C are different classes is just a minor bump in the road. The code that sets up this ActionListener simply needs to be able to get hold of references to 1) the text field whose actions we are interested in observing, and 2) the label whose text we are interested in changing.
Now, if Panel B and Panel C weren't separate classes, we would probably just have references to the text field and label handily laying around in member variables of our JFrame window. But Panel B and Panel C are separate classes, so we'll need to ask for their help. Well, actually, not so much ask as demand by dint of a little reprogramming...
First, have Panel B expose the text field with a getter method:
public class PanelB extends JPanel {
// ...
private JTextField textBox;
// ...
public JTextField getTextBox(){
return textBox;
}
}
Then, expose Panel C's label with a getter method:
class PanelC extends JPanel {
// ...
private JLabel label;
// ...
public JLabel getLabel() {
return label;
}
}
Now you can set up an ActionListener in more or less the usual way:
class MyFrame extends JFrame {
PanelB panelB = new PanelB();
PanelC panelC = new PanelC();
public MyFrame()
{
// ...
final JTextField panelBtf = panelB.getTextBox();
final JLabel panelClabel = panelC.getLabel();
panelBtf.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent ae)
{
panelClabel.setText(panelBtf.getText());
}}
);
}
}
I'm making a simple conversion tool to convert dollars to euro's and vice versa.
The whole purpose is just to experiment and learn this cool tool, java.
I have a JLabel at the top with an icon of a euro to indicate the starting currency. I have a button bellow this that I want to use to change that icon to a dollar one.
I am currently plying around with an ActionListener and trying different variations of setIcon/setIconImage (every itteration I can think of seeing that nothing has worked thus far).
public class MoneyConverter extends JFrame implements ActionListener{
//add label and icon showing base conversion currency
JLabel startcur = new JLabel("<--- Starting Curency", new ImageIcon("C:\\Users\\Russel\\Desktop\\1euro.gif"), SwingConstants.CENTER);
JButton euro = new JButton("Swap to Euro");
JButton dollar = new JButton("Swap to Dollar");
I then set up a
public MoneyConverter(){}
method and add all my components to a grid layout and add ActionLister's to my convert buttons.
e.g.
dollar.addActionListener(this);
euro.addActionListener(this);
After the usual code (setVisible and the likes that I will omit for your sake as I don't see it interfering with this, please let me know if I should include it all)
public void ActionPerformed (ActionEvent e){
Object source = e.getSource();
if (source.equals(euro)){
startcur.setIcon(new ImageIcon("C:\\Users\\Russel\\Desktop\\1.gif"));
}
}
This part has been changed many times and is the main reason for this post, how do I change this icon in the JLabel? - I will also be setting the conversion rate in here depending if they choose to start with dollars or euros. (Rate won't be actual rate.)
First, create and store a new ImageIcon
ImageIcon image = new ImageIcon(getClass().getResource("/nameOfImage.jpg"));
Then put this in your Action Listener
label.setIcon(image);
label.setText("");
You have to make sure you have a resource folder set up for your project. You can read how to do that in IntelliJ or Eclipse
You are also declaring the actionPerformed() wrong. I suggest reading up on this You should be doing it like this.
#Override
public void actionPerformed(ActionEvent e)
{
}
Conventionally, in java, method names start with a lower case letter and Classes start with an upper case.
The whole purpose is just to experiment and learn this cool tool, java.
Then I'll show you how to improve this program in a number of ways.
//Don't make your Swing class implement Actionlistener and add it as a
//listener to itself in the constructor before it's fully initialized
public class MoneyConverter extends JFrame {
//These don't have to be loaded at runtime, so make them into constants
//Java variables and methods follow thisNamingConvention
private static final Icon euroIcon = new ImageIcon("C:\\Users\\Russel\\Desktop\\1euro.gif");
private static final Icon dollarIcon = new ImageIcon("C:\\Users\\Russel\\Desktop\\1dollar.gif");
//These you probably want to use later so save them as private class variables
//Make them final so you can access them in the ActionListeners below
private final JLabel currencyLabel;
private final JButton euroButton;
private final JButton dollarButton;
public MoneyConverter() {
//These are declared final and are are therefore usually set first in constructor
this.currencyLabel = new JLabel("<--- Starting Curency", euroIcon, SwingConstants.CENTER);
this.euroButton = new JButton("Swap to Euro");
this.dollarButton = new JButton("Swap to Dollar");
//Write your own separate ActionListener for each button
euroButton.addActionListener(new ActionListener() {
#Override
public void run() {
currencyLabel.setIcon(euroIcon);
//These two are required for you to see the effect
//This should also be the solution to your initial problem
currencyLabel.revalidate();
currencyLabel.repaint();
}
});
dollarButton.addActionListener(new ActionListener() {
#Override
public void run() {
currencyLabel.setIcon(dollarIcon);
currencyLabel.revalidate();
currencyLabel.repaint();
}
});
//Add your components here using whatever layout manager you want.
}
public static void main(String []args){
//Start new Swing applications like this to prevent it from
//clogging the rest of the program
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MoneyConverter();
}
});
}
}
Im using Observer Pattern to display whos turn it is to play. And im being given the right data from my Observable, thats why i dont post it. As you can see, the text is being set, as i can get call label.getText().
How come the label wont update?
Ive tried repaint, revalidate, SwingUtilities.invokeLater, updateUI, and all sort of stuff. I do not know what im doing wrong.
Thank you in advanced.
public class PlayersTurnPane extends JPanel implements Observer {
private JLabel label = new JLabel();
public PlayersTurnPane() {
setLayout(new BorderLayout());
setBackground(Color.gray);
label.setText("White");
label.setHorizontalAlignment(getWidth() / 2);
label.setFont(new Font("Serif", Font.PLAIN, 25));
add(label);
}
#Override
public void update(Observable arg0, Object arg1) {
label.setText((String)arg1); //But it will not update label
System.out.println(label.getText()); //The text is being set
}
}
My Observable
public class TurnObserver extends Observable {
public TurnObserver(PlayersTurnPane playersTurnPane) {
addObserver(playersTurnPane);
}
public void setTurn(String turn){
setChanged();
notifyObservers(turn);
clearChanged();
}
}
Where i send the data
private TurnObserver turnObserver = new TurnObserver(new PlayersTurnPane());
public void grabPiece(BoardTile boardTile) {
if(focusedPiece != null && playerTurn.getCurrentTurn() == focusedPiece.getPieceColor()) {
boardTile.add(focusedPiece);
updateUI();
playerTurn.changeTurn();
turnObserver.setTurn(playerTurn.getCurrentTurn());
}
}
The panel is added here
public class EastPanel extends JPanel {
public EastPanel() {
setLayout(new GridLayout(3,0));
add(new TimePane());
add(new MoveHistoryPane());
add(new PlayersTurnPane());
}
}
If System.out.println(label.getText()); is being printed, then you've got a Swing threading issue in code not shown, likely due to long-running code being run on the Swing event thread, preventing this thread from doing its job -- like painting the updated text on the JLabel. Be sure that all long running code is done on a background thread and that Swing state changes and queries are made on the Swing event thread. Read Concurrency in Swing for the details.
re,
And im being given the right data from my Observable, thats why i dont post it.
But it's that unposted code which holds the key to your problem.
Dang, Mad is correct: you're shadowing:
public TurnObserver() {
addObserver(new PlayersTurnPane());
}
You're adding a new PlayersTurnPane as your observer, but it's likely not the displayed PlayersTurnPane! Key: every time you call new on a constructor, you create a new object, here a PlayersTurnPane. You should pass in the displayed PlayersTurnPane, not a new one.
You've got problems with code like this:
public class EastPanel extends JPanel {
public EastPanel() {
setLayout(new GridLayout(3,0));
add(new TimePane());
add(new MoveHistoryPane());
add(new PlayersTurnPane());
}
}
The dsiplayed PlayersTurnPane is being created anonymously -- the object is being added to a display, but this object is not being referenced by any variable, and this needs to be done if you're going to pass the exact same PlayersTurnPane object into the method that makes it an observable.
I suggest that you use class fields and not anonymous objects where you need a reference to the object. So here, perhaps better would be:
public class EastPanel extends JPanel {
public EastPanel(PlayersTurnPane playersTurnPane) {
setLayout(new GridLayout(3,0));
add(new TimePane()); // you likely need parameters for
add(new MoveHistoryPane()); // these two objects as well
add(playersTurnPane);
}
}
The key concept that you seem to be missing is one of the importance of references which are pointers to an object of interest. Read up on this.
I have this piece of code:
public class GUI extends JFrame {
private PlaneUI planeui;
public GUI(PlaneUI planeui) {
this.planeui = planeui;
}
//We have put the code that creates the GUI inside a method
public GUI() {
start();
planeui.display();
} ...
This is just a test and I need the method "planeui.display" to work when the program starts, together with the method "start();" which already works.
public final class PlaneUI extends JFrame {
public void display() {
//Creates a new JPanel object
JPanel panelStart = new JPanel();
getContentPane().add(panelStart);
//Changing the default layout from Flowlayout to absolute
panelStart.setLayout(null);
setTitle("Reservationer"); //Sets the window title
setSize(236, 256); //Sets the default size of the window
setLocationRelativeTo(null); //Start location of the window (centered)
setDefaultCloseOperation(EXIT_ON_CLOSE); //Exits the window
}
}
I have imported the needed libraries and I feel like the problem lies in an object that isn't created correctly since I get a nullpointerexception. I tried running this planeUI class in the main method and it worked correctly. I just can't get it to work this way..
In function PlaneUI.display() add one last line setVisible(true) because your adding everything but not displaying anything
you have to add this into your display() method:
setVisible(true);
Otherwise, all you are doing is setting all the aspects of the JFrame and adding the JPanel to it. You have to make it visible afterwards.
I want to update the board configuration by clicking on a JButton. However, sometimes the image is displayed on the frame. sometimes is not. Everytime there is a significant delay after I click on the button. I tried debugging and found there might be a endless loop in :EventDispatchThread.class
(this is the class from java library)
void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
addEventFilter(filter);
doDispatch = true;
while (doDispatch && cond.evaluate()) {
if (isInterrupted() || !pumpOneEventForFilters(id)) {
doDispatch = false;
}
}
removeEventFilter(filter);
}
The endless loop is the while loop above.
Below is my listener class:
public class PlaceListener implements ActionListener{
private JTextField _text1;
private Board board;
private JTextField _text2;
private ArrayList<NewGUI> _guiList;
private int _numOfPlayer;
public PlaceListener(JTextField text1, JTextField text2, Board b,ArrayList<NewGUI> guiList, int numOfPlayer,NewGUI gui)
{
_text1 = text1;
_text2 = text2;
board = b;
_guiList = guiList;
_numOfPlayer = numOfPlayer;
}
#Override
public void actionPerformed(ActionEvent e) {
int x = Integer.parseInt(_text1.getText());
int y = Integer.parseInt(_text2.getText());
board.Place(y, x);
for(int j = 0;j<_numOfPlayer;j++)
{
NewGUI gui = _guiList.get(j);
gui.updateBoard();
gui.updateCurrTile();
gui.updateScore();
gui.updateTurn();
}
}
}
The basic idea is: I have an array of GUI. after each click, the listener will call all the GUIs within the array to update their configuration.
I also tried to update the board configuration within the GUI class directly, and it turns out to work well. I'm extremely confused! Can anyone help me out? Thanks!!
This is the main GUI class:
public class NewGUI {
private JFrame _frame;
private Board _board;
private JLabel _turnLabel;
private JTextArea _textArea;
private JLabel _currTileLabel;
private JPanel _boardPanel;
public NewGUI(Board board,int whos,ArrayList<NewGUI> guiList,int numOfPlayer)
{
_board = board;
_frame = new JFrame("Metro");
//turnLabel
_turnLabel = new JLabel();
_turnLabel.setText("Current player is: "+_board.getCurrPlayer());
_turnLabel.setSize(110, 40);
_turnLabel.setLocation(0, 0);
_frame.add(_turnLabel);
//mainPlayerLabel
JLabel mainPlayerLabel = new JLabel("Player"+whos+" 's window");
mainPlayerLabel.setSize(120, 20);
mainPlayerLabel.setLocation(400,0);
_frame.add(mainPlayerLabel);
//JTextArea to hold scores
_textArea = new JTextArea();
_textArea.setText(_board.displayScore());
_textArea.setSize(160,140);
_textArea.setLocation(730, 170);
_frame.add(_textArea);
_boardPanel = new JPanel();
_boardPanel.setSize(560, 560);
_boardPanel.setLocation(170, 80);
_boardPanel.setLayout(null);
// _boardPanel.setBackground(java.awt.Color.BLACK);
_frame.add(_boardPanel);
//Button Panel
JPanel buttonPanel = new JPanel();
buttonPanel.setSize(300, 150);
buttonPanel.setLocation(280, 650);
buttonPanel.setBackground(java.awt.Color.blue);
_frame.add(buttonPanel);
//Current Tile Label
_currTileLabel = new JLabel("Current Tile is: ");
_currTileLabel.setIcon(new ImageIcon(NewGUI.class.getResource(_board.getCurrTile().tileType()+".png")));
_currTileLabel.setSize(170, 60);
_currTileLabel.setLocation(20, 620);
_frame.add(_currTileLabel);
//2 input JTextField
JTextField text1 = new JTextField(3);
JTextField text2 = new JTextField(3);
text1.setSize(20, 20);
text2.setSize(20, 20);
text1.setLocation(620, 680);
text2.setLocation(640, 680);
_frame.add(text1);
_frame.add(text2);
//Buttons
JButton buttonPlace = new JButton("Place");
JButton buttonCommit = new JButton("Commit");
JButton buttonRemove = new JButton("Remove");
JButton buttonResign = new JButton("Resign");
buttonPlace.addActionListener(new PlaceListener(text1,text2,_board,guiList,numOfPlayer,this));
buttonCommit.addActionListener(new CommitListener(_board,guiList,numOfPlayer));
buttonRemove.addActionListener(new RemoveListener(_board,guiList,numOfPlayer,this));
buttonResign.addActionListener(new ResignListener(_board));
//Add buttons onto buttonPanel
buttonPanel.add(buttonCommit);
buttonPanel.add(buttonResign);
buttonPanel.add(buttonRemove);
buttonPanel.add(buttonPlace);
buttonPanel.setLayout(new FlowLayout());
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.setSize(900, 900);
_frame.setLayout(null);
_frame.setVisible(true);
}
public void updateBoard()
{
_boardPanel.removeAll();
//scan and refresh the board configuration.
for(int i = 1; i<13;i++)
{
for(int j = 1; j<13;j++)
{
if(_board.getBoard()[i][j]!=null)
{
for(int e = 65; e<89;e++){
char temp = (char)e;
if(_board.getBoard()[i][j].tileType()==temp)
{
JLabel label = new JLabel(new ImageIcon(NewGUI.class.getResource(temp+".png")));
label.setSize(40,40);
label.setLocation(40+(i-1)*40, 40+(j-1)*40);
_boardPanel.add(label);
break;
}
}
}
}
}
}
public void updateTurn()
{
_turnLabel.setText("Current player is: "+_board.getCurrPlayer());
}
public void updateScore()
{
_textArea.setText(_board.displayScore());
}
public void updateCurrTile()
{
_currTileLabel.setIcon(new ImageIcon(NewGUI.class.getResource(_board.getCurrTile().tileType()+".png")));
}
public static void main(String[] args)
{
Board b = new Board(3,true);
NewGUI gui = new NewGUI(b,1);
b.Place(4, 4);
b.Commit();
b.Place(12, 12);
b.Commit();
b.Place(3, 3);
gui.updateBoard();
}
}
See the last static main class? when I test it , all the update methods work well! But when I use listener to perform the method all. The updateBoard refuses to work.
So it looks like your code might be the problem here. Again EventDispatchThread uses an endless loop to work to pump events so that is obvious, and can be disregarded as the actual problem. You're problem comes from using removeAll(), and instantiating a few thousand labels every time they click the button (What's 13 x 13 x 89-65? 4056!). That's going to cause a lot of redrawing and relayout unnecessarily. So the pause you see is the performance of your code because it's not efficient. Don't believe try this:
public void updateBoard() {
long start = System.currentTimeInMillis();
// existing code goes here
long duration = System.currentTimeMillis() - start;
System.out.printf("UpdateBoard timing: %,d ms%n", duration );
}
If your code is anywhere over 10-100ms it will feel glitchy. In fact 100ms is on the slow side and a human can detect a delay of 100ms.
You probably need to re-evaluate your design, and either reuse the existing labels and simply call setImage() to change them. After all that's the whole point of using a persistent UI component model over using raw paint calls. Instantiate them once and reuse.
You are also creating thousands of images with new ImageIcon() calls too. You probably only need one icon and just have all labels point to the same image that will dramatically reduce your memory usage as well. In fact if you follow my advice I think you'll see dramatic speed and memory improvements.
If you can't find a suitable way to reuse JLabel then consider writing your own component by subclassing JComponent or JPanel (if you plan on using a container) and override paintComponent(). I see you're NOT using a LayoutManager and instead choosing to do everything using absolute positioning. If you are going to do absolute positioning you might want to just paint it yourself. Painting is more lower level interface, but you have full control. You'll have to handle positioning, word wrapping, all yourself. But, it will be very efficient, and you can redraw from a data model.
Since all your doing is draw images in a grid pattern I think drawing those with the Java2D api would be a better idea than instantiating that many JLabels and ImageIcons. If you subclass JPanel you can add JComponents to the panel for things like score, etc. But, draw the grid in the paintComponent() method.