I'm creating a small Game of Life application. I'm using a 'dynamic universe' for all my cells (named Tiles in my project). But for some reason my JScrollPane and JButtons aren't rendering into the frame. I just get a empty JFrame. The controller is returning values and the buttons are getting constructed and added to the panel. It's just that jsp.setViewportView(p); doesn't seem to update the UI.
Main:
GOLController controller = new GOLController();
controller.run();
SwingUtilities.invokeLater(() -> {
GameOfLifeFrame frame = new GameOfLifeFrame(controller);
frame.init();
});
UI class:
package org.gameoflife.ui;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.gameoflife.controller.GOLController;
import org.gameoflife.model.Tile;
public class GameOfLifeFrame extends JFrame {
private final GOLController controller;
private JScrollPane jsp;
public GameOfLifeFrame(GOLController controller) throws HeadlessException {
super("Game of Life");
this.controller = controller;
}
public void init() {
jsp = new JScrollPane();
add(jsp);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
controller.setLock();
this.draw();
controller.releaseLock();
}
public void draw(){
List<List<Tile>> currentState = controller.getTiles();
GridLayout layout = new GridLayout(currentState.size(), currentState.get(0).size());
JPanel p = new JPanel(layout);
currentState.stream().forEach((currentTiles) -> {
currentTiles.stream().map((t) -> {
JButton b=new JButton(" ");
b.setBackground(t.isHasLife() ? Color.GREEN : Color.BLACK);
return b;
}).forEach((b) -> {
p.add(b);
});
});
jsp.removeAll();
jsp.setViewportView(p);
}
}
I'm probably overlooking something really stupid, any help is appreciated.
This: jsp.removeAll() is going to be problematic, as it's likely removed the viewport AND the JScrollBars, it's also no required, as setting the viewportView will do the same thing anyway
Remember, JScrollPane is a specailsed component, that consists of a JViewPort and two JScrollBars, the actually content lives on the JViewport, not the JScrollPane
Related
Can't figure out why this wont work, I get a blank panel added to my gridbag panel.
I added a JLabel("test") between the frames I'm trying to add just to see that something IS getting added, it just isn't visible?
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Label;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
public class Workspace extends JTabbedPane {
static JFrame frame = new JFrame();
private JPanel grid;
private String text = new String("testing.");
Workspace() {
this.addTab("title", growPanel());
}
public static void main(String[] args) throws IOException {
createAndShowGUI();
}
private JPanel growPanel() {
JPanel gp = new JPanel(false);
gp.setPreferredSize(new Dimension(800,600));
//Add a button that adds my frame
JButton addComponentBtn = new JButton("Add Component");
addComponentBtn.addActionListener(e-> {
try {
addComponent();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
});
gp.add(addComponentBtn, BorderLayout.PAGE_START);
grid = new JPanel(new GridLayout(0, 2, 10, 10)); //any number of rows, 2 columns, H and V gap
JScrollPane sp = new JScrollPane(grid);
sp.setPreferredSize(new Dimension(800,600));
gp.add(sp, BorderLayout.CENTER);
this.setVisible(true);
return gp;
}
void addComponent() throws IOException {
System.out.println("adding");
grid.add(new JLabel("test"));
grid.add(new intf());
this.repaint();
frame.pack();
}
private static void createAndShowGUI() throws IOException {
//Create and set up the window.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add content to the window.
frame.add(new Workspace(), BorderLayout.CENTER);
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
And the intf.java class for the JInternalFrame:
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class intf extends JInternalFrame {
public intf () {
super("Document",
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
JPanel jp = new JPanel();
JLabel jl = new JLabel("Hi I'm a label");
jp.add(jl);
this.add(jp);
this.pack();
this.repaint();
}
}
Edit:
The JPanel is used to hold the Layout, I don't think I can assign one to the TabbedPane?
Users will load in images which will appear on the left side of the GridLayout (I will upgrade this to a GridBag). After selecting regions on said images, graphs will appear on the right side. This is also why we use a scrollPane so that there is no limit to the number of images loaded. The TabbedPane will be used to split the load if a user loads in too many images (this is all not in the demo code shown here and not relevant to the problem).
The code I posted is for testing why the JInternalFrame doesn't show, so it only shows what is relevant to getting that to work.
Thank you all for your input.
It turned out to be a simple mistake of not having a necessary .setVisible(true)
Under the addComponent() method, the problem is solved when I replace
grid.add(new intf());
with
intf a = new intf();
a.setVisible(true);
grid.add(a);
I want to add a JPanel to a JLayeredPane when the user clicks enter, but the JPanel is not showing up.
If i add the JPanel to the JLayeredPane in the JFrame's constructor, everything is working correctly.
What do i have to do, that the JPanel is showing up, when the user clicks 'enter'?
Here's the code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
public class Test extends javax.swing.JFrame {
public static void main(String[] args) {
Test test = new Test();
test.setSize(800, 500);
test.setVisible(true);
}
public Test() {
setLayout(new BorderLayout());
//LayeredPane on JFrame
JLayeredPane jlp = new JLayeredPane();
jlp.setLayout(new BorderLayout());
this.add(jlp, BorderLayout.CENTER);
//Adds a JPanel to the North
JPanel jPNorth = new JPanel();
jPNorth.setBackground(Color.RED);
jlp.add(jPNorth, BorderLayout.NORTH, JLayeredPane.DEFAULT_LAYER);
//Adds Enter Keybinding
InputMap key_input_map = jlp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap key_action_map = jlp.getActionMap();
key_input_map.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false), "add_jpanel");
key_action_map.put("add_jpanel", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JPanel jPSouth = new JPanel();
jPSouth.setBackground(Color.YELLOW);
jlp.add(jPSouth, BorderLayout.SOUTH, JLayeredPane.DEFAULT_LAYER);
System.out.println("enter");
}
});
}
}
Thanks,
Jumagoro
You did everything correct, the solution is very simple. When you dynamically add swing Components to each other, you must to use component.repaint(); and component.revalidate(); to redraw the elements. Add the two commands after everything is added. So your actionPerformed method should be changed to the following:
public void actionPerformed(ActionEvent e) {
JPanel jPSouth = new JPanel();
jPSouth.setBackground(Color.YELLOW);
jlp.add(jPSouth, BorderLayout.SOUTH, JLayeredPane.DEFAULT_LAYER);
//Need these to here!
jlp.repaint();
jlp.revalidate();
System.out.println("enter");
}
My question is about layout in Java Swing.
I want to make a screen like shown below. I saw this video on youtube and made a gif of the part I want.
I want 2 panels and a button like this:
When i clicked the button the JPanel will be hidden and JTable's width will be 100% like html/css like this; (And when button clicked again JPanel will be shown etc..)
How can I do this? Which layout should I use?
There is more than one way to do it, but here's an example that uses BorderLayout as the main layout, and places the button in a left aligning FlowLayout:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
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.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class LayoutDemo {
private LayoutDemo() {
JFrame frame = new JFrame("Demo");
frame.setLocationByPlatform(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel buttonHolder = new JPanel(new FlowLayout(FlowLayout.LEADING));
frame.add(buttonHolder, BorderLayout.NORTH);
JButton button = new JButton("Toggle visibility");
buttonHolder.add(button);
final JPanel left = new JPanel();
left.setPreferredSize(new Dimension(100, 200));
left.setBackground(Color.BLUE);
frame.add(left, BorderLayout.LINE_START);
JLabel table = new JLabel("This pretends to be a table", SwingConstants.CENTER);
table.setPreferredSize(new Dimension(400, 200));
frame.add(table);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
left.setVisible(!left.isVisible());
}
});
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LayoutDemo();
}
});
}
}
I used setPreferredSize() to give the components some reasonable default size, but usually it should be automatically calculated by the layout manager from the sizes of the child components, or in case of a custom component, you should override getPreferredSize() return what is appropriate for the component.
The result looks like:
At the moment I write a little client application. I have a window with a JTextArea (Display area for the server output) and a user-list.
My plan is to show/hide this user-list over a menu item, but I don't know how. My ideas:
Use a BorderLayout: Without a JScrollPane for the list. It works, but I cannot change the width of the user-list (Because BorderLayout.WEST and BorderLayout.EAST ignore the width)
Use a BorderLayout with a JScrollPane for the user list and show/hide the JScrollPane -> Does not work, don't ask me why...anyway, this way is not a nice solution
Use a JSplitPane, set the resize weight to 0.9. When the user-list should disappear, I minimize the right component (aka the user list) -> How ?
My code at the moment:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
public class SplitPaneTest extends JFrame implements ActionListener
{
private JSplitPane splitPane;
private JTextArea textDisplay;
private JList<String> listUser;
private JScrollPane scrollTextDisplay;
private JScrollPane scrollListUser;
private JCheckBox itemShowUser;
public static void main(String[] args)
{
new SplitPaneTest();
}
public SplitPaneTest()
{
setTitle("Chat Client");
setMinimumSize(new Dimension(800, 500));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textDisplay = new JTextArea();
listUser = new JList<>();
DefaultListModel<String> modelUser = new DefaultListModel<>();
listUser.setModel(modelUser);
modelUser.addElement(new String("User 1"));
modelUser.addElement(new String("User 2"));
modelUser.addElement(new String("User 3"));
scrollTextDisplay = new JScrollPane(textDisplay);
scrollListUser = new JScrollPane(listUser);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setLeftComponent(scrollTextDisplay);
splitPane.setRightComponent(scrollListUser);
splitPane.setResizeWeight(0.9);
setContentPane(splitPane);
JMenuBar menubar = new JMenuBar();
JMenu menuWindow = new JMenu("Window");
itemShowUser = new JCheckBox("Show user list");
itemShowUser.addActionListener(this);
itemShowUser.setSelected(true);
menuWindow.add(itemShowUser);
menubar.add(menuWindow);
setJMenuBar(menubar);
setVisible(true);
}
public boolean isUserListEnabled()
{
return itemShowUser.isSelected();
}
public void setUserListEnabled(boolean status)
{
scrollListUser.setVisible(status);
}
#Override
public void actionPerformed(ActionEvent ae)
{
if(ae.getSource() == itemShowUser)
{
boolean status = isUserListEnabled();
setUserListEnabled(status);
}
}
}
And the result is:
And with hidden JScrollPane scrollListUser:
Can anybody give me a tipp ? The user-list is still visible ( I thought the JSplitPane would repaint..) .I come from Qt (C++) and in Qt I could use a dock widget - but Swing does not have one and to use third libs....I don't know - maybe there is a solution.
Looks like the splitPane can't handle invisible components well - a way out is to add/remove the scrollPane as appropriate:
public void setUserListEnabled(boolean status)
{
splitPane.setRightComponent(status ? scrollListUser : null);
splitPane.revalidate();
}
I have a show button to show a JTable on click but the table is not visible.
Note: when I remove the JScrollPane the code works properly but the header of the table is not shown, so any help please to make this code work properly without removing the JScrollPane
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class Training extends JFrame {
public Training() {
getContentPane().setLayout(new FlowLayout());
JTable table = new JTable();
table.setModel(new DefaultTableModel(new Object[][] { { "joe", "joe" },
{ "mickel", "mickel" }, }, new String[] { "LastName",
"FirstName" }));
final JScrollPane pane = new JScrollPane(table);
pane.setVisible(false);
getContentPane().add(pane);
JButton btn = new JButton("show");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
pane.setVisible(true);
}
});
}
public static void main(String[] args) {
Training app = new Training();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(600, 600);
app.setVisible(true);
}
}
After pane.setVisible(true); add the following:
getContentPane().validate();
getContentPane().repaint();
A few things to note:
Never extends JFrame class unnecessarily, or else you might need to extend another class which is very necessary but in java a single class may not extend more than one other class (no multiple inheritance).
Always create Swing components on Event Dispatch Thread via SwingUtilities.invokeLater(Runnable r) block.
Do not use setSize(..) call JFrame#pack() before setting JFrame visible
No need for getContentPane.add(..) or getContentPane().setLayout(..), simply call add(..) or setLayout(..) on JFrame instance as these calls are fowared to the contentPane.
The problem you have is you do not refresh you frame/container after setting pane visible. I disagree with #Dan. Do not use validate() (getContentPane() is not necesarry either) rather:
revalidate();
repaint();
as revalidate() covers validate(). Also validate is used when new JComponents are added to a visible component, whereas revalidate() is used when JComponent is removed/added from a visible component.
Here is a fixed version of the code with the above implemented:
After button pressed:
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.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class Training {
private JFrame frame;
public Training() {
frame = new JFrame();
frame.setLayout(new FlowLayout());
JTable table = new JTable();
table.setModel(new DefaultTableModel(new Object[][]{{"joe", "joe"},
{"mickel", "mickel"},}, new String[]{"LastName",
"FirstName"}));
final JScrollPane pane = new JScrollPane(table);
pane.setVisible(false);
frame.add(pane);
JButton btn = new JButton("show");
frame.add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.setVisible(true);
frame.pack();//this is so the frame will resize after adding pane
frame.revalidate();
frame.repaint();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Training();
}
});
}
}
UPDATE:
Also for a more reusable Layout, why not add all components to a JPanel, and add that JPanel to the JFrame, thus if you ever need to add more stuff its simple.