Draw Clickable Lines Between Buttons Swing - java

I have a gridbag of objects laid out in a specific pattern. Clicking on an object will trigger certain actions such as displaying information about that object or navigating to another screen.
Each object also has connections to other objects. These connections need to be clickable as clicking on them will display more information about that connection.
The following example shows what I am aiming for. Each blue circle in the grid is a clickable object and each red line between them is a clickable connection.
I have tried storing both the objects and connections as Jbuttons in a single grid bag layout and have had some success in that the connections are clickable.
However the connections span multiple cells in a single direction and the Jbuttons expand to fill the layout cells. This causes the ends of the connections to pass through the center of the objects and finish at the border of the grid bag cell.
Here is a drawing of what I mean:
Is there some simpler way to make this kind of interface / has anyone done this before?

Okay, very basic, but the idea is to paint the lines manually between the objects.
In this case, I created a simple Connection object which maintained the relationships between buttons. I then simply used the paintComponent method of the JPanel to paint the actual lines.
I'll leave you to devise a better path generation method ;)
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JButton[] buttons = new JButton[]{
new JButton("1"),
new JButton("2"),
new JButton("3"),
new JButton("4"),};
private List<Connection> connections;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(10, 10, 10, 10);
add(buttons[0], gbc);
gbc.gridx = 2;
gbc.gridy = 0;
add(buttons[1], gbc);
gbc.gridx = 1;
gbc.gridy = 1;
add(buttons[2], gbc);
gbc.gridx = 2;
gbc.gridy = 2;
add(buttons[3], gbc);
connections = new ArrayList<Connection>(25);
connections.add(new Connection(buttons[0], buttons[1]));
connections.add(new Connection(buttons[0], buttons[2]));
connections.add(new Connection(buttons[0], buttons[3]));
connections.add(new Connection(buttons[1], buttons[3]));
connections.add(new Connection(buttons[2], buttons[3]));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
for (Connection connection : connections) {
JButton source = connection.getSource();
JButton dest = connection.getDestination();
if (source.getX() == dest.getX()) {
// Same column...
g2d.drawLine(source.getX() + source.getWidth() / 2, source.getY(),
dest.getX() + source.getWidth() / 2, dest.getY());
} else if (source.getY() == dest.getY()) {
// Same row...
g2d.drawLine(source.getX(), source.getY() + source.getHeight() / 2,
dest.getX(), dest.getY() + dest.getHeight() / 2);
} else {
Path2D path = new Path2D.Double();
path.moveTo(horizontalCenter(source), verticalCenter(source));
path.curveTo(horizontalCenter(source), verticalCenter(dest),
horizontalCenter(source), verticalCenter(dest),
horizontalCenter(dest), verticalCenter(dest));
g2d.draw(path);
}
}
g2d.dispose();
}
protected double horizontalCenter(JComponent bounds) {
return bounds.getX() + bounds.getWidth() / 2d;
}
protected double verticalCenter(JComponent bounds) {
return bounds.getY() + bounds.getHeight() / 2d;
}
protected boolean hasIntersection(Line2D line, JComponent... exclude) {
List<JComponent> toExclude = Arrays.asList(exclude);
boolean intersects = false;
for (Component comp : getComponents()) {
if (!toExclude.contains(comp)) {
if (line.intersects(comp.getBounds())) {
System.out.println(line.getP1() + "-" + line.getP2() + " intersets with " + ((JButton)comp).getText() + "; " + comp.getBounds());
intersects = true;
break;
}
}
}
return intersects;
}
protected Line2D lineDownTo(JComponent from, JComponent to) {
return new Line2D.Double(horizontalCenter(from), from.getY(), horizontalCenter(from), verticalCenter(to));
}
protected Line2D lineAcrossTo(JComponent from, JComponent to) {
return new Line2D.Double(from.getX(), verticalCenter(from), horizontalCenter(to), verticalCenter(from));
}
protected Point2D centerOf(Rectangle bounds) {
return new Point2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
}
protected boolean canGoDownTo(Point2D startPoint, Point2D endPoint, JComponent to, JComponent from) {
Point2D targetPoint = new Point2D.Double(startPoint.getX(), endPoint.getY());
return !hasIntersection(new Line2D.Double(startPoint, targetPoint), to, from);
}
public class Connection {
private final JButton source;
private final JButton destination;
public Connection(JButton source, JButton destination) {
this.source = source;
this.destination = destination;
}
public JButton getSource() {
return source;
}
public JButton getDestination() {
return destination;
}
}
}
}

Related

How to make all images in Grid Overlay flush?

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.

How to add zooming and panning functions to a JPanel?

So I have a program in Java which uses a grid bag layout to create cells(like graph paper), and if a mouse is over one of the cell said cell will change its color. I would like to implement a zoom function, using mouse scroll wheel. Also I would like to add a function that, when your zoomed in, you can pan around by clicking and dragging the zoomed in canvas. To specify panning around, think of clicking and dragging to move around a zoomed in map, like google maps.
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.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
public class Testing {
public int RowI = 10;
public int ColI = 10;
public static void main(String[] args) {
new Testing();
}
public Testing() {
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 {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
for (int row = 0; row < RowI; row++) {
for (int col = 0; col < ColI; col++) {
gbc.gridx = col;
gbc.gridy = row;
CellPane cellPane = new CellPane();
Border border = null;
if (row < 4) {
if (col < 4) {
border = new MatteBorder(1, 1, 0, 0, Color.GRAY);
} else {
border = new MatteBorder(1, 1, 0, 1, Color.GRAY);
}
} else {
if (col < 4) {
border = new MatteBorder(1, 1, 1, 0, Color.GRAY);
} else {
border = new MatteBorder(1, 1, 1, 1, Color.GRAY);
}
}
cellPane.setBorder(border);
add(cellPane, gbc);
}
}
}
}
public class CellPane extends JPanel { //The CellPane class changes the color of an individual cell based on whether or no the mouse I on a cell.
private Color defaultBackground; //This is a private color that is only used by the mouseListener.
public CellPane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) { //If mouse is on cell turn cell the given color(in this case green).
defaultBackground = getBackground();
setBackground(Color.GREEN);
}
#Override
public void mouseExited(MouseEvent e) { //If mouse is not on cell revert to default background.
setBackground(defaultBackground);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50); //Cell size on x and y axis.
}
}
}
Maybe this question will answer what you're looking for.

Multiple JPanels completely on top of each other

I am creating an program in which I can draw a map and add different roads etc to it. I have planned to add the map terrain on one jpanel, and the roads etc on another, on top of each other. But I can't get them to work. I don't know how to add multiple jpanels completely on top of each other and making them all able to draw.
The basic approach to this problem would be to use a JLayeredPane and something like GridBagLayout.
The JLayeredPane will give you some better control over the z-deepthness of the various layers and the GridBagLayout will allow you to lay components over each other as you need.
You could also take a look at OverlayoutLayout, but never having used it, I can't comment.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MapLayers {
public static void main(String[] args) {
new MapLayers();
}
public MapLayers() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MapPane mapPane = new MapPane();
try {
mapPane.addLayer(new ImageLayer(ImageIO.read(new File("Ponie.png")), 360, 10));
mapPane.addLayer(new ImageLayer(ImageIO.read(new File("Layer01.png")), 0, 0));
} catch (IOException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(mapPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ImageLayer extends JComponent {
private Image bg;
private int xOffset;
private int yOffset;
public ImageLayer(Image image, int x, int y) {
bg = image;
xOffset = x;
yOffset = y;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(bg, xOffset, yOffset, this);
g2d.dispose();
}
}
}
public class MapPane extends JLayeredPane {
private BufferedImage bg;
public MapPane() {
try {
bg = ImageIO.read(new File("PirateMap.jpg"));
} catch (IOException exp) {
exp.printStackTrace();
}
setLayout(new GridBagLayout());
}
public void addLayer(JComponent layer) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(layer, gbc);
}
#Override
public Dimension getPreferredSize() {
return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - bg.getWidth()) / 2;
int y = (getHeight()- bg.getHeight()) / 2;
g2d.drawImage(bg, x, y, this);
g2d.dispose();
}
}
}
}
Take a look at How to use LayeredPanes for some more details
Are you intending to make the JPanels transparent? You can call setOpaque(false), which will prevent the panel from flooding a background rectangle. There is some useful information on stacking panes in the Swing tutorial.

making a button - java

Ok, i know that this question has been asked before, but i just can't seem to get my application to work with the help from other peoples posts.
I am just trying to make a screen with a picture i made and then 3 buttons (just like a game menu). I have done the image and i am now trying to make the 3 buttons like this.
my code at the moment is this:
public class Test {
static int WIDTH = 700;
static int HEIGHT = 600;
public static void main(String[] args) {
JLabel label = new JLabel();
JFrame f = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("hello again");
button.addActionListener ((ActionListener) new Action1());
label.setIcon(new ImageIcon("C:\\Users\\barney\\workspace\\game\\res\\background.jpg"));
f.add(panel);
f.add(label);
f.setVisible(true);
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setSize(WIDTH, HEIGHT);
f.setResizable(false);
f.setLocationRelativeTo(null);
f.setTitle("2d platformer v1.1");
f.setVisible(true);
}
}
So how would i add the buttons like in the picture
also could you tell me if i have set my code out wrong as i am new to java.
thanks.
I'm going to be smacked around for this...
label.setLayout(new GridBagLayout());
GridBagConstraints gbc = GridBagConstraints();
gbc.insets = new Insets(4, 0, 4, 0);
gbc.gridwidth = GridBagConstraints.REMAINDER;
label.add(button, gbc);
// add the other buttons
Updated
Personally, I'd create a custom JPanel, that would paint the background and then add buttons to it, using the GridBagLayout from above
Updated
The problem with this question is "it depends"
It depends on how you want to implement the actual buttons. Because you've left "blank" spaces, I assume you intend to render some buttons onto the image. I would personally, render a full image and then add some normal buttons onto, but it's your project not mine.
If you wanted to use something like JButton (or any other Component), you would need a custom layout manager to help you align the components with gaps you have left. Hence the reason I would simply do a nice background and use something like GridBagLayout instead...
I apologize, I may have gone slightly over board, but I was having fun.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ButtonImage {
public static void main(String[] args) {
new ButtonImage();
}
public ButtonImage() {
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 MenuPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class MenuPane extends JPanel {
public static final Rectangle NEW_GAME_BOUNDS = new Rectangle(221, 157, 262, 85);
public static final Rectangle LOAD_GAME_BOUNDS = new Rectangle(221, 276, 262, 85);
public static final Rectangle EXIT_GAME_BOUNDS = new Rectangle(221, 396, 262, 85);
private BufferedImage img;
private Rectangle selectedBounds;
public MenuPane() {
try {
img = ImageIO.read(getClass().getResource("/vtr1a.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter mouseHandler = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
if (getNewGameBounds().contains(e.getPoint())) {
System.out.println("in new");
selectedBounds = getNewGameBounds();
} else if (getLoadGameBounds().contains(e.getPoint())) {
System.out.println("in load");
selectedBounds = getLoadGameBounds();
} else if (getExitGameBounds().contains(e.getPoint())) {
System.out.println("in exit");
selectedBounds = getExitGameBounds();
} else {
selectedBounds = null;
}
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
if (getNewGameBounds().contains(e.getPoint())) {
System.out.println("New Game");
} else if (getLoadGameBounds().contains(e.getPoint())) {
System.out.println("Load Game");
} else if (getExitGameBounds().contains(e.getPoint())) {
System.out.println("Exit Game");
}
}
};
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
}
#Override
public Dimension getPreferredSize() {
return img == null ? super.getPreferredSize() : new Dimension(img.getWidth(), img.getHeight());
}
protected Point getImageOffset() {
Point p = new Point();
if (img != null) {
p.x = (getWidth() - img.getWidth()) / 2;
p.y = (getHeight() - img.getHeight()) / 2;
}
return p;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
Point p = getImageOffset();
g2d.drawImage(img, p.x, p.y, this);
drawText(g2d, "New Game", getNewGameBounds());
drawText(g2d, "Load Game", getLoadGameBounds());
drawText(g2d, "Exit Game", getExitGameBounds());
g2d.dispose();
}
}
protected void drawText(Graphics2D g2d, String text, Rectangle bounds) {
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.GRAY);
if (selectedBounds != null) {
if (bounds.contains(selectedBounds)) {
RadialGradientPaint rpg = new RadialGradientPaint(
new Point(bounds.x + (bounds.width / 2), bounds.y + (bounds.height / 2)),
Math.min(bounds.width, bounds.height),
new float[]{0f, 1f},
new Color[]{new Color(252, 180, 42), new Color(97, 205, 181)}
);
g2d.setPaint(rpg);
RoundRectangle2D fill = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, 22, 22);
g2d.fill(fill);
g2d.setColor(Color.WHITE);
}
}
g2d.drawString(
text,
bounds.x + ((bounds.width - fm.stringWidth(text)) / 2),
bounds.y + ((bounds.height - fm.getHeight()) / 2) + fm.getAscent());
}
protected Rectangle getNewGameBounds() {
return getButtonBounds(NEW_GAME_BOUNDS);
}
protected Rectangle getLoadGameBounds() {
return getButtonBounds(LOAD_GAME_BOUNDS);
}
protected Rectangle getExitGameBounds() {
return getButtonBounds(EXIT_GAME_BOUNDS);
}
protected Rectangle getButtonBounds(Rectangle masterBounds) {
Rectangle bounds = new Rectangle(masterBounds);
Point p = getImageOffset();
bounds.translate(p.x, p.y);
return bounds;
}
}
}
You could, just as easily, use separate images for each button and instead of rendering the text/graphics, render a image at the required position.
Check out Custom Painting and 2D Graphics for some more info.
I guess you didn't add Button to JFrame
f.add(button)
As you want different buttons than Swing button, to make the things simpler, you can draw your buttons in a JPanel (using the paintComponent method and Java 2D). Each JPanel will be a button. Them, you will use some mouse events to detect clicks, etc. Another approach is do the same thing that you are doing with your background (several JLabels with images) and register mouse events in each label. You can also create your own Look And Feel for your buttons, but this is not so simple. Of course, you will need to change your layout manager to fit your buttons.

How to auto-adjust font size of multiple JLabel based on container size in a smooth way?

I need to resize the font of multiple JLabel based on the scaling factor used to resize the container. To do this, I am setting the font of each JLabel to null so that they take the font of the container. It works, but it also produces strange results.
To be specific, the text seems to "lag" behind the container and sometimes it gets even truncated. I would like to avoid this behavior. Any idea how?
Example code simulating the behavior:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.AffineTransform;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TextResize implements Runnable {
public static void main(String[] args) {
TextResize example = new TextResize();
SwingUtilities.invokeLater(example);
}
public void run() {
JFrame frame = new JFrame("JLabel Text Resize");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 400));
Container container = frame.getContentPane();
container.setLayout(new BorderLayout());
final JPanel labelContainer = new JPanel(new GridBagLayout());
labelContainer.setBorder(BorderFactory.createLineBorder(Color.black));
//initial font
final Font textFont = new Font("Lucida Console", Font.PLAIN, 10).deriveFont(AffineTransform.getScaleInstance(1, 1));
labelContainer.setFont(textFont);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(0, 10, 0, 10);
c.weightx = 1;
for (int i = 0; i < 5; i++) {
JLabel f = new JLabel("Text here with possibly looooooooong words");
f.setBorder(BorderFactory.createLineBorder(Color.green));
f.setFont(null);//take the font from parent
c.gridy = i;
labelContainer.add(f, c);
}
JSlider slider = new JSlider(0,50000,10000);
slider.addChangeListener(new ChangeListener() {
double containerWidth = labelContainer.getPreferredSize().getWidth();
double containerHeight = labelContainer.getPreferredSize().getHeight();
#Override
public void stateChanged(ChangeEvent ev) {
JSlider source = (JSlider) ev.getSource();
double scale = (double) (source.getValue() / 10000d);
//scaling the container
labelContainer.setSize((int) (containerWidth * scale), (int) (containerHeight * scale));
//adjusting the font: why does it 'lag' ? why the truncation at times?
Font newFont = textFont.deriveFont(AffineTransform.getScaleInstance(scale, scale));
labelContainer.setFont(newFont);
//print (font.getSize() does not change?)
System.out.println(scale + " " + newFont.getTransform() + newFont.getSize2D());
}
});
container.add(slider, BorderLayout.NORTH);
JPanel test = new JPanel();
test.setLayout(null);
labelContainer.setBounds(5, 5, labelContainer.getPreferredSize().width, labelContainer.getPreferredSize().height);
test.add(labelContainer);
container.add(test, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Picture:
http://i.stack.imgur.com/tZLOO.png
Thanks,
-s
You can use any of the following methods:
by #trashgod
by #StanislavL
by #coobird
I sort of solved the problem adding:
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
super.paintComponent(g2d);
}
Thanks anyway.
If performance speed is an issue, then you might find the following information about the 3 methods pointed to by MKorbel above useful.
Coobird's code has some limitations if used on a multi-call basis (eg in a sizeChanged Listener or a LayoutManager)
Trashgod's method is between 2 and 4 times slower than Stanislav's (but it also is designed to fill the area BOTH directions as the OP asked in that question, so not unexpected.)
The code below improves on Stanislav's rectangle method (by starting from the current font size each time rather than reverting back to MIN_FONT_SIZE each time) and thus runs between 20 and 50 times faster than that code, especially when the window/font is large.
It also addresses a limitation in that code which only effectively works for labels located at 0,0 (as in the sample given there). The code below works for multiple labels on a panel and at any location.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
// Improved version of http://java-sl.com/tip_adapt_label_font_size.html
public class FontResizingLabel extends JLabel {
public static final int MIN_FONT_SIZE=3;
public static final int MAX_FONT_SIZE=240;
Graphics g;
int currFontSize = 0;
public FontResizingLabel(String text) {
super(text);
currFontSize = this.getFont().getSize();
init();
}
protected void init() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
adaptLabelFont(FontResizingLabel.this);
}
});
}
protected void adaptLabelFont(JLabel l) {
if (g==null) {
return;
}
currFontSize = this.getFont().getSize();
Rectangle r = l.getBounds();
r.x = 0;
r.y = 0;
int fontSize = Math.max(MIN_FONT_SIZE, currFontSize);
Font f = l.getFont();
Rectangle r1 = new Rectangle(getTextSize(l, l.getFont()));
while (!r.contains(r1)) {
fontSize --;
if (fontSize <= MIN_FONT_SIZE)
break;
r1 = new Rectangle(getTextSize(l, f.deriveFont(f.getStyle(), fontSize)));
}
Rectangle r2 = new Rectangle();
while (fontSize < MAX_FONT_SIZE) {
r2.setSize(getTextSize(l, f.deriveFont(f.getStyle(),fontSize+1)));
if (!r.contains(r2)) {
break;
}
fontSize++;
}
setFont(f.deriveFont(f.getStyle(),fontSize));
repaint();
}
private Dimension getTextSize(JLabel l, Font f) {
Dimension size = new Dimension();
//g.setFont(f); // superfluous.
FontMetrics fm = g.getFontMetrics(f);
size.width = fm.stringWidth(l.getText());
size.height = fm.getHeight();
return size;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.g=g;
}
public static void main(String[] args) throws Exception {
FontResizingLabel label=new FontResizingLabel("Some text");
JFrame frame=new JFrame("Resize label font");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(label);
frame.setSize(300,300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

Categories

Resources