I'm having some problems while working on a project for school. The JDialog renders very slow. Let me show you.
This is the GUI:
When I click "Advance time" it takes around 3 seconds to open the JDialog. When it's open I get this (which is fine):
But when I drag that JDialog around, I get this (which is not ok):
It also takes very long to close the JDialog. It closes, but you can still see it:
This is the snippet of code where I think the problem lays, it's the code for the menu-bar:
private JMenu editMenu()
{
JMenu editMenu = new JMenu("Edit");
editMenu.setMnemonic(KeyEvent.VK_E);
JMenuItem advanceTimeMenuItem = new JMenuItem("Advance time");
advanceTimeMenuItem.setMnemonic(KeyEvent.VK_A);
advanceTimeMenuItem.setToolTipText("Advance the internal clock");
advanceTimeMenuItem.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent event)
{
//We first create panel to add to the dialog.
JPanel panel = new JPanel();
//Calendar
AdvanceTimePanel calendar = new AdvanceTimePanel(internalClockController);
//Button that will be used to confirm the system time change.
JButton btnSave = new JButton("Save");
//Add actionlistener to the save button
btnSave.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e)
{
Date selectedDate = calendar.getDate();
try
{
internalClockController.updateDateTime(selectedDate);
} catch (InvalidInternalClockException e1)
{
System.out.println("InvalidInternalClockException: " + e1.getMessage());
}
}
});
//Add the components to the panel.
panel.add(btnSave);
//This is the calendar added to the panel.
panel.add(calendar);
//Create the dialog and add the panel to it.
JDialog jDialog = new JDialog();
jDialog.add(panel);
jDialog.setBounds(100, 100, 400, 200);
jDialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jDialog.setVisible(true);
}
});
editMenu.add(advanceTimeMenuItem);
Does someone have an idea how to speed things up?
Thanks in advance.
Events are handled on one single thread. (Also repaint events.) For the application to remain
responsive, perform longer actions a bit later by using invokeLater, as below:
#Override
public void actionPerformed(ActionEvent event)
{
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
... // All code here
}
});
}
This is quite wordy; Java 8 allows:
advanceTimeMenuItem.addActionListener(
(event) -> {
EventQueue.invokeLater(
() -> {
... // All code
});
});
Related
I have a JPanel holding a JButton and JScrollPane (in turn holding a JTable) and am currently running into two issues which I believe are related:
The JButton listener's actionPerformed() method is not invoked upon click. The only way in which I can get it to be invoked is by calling doClick() on the JButton. The JButton color changes upon hover but no click animation is shown when the mouse is pressed.
Secondly, if a cell is clicked within the JTable, the cell located 2 rows down in the same column registers the click instead. This offset does not occur when clicking in the column headers (i.e. to adjust cell widths), only when within the cell area.
Left-hand panel. Click position circled
public class InventoryPanel extends JPanel {
// Parent Business object reference for communication and JFrame
private Business parent;
private AddItemPanel addItemPanel;
// Inventory table items
private DefaultTableModel inventoryModel;
private JTable inventoryTable;
private JScrollPane inventoryScrollPane;
private JLabel updateLbl;
private JButton addItemBtn;
// Columns for inventory table
private static final String[] INVENTORY_COLUMNS = {"Item","Stock","Restocking Level","Edit"};
public InventoryPanel(Business parent) {
this.parent = parent;
initGUI();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
//doStuff
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(new PrintStream(System.out));
}
}
}
}).start();
}
// INITIALISES GUI
public void initGUI() {
this.setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel titleLabel = new JLabel("<html><B>Inventory</B></html>");
this.add(titleLabel);
// Create empty inventory table
inventoryModel = new DefaultTableModel(new Object[3][4],INVENTORY_COLUMNS);
inventoryTable = new JTable(inventoryModel);
inventoryScrollPane = new JScrollPane(inventoryTable);
// Create button to allow items to be added
addItemBtn = new JButton("Add item");
addItemBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("ADD ITEM PRESSED");
}
});
updateLbl = new JLabel("Loading inventory...");
this.add(addItemBtn);
this.add(inventoryScrollPane);
this.add(updateLbl);
}
I've tried removing the table from the panel to see if that solves the JButton issue and visa-versa, but no luck. I've also tried changing the project JDK but no luck there either.
There are other JPanels adjacent to the troublesome one in a JFrame which work perfectly fine. Any ideas?
Edit: I can create a working instance of the InventoryPanel alone in a frame in another project, as mentioned in the comments. However the exact same code (no calls being made to other objects/methods) in the current project now produces ClassCastExceptions. After some googling this seems to be due to non-EDT threads updating the GUI.
However there is no use of the Business class, and all GUI operations are performed using the SwingUtilities.invokeLater() method like so:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("test");
frame.add(new InventoryPanel());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
Note: the no-argument constructor InventoryPanel() just calls initGUI().
Thanks for the help so far...still very confused by this.
I'm trying to programmatically click a JButton, which is fine, the doClick() method works prefectly. The problem is that I want to be able to programmatically click whatever button is currently in focus.
I can programmatically give a button focus just fine with .grabFocus() (at least it would seem so) but for some reason .isFocusOwner() always returns false.
If the code is run you can visually confirm that the button 'b2' is indeed in focus, however both if(frame.getFocusOwner() instanceof JButton) and if(b2.isFocusOwner) return false.
The code below illustrates the problem I'm having.
I imagine I've missed something obvious, but any advice would be fantastic.
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton b1 = new JButton("b1");
JButton b2 = new JButton("b2");
JTextField j1 = new JTextField(10);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Push the button...");
}
});
b2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("...and let it go...");
}
});
panel.add(b1);
panel.add(b2);
panel.add(j1);
frame.add(panel);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setVisible(true);
//// The two problems are below
// It looks like this does give 'b2' the focus, at least as far as the generated GUI is concerned
b2.grabFocus();
// First - Always returns false
if(frame.getFocusOwner() instanceof JButton) {
JButton focusedButton = (JButton) frame.getFocusOwner();
focusedButton.doClick();
System.out.println("In focus?");
}
else {
System.out.println("Apparently not");
}
// Second - Also always returns false
if(b2.isFocusOwner()) {
System.out.println("In focus...");
}
else {
System.out.println("Not in focus");
}
}
Not all code executes synchronously. Some code get added to the end of the Event Dispatch Thread (EDT). It appears that this is the case for focus requests. So when the if statements are executed, focus has not yet been placed on the component.
The solution is to wrap your code with a SwingUtilties.invokeLater() so the code gets added to the end of the EDT, so it can execute after the component has received focus:
//b2.grabFocus();
b2.requestFocusInWindow();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// First - Always returns false
if(frame.getFocusOwner() instanceof JButton) {
JButton focusedButton = (JButton) frame.getFocusOwner();
focusedButton.doClick();
System.out.println("In focus?");
}
else {
System.out.println("Apparently not");
}
// Second - Also always returns false
if(b2.isFocusOwner()) {
System.out.println("In focus...");
}
else {
System.out.println("Not in focus");
}
}
});
Also, don't use grabFocus(), you should use requestFocusInWindow(). Read the API for more information.
I have a JFrame with a Button that opens a different JFrame. But I want the button to only open the second frame once. Problem is, every time I click it I get a new instance of the frame. This must be a very common problem, since I'm following a book on how to create this GUI. I find it odd that the author didn't mention this "feature".
So how do I keep my button from opening multiple copies of the new frame?
Instead of letting the button create a new JFrame every time, make the second JFrame a member of the first JFrame, and only let the button call jframe2.setVisible(true);:
class JFrame1 {
JFrame2 jframe2=...;
JButton button=...;
JFrame1() {
...
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jframe2.setVisible(true);
}
});
...
}
}
UPDATED!
try this:
JFrame frame2 = new JFrame(); // instance variable
...
//when button is clicked
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!frame2.isVisible())
frame2.setVisible(true);
}
});
make sure you are handling the closing of all of the JFrames manually like this:
frame2.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// handle closing the window
frame2.setVisible(false);
frame2.dispose();
}
});
instead of using the JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
hope this helps.
You should keep a reference to the sub frame you open for first time. At second time you first check if you have a reference or not and then decide to create a new frame or to put the focus onto the existing open frame.
Example as answer to comment of OP (similar to other answer of #AlexanderTorstling, but not immediately creating the sub frame):
class MainFrame extends JFrame {
private JFrame subFrame = null;
MainFrame() {
...
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (subFrame == null) {
subFrame = new JFrame();
...
}
subFrame.setVisible(true);
}
});
}
}
This example has also the advantage to give you the possibility to close the subframe via a registered WindowAdapter if the main frame is closed.
please try this one
JFrame frame2 = new JFrame(); // instance variable
boolean secondWindowIsOpne = false;
...
//when button is clicked
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(secondWindowIsOpne == false){
frame2.setVisible(true);
secondWindowIsOpne = true;
}
else{
System.out.println("This Window is already running");
}
});
make sure you are handling the closing of all of the JFrames manually like this:
frame2.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
// handle closing the window
secondWindowIsOpne = false;
frame2.setVisible(false);
frame2.dispose();
}
});
I want to start by saying that I'm new to swing. So all that I am trying to do if use a JFileChooser to select a directory. I can open, navigate and select a directory. The problem comes when I press any button that closes the dialog. When I do that then my application freezes. When ever it freezes just the panel that the dialog box is returning to turns white. When I step with the debugger the hang happens immediately aftet the dialog closes and the if statement is not reached. Also I am doing this inside of an Eclipse plugin if that makes a different. In particular it is hosted inside of a View. Code below:
public class TexturePacker extends ViewPart {
public void createPartControl(Composite parent) {
Composite composite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND);
frame = SWT_AWT.new_Frame(composite);
frame.add(new TexturePackerPanel(frame));
}
}
public class TexturePackerPanel extends JPanel {
//This is called from initialize(), which is called in the constructor
private void initializeConfigPanel() {
JPanel configPanel = new JPanel();
JTextBoxk outputDirectory = new JTextField();
configPanel.add(inputDirectory);
JButton fileButton = new JButton("Folder");
fileButton.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent arg0) {
JFileChooser file = new JFileChooser(outputDirectory.getText());
file.setDialogTitle("Select Output Directory");
file.setDialogType(JFileChooser.OPEN_DIALOG);
file.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = file.showDialog(frame, "Choose");
if(returnVal == JFileChooser.APPROVE_OPTION) {
outputDirectory.setText(file.getSelectedFile().getAbsolutePath());
}
}
//Other blank MouseListener methods//
});
configPanel.add(fileButton);
}
}
System Info:
Windows 8 64bit
Java 7
Eclipse 4.2 SR1 EE Edition
I'm pretty sure that the problem is caused by Swing not playing nice inside of eclipse. I have successfully gotten it working using SWT Directory Dialog. So I am going to just convert the whole JPanel to SWT. Thanks everyone's help, I now know a lot more about how Swing works.
For this you really should be using an ActionListener instead of a MouseListener.
Aside from that I think this is a threading issue. You should read the documents on concurrency in swing.
Surround the JFileChooser part with
SwingUtilities.invokeLater(new Runnable() {
public void run() {
\\Your JFileChooser bit
}
Edit 1:
I've run the following slightly edited code but can't reproduce your problem
public static void main(final String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JPanel configPanel = new JPanel();
final JButton fileButton = new JButton("Folder");
final JFrame frame = new JFrame();
frame.setVisible(true);
frame.add(configPanel);
// JButton outputDirectory = new JButton("XX");
fileButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent arg0) {
final JFileChooser file = new JFileChooser();
file.setDialogTitle("Select Output Directory");
file.setDialogType(JFileChooser.OPEN_DIALOG);
file.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
final int returnVal = file.showDialog(frame, "Choose");
if(returnVal == JFileChooser.APPROVE_OPTION) {
// outputDirectory.setText(file.getSelectedFile().getAbsolutePath());
System.out.println(returnVal);
}
}
});
configPanel.add(fileButton);
}
});
}
I currently have a button that, when clicked, performs a method that creates a jframe with a panel that loads multiple images. If this button is clicked multiple times, the images keep adding onto the prexisting images loaded onto the jframe. What code should I use so that if the button is clicked after the jframe and elements have been loaded, after clicking once, nothing else will be added.
Many thanks
What about:
public void actionPerformed(ActionEvent evt) {
button.setEnabled(false);
// the other code that creates imgFrame
imgFrame.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent evt) {
button.setEnabled(true);
}});
imgFrame.setVisible(true);
}
don't create lots of JFrames on the runtime, because these Object never gone from Used JVM Memory untill current JVM instance exist
you have look at CardLayout that very confortly to solve your issues with multiple of views (in this case in one JFrame)
put Images as Icon/ImageIcon to the JLabel
Disable the button when frame show up, and when the frame close, enable the button.
public void actionPerformed(ActionEvent evt) {
final JButton finalButton = button;
button.setEnabled(false);
JFrame frame = new JFrame()
{
protected void processWindowEvent(WindowEvent e)
{
if (e.getID() == WindowEvent.WINDOW_CLOSING)
{
finalButton.setEnabled(true);
}
super.processWindowEvent(e);
}
};
frame.setVisible(true);
}
I suggest a boolean that is false when the program starts, and then when the button is clicked, it tests if the boolean is false. If it is false, then create the stuff that you want, and then make it true. If it is true, do nothing, or alert the user not to click the button more that once, or something of that matter
boolean isAlreadyCreated = false;
yourButton.addActionListener(new ActionListener()
{
if(!isAlreadyCreated)
{
//CREATE YOUR NEW FRAME
isAlreadyCreated = true;
}
});