JGraphX is ruining the bounds of Swing-components - java

I am trying to implement an interaction between some self-written GUI-Elements (like Java-swing Buttons) and JGraphX. In order to do that, I first just want to display a Button next to a JGraph-Element - and I'm stuck.
My code to display the Button itself works fine:
import javax.swing.*;
public class HelloWorld extends JFrame {
public HelloWorld()
{
super("Everything works");
}
public static void main(String[] args)
{
hello_world frame = new hello_world();
frame.setTitle("bub");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
button.setBounds(10, 10, 100, 50);
frame.setVisible(true);
frame.add(button);
}
}
But as soon as I am adding the JGraph-Component, the Button ist displayed fullscreen. I have no Idea why and how to prevent that. My Code:
import javax.swing.*;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
public class HelloWorld extends JFrame {
private mxGraph graph;
private Object window;
public HelloWorld()
{
super("Nothing works");
graph = new mxGraph();
window = graph.getDefaultParent();
graph.getModel().beginUpdate();
try
{
Object v2 = graph.insertVertex(window, null, "Hello World!", 200, 150, 80, 30);
}
finally
{
graph.getModel().endUpdate();
}
mxGraphComponent graphComponent = new mxGraphComponent(graph);
getContentPane().add(graphComponent);
}
public static void main(String[] args)
{
hello_world frame = new hello_world();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
JButton button = new JButton("left");
button.setBounds(10, 10, 100, 50);
frame.setVisible(true);
frame.add(button);
}
}
The result looks like this (I manually resized the window to show you the button. Naturally, the button would just fill the space behind the JGraph-Component and therefor would be invisible):

Swing uses layout managers. The default layout manager for a JFrame is the BorderLayout. When you add a component to the frame the default constraint used is BorderLayout.CENTER. Only a single component can be added to the CENTER of the BorderLayout.
The setBounds(...) will only work temporarily. As soon as the frame is resized the layout manager is invoked and the button will be given the new size/location based on the rules of the layout manager.
The solution is to use layout managers properly. I'm not sure what layout you are trying to achieve so all I can suggest is that you read the Swing tutorial on Layout Managers for working examples to get you started. Then you can nest panels with different layout managers to achieve your desired effect.
Start with something simple like:
frame.add(button, BorderLayout.PAGE_END);
frame.setVisible(true);
//frame.add(button);
The above will display your graph in the center of the frame and the button at the bottom.

Related

JFrame: Can't launch new window

I'm trying to learn Java and JFrames. I put together this simple app where the Main class launches the MainGui class. MainGui will contain a bunch of buttons that each does something different (right now it only has the canadaFlagButton). I have no problem getting the window for MainGui to open with the button I created.
However, when I click the canadaFlagButton, it is supposed to launch a new window but instead, nothing happens.
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainGui extends JFrame implements ActionListener {
JButton canadaFlagButton;
MainGui() {
canadaFlagButton = new JButton("Canada Flag");
canadaFlagButton.setBounds(100, 100, 200, 50);
canadaFlagButton.setFocusable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 600);
this.setLayout(null);
this.add(canadaFlagButton);
this.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==canadaFlagButton) {
CanadaFlag window1 = new CanadaFlag();
}
}
}
//Can I draw the Canadian flag?
This is the CanadaFlag class:
import javax.swing.*;
import java.awt.*;
public class CanadaFlag extends JFrame {
CanadaFlag() {
//creating label for maple leaf
JLabel leafLabel = new JLabel();
ImageIcon leaf = new ImageIcon("mapleleaf.png");
leafLabel.setIcon(leaf);
leafLabel.setVerticalAlignment(JLabel.CENTER);
leafLabel.setHorizontalAlignment(JLabel.CENTER);
JPanel redLeftPanel = new JPanel();
redLeftPanel.setBackground(Color.red);
redLeftPanel.setBounds(0, 0, 400, 1000);
JPanel redRightPanel = new JPanel();
redRightPanel.setBackground(Color.red);
redRightPanel.setBounds(1000, 0, 400, 1000);
JPanel whiteMiddlePanel = new JPanel();
whiteMiddlePanel.setBackground(Color.white);
whiteMiddlePanel.setBounds(400, 0, 600, 1000);
whiteMiddlePanel.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setSize(1400, 1000);
this.setVisible(true);
this.add(redLeftPanel);
this.add(redRightPanel);
this.add(whiteMiddlePanel);
whiteMiddlePanel.add(leafLabel);
}
}
An application should only have a single main JFrame. If you need other windows then you would typically use a JDialog.
Swing was designed to be used with layout manager. If you are trying to learn Swing then check out the Swing tutorial on Layout Managers for more information and working example.
it is supposed to launch a new window but instead, nothing happens
You never add the ActionListener to the button.
canadaFlagButton.addActionListener( this );
The tutorial also has a section on How to Use Buttons that contains a working example. I suggest you keep a reference to the tutorial for examples of all Swing basics.

JButton doesnt show text; fills JFrame

I am trying to make a JFrame with a button in it, but my button doesnt have my wanted text! I'm setting it in the button constructor AND afterwards with setText, but it still doesnt show up! Also, the button fills the whole frame, is there a way to make it not stick to the edges of the JFrame?
import javax.swing.*;
public class main
{
public static void main(String[] args)
{
JFrame mainWindow = new JFrame("8 Game");
mainWindow.setSize(200, 200);
JButton eightButton = new JButton("8");
eightButton.setText("8");
eightButton.setSize(30, 30);
eightButton.setBounds(5, 5, 25, 25);
eightButton.setContentAreaFilled(false);
eightButton.setAction(new buttonAction());
mainWindow.add(eightButton);
mainWindow.setVisible(true);
}
}
Why does it work for me and not you? (with the FlowLayout suggested by others
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
public class main
{
public static void main(String[] args)
{
JFrame mainWindow = new JFrame("8 Game");
mainWindow.setLayout(new FlowLayout(FlowLayout.CENTER));
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setSize(200, 200);
JButton eightButton = new JButton("8");
eightButton.setText("8");
eightButton.setSize(30, 30);
eightButton.setBounds(5, 5, 25, 25);
//eightButton.setAction(new buttonAction());
//eightButton.setContentAreaFilled(false);
mainWindow.add(eightButton);
mainWindow.setVisible(true);
}
}
EDIT
an Action needs a title. If you don't specify one, the button will have no title. If you did this
eightButton.setAction(new buttonAction(), "8");
it would work.
Use a layout manager that respects the component's preferred size
mainWindow.setLayout(new FlowLayout(FlowLayout.CENTER));
Swing components expect a layout manager context when they are added to a window.
The default layout is BorderLayout, which is why you're getting that odd behavior. With only a single element, BorderLayout fills the pane with that element.
Try something like FlowLayout or AbsoluteLayout (or null)
http://docs.oracle.com/javase/tutorial/uiswing/layout/none.html
Use another LayoutManager default frame's layout manager is BorderLayout that if you add a component without specification will add to the center. You can use FlowLayout. See example with SwingUtilities.invokeLater you ensure that will run in EDT.
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame mainWindow = new JFrame("8 Game");
mainWindow .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setLayout(new FlowLayout());
mainWindow.setLocationRelativeTo(null);
JButton eightButton = new JButton("8");
eightButton.setText("8");
eightButton.setContentAreaFilled(false);
eightButton.setAction(new buttonAction());
mainWindow.add(eightButton);
mainWindow.pack();
mainWindow.setVisible(true);
}
});
}
Take a look of more complete correct examples in official tutorials How to use Buttons

JFrame Button Logic error

I've been spending some time relearning java and a peculiar logic error hit me here.
import javax.swing.*;
import java.awt.*;
class Frame
{
public static void main (String args[])
{
JFrame frame = new JFrame("Tester Frame");
frame.setSize(400, 500);
JButton btn1 = new JButton("FOO");
btn1.setSize(150, 50);
btn1.setLocation(45, 0);
JButton btn2 = new JButton("BAR");
btn2.setSize(150, 50);
btn2.setLocation(205, 0);
Container content = frame.getContentPane();
content.setBackground(Color.blue);
content.add(btn1);
content.add(btn2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}//end main
}
I've created 2 JButton objects, and they should be the same size, with different location and text. This of course is not the case, the "FOO" button is exactly where and how I want it to be, but the "BAR" button is the size of the entire frame.
Help!
1) You are attempting to use Absolute LayoutManager via setSize and setLocation etc, but without calling setLayout(null) on the component you are adding the JButtons to. However this is not a best practice in Swing.
When adding to JFrame contentpane default layout is BorderLayout which adds components to is default position of BorderLayout.CENTER.
Have a read on A Visual Guide to Layout Managers
2) Also when using a correct LayoutManager you would omit JFrame#setSize(..) call and replace it with JFrame#pack() before setting the JFrame visible.
3) Also have a read on Concurrency in Swing specifically on The Event Dispatch Thread
which dictates all swing components be created on EDT via SwingUtillities.invokeXXX(..) block:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create and manipulate swing components here
}
});
4) Also rather use JFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); as this will allow any other threads timers etc to carry on execution even after JFrame has been disposed.
add:
frame.getContentPane().setLayout(null);
To your code after the line:
frame.setSize(400, 500);
Components added to a container are tracked in a list. The order of the list will define the components' front-to-back stacking order within the container. If no index is specified when adding a component to a container, it will be added to the end of the list (and hence to the bottom of the stacking order).In your code the buttons are stacked over the other.That is why you get this Error(as you think it is).
This will solve your problem:-
import javax.swing.*;
import java.awt.*;
class OP3
{
public static void main (String args[])
{
JFrame frame = new JFrame("Tester Frame");
frame.setSize(400, 500);
JButton btn1 = new JButton("FOO");
btn1.setSize(150, 50);
btn1.setLocation(45, 0);
JButton btn2 = new JButton("BAR");
btn2.setSize(150, 50);
btn2.setLocation(205, 0);
JPanel p = new JPanel(new FlowLayout());
p.add(btn1);
p.add(btn2);
frame.add(p);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}//end main
}
Just add a panel to the frame and add the buttons to the panel.
import javax.swing.*;
import java.awt.*;
class source
{
public static void main (String args[])
{
JFrame frame = new JFrame("Tester Frame");
frame.setSize(400, 500);
JPanel panel=new JPanel();//panel added here
panel.setSize(frame.size());
panel.setLocation(0, 0);
JButton btn1 = new JButton("FOO");
btn1.setSize(150, 50);
btn1.setLocation(45, 0);
JButton btn2 = new JButton("BAR");
btn2.setSize(150, 50);
btn2.setLocation(205, 0);
panel.add(btn1);
panel.add(btn2);
Container content = frame.getContentPane();
content.setBackground(Color.blue);
content.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}//endmain

How set location of an image in a JFrame

I'm trying to set the the location of an image in a JFrame. I thought label.setLocation(100, 100); would set the location of the image to 100, 100 (top left corner), but it doesn't seem to do anything no matter were I put it. I even tried panal.setLocation(100, 100). Both do nothing, I get no errors and the image does appears but at 0, 0. What am I doning wrong? Here's my code:
import javax.swing.*;
public class DisplayImage {
public DisplayImage() {
JFrame frame = new JFrame("Display Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = (JPanel)frame.getContentPane();
JLabel label = new JLabel();
label.setIcon(new ImageIcon("src/img/school.png"));
label.setLocation(100, 100);
panel.add(label);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public static void main (String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DisplayImage();
}
});
}
}
By default a JFrame uses a BorderLayout. When you add your label to the frame the label is added to the CENTER of the BorderLayout. The layout manager will override the location.
One simple solution is to add an EmptyBorder to the label with the top/left insets being 100. Then instead of add the label to the center you would add the label to the NORTH. The code would be something like:
label.setBorder( new EmptyBorder(...) );
panel.add(label, BorderLayout.SOUTH);
As a general rule you should not be trying to specify exact location of a component. Let the layout managers to their jobs.
The JFrame, by default, uses a BorderLayout as it's layout manager. This will override any settings you supply to the setLocation method.
You have a number of options...
Use something like JLayeredPane, which does not, but default, have layout manager of it's own set by default. See How to use layered panes for more details
Create a custom component capable of renderering the image where you want it. Check out Performing Custom Painting for more details
Create your own layout manager that performs the operations you want...
This works for me:
import java.awt.Dimension;
import javax.swing.*;
public class DisplayImage {
public DisplayImage() {
JFrame frame = new JFrame("Display Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = (JPanel)frame.getContentPane();
panel.setLayout(null);
JLabel label = new JLabel();
label.setIcon(new ImageIcon("rails.png"));
panel.add(label);
Dimension size = label.getPreferredSize();
label.setBounds(100, 100, size.width, size.height);
frame.setSize(300, 200);
frame.setVisible(true);
}
public static void main (String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DisplayImage();
}
});
}
}
But you should read this:
http://docs.oracle.com/javase/tutorial/uiswing/layout/none.html

Newbie JLayeredPane issue

I just can't get past square one on JLayeredPanes. (See my original question of yesterday. I have been studying the JLayeredPane tutorial and API. These tutorials are geared somewhat differently to what I am ultimately trying to produce.
Going back to square one, I took Oracle's JFrame Example and modified it to include Layered panes.
Here is the code:
package components;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* FrameDemo.java requires no other files. */
public class FrameDemo {
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainLayer = new JPanel(new BorderLayout());
mainLayer.setPreferredSize(new Dimension(640, 480));
frame.setContentPane(mainLayer);
frame.getLayeredPane().add(mainLayer, JLayeredPane.DEFAULT_LAYER, 0);
JLabel emptyLabel = new JLabel("LABEL");
emptyLabel.setPreferredSize(new Dimension(320, 240));
mainLayer.add(emptyLabel, BorderLayout.NORTH);
JPanel subLayer = new JPanel(new BorderLayout());
JLabel subLabel = new JLabel("SUBLABEL");
subLabel.setPreferredSize(new Dimension( 200, 100));
subLabel.setBackground(Color.YELLOW);
subLayer.add(subLabel, BorderLayout.SOUTH);
subLayer.setVisible(true);
subLabel.setVisible(true);
frame.getLayeredPane().add(subLayer, JLayeredPane.PALETTE_LAYER, 0);
//Display the window.
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();
}
});
}
}
Why doesn't it work? IOW, why doesn't the sublabel show up? It's at a higher level than the main layer.
One thought is why am I adding mainLayer to both the Content Pane and the Layered Pane? If I don't do that, nothing shows up. I.e, by commenting out this line, I just get a blank frame.
// frame.setContentPane(mainLayer);
Obviously, I'm not understanding something. But what is it?
I should add that obviously, this simple demo can be done without Layered Panes. But my ultimate goal is to have a layer that can be turned on and off programatically. But I can't even get this simple case to work. If I can get over this hump, I think the rest will be easier.
ADDENDUM:
What I want to acheive is illustrated by the following Code, which is very similar to what TrashGod set up below and it works. There is a JLayeredPane with a constant layer (layered at Integer(0)) and a floating layer layered initially at Integer(-1) but togglable by the F7 and F8 keystrokes between the Integer(-1) layer and the Integer(1) layer, thereby allowing it to float above or below the constant layer.
package components;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* MyLayeredPaneDemo.java requires no other files. */
public class MyLayeredPaneDemo {
private JFrame frame;
private JLayeredPane mainPanel;
private JPanel constantLayer;
private JPanel floatingLayer;
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private MyLayeredPaneDemo() {}
private void createAndShowGUI() {
//Create and set up the window.
this.frame = new JFrame("MyLayeredPaneDemo");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.setPreferredSize(new Dimension(640, 480));
mainPanel = new JLayeredPane();
constantLayer = new JPanel(new BorderLayout(0,0));
floatingLayer = new JPanel(new BorderLayout(0,0));
// constantLayer.setPreferredSize();
constantLayer.setOpaque(true);
constantLayer.setBackground(Color.BLUE);
JLabel constantLabel = new JLabel("MAIN LAYER");
constantLayer.setPreferredSize(new Dimension(640, 480));
constantLayer.add(constantLabel, BorderLayout.CENTER);
JLabel subLabel = new JLabel("SUB LAYER");
floatingLayer.setBackground(Color.YELLOW);
floatingLayer.add(subLabel, BorderLayout.SOUTH);
floatingLayer.setOpaque(true);
floatingLayer.setVisible(true);
floatingLayer.setVisible(true);
subLabel.setBackground(Color.YELLOW);
mainPanel.add(constantLayer, new Integer(0), 0);
constantLayer.setBounds(0,0,640,480);
mainPanel.add(floatingLayer, new Integer(-1), 0);
floatingLayer.setBounds(100, 360, 300, 90 );
frame.add(mainPanel, BorderLayout.CENTER);
//Display the window.
mapKeyToAction(frame.getRootPane(),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0),
"Hide Layer",
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("F7 pressed");
mainPanel.setLayer(floatingLayer, new Integer(-1));
}
});
mapKeyToAction(frame.getRootPane(),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),
"Show Layer",
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("F8 pressed");
mainPanel.setLayer(floatingLayer, new Integer(1));
}
});
frame.pack();
frame.setVisible(true);
frame.getRootPane().setFocusable(true);
boolean ok = frame.getRootPane().requestFocusInWindow();
System.out.println("focus ok: " + ok);
}
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() {
new MyLayeredPaneDemo().createAndShowGUI();
}
});
}
private static void mapKeyToAction(JComponent component,
int whichMap, KeyStroke keystroke,String key, Action action) {
component.getInputMap(whichMap).put(keystroke, key);
component.getActionMap().put(key, action);
}
}
However, I am having trouble getting this to work in my real case. The difference between the two is that here, my JLayeredPane is owned by the Frame, whereas in my real application, I want the JLayeredPane to be owned by a JPanel is that some levels down in the containment hierarchy from the Frame, and whose size is set by a GridBagLoyout in its parent, and the size is therefore unknowable at the time its constructor is called, making it difficult to call setBounds() which I need to do on a child of a JLayeredPane.
FURTHER ADDENDUM. I know that the Oracle Tutorials mention a case where Layouts rather than absolute positioning is used with a JLayeredPane. The difference between this case and mine is that in my case the layers occupy the same horizontal space on different layers, whereas in this case, the components on different layrers occupy different horizontal spaces. It's almost as if we need a 3D Layout Manager!
"By default, a layered pane has no layout manager."—How to Use Layered Panes
Addendum: I need to avoid using the Frame's layered pane and instead add a layered pane to the window.
Yes, The Root Pane is an instance of JRootPane, which contains a JLayeredPane. In particular, "The layered pane contains the menu bar and content pane, and enables Z-ordering of other components."
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameDemo {
private static void createAndShowGUI() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane mainLayer = new JLayeredPane();
frame.add(mainLayer, BorderLayout.CENTER);
JLabel label = new JLabel("LABEL", JLabel.CENTER);
label.setBounds(100, 100, 200, 100);
label.setOpaque(true);
label.setBackground(Color.cyan);
mainLayer.add(label, 1);
JPanel subLayer = new JPanel(new BorderLayout());
JLabel subLabel = new JLabel("SUBLABEL", JLabel.CENTER);
subLabel.setBounds(20, 20, 200, 100);
subLabel.setOpaque(true);
subLabel.setBackground(Color.yellow);
subLayer.add(subLabel, BorderLayout.SOUTH);
mainLayer.add(subLabel, 2);
frame.pack();
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
The solution I came up and thanks to trashgod which I expect is good advice too is to implement ComponentListener and capture the component resize event. At that point you can get the actual bounds of the container and use it to set the actual bounds of the layer JPanels which are always in some fixed relation to the bounds of the component that contains them. It works.
Trashgod's solution would also work I believe but I have not tried it.

Categories

Resources