Passing containers between JFrame and JDialog causing missing panels? - java

As for testing-reasons I tried to open a JDialog window with the panel and its contents of the main application frame. As I already had anything in there I wanted to see if I could simply set the JDialogs contentPane to the one I passed over. So in simplyfied form this came together:
testsforSO.java :
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class testsforSO extends JFrame {
private static final long serialVersionUID = -3890178393751567629L;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testsforSO frame = new testsforSO();
frame.setSize(300, 300);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public testsforSO() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("I am the Frame");
getContentPane().setLayout(new BorderLayout(0, 0));
JPanel panel = new JPanel();
panel.setLayout(null);
JButton btnRestart = new JButton("Restart");
btnRestart.setBounds(10, 10, 50, 50);
btnRestart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
testsforSO.main(null);
dispose();
}
});
panel.add(btnRestart);
getContentPane().add(panel);
repaint();
// -----------DELETE These comments-------------
// JDialog myDialg = new JDialog(this);
// myDialg.setContentPane(panel);
// myDialg.setVisible(true);
// myDialg.setSize(300,300);
// myDialg.setLocation(new Point(250, 250));
// myDialg.setTitle("I am Dialog from within the script");
myDialog.main(panel);
}
}
and myDialog.java :
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.JDialog;
public class myDialog extends JDialog {
private static final long serialVersionUID = 7079322237622743228L;
public static void main(Container myContainer) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
myDialog frame = new myDialog(myContainer);
frame.setVisible(true);
frame.setContentPane(myContainer);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public myDialog(Container myContainer) {
setContentPane(myContainer);
getContentPane().setLayout(null);
setBounds(200,200,200,200);
}
}
When starting the main frame I assumed it would contain the restarting button as well as the dialog does. But interestingly it was only the dialog with the button.
However when hitting it, the main frame properly restarted, a second dialog was set up and it contained the button again. This time however the main frame had the button as well, just without any function. Clicking on it does absolutely nothing.
Then I tried further and checked if that behaviour would change if I added the dialog directly into the main applications code (see the commented lines) and, starting the application once again only the dialog in its own class showed the button. Even hitting this one now restarted properly but the button won't show up on any other window except the lonely declared dialog.
What am I missing here and how could I refactor my code to work properly if even at all in this manner?

Understand that Swing components can only be present in one container, and while you may see the visual residue of a component in a container, the actual component is only present in the last container added to.
Myself, if I wanted dialog and jframe to have the same content pane components, I'd create a factory method to create the contentPane, and then use it to create two unique but identical contentPanes.
Also, I'd be remiss if I didn't mention something about your use of null layouts. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
For instance, when I ran your code, this is the dialog that I saw:
You hard coded the button's size to be too small to show its text on my platform. If you had used layout managers, and called pack() on the top-level window, the button would show appropriately.

Related

How to remove minimize button and keep maximize and close button in JFrame

I want to remove only minimize button from JFrame
but want maximize and close button in JFrame title bar.
Here I am talking about removing not disabling.
I don't think removing the minimize button is a good thing. But may be you can use the setUndecorated() method to remove the title bar and window edges. And you'll have to add your own close and maximize buttons to perfom those action.
Here is an example :
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.JButton;
public class Example {
public Example() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);//<---- this will disable the frame decorations
JPanel panel = new JPanel();
panel.add(new JLabel("titlebar"));
//Add button maximize
JButton button_max=new JButton("Maximize");
button_max.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if(frame.getExtendedState() == JFrame.NORMAL) {
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
} else {
frame.setExtendedState(JFrame.NORMAL);
}
}
});
panel.add(button_max);
//Add button close
JButton button_close = new JButton(new AbstractAction("Close") {
private static final long serialVersionUID = -4901571960357967734L;
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
panel.add(button_close);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
Edit :
As #peeskillet states in the comment, even with this method the window still can be resized and draged by the user. The ComponentResizer class allows to do that.
Here is a an SO post which gives a good example to use this class with Jframe.
It's a very big hack, which works only with Synthetica L&F because it provides a painted title bar. Note: this L&F is not free to use. So if you use it you must by a license.
When you use this L&F you can iterate over all component starting from root pane to find an instance of SyntheticaTitlePane. On success you can try to access the field iconifyAction using Reflection Framework and use the method Action.setEnabled(false) on it.
I have no idea how to access the standard title bar because it's native. Probably it's impossible.

Java running main method of other class, when JButton is pressed

I am trying to develop a JFrame which has two buttons that would let me to call the main method of other classes. The first try was to put it directly into the actionPerformed of each button, this will cause the JFrame of the other class to open but showing only the title of it and not showing any contents of the JPanel additionally freezing the program (can't even press close button, have to go into task manager or eclipse to kill it). The second try was adding a method call in actionPerformed, and adding the method will this time call the main method of other class however the same result (freeze of program).
For testing purposes I have placed the call to main method of other class, straight in this class main method which has proven to me that the frame of other class has successfully appeared, including all its JPanel contents, functionality etc.
I know I could make some kind of infinite loop in my main method to wait until a boolean is set to true, but then I though there must be some less-expensive way to get it working. So here I am asking this question to you guys.
Here is the code of the 2nd try;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Chat {
public static void main (String[] args) {
JFrame window = new JFrame("Chat Selection");
//Set the default operation when user closes the window (frame)
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set the size of the window
window.setSize(600, 400);
//Do not allow resizing of the window
window.setResizable(false);
//Set the position of the window to be in middle of the screen when program is started
window.setLocationRelativeTo(null);
//Call the setUpWindow method for setting up all the components needed in the window
window = setUpWindow(window);
//Set the window to be visible
window.setVisible(true);
}
private static JFrame setUpWindow(JFrame window) {
//Create an instance of the JPanel object
JPanel panel = new JPanel();
//Set the panel's layout manager to null
panel.setLayout(null);
//Set the bounds of the window
panel.setBounds(0, 0, 600, 400);
JButton client = new JButton("Run Client");
JButton server = new JButton("Run Server");
JLabel author = new JLabel("By xxx");
client.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run client main
runClient();
}
});
server.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run server main
}
});
panel.add(client);
client.setBounds(10,20,250,200);
panel.add(server);
server.setBounds(270,20,250,200);
panel.add(author);
author.setBounds(230, 350, 200, 25);
window.add(panel);
return window;
}
private static void runClient() {
String[] args1={"10"};
ClientMain.main(args1);
}
}
Only one main method is allowed per application. Honestly I am not sure what you are trying to do or think is supposed to happen when you call main on other classes. When you call main on other classes all you are doing is calling a method that happens to be called main and passing args to it. Your freezing is probably because you are not using Swing correctly:
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
The problem you're having is that Java Swing is single threaded. When you're running the main function of the other class, however you do it, the GUI won't be able to keep running until it returns. Try spawning off a new thread that calls the second main method.
private static void runClient() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
String[] args1={"10"};
ClientMain.main(args1);
}
});
}
EDIT: Updated, as per #Radiodef's suggestion. Missed at the top when you said this second class had to display things on the GUI. Definitely want to go with the invokeLater then.

how to make JInternalFrame fill the Container and disable the dragging feature?

I'm working on a project, there are JInternalFrames in the mainframe. Now, we need to let them to be JFrame. I'm considering using a JFrame to hold on JInternalFrame. The problem is that the titlebar of Internalframe is there, and user can drag it around.
Is there any way to make the Internal frame work like a pane in the JFrame?
After searching on the Internet, I found somebody removes the titlepane.
Do you have any good idea on this?
Thanks you!
update:
Maybe I was on the wrong track. The real problem is the JInternal frame can not get out of the main Frame, or any way to make it look like it's out side of the frame?
Is there any way to make the Internal frame work like a pane in the
JFrame
Im not sure by what you mean by pane, but I guess like a JPanel? Of course you can but why, would be my question, unless you want some sort of quick floating panel, but than you say you dont want it draggable? So Im bit unsure of your motives and makes me weary to answer....
The problem is that the titlebar of Internalframe is there
Well Here is code to remove the titlepane (found it here):
//remove title pane http://www.coderanch.com/t/505683/GUI/java/JInternalframe-decoration
BasicInternalFrameTitlePane titlePane =(BasicInternalFrameTitlePane)((BasicInternalFrameUI)jInternalFrame.getUI()).getNorthPane();
jInternalFrame.remove(titlePane);
and user can drag it around.
And I found this to make JInternalFrame unmovable by removing the MouseListeners which make it movable, but it is important to note its not necessary to remove the MouseListeners as the method used to make it undraggable will remove the NorthPane which the MouseListener is added too thus its unnecessary for us to remove it ourselves.:
//remove the listeners from UI which make the frame move
BasicInternalFrameUI basicInternalFrameUI = ((javax.swing.plaf.basic.BasicInternalFrameUI) jInternalFrame.getUI());
for (MouseListener listener : basicInternalFrameUI.getNorthPane().getMouseListeners()) {
basicInternalFrameUI.getNorthPane().removeMouseListener(listener);
}
And as per your title:
how to make JInternalFrame fill the Container
Simply call setSize(int width,int height) on JInternalFrame with parameters of the JDesktopPanes width and height (JDesktopPane will be sized via overriding getPreferredSize()).
Which will give us this:
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.MouseListener;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
import javax.swing.plaf.basic.BasicInternalFrameUI;
/**
*
* #author David
*/
public class Test {
public Test() {
createAndShowGUI();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void createAndShowGUI() throws HeadlessException {
JFrame frame = new JFrame();
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final JDesktopPane jdp = new JDesktopPane() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
frame.setContentPane(jdp);
frame.pack();
createAndAddInternalFrame(jdp);
frame.setVisible(true);
}
private void createAndAddInternalFrame(final JDesktopPane jdp) {
JInternalFrame jInternalFrame = new JInternalFrame("Test", false, false, false, false);
jInternalFrame.setLocation(0, 0);
jInternalFrame.setSize(jdp.getWidth(), jdp.getHeight());
//remove title pane http://www.coderanch.com/t/505683/GUI/java/JInternalframe-decoration
BasicInternalFrameTitlePane titlePane = (BasicInternalFrameTitlePane) ((BasicInternalFrameUI) jInternalFrame.getUI()).getNorthPane();
jInternalFrame.remove(titlePane);
/*
//remove the listeners from UI which make the frame move
BasicInternalFrameUI basicInternalFrameUI = ((javax.swing.plaf.basic.BasicInternalFrameUI) jInternalFrame.getUI());
for (MouseListener listener : basicInternalFrameUI.getNorthPane().getMouseListeners()) {
basicInternalFrameUI.getNorthPane().removeMouseListener(listener);
}
*/
jInternalFrame.setVisible(true);
jdp.add(jInternalFrame);
}
}
Given your requirements, I suggest you just use a simple JPanel inside your JFrame content pane.

JProgressBar not visible until told to show

I'm trying to make an application which shows a JProgressBar only while it is performing actions. My problem is, when the program is first opened, I set the JProgressBar visibility to false, then to true when an action is being performed and after it is done, back to false. It seems like it would work, and it does, just not when I make it not visible by default. If the visibility is true by default then it works well, but that's not quite what I want. How could I make it so that it isn't visible until I set it to be visible?
SSCCE just incase my question wasn't clear enough:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class SmileBack {
private JFrame frame;
private JPanel panel, container;
private JButton loadButton;
private JProgressBar progressBar;
public static void main(String[] arguments) {
new SmileBack().constructFrame();
}
public void constructFrame() {
frame = new JFrame("RSTracker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getContentPane());
frame.pack();
frame.setVisible(true);
}
public JPanel getContentPane() {
panel = new JPanel(new BorderLayout());
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
//progressBar.setVisible(false); // doesn't work when this is uncommented
loadButton = new JButton("Load memberlist");
loadButton.setEnabled(true);
loadButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new Thread(new Runnable() {
#Override
public void run() {
progressBar.setVisible(true);
// do my stuff here...
try {
Thread.sleep(2000); // just for the sake of example
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setVisible(false);
}
}).start();
}
});
container = new JPanel(new FlowLayout());
container.add(loadButton);
container.add(progressBar);
panel.add(container);
return panel;
}
}
Ignore the name, I was listening to that song while creating this. :)
This is probably not the way it should be designed, but this code fixes the problem while still using the natural size (pack()) needed to display the button and progress bar. This is achieved by setting the progress bar to invisible after pack is called, but before setting the GUI visible.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class SmileBack {
private JFrame frame;
private JPanel panel, container;
private JButton loadButton;
private JProgressBar progressBar;
public static void main(String[] arguments) {
new SmileBack().constructFrame();
}
public void constructFrame() {
frame = new JFrame("RSTracker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getContentPane());
// after this, everything is instantiated;
frame.pack();
setProgressBarVisibility(false);
frame.setVisible(true);
}
public void setProgressBarVisibility(boolean visible) {
progressBar.setVisible(visible);
}
public JPanel getContentPane() {
panel = new JPanel(new BorderLayout());
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
loadButton = new JButton("Load memberlist");
loadButton.setEnabled(true);
loadButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
new Thread(new Runnable() {
#Override
public void run() {
progressBar.setVisible(true);
// do my stuff here...
try {
Thread.sleep(2000); // just for the sake of example
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setVisible(false);
}
}).start();
}
});
container = new JPanel(new FlowLayout());
container.add(loadButton);
container.add(progressBar);
panel.add(container);
return panel;
}
}
any events in your case (from Runnable#Thread) doesn't invoke EventDispashThread you have to wrapp that into invokeLater, otherwise since JProgressBar will be visible but after long running taks ended shouldn't be hidden
1) all changes to the GUI must be done on EDT
2) you can invoke EDV from Swing's Listeners, SwingWorker's methods process and done and invoke changes to the GUI by using invokeLater (in special cases invokeAndWait)
3) Runnable#Thread by default doesn't invoke Swing's Methods nor EDT, there must be output to the GUI wrapped into invokeLater (in special cases invokeAndWait), more in the Concurency in Swing, inc. thread safe methods as are setText(), append() etc.
In your thread call the progressBar.setVisible(true); inside SwingUtilities.invokeAndWait().
The code you have posted works perfectly well. The problem is when you call frame.pack(), the frame resizes to fit all visible component. When you have progressBar visibility set to false, the frame ignores this component and sizes accordingly. So when progressBar.setVisible(true) is called later, the component is shown but the frame is not big enough for you to see the component. If you just drag and increase the size of the frame, you can see the progressBar
I suggest that you provide explicit frame size like frame.setSize(200,400) and dont call frame.pack().

Java swing JComponent "size"

I'm doing a project where i need some custom swing components. So far I have made a new button with a series of images (the Java Metal look doesn't fit with my UI at all). Ive implemented MouseListener on this new component and this is where my problem arises. My widget changes image on hover, click etc except my MouseListener picks up mouse entry into a the entire GridLayout container instead of into the image. So I have an image of about 200*100 and the surrounding container is about 400*200 and the mouseEntered method is fired when it enters that GridLayout section (even blank space parts of it) instead of over the image. How can I make it so that it is only fired when I hover over the image? Ive tried setting size and bounds and other attributes to no avail.
EDIT: Here's a demonstration of my issue. As you can see (sort of, colors are very similar) the bottom right button is highlighted just by entering its section of the GridlLayout. I only want it highlighted when I'm over the image actual, not the GridLayout section.
I Won't add the MouseListener methods because they just involve switching the displayed image.
public customWidget()
{
this.setLayout(new FlowLayout());
try {
imageDef=ImageIO.read(new File("/home/x101/Desktop/buttonDef.png"));
imageClick=ImageIO.read(new File("/home/x101/Desktop/buttonClick.png"));
imageHover=ImageIO.read(new File("/home/x101/Desktop/buttonHover.png"));
current=imageDef;
} catch (IOException e)
{
e.printStackTrace();
}
this.addMouseListener(this);
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
g.drawImage(current, 0, 0, current.getWidth(), current.getHeight(), null);
}
EDIT: added code section
As an alternative, consider the The Button API, which includes the method setRolloverIcon() "to make the button display the specified icon when the cursor passes over it."
Addendum: For example,
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ButtonIconTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
private static void createAndShowGui() {
String base = "http://download.oracle.com/"
+ "javase/tutorial/uiswing/examples/components/"
+ "RadioButtonDemoProject/src/components/images/";
ImageIcon dog = null;
ImageIcon pig = null;
try {
dog = new ImageIcon(new URL(base + "Dog.gif"));
pig = new ImageIcon(new URL(base + "Pig.gif"));
} catch (MalformedURLException ex) {
ex.printStackTrace(System.err);
return;
}
JFrame frame = new JFrame("Rollover Test");
JPanel panel = new JPanel();
panel.add(new JLabel(dog));
panel.add(new JLabel(pig));
JButton button = new JButton(dog);
button.setRolloverIcon(pig);
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
I assume your image contains ONLY 4 'customWidget' objects (in a 2x2 grid).
Your code is working as I would expect. Your MouseListener methods are responding to MouseEvents for 'customWidget' (not the image drawn in 'customWidget'), which is sized to take up 1/4 of the image, so they will respond when it enters the enlarged area. The error is actually in your Test program, because you are allowing the custom button widget to be larger than the image.
If you want a Test program that provides an image similar to yours, you should create a larger grid (say 4x4), and then only place your buttons in every other grid node. Place an empty component into the gaps.
Although I won't answer to your particular question, I hope this helps:
If the components just look wrong maybe you should reuse Swing components and just write a custom Look&Feel or theme.
It would certainly help ensuring the look of the application is consistent and at least you are using the right tool for the task you want to accomplish.
As a sidenote, be aware that Java comes with multiple Look&feels, including Look&Feels made to mimic the native OS theme.
See: http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html

Categories

Resources