Hereunder is a minimal example of a bug I can't fix in a Java program.
The bug consists is that the lines are randomly shown in the JFrame: sometimes I see the board at runtime, sometimes not.
Any hint greatly appreciated.
import java.awt.Graphics;
import javax.swing.JFrame;
public class EssaiDrawLine extends JFrame{
public static void main(String[] args) {
EssaiDrawLine mafenetre = new EssaiDrawLine();
mafenetre.setVisible(true);
}
// constructeur
public EssaiDrawLine() {
setTitle("mon titre");
setSize(600, 500);
}
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i <= 8; i++) {
g.drawLine(50 * i + 10, 40, 50*i + 10, 440);
g.drawLine(10, 50 * i + 40, 410, 50 * i + 40);
}
g.drawString("Cliquer pour obtenir la prochaine solution", 20, 470);
}
}
Another option: don't draw the lines but instead create a grid of components, such as a 2-D array of JPanels held in another JPanel, one that uses GridLayout, and then give the JPanels a MouseListener so that they can respond to mouse events. For example, a simple version of this could look something like so (please read the comments in the code):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial") // get rid of warning
public class EssaiDrawLine3 extends JPanel {
private static final int SIDE = 8;
private static final int SQR_LENGTH = 50;
private static final int GAP = 5;
private static final Color CLICK_COLOR = Color.RED;
private static final Color SQR_COLOR = Color.LIGHT_GRAY;
private static final String CLICK_TEXT = "Cliquer pour obtenir la prochaine solution";
// our grid of JPanel
private JPanel[][] grid = new JPanel[SIDE][SIDE];
public EssaiDrawLine3() {
// JPanel to hold the grid
// give it an 8x8 GridLayout with 1 pixel gap for lines to show
JPanel gridHolder = new JPanel(new GridLayout(SIDE, SIDE, 1, 1));
// background color that shows through as lines in gaps in grid
gridHolder.setBackground(Color.BLACK);
gridHolder.setBorder(BorderFactory.createLineBorder(Color.black));
// mouse listener to add to each JPanel cell
MyMouse myMouse = new MyMouse();
// nested for loop to create our grid of JPanels
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
// create a single cell
JPanel cellPanel = new JPanel();
// size it 50x50 pixels
cellPanel.setPreferredSize(new Dimension(SQR_LENGTH, SQR_LENGTH));
// add the mouse listener to it
cellPanel.addMouseListener(myMouse);
// give it a default background color
cellPanel.setBackground(SQR_COLOR);
// place it into the 2-D array of JPanel
grid[row][col] = cellPanel;
// place it into the grid layout-using JPanel
gridHolder.add(cellPanel);
}
}
// display text at the bottom
JLabel label = new JLabel(CLICK_TEXT, SwingConstants.CENTER);
// allow gaps around the main JPanel
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
// give the main JPanel a BorderLayout
setLayout(new BorderLayout(GAP, GAP));
add(gridHolder); // add the grid to the CENTER position
// add the JLabel to the bottom position
add(label, BorderLayout.PAGE_END);
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
// get the JPanel cell that was clicked
JComponent comp = (JComponent) e.getSource();
// get its color and change it
Color color = comp.getBackground();
if (color.equals(CLICK_COLOR)) {
comp.setBackground(SQR_COLOR);
} else {
comp.setBackground(CLICK_COLOR);
}
}
}
private static void createAndShowGui() {
EssaiDrawLine3 mainPanel = new EssaiDrawLine3();
JFrame frame = new JFrame("Essai Draw Line3");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Never draw directly in the JFrame but instead within a JPanel's paintComponent method, and then display that JPanel in your JFrame. This is all well explained in the standard tutorials: : Lesson: Performing Custom Painting
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
#SuppressWarnings("serial")
public class EssaiDrawLine2 extends JPanel {
private static final int PS_WIDTH = 600;
private static final int PS_HEIGHT = 500;
public EssaiDrawLine2() {
setPreferredSize(new Dimension(PS_WIDTH, PS_HEIGHT));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i <= 8; i++) {
g.drawLine(50 * i + 10, 40, 50 * i + 10, 440);
g.drawLine(10, 50 * i + 40, 410, 50 * i + 40);
}
g.drawString("Cliquer pour obtenir la prochaine solution", 20, 470);
}
private static void createAndShowGui() {
EssaiDrawLine2 mainPanel = new EssaiDrawLine2();
JFrame frame = new JFrame("Essai Draw Line");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Related
I am trying to implement a GUI for a maze-based game I created that meets the following specific conditions:
The GUI itself has a set size and is not resizable (line 41) .
The master panel (line 57) that contains all the maze images is scrollable. All maze image components are flush with each other.
If maze is small enough, then entire maze will be visible in master panel.
If maze is very large, then user would need to scroll.
The master panel needs to be accessed by a mouse listener (line 130) that returns the component that is being clicked.
The following code seems to meet criteria 1 and 3, but fails criteria 2:
public class MazeGui extends JFrame implements DungeonView {
private final Board board;
public MazeGui(ReadOnlyModel m) {
//this.setSize(m.getNumRows()*100, m.getNumCols()*100);
this.setSize(600, 600);
this.setLocation(200, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.board = new Board(m);
JScrollPane scroller = new JScrollPane(board);
this.add(scroller, BorderLayout.CENTER);
setTitle("Dungeon Escape");
}
private class Board extends JPanel {
private ReadOnlyModel m;
public Board(ReadOnlyModel m) {
this.m = m;
GridLayout layout = new GridLayout(m.getNumRows(),m.getNumCols(), 0, 0);
// layout.setHgap(-100);
// layout.setVgap(-100);
this.setLayout(layout);
this.setSize(m.getNumRows()*64,m.getNumCols()*64);
for (int i = 0; i < m.getNumRows() * m.getNumCols(); i++) {
try {
// load resource from the classpath instead of a specific file location
InputStream imageStream = getClass().getResourceAsStream(String.format("/images/%s.png", m.getRoomDirections(i + 1)));
// convert the input stream into an image
Image image = ImageIO.read(imageStream);
// add the image to a label
JLabel label = new JLabel(new ImageIcon(image));
label.setPreferredSize(new Dimension(64, 64));
JPanel panel = new JPanel();
panel.setSize(64, 64);
String name = String.format("%d", i);
panel.setName(name);
panel.add(label);
// add the label to the JFrame
//this.layout.addLayoutComponent(TOOL_TIP_TEXT_KEY, label);
this.add(panel);
} catch (IOException e) {
JOptionPane.showMessageDialog(this, e.getMessage());
e.printStackTrace();
}
}
}
}
#Override
public void addClickListener(DungeonController listener) {
Board board = this.board;
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(String.format("(%d,%d)", e.getX(), e.getY()));
JPanel panel = (JPanel) board.getComponentAt(e.getPoint());
System.out.println(panel.getName());
}
};
board.addMouseListener(mouseListener);
}
#Override
public void refresh() {
this.repaint();
}
#Override
public void makeVisible() {
this.setVisible(true);
}
}
Here is an image of what it produces:
First, I'd make use of a different layout manager, one which would try and expand to fit the size of the underlying container.
Then, I would let the components do their jobs. I don't know why you're adding the label to another panel, the panel doesn't seem to be adding additional functionality/features and is just adding to the complexity.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
List<Maze.Direction> directions = new ArrayList<>(32);
directions.add(Maze.Direction.EAST_SOUTH);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
System.out.println(directions.size());
Maze maze = new DefaultMaze(5, 6, directions);
MazeGui frame = new MazeGui(maze);
frame.addClickListener(null);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Maze {
enum Direction {
EAST_SOUTH("EastSouth.png"), EAST_SOUTH_WEST("EastSouthWest.png"), SOUTH_WEST("SouthWest.png"),
NORTH_EAST_SOUTH("NorthEastSouth.png"), NORTH_EAST_SOUTH_WEST("NorthEastSouthWest.png"),
NORTH_SOUTH_WEST("NorthSouthWest.png"), NORTH_SOUTH("NorthSouth.png"), NORTH("North.png");
private BufferedImage image;
private Direction(String name) {
try {
image = ImageIO.read(getClass().getResource("/images/" + name));
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public BufferedImage getImage() {
return image;
}
}
public int getRows();
public int getColumns();
public Direction getRoomDirections(int index);
}
public class DefaultMaze implements Maze {
int rows;
int columns;
private List<Direction> directions;
public DefaultMaze(int rows, int columns, List<Direction> directions) {
this.rows = rows;
this.columns = columns;
this.directions = directions;
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns;
}
#Override
public Direction getRoomDirections(int index) {
return directions.get(index);
}
}
public class MazeGui extends JFrame {
// Missing code
public interface DungeonController {
}
private final Board board;
public MazeGui(Maze m) {
this.setSize(600, 600);
this.setResizable(false);
this.board = new Board(m);
JScrollPane scroller = new JScrollPane(board);
this.add(scroller, BorderLayout.CENTER);
setTitle("Dungeon Escape");
}
public Board getBoard() {
return board;
}
public void addClickListener(DungeonController listener) {
Board board = getBoard();
board.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Component cell = board.getComponentAt(e.getPoint());
System.out.println(cell.getName());
board.highlight(cell.getBounds());
}
});
}
private class Board extends JPanel {
private Rectangle selectedCell;
private Maze maze;
public Board(Maze maze) {
this.maze = maze;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
for (int index = 0; index < maze.getRows() * maze.getColumns(); index++) {
Maze.Direction direction = maze.getRoomDirections(index);
JLabel label = new JLabel(new ImageIcon(direction.getImage()));
label.setName(direction.name());
add(label, gbc);
gbc.gridx++;
if (gbc.gridx >= maze.getColumns()) {
gbc.gridx = 0;
gbc.gridy++;
}
}
// addMouseListener(new MouseAdapter() {
// #Override
// public void mouseClicked(MouseEvent e) {
// Component component = getComponentAt(e.getPoint());
// selectedCell = null;
// if (component != null) {
// selectedCell = component.getBounds();
// }
// repaint();
// }
// });
}
public void highlight(Rectangle bounds) {
selectedCell = bounds;
repaint();
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (selectedCell != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 0, 255, 128));
g2d.fill(selectedCell);
g2d.dispose();
}
}
}
}
}
The GUI itself has a set size and is not resizable
So the issue here is that you are forcing the "board" panel to have an arbitrary size.
this.setSize(600, 600);
The actual size of the panel should be 8 * 64 = 512. So extra space is being added to each grid.
Don't hardcode size values.
It is the job of the layout manager to determine the preferred size of each component.
So instead of using setSize(...) you should pack() the frame before making it visible:
this.pack();
this.setVisible(true);
When you do this you will see that the maze fits completely in the frame.
If you want extra space around the maze then you need to add a "border" to your board:
setBorder( new EmptyBorder(88, 88, 88, 88) );
GridLayout layout = new GridLayout(m.getNumRows(),m.getNumCols(), 0, 0);
Turns out I should have been using GridBagLayout!
There is no need to change layout managers, only use the layout managers more effectively.
If you really for some reason need to specify a fixed frame size then you can make the following change:
//this.add(scroller, BorderLayout.CENTER);
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add(scroller, new GridBagConstraints());
this.add(wrapper, BorderLayout.CENTER);
This will allow the "board" panel to be displayed at its preferred size and the "board" panel will be centered in its parent container.
Using these tips will help you effectively create more complicated layouts.
i have to do a sudoku solver with a gui for school. The gui is almost done, i just need to draw a grid for the sudoku board.
I have implemented the sudoku board with the GridLayout (9x9 JTextFields) but i am not allowed to draw the borders directly within this method.
For the grid i have to use an own method:
public void paint(Graphics g) {
super.paintChildren(g);
}
I have drawn a grid already:
import javax.swing.*;
import java.awt.*;
public final class Field extends JPanel {
private JPanel sudokuPanel = new JPanel();
private JPanel sudokuBox = new JPanel();
/**
* 9x9 Sudoku grid.
*/
public Sudoku() {
setPreferredSize( new Dimension(400, 380));
sudokuPanel.setLayout(new GridLayout(3, 3));
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
sudokuBox = new JPanel(new GridLayout(3, 3));
for (int cell = 0; cell < 9; cell++) {
sudokuBox.add(new JTextField());
}
sudokuPanel.add(sudokuBox);
add(sudokuPanel);
setVisible(true);
setBorder(null);
}
}
}
#Override
public void paint(Graphics g) {
super.paintChildren(g);
g.setColor(Color.BLACK);
for ( int x = 0; x < 360 ; x += 40 ) {
for ( int y = 0; y < 360; y += 40 ) {
graphics.drawRect( x, y, 40, 40 );
}
}
}
The Layout of the JTextFields (sudokuBox), is set up in an own class.
Requirements: The sudokuPanel has to stay centred in my JFrame and does not have to scale, when i resize the Window manually on my Desktop (I use an BorderLayout for that and that just works fine).
The Problem is, that my Grid stays in the upper left corner of my Window. It does not move, when i make the Window smaller and bigger. How do i make, that the drawn grid lays over my sudokuPanel, even, when i am resize the Window of the Application?
SudokuGUI - Problem
Thanks for every hint!
To center the grid both vertically and horizontally just create a JPanel with GridBagLayout and add to it a single Component (the grid). This can be seen in this answer.
For example, to make a centered grid of 9x9 JTextFields with black lines, you can:
import java.awt.Color;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class MainWithTextFields {
private static void createAndShowGUI() {
final Border fieldBorder = BorderFactory.createLineBorder(Color.BLACK);
final JPanel grid = new JPanel(new GridLayout(9, 0));
for (int i = 0; i < 81; ++i) {
final JTextField field = new JTextField(2);
field.setHorizontalAlignment(JTextField.CENTER); //Center text horizontally in the text field.
field.setBorder(fieldBorder); //Add the colored border.
grid.add(field);
}
final JPanel centeredGrid = new JPanel(new GridBagLayout());
centeredGrid.add(grid);
final JFrame frame = new JFrame("Sudoku");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(centeredGrid);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(MainWithTextFields::createAndShowGUI);
}
}
Or, to make a centered grid of 9x9 JTable cells, you can:
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
public class MainWithTable {
private static void createAndShowGUI() {
final JPanel grid = new JPanel(new BorderLayout());
grid.add(new JTable(9, 9), BorderLayout.CENTER);
final JPanel centeredGrid = new JPanel(new GridBagLayout());
centeredGrid.add(grid);
final JFrame frame = new JFrame("Sudoku");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(centeredGrid);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(MainWithTable::createAndShowGUI);
}
}
Now resizing the frame, won't resize the contents, but instead make them go to the center of it.
You also don't need to override paintComponent at all in those cases. If it is a requirement to override it in order to paint the borders of the JTextFields, just create the GridLayout setting the horizontal and the vertical gap to be for example 3 pixels, and the middle pixels draw them with black lines.
How I can create a JPanel with a lot of buttons aligned float left and with vertical scrollbars only?
The buttons should be sorted like below.
1 2 3 4
5 6 7 8
9 10 11 12
If you use GridLayout, then you will not be able to add a scrollpane since it will resize automatically to fit all the components inside of it. An easier approach is to use a FlowLayout and setPreferredSize(...) to set the size of your panel. Though it is not advised to set the size of panels, you still need to have the scrollbar put into use somehow. Here is a MCVE:
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Example extends JFrame {
private final int BUTTON_WIDTH = 100;
private final int BUTTON_HEIGHT = 50;
private final int BUTTON_ROWS = 3;
private final int BUTTON_COLUMNS = 4;
private final int OFFSET = 20;// the width of the actual scroll bar in pixels (approximately).
private final int PANEL_WIDTH = BUTTON_WIDTH * BUTTON_COLUMNS + OFFSET;
private final int PANEL_HEIGHT = BUTTON_HEIGHT * BUTTON_ROWS + OFFSET;
private final int SCROLL_HEIGHT = 100;//or whatever you would like...
private final JButton[] buttons = new JButton[BUTTON_ROWS * BUTTON_COLUMNS];
public Example() {
JPanel panel = new JPanel(new FlowLayout());
JScrollPane scroll = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
panel.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
scroll.setPreferredSize(new Dimension(PANEL_WIDTH + OFFSET, SCROLL_HEIGHT));
for (int i = 0; i < buttons.length; i++) {
JButton button = new JButton((i + 1) + "");
buttons[i] = button;
button.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT));
panel.add(button);
}
//if you want the panel to resize when window is stretched.
//setLayout(new FlowLayout(FlowLayout.CENTER));
add(scroll);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
new Example();
}
}
Add the buttons to a (panel with a) grid layout to arrange them in rows and columns. Add that panel to a scroll pane, then add the scroll pane to the line start constraint of a border layout, and they will appear on the left.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class LeftAlignedButtonGrid {
private JComponent ui = null;
LeftAlignedButtonGrid() {
initUI();
}
public void initUI() {
if (ui!=null) return;
/* BorderLayout offers a LINE_START constraint that will put a
single child component on the left hand side of the GUI (in any
locale that uses left-to-right text orientation) */
ui = new JPanel(new BorderLayout(4,4));
JPanel buttonPanel = new JPanel(new GridLayout(0,4,2,2));
for (int ii=1; ii<13; ii++) {
buttonPanel.add(new JButton("" + ii));
}
ui.add(new JScrollPane(buttonPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
BorderLayout.LINE_START);
ui.setBorder(new EmptyBorder(4,4,4,4));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
LeftAlignedButtonGrid o = new LeftAlignedButtonGrid();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
// comment this out to allow the height of the GUI to be reduced,
// thus making the vertical scroll bar to have a purpose!
//f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I have the following problem, when i want to add 2JPanels to my JFrame only one is visible, depending on which I added to the frame last. I overrided the JPanels default paintComponent() method on both of the JPanels. How can i fix this?
Code snippet:
Border:
public class BorderDrawer extends JPanel{
private int _width,_height;
BorderDrawer(int width,int height)
{
setOpaque(false);
_width = width;
_height = height;
}
#Override
protected void paintComponent(Graphics g) {
final int BUTTON_WIDTH = 20,BUTTON_HEIGHT = 20;
int MINES_HORIZONTALLY = _width;
int MINES_VERTICALLY = _height;
super.paintComponent(g);
try{
BufferedImage topLeftCorner = ImageIO.read(this.getClass().getResource("topLeftCorner.png"));
g.drawImage(topLeftCorner, 0, 0, null);
....// drawing other border components
}
}
Clock:
public class GraphicTimer extends JPanel{
Timer _aktTimer = null;
int seconds;
int _width = 0;
GraphicTimer(int width)
{
setSize(52, 31);
setOpaque(false);
_width = width;
int delay = 1000; //milliseconds
_aktTimer = new Timer(delay, taskPerformer);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
try
{
final int BUTTON_WIDTH = 20,BUTTON_HEIGHT = 20;
int MINES_HORIZONTALLY = _width;
int HORIZONTAL_ENDING = 15+BUTTON_WIDTH*MINES_HORIZONTALLY;
BufferedImage clock = ImageIO.read(this.getClass().getResource("clock.png"));
g.drawImage(clock,HORIZONTAL_ENDING-54,22, null);
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
....
}
JFrame:
public class DrawerField extends JFrame implements Serializable{
//...
public DrawerField()
{
super("MineSweeper");
_FIELD = new Field();
constructorInit();
}
public void constructorInit()
{
_buttons = new FieldButton[_height][_width];
_isMouseEventEnabled = true;
fieldPanel = new JPanel();
smilePanel = new JPanel();
//INITS
int fieldSizeWidth = (_width)*20;
int fieldSizeHeight = (_height)*20; // Magic size
fieldPanel.setSize(fieldSizeWidth,fieldSizeHeight); // 20x20
fieldPanel.setLocation(15, 70);
fieldPanel.setLayout(new GridLayout(_width,_height));
int fullWindowWidth = fieldSizeWidth+36;
int fullWindowHeight = fieldSizeHeight+142;
setSize(fullWindowWidth,fullWindowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
restartButton = new RestartButton(this);
smilePanel.setSize(34,34);
smilePanel.add(restartButton);
smilePanel.setLayout(new GridLayout(1,1));
smilePanel.setLocation((int)fullWindowWidth/2-(34/2)-1,20);
///INITIALS
for(int i = 0; i < _height; i++)
{
for(int j = 0; j < _width ; j++)
{
_buttons[i][j] = new FieldButton(_hidden[i][j],true,i,j,this);
fieldPanel.add(_buttons[i][j]);
}
}
add(smilePanel);
add(fieldPanel);
borderDrawer = new BorderDrawer(_width,_height);
_graphicTimer = new GraphicTimer(_width);
_graphicTimer.start();
add(_graphicTimer); // This is the two lines which change the result
add(borderDrawer);
MenuBar menuBar = new MenuBar(this);
setJMenuBar(menuBar);
setVisible(true);
}
//...
}
Easier and compilable example:
public class Main {
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(500, 500);
MyPanel panel1 = new MyPanel(30,30);
frame.add(panel1);
MyPanel panel2 = new MyPanel(70,30);
frame.add(panel2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.repaint();
frame.setVisible(true);
}
public static class MyPanel extends JPanel
{
int _x,_y;
MyPanel(int x, int y)
{
_x = x; _y = y;
}
#Override
public void paint(Graphics g) {
g.drawOval(_x,_y,20,20);
}
}
}
My main goal is to add 2 circles to the JFrame without using any Layout.( As you can see in the example above I already have a lot of things on my JFrame that's why I don'T want to use layouts). The problem is the same in this example, when i add the 2nd circle the first is disappearing.
Its simple. Adding a panel in a frame adds it to the content pane of the frame. This contentpane has a default layout of BorderLayout which means, every time you add a panel, it gets added to the center of the content pane and gets replaces the previous one. This is the reason why you see only the last one. Its always good to use a Jpanel set to the layout of your choice put everything that needs to be shown on the screen in that panel. If you don't want to do that, you may also call getContentPane() from the frame and play with the returned instance of JPanel.
Here is the example code for you:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Frame extends JFrame {
public Frame() {
setTitle("Two panels");
setSize(new Dimension(500,500));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Here goes your code
JPanel p= (JPanel) getContentPane();
p.setLayout(new GridLayout(1,2)); //set your own layout
p.add(new MyPanel(Color.BLUE)); //add panel with blue border
p.add(new MyPanel(Color.GREEN));//add panel with green border
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Frame f= new Frame();
f.setVisible(true);
}
});
}
}
class MyPanel extends JPanel {
public MyPanel(Color color) {
setBorder(BorderFactory.createLineBorder(color));
}
}
Run it and see... You should be able to see something like this:
I am currently trying to make a program using four panels. Three panels are grouped together on the left side of the JFrame using GridLayout and the last one takes up the right side of the screen. Ideally, the left panel will take up the 2/3 of the left side of the JFrame and the last third will be taken up by the fourth panel. I have attempted to do this in many ways and can't figure it out. The current display splits the screen in half and displays yellow, red, blue on top of each other, but only 540 pixels long, while the green is covering up the rest of the left panel.
This has the green rectangle representing the last panel and it is overlapping the yellow red and blue rectangles which should be 720 pixels long. I have accomplished properly displaying the way I want by re-sizing the JFrame until i made it work. By re-sizing till the rectangles line up i managed to make it look like it should. I moved the right side of the JFrame until the two respective panels lined up with no white space or overlap, unfortunately, in order to do this there is a ton of excess white space on the side.
Here is the code i used to make this display. I am a complete novice with swing and might have completely done this wrong. I am not set on using GridLayout, I just want to make this work correctly.
import java.awt.*;
import javax.swing.*;
class testJPanel1 extends JPanel //topLeft
{
int x,y;
//JPanel Tjp1;
public testJPanel1()
{
x=720;
y=250;
setSize(x,y);
setVisible(true);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//g.fillOval(x,y,50,50);
g.setColor(Color.yellow);
g.fillRect(0, 0, x, y);
g.setColor(Color.black);
g.drawString("1",x,y);
System.out.println("Hey 1 works");
}
}
class testJPanel2 extends JPanel //mid left
{
int x,y;
public testJPanel2()
{
x=720;
y=260;
setVisible(true);
setSize(x,y);
}
#Override
public void paintComponent(Graphics g)
{super.paintComponent(g);
//g.fillOval(x,y,50,50);
g.setColor(Color.red);
g.fillRect(0, 0, x, y);
g.setColor(Color.black);
g.drawString("2",x,y);
System.out.println("Hey 2 works");
}
}
class testJPanel3 extends JPanel //bot left
{
int x,y;
int boundsx;
int boundsy;
public testJPanel3()
{
x=720;
y=250;
boundsx=200;
boundsy=200;
setVisible(true);
setSize(x,y);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
//g.fillOval(x,y,50,50);
g.setColor(Color.blue);
g.fillRect(0, 0, x, y);
g.setColor(Color.black);
g.drawString("3",x,y);
System.out.println("Hey 3 works");
// g.drawRect(0,0,boundsx,boundsy);
}
}
class testJPanel4 extends JPanel //BIG one on the right
{
int x,y;
int boundsx;
int boundsy;
public testJPanel4()
{
x=360;
y=760;
boundsx=200;
boundsy=200;
setVisible(true);
setSize(x,y);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.green);
//g.fillOval(x,y,50,50);
g.fillRect(0,0,x,y);
g.setColor(Color.black);
g.drawString("4",x,y);
System.out.println("Hey 4 works");
//g.drawRect(0,0,boundsx,boundsy);
}
}
class Left_Panel extends JPanel //Combines 1 and 2 and 3
{
testJPanel1 tjp1;
testJPanel2 tjp2;
testJPanel3 tjp3;
public Left_Panel()
{
setLayout(new GridLayout(3,1));
tjp1= new testJPanel1();
tjp2= new testJPanel2();
tjp3= new testJPanel3();
add(tjp1);
add(tjp2);
add(tjp3);
}
}
class combo_Panel extends JPanel //combines left and right panels together
{
Left_Panel lp;
testJPanel4 tjp4;
//GOAL IS TO MAKE THIS PANEL lp is 720,760 pixels and tjp4 360,760...still doesnt work
public combo_Panel()
{
setLayout(new GridLayout(1,2)); //HOW TO make this work the way i want it or use something else i want
lp= new Left_Panel();
lp.setSize(720,760);
tjp4= new testJPanel4();
tjp4.setSize(360,760);
add(lp);
add(tjp4);
}
}
class Paint_Window extends JFrame
{
combo_Panel combo;
Paint_Window(String title)
{
super(title);
//setLayout( new BoxLayout(combo,2));
combo = new combo_Panel();
setBounds(new Rectangle(1080,760));
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//setLayout(new FlowLayout());
add(combo);
//add(tjp5);
//tjp1.repaint();
}
}
public class testJPanel
{
public static void main(String args[])
{
Paint_Window window = new Paint_Window("Make your choice");
}
You have a few options, you could try laying out all three panels in a single container using a GridLayout, but that might not be possible, the other choice might be to use a GridBagLayout and adjust the weightx property to suit your requirements, for example...
Note the red "border" around the blue and green panels, this is showing the parent container for these two components
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.PopupMenu;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class TestLayout {
public static void main(String[] args) {
new TestLayout();
}
public TestLayout() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel leftSide;
private JPanel rightSide;
public TestPane() {
setLayout(new GridBagLayout());
leftSide = new JPanel(new GridLayout(1, 2));
leftSide.setBackground(Color.RED);
leftSide.setBorder(new EmptyBorder(1, 1, 1, 1));
leftSide.add(createPanel(Color.BLUE));
leftSide.add(createPanel(Color.GREEN));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0.67;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(leftSide, gbc);
gbc.gridx = 1;
gbc.weightx = 0.33;
rightSide = createPanel(Color.MAGENTA);
add(rightSide, gbc);
}
protected JPanel createPanel(Color color) {
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 100);
}
};
panel.setBackground(color);
return panel;
}
}
}