As part of my Diploma in Software Dev we have to create a Java GUI to manage a Soccer League. My group has produced numerous JPanels that in the click-through prototype we put into JTabbedPane. This worked fine up until now where I'm moving them into separate files and into a MVC layout.
I'm using a window class to hold the top level JFrame and menubar and adding in the tabbed panes to that. This works fine in the constructor but when I remove them from the constructor and try to add them from the Bootstrap via the Window.attachTabbedPanel(String name, JPanel panel) it doesn't display but querying tabbedPane.getTabCount() display an incrementing number of tabs.
Here's a stripped back set of code:
The Bootstrap file:
public class Bootstrap {
private Window mainWindow;
public Bootstrap() {
//Window class is our containing JFrame and JMenuBar
mainWindow = new Window();
//Load up our view classes
TestTab tab1 = new TestTab();
TestTab tab2 = new TestTab();
//Attach them
mainWindow.attachTabbedPanel("Tab1", tab1.getScreen());
mainWindow.attachTabbedPanel("Tab2", tab2.getScreen());
} // Bootstrap()
public gui.Window getWindow(){
return mainWindow;
}
} // Bootstrap
This is called by the Main file:
public class Main {
public static void main(String[] args) {
Bootstrap RunMVC = new Bootstrap();
gui.Window mainWindow = RunMVC.getWindow();
mainWindow.run();
} // main()
} // Main
The problem starts here at the Window class, I've added in a In Constructor tab to check I haven't stuffed up the tabbedPane but it works fine at that point.
public class Window {
private JFrame frame;
private JMenuBar menuBarMain;
private JMenu mnFile;
private JTabbedPane tabbedPane;
private int count;
/**
* Create the application.
*/
public Window() {
//Build the frame
frame = new JFrame();
frame.setBounds(100, 100, 1280, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new CardLayout(0, 0));
//End Build frame
TestTab conTab = new TestTab();
//Add the tabbed pane to hold the top level screens
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
frame.getContentPane().add(tabbedPane, "name_1");
tabbedPane.addTab("In Consructor", conTab.getScreen());
count = 1;
}
public void attachTabbedPanel(String name, JPanel panel){
System.out.println("Window: adding Jpanel name: "+name);
System.out.println("panel is a: "+panel);
tabbedPane.addTab(name, panel);
tabbedPane.updateUI();
System.out.println("Number of tabs: "+tabbedPane.getTabCount());
System.out.println("Last Tab .isEnabledAt() "+tabbedPane.isEnabledAt(count++));
tabbedPane.updateUI();
}
/**
* Launch the window.
*/
public void run() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Window window = new Window();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
And lastly the Panel:
public class TestTab {
private JPanel screen;
private JLabel lblSeason;
private JButton btnEdit;
private JLabel lblRounds;
public JPanel getScreen() {
return screen;
}
/**
* Initialize the contents of the frame.
*/
public TestTab() {
screen = new JPanel();
screen.setLayout(new MigLayout("", "[8%,right][10%,left][8%,right][10%,left][grow][50%]", "[][][grow]"));
lblSeason = new JLabel("Test");
screen.add(lblSeason, "flowx,cell 0 0");
btnEdit = new JButton("Edit Test");
screen.add(btnEdit, "cell 5 0,alignx right");
lblRounds = new JLabel("More Testing");
screen.add(lblRounds, "cell 0 1,alignx left");
}
}
Your error is here:
public void run() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Window window = new Window();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
You are using new instance of window instead of using created earlier, try to use this code
public void run() {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
updateUI() does not do what you think it does. It should never be called directly - it is called from the superclass constructor to allow the Swing look and feel delegate to initialize itself.
Eliminating the call to updateUI() may solve your problem; or if the tabbed pane is already on screen, you may need to force a repaint/revalidate - the incantation for that is invalidate(); revalidate(); repaint();.
Related
I'm designing GUI of a download manager in java, in the main frame of the program (which is a JFrame) there are several buttons, one of them is Settings button which opens into a JDialog, in Settings section user can choose between several default look and feels (like steel, Nimbus etc).
the problem is, I want to apply the change of look and feel to both Setting section and mainframe but as Setting section is a different frame and class, the change just takes effect in Settings frame, I don't know how to make this change affect the main panel.
Code (part of Settings class):
UIManager.LookAndFeelInfo[] LAFInfo = UIManager.getInstalledLookAndFeels();
String [] LookAndFeels = new String[LAFInfo.length];
for (int i = 0; i < LAFInfo.length; i++) {
LookAndFeels [i] = LAFInfo[i].getName();
JComboBox<String> lookAndFeelChoices = new JComboBox<>(LookAndFeels);
lookAndFeelChoices.setBounds(200,220,200,30);
settings.add(lookAndFeelChoices);
lookAndFeelChoices.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
try {
UIManager.setLookAndFeel(LAFInfo[lookAndFeelChoices.getSelectedIndex()].getClassName());
SwingUtilities.updateComponentTreeUI(Settings.super.getContentPane())
} catch (Exception ei) {
System.out.println(ei);
}
}
});
}
Here is an example on how to achieve that:
pass the calling window to your dialog, and update it using SwingUtilities.updateComponentTreeUI
public class Main {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("HelloWorldSwing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton open=new JButton("Open Look and feel chooser");
open.addActionListener(e -> {
new LookAndFeelDialog(frame);
});
frame.setLayout(new FlowLayout());
frame.getContentPane().add(open);
frame.setLocationRelativeTo(null);
frame.setPreferredSize(new Dimension(400, 100));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
And the JDialog
class LookAndFeelDialog extends JDialog {
public LookAndFeelDialog(Frame callingFrame) {
super(callingFrame, "Pick a look and feel");
setPreferredSize(new Dimension(300,100));
UIManager.LookAndFeelInfo[] LAFInfo = UIManager.getInstalledLookAndFeels();
String [] LookAndFeels = new String[LAFInfo.length];
for (int i = 0; i < LAFInfo.length; i++) {
LookAndFeels [i] = LAFInfo[i].getName();
JComboBox<String> lookAndFeelChoices = new JComboBox<>(LookAndFeels);
add(lookAndFeelChoices);
lookAndFeelChoices.addItemListener(e -> {
try {
UIManager.setLookAndFeel(LAFInfo[lookAndFeelChoices.getSelectedIndex()].getClassName());
// update current window
SwingUtilities.updateComponentTreeUI(getContentPane());
// update calling frame
SwingUtilities.updateComponentTreeUI(callingFrame);
} catch (Exception ei) {
ei.printStackTrace();
}
});
}
setLocationRelativeTo(callingFrame);
pack();
setVisible(true);
}
}
I have 2 classes. Both implements runnable to create the GUI. The first one is the main, and the second one is the secondary class.
I want within the actionlistener of the main class to startup the secondary class.
Here is the code (the two classes are separated files):
public class Main implements Runnable
{
private JTextField txt1, txt2;
private JLabel lbl1, lbl2;
public void run()
{
JFrame frame = new JFrame("Secondary");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
JPanel background = new JPanel();
background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));
.........
// Horizontally adding the textbox and button in a Box
Box box = new Box(BoxLayout.Y_AXIS);
......
background.add(box);
pane.add(background);
frame.pack();
frame.setVisible(true);
}
private class SListener implements ActionListener
{
public void actionPerformed(ActionEvent a)
{
Secondary s = new Secondary();
}
}
public static void main (String[] args)
{
Main gui = new Main();
SwingUtilities.invokeLater(gui);
}
}
public class Secondary implements Runnable
{
private JTextField txt1, txt2;
private JLabel lbl1, lbl2;
public Secondary()
{
Secondary gui = new Secondary();
SwingUtilities.invokeLater(gui);
}
public void run()
{
JFrame frame = new JFrame("Secondary");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
JPanel background = new JPanel();
background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));
.........
// Horizontally adding the textbox and button in a Box
Box box = new Box(BoxLayout.Y_AXIS);
......
background.add(box);
pane.add(background);
frame.pack();
frame.setVisible(true);
}
}
I want to keep the code in two files, I don't want to mixed the two classes in one file.
As you can see from the code, in the Secondary class, in it's constructor I create an Instance of the Secondary class and I run the gui so that when the Instance of this class is created in the Main class, to run the gui.
Unfortunately this technique is not working.
Any ideas?
Thanks
The following line are complety wrong:
public Secondary(){
Secondary gui = new Secondary();
SwingUtilities.invokeLater(gui);
}
Each time you call new Secondary() somewhere in your code, the above code will be triggered, which in turn calls new Secondary() again, and again, and again, ... and your program is blocked.
You probably want to replace it either by
public Secondary(){
SwingUtilities.invokeLater(this);
}
which will avoid the loop, but this is weird behaviour for a constructor.
It makes much more sense to switch to an empty constructor (or delete it all together)
public Secondary(){
}
and rewrite your listener to
public void actionPerformed(ActionEvent a){
Secondary s = new Secondary();
SwingUtilities.invokeLater( s );
}
I would recommend that you completely re-design your program. I find that it is most helpful to gear my GUI's towards creation of JPanels, not top level windows such as JFrame, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. I find that this greatly increase the flexibility of my GUI coding, and is exactly what I suggest that you do. So...
Your first class creates a JPanel that is then placed into a JFrame.
In the first class's ActionListener, create an instance of the 2nd class, place it into a JDialog (not a JFrame), and then display it.
For example,
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class TwoWindowEg {
public TwoWindowEg() {
// TODO Auto-generated constructor stub
}
private static void createAndShowGui() {
GuiPanel1 mainPanel = new GuiPanel1();
JFrame frame = new JFrame("Main GUI");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class GuiPanel1 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private GuiPanel2 guiPanel2 = new GuiPanel2(); // our second class!
private JDialog dialog = null; // our JDialog
public GuiPanel1() {
setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
add(new JButton(new LaunchNewWindowAction("Launch New Window")));
add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class LaunchNewWindowAction extends AbstractAction {
public LaunchNewWindowAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
if (dialog == null) {
// get the Window that holds this JPanel
Window win = SwingUtilities.getWindowAncestor(GuiPanel1.this);
dialog = new JDialog(win, "Second Window", ModalityType.APPLICATION_MODAL);
dialog.add(guiPanel2);
dialog.pack();
}
dialog.setVisible(true);
}
}
}
class GuiPanel2 extends JPanel {
public GuiPanel2() {
setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
add(new JLabel("The second JPanel/Class"));
add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
}
}
class DisposeAction extends AbstractAction {
public DisposeAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Component comp = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
win.dispose();
}
}
Alternatively, you could swap JPanel "views" using a CardLayout, but either way, you will want to avoid showing two JFrames. Please have a look at The Use of Multiple JFrames, Good/Bad Practice?.
If I have a collection of objects:
public class Party {
LinkedList<Guy> partyList = new LinkedList<Guy>();
public void addGuy(Guy c) {
partyList.add(c);
}
}
And a tabbedPane:
public class CharWindow
{
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
CharWindow window = new CharWindow();
window.frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public CharWindow()
{
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize()
{
frame = new JFrame();
frame.setBounds(100, 100, 727, 549);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP);
frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
JTabbedPane PartyScreen = new JTabbedPane(SwingConstants.TOP);
tabbedPane.addTab("Party Screen", null, PartyScreen, null);
JTabbedPane tabbedPane_2 = new JTabbedPane(SwingConstants.TOP);
tabbedPane.addTab("New tab", null, tabbedPane_2, null);
JTabbedPane tabbedPane_3 = new JTabbedPane(SwingConstants.TOP);
tabbedPane.addTab("New tab", null, tabbedPane_3, null);
}
}
How would I add content to the tabbedPane "Party Screen" such that it displays a JLabel "Name" and a vertical JSeparator for each item in my LinkedList?
First, JTabbedPane is the widget that creates a new tab for each element panel. It is not the child of a tabbed interface. (Eg. JTabbedPane should hold a JPanel called partyScreen.)
JTabbedPane tabbedPanel = new JTabbedPane(); // holds all tabs
// this is how you add a tab:
JPanel somePanel = new JPanel();
tabbedPanel.addtab("Some Tab", somePanel);
// this is how you'd add your party screen
JPanel partyScreen = new JPanel();
tabbedPanel.addTab("Party Screen", partyScreen);
Remember, Java naming convention has a variable start with a lower case letter -- so partyScreen is preferred to PartyScreen.
Then iterate through each Guy object in Party and add the appropriate components. I have no idea why you're using a LinkedList instead of a List, but I'll assume you have a good reason not included in the code above.
// myParty is an instance of Party; I assume you have some sort of accessor to
// the partyList
LinkedList<Guy> partyList = myParty.getPartyList();
ListIterator<Guy> it = partyList.listIterator();
while( it.hasNext() ) {
Guy g = it.next();
partyScreen.add(new JLabel( g.getName() ));
partyScreen.add(new JSeparator() );
}
Depending on how you want it to be arranged in the partyScreen panel, you'll probably want to look into a Layout Manager.
I've got one class called WindowTemplate that is the base for other (more complex) windows. It is an abstract class and then I'm trying to use the "extend" trick to add more stuff to the new window, keeping the original "skeleton". That is my problem though, because if I run WindowTemplate.createWindow(); or a_Welcome.createWindow(); (they should be point to the same thing), I get my "base" window. But when I run a_Welcome window = new a_Welcome(); (what should be the base + new stuff) I get only the extra bits that I added without the original features. Here is my code:
package windows;
import java.awt.*;
import javax.swing.*;
public abstract class WindowTemplate extends JFrame {
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
public static void createWindow() {
JFrame myFrame = new JFrame("My first window");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setSize(550, 450);
myFrame.setLocationRelativeTo(null);
// JLabel emptyLabel = new JLabel("");
// emptyLabel.setPreferredSize(new Dimension(550, 450));
// myFrame.getContentPane().setLayout(new CardLayout());
// myFrame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
// myFrame.pack();
}
}
the class with new window and some extra stuff (ignore a_):
package windows;
import java.awt.*;
import javax.swing.*;
public class a_Welcome extends WindowTemplate {
public a_Welcome() {
JPanel area = new JPanel();
JLabel text = new JLabel("One line another line and another line"); // , JLabel.CENTER);
// text.setBounds(80, 400, 400, 50);
add(area);
// area.setLayout(null);
area.add(text, new CardLayout());
// area.add(text); // , BorderLayout.CENTER);
Font font = new Font("SansSerif", Font.BOLD, 30);
text.setFont(font);
text.setForeground(Color.green);
area.setBackground(Color.darkGray);
area.setSize(550, 450);
}
}
// timer-after 5 seconds-go to the next window (countdown in the bottom right corner)
and the main:
package windows;
public class Launcher {
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// WindowTemplate.createWindow();
// a_Welcome.createWindow();
a_Welcome window = new a_Welcome();
window.setVisible(true);
}
});
}
}
Thanks for your help!
Static method createWindow() always creates a new JFrame which is not a superclass of the WindowTemplate. Constructor of the a_Window is adding components to the WindowTemplate which hasn't been initialized since the static createWindow() creates an independent frame.
I would suggest you to change the static createWindow() into WindowTemplate constructor and try running main once again.
package windows;
import java.awt.*;
import javax.swing.*;
public abstract class WindowTemplate extends JFrame {
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
public WindowTemplate () {
JFrame myFrame = new JFrame("My first window");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setSize(550, 450);
myFrame.setLocationRelativeTo(null);
// JLabel emptyLabel = new JLabel("");
// emptyLabel.setPreferredSize(new Dimension(550, 450));
// myFrame.getContentPane().setLayout(new CardLayout());
// myFrame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
// myFrame.pack();
}
}
You have another JFrame defined in the static createWindow() method. This means that you are adding the components to this frame that is scoped to the createWindow() method only and in the constructor you are adding to the a_Welcome instance.
You should do something like this
public class BaseWindow() {
//Constructor
public BaseWindow() {
init();
}
public void init() {
//add basic components
}
}
public class SubClassWindow() {
public SubClassWindow() {
super();
}
#Override
public void init() {
super.init(); //important so you get the base stuff
//add other components
}
}
Code not tested.
Another approach you might consider would be to have a JFrame that is just a wrapper and compose the window by adding a panel. Let's say you want a toolbar at the top of every window you're creating. Each window would have different buttons on the toolbar and a different set of components at the bottom. This way you are doing composition instead of inheritance, because inheritance can get ugly later on. (For discussions on that point, see this, this, and this for starters)
That would look something like:
public interface AppPanel {
List<JButton> getToolbarButtons();
boolean okToClose();
JPanel getGui();
}
public MyPanel extends JPanel implements AppPanel {
//standard swing components stuff set up here
public List<JButton> getToolbarButtons() {
//set up buttons and their actions
return buttonList;
}
public boolean okToClose() {
//ask user if they want to save, etc.
return true;
}
public JPanel getGui() {
return this;
}
}
public AppFrame extends JFrame {
private AppPanel panel;
public static AppFrame createFrame(AppPanel panel) {
AppFrame frame = new AppFrame(panel);
return frame;
}
public AppFrame(AppPanel panel) {
super();
this.panel = panel;
add(panel.getGui(), someLayoutConstraints);
panel.getToolbarButtons(); //do stuff with the buttons
//...
this.addWindowListener(new WindowAdapter() {
public void WindowClosing(WindowEvent e) {
if (panel.isOkToClose()) {
setVisible(false);
}
}
});
}
}
Problem: Code does not turn the button back on (Example gives it 5 seconds for you to press)
Example code:
public static void main(String[] args) throws InterruptedException
{
Example call = new Example();
Thread.sleep(5000);
call.ButtonSwitch(1);
}
NOTE: this is the smallest coding i could make to show my problem
public class Example extends JFrame implements ActionListener {
static Example frame2 = new Example();
GridLayout experimentLayout = new GridLayout(0,1);
JPanel Game = new JPanel();
JButton button1 = new JButton("Press");
public Example()
{
Create();
}
public void Set()
{
setResizable(false);
}
public static void Create() {
/* Use an appropriate Look and Feel */
try {
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
} catch (UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (InstantiationException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
/* Turn off metal's use of bold fonts */
UIManager.put("swing.boldMetal", Boolean.FALSE);
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
//Create and set up the window.
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
frame2.addComponentsToPane(frame2.getContentPane());
//Display the window.
frame2.pack();
frame2.setVisible(true);
}
public void addComponentsToPane(final Container pane)
{
Game.setLayout(experimentLayout);
JPanel controls = new JPanel();
controls.setLayout(new GridLayout(2,3));
//Set up components preferred size
JButton b = new JButton("Just fake button");
Dimension buttonSize = b.getPreferredSize();
Game.setPreferredSize(new Dimension((int)(buttonSize.getWidth() * 2),
(int)(buttonSize.getHeight() * 1)* 4));
Game.add(button1);
button1.addActionListener(this);
//Process the Apply gaps button press
pane.add(Game, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.CENTER);
pane.add(controls, BorderLayout.SOUTH);
}
//Turns button off On Click
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == button1)
{
button1.setEnabled(false);
}
}
//This does not turn the button on but tries to
public void ButtonSwitch(int num)
{
if (num == 1)
{
System.out.println("This is called");
button1.setEnabled(true);
}
}
}
I want to make the method Enable the button, but if this is not possible a way to do this in action listener without user input would be the second option (which would look like the Button switch method placed inside the ActionListener)
The problem comes from the bad design of the class. The point is that you are not calling setEnabled(true) and setEnabled(false) on the same button1. In your main,
Example call = new Example();
Thread.sleep(5000);
call.ButtonSwitch(1);
the last line invokes setEnabled(true) on the button of call, while the actionPerformed invokes the setEnabled(false) on the button of frame2.
Regardless, you are doing it wrong:
Don't mix the main (entry) thread with the EDT.
Don't hold a member of the same class type as the containing class (unless there's a special reason to do so).
Here is a real MCVE of a working code:
public class Example extends JFrame {
JButton button = new JButton("Press");
Timer timer = new Timer(5000, e -> button.setEnabled(true));
public Example() {
add(button);
button.addActionListener(e -> {
button.setEnabled(false);
timer.start();
});
timer.setRepeats(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) throws InterruptedException {
SwingUtilities.invokeLater(() -> new Example());
}
}
Notes:
Method and non-final variable names start with a lowercase.
Don't use setPreferredSize, override getPreferredSize instead.