So I'm using Swing and trying to use a JTabbedPane to encompass several JPanels with JScrollPanes to scroll through a list of JPanels. The issue that arises is whenever I change tabs, among other things, the custom border function I have in the JPanels inside the scroll pane results in the components painting over the top of the JTabbedPane header and I have no idea of how to fix this problem: AbstractBorder paints over JTabbedPane header. I have provided code that mimics what I'm trying to do below. To replicate the issue, scroll down some on one tab until one of the outlined panels is halfway visible at the top. Then, switch to a different tab and then back. Then, scroll some more and the issue will present itself at the top of the JTabbedPane
public class Example extends JFrame {
private final JTabbedPane tabbedPane;
public Example() {
setTitle("MIN Example");
setSize(600, 700);
setLayout(new BorderLayout());
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.BLACK);
setExtendedState(JFrame.MAXIMIZED_BOTH);
tabbedPane = new JTabbedPane();
tabbedPane.setBorder(BorderFactory.createEmptyBorder());
tabbedPane.setFocusable(false);
add(tabbedPane, BorderLayout.CENTER);
createTabs();
setVisible(true);
}
private void createTabs() {
// Get the list of guilds
List<String> tabs = new ArrayList<>(Arrays.asList("Tab 1", "Tab 2", "Tab 3"));
// Iterate through the tabs and create the tab
for (String tab : tabs) {
// Initialize the list JPanel and formatting
JPanel panelList = new JPanel();
panelList.setLayout(new GridBagLayout());
// House the list panel inside a JScrollPane
JScrollPane listScroll = new JScrollPane(panelList);
listScroll.setBorder(BorderFactory.createEmptyBorder());
// Create tab and formatting
tabbedPane.addTab(tab, listScroll);
populateList(panelList);
}
}
private void populateList(JPanel tab) {
// Create GBC for formatting
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.weighty = 1;
// Remove all components from the JPanel
tab.removeAll();
// Add filler JPanel
JPanel filler = new JPanel();
filler.setOpaque(false);
tab.add(filler, gbc);
// Update GBC constraints
gbc.insets = new Insets(10, 10, 0, 10);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = GridBagConstraints.RELATIVE;
// Go through the tabs and add JPanels
for (int i = 10; i >= 0; i--) {
JPanel tempPanel = new JPanel();
tempPanel.setBorder(new RoundedBorder(Color.BLACK, 6, 16));
tab.add(tempPanel, gbc, 0);
tempPanel.setPreferredSize(new Dimension(0, 100));
}
// Refresh the console to display updated lists
validate();
repaint();
}
}
public class RoundedBorder extends AbstractBorder {
private final Color color;
private final int thickness;
private final int radii;
private final Insets insets;
private final BasicStroke stroke;
private final int strokePad;
RenderingHints hints;
/**
* Creates the rounded border
*
* #param color The color of the border outline
* #param thickness The thickness of the border outline
* #param radii The radius of the rounded border
*/
public RoundedBorder(Color color, int thickness, int radii) {
this.thickness = thickness;
this.radii = radii;
this.color = color;
stroke = new BasicStroke(thickness);
strokePad = thickness / 2;
hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int pad = radii + strokePad;
int bottomPad = pad + strokePad;
insets = new Insets(pad, pad, bottomPad, pad);
}
#Override
public Insets getBorderInsets(Component c) {
return insets;
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
return getBorderInsets(c);
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2 = (Graphics2D) g;
int bottomLineY = height - thickness;
RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(strokePad, strokePad, width - thickness, bottomLineY, radii, radii);
Area area = new Area(bubble);
g2.setRenderingHints(hints);
// Paint the background color of the parent
Component parent = c.getParent();
if (parent != null) {
Color background = parent.getBackground();
Rectangle rect = new Rectangle(0,0,width, height);
Area borderRegion = new Area(rect);
borderRegion.subtract(area);
g2.setClip(borderRegion);
g2.setColor(background);
g2.fillRect(0, 0, width, height);
g2.setClip(null);
}
g2.setColor(color);
g2.setStroke(stroke);
g2.draw(area);
}
}
The only things I have tried to do to fix this issue was changing the order of adding the JTabbedPane (Add before the tabs were populated with panels) to no avail. Help would be greatly appreciated, thank you.
In the future, an MRE should include:
the import statements
the main() method
In the original code the "clip" of the Graphics is being set to null so the entire Rectangle of the panel appears to be painted.
I modified the painting of the parent to use a separate Graphics object so the original "clip" area is not affected:
// Paint the background color of the parent
Component parent = c.getParent();
if (parent != null) {
Graphics2D g2d = (Graphics2D)g2.create();
Color background = parent.getBackground();
//Rectangle rect = new Rectangle(0, 0, width, height);
Rectangle rect = g2d.getClip().getBounds();
Area borderRegion = new Area(rect);
borderRegion.subtract(area);
g2d.setClip(borderRegion);
g2d.setColor(background);
g2d.fillRect(0, 0, width, height);
g2d.dispose();
}
Related
Similar to JButton showing up in the wrong spot after repaint is called, but the responses to that question only addressed the fact that he wasn't using a layout manager, while I am using a layout manager (poorly), so it didn't really help, unfortunately.
Details
Intent of the Program:
To show a preview of a 1920x1080 grid (scaled down to 640x480 to save space), stretching and shrinking as you change the height of each square, width of each square, and the number of columns it'll have. (You specify a number of total squares to be in the grid first, so the number of rows is inferred by the program.)
Structure:
One top-level JFrame.
Contains two JPanels: the Grid, and the sidebar, using a BorderLayout to snap them to the east and west sides.
Sidebar is one JPanel containing all of the JComponents in a Y-Axis aligned BoxLayout.
Grid extends JComponent, and uses Graphics.drawLine() to draw the grid.
Each component in the sidebar calls Grid.repaint() when changed to update the grid.
Current UI, with the two main JPanels outlined in red.
The Problem
Whenever I change any of the components and thus call Grid.repaint():
The grid doesn't clear, resulting in multiple lines appearing;
All of the sidebar components get painted at the top-left corner, while still showing/functioning on the sidebar;
The grid resizes itself to be wider than normal for some reason.
Current UI, but borked.
What I've Tried
Changing the repaint() region to be a rectangle that only covers the grid,
Checking the documentation for anything about this,
Google,
Asking you guys.
The Code
Reprex: (Simplified to "Press the button to reproduce", while still keeping the essence of the potential problem areas.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BuildGridGUI2
{
static final int WIDTH_MIN = 100;
static final int WIDTH_MAX = 2000;
static final int WIDTH_INIT = 520;
static final int HEIGHT_MIN = 100;
static final int HEIGHT_MAX = 2000;
static final int HEIGHT_INIT = 300;
int widthOfFrames = 255;
int heightOfFrames = 255;
int numCols = 3;
int numFrames = 1;
private final JComponent makeUI()
{
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
Grid g = new Grid();
JComponent j = makeSideMenu(g);
g.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.red),
g.getBorder()));
j.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(Color.red),
j.getBorder()));
p.add(j, BorderLayout.EAST);
p.add(g, BorderLayout.WEST);
return p;
}
private final JComponent makeSideMenu(Grid grid)
{
JPanel p = new JPanel();
JLabel numColsSelectorLabel = new JLabel("Frames Per Column");
JSpinner numColsSelectorField = new JSpinner();
JLabel widthLabel = new JLabel("Width per Frame (Pixels)");
JSpinner widthSpinner = new JSpinner();
JSlider widthSlider = new JSlider(WIDTH_MIN, WIDTH_MAX, WIDTH_INIT);
JLabel heightLabel = new JLabel("Height per Frame (Pixels)");
JSpinner heightSpinner = new JSpinner();
JSlider heightSlider = new JSlider(BuildGridGUI2.HEIGHT_MIN, BuildGridGUI2.HEIGHT_MAX, BuildGridGUI2.HEIGHT_INIT);
JButton confirmButton = new JButton("Confirm");
numColsSelectorField.setEditor(new JSpinner.NumberEditor(numColsSelectorField));
numColsSelectorField.setMaximumSize(numColsSelectorField.getPreferredSize());
widthSlider.setMajorTickSpacing(300);
widthSlider.setMinorTickSpacing(20);
widthSlider.setPaintTicks(true);
widthSlider.setPaintLabels(true);
widthSpinner.setEditor(new JSpinner.NumberEditor(widthSpinner));
widthSpinner.setPreferredSize(new Dimension(70, 30));
widthSpinner.setMaximumSize(widthSpinner.getPreferredSize());
heightSlider.setMajorTickSpacing(300);
heightSlider.setMinorTickSpacing(20);
heightSlider.setPaintTicks(true);
heightSlider.setPaintLabels(true);
heightSpinner.setEditor(new JSpinner.NumberEditor(heightSpinner));
heightSpinner.setPreferredSize(new Dimension(70, 30));
heightSpinner.setMaximumSize(heightSpinner.getPreferredSize());
confirmButton.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e)
{
widthOfFrames = 200;
grid.refresh();
}
});
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.setPreferredSize(new Dimension(300, 480));
p.setSize(new Dimension(300, 480));
numColsSelectorLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
numColsSelectorField.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
confirmButton.setAlignmentX(Component.CENTER_ALIGNMENT);
widthLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
heightLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
p.add(Box.createRigidArea(new Dimension(300, 30)));
p.add(numColsSelectorLabel);
p.add(Box.createRigidArea(new Dimension(300, 5)));
p.add(numColsSelectorField);
p.add(Box.createRigidArea(new Dimension(300, 25)));
p.add(widthLabel);
p.add(Box.createRigidArea(new Dimension(300, 3)));
p.add(widthSpinner);
p.add(widthSlider);
p.add(Box.createRigidArea(new Dimension(300, 25)));
p.add(heightLabel);
p.add(Box.createRigidArea(new Dimension(300, 3)));
p.add(heightSpinner);
p.add(heightSlider);
p.add(Box.createRigidArea(new Dimension(300, 45)));
p.add(confirmButton);
return p;
}
private static void createAndShowGUI() {
BuildGridGUI2 b = new BuildGridGUI2();
JFrame mainFrame = new JFrame("Grid Builder");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(940, 480);
mainFrame.getContentPane().add(b.makeUI());
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
class Grid extends JComponent
{
static final int CANVAS_WIDTH = 640;
static final int CANVAS_HEIGHT = 480;
private final double conversionRatio = 4.0/9.0; // 1080p scaled down to 480p preview
private int squareHeight, squareWidth, numColumns, numRows;
public Grid()
{
setOpaque(true);
setSize(CANVAS_WIDTH, CANVAS_HEIGHT); // trying to get the size to work properly.
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
updateVars();
int k;
for (k = 0; k < numColumns; k++)
{
// #param: (x1, y1), (x2, y2)
g.drawLine(k * squareWidth, 0, k * squareWidth, CANVAS_HEIGHT);
}
for (k = 0; k < numRows; k++)
{
g.drawLine(0, k * squareHeight, CANVAS_WIDTH, k * squareHeight);
}
}
public void refresh()
{
// Attempting to set the repaint area to only the grid region
// because CTRL+F is free and parameters are not
repaint(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
public void updateVars()
{
this.squareWidth = (int)(
(double)BuildGridGUI2.this.widthOfFrames
*
conversionRatio);
this.squareHeight = (int)(
(double)BuildGridGUI2.this.heightOfFrames
*
conversionRatio);
this.numColumns = BuildGridGUI2.this.numCols;
this.numRows = (int)(
(
(double)BuildGridGUI2.this.numFrames
/
numColumns
)
+ 0.5);
}
}
}
Actual Source Code (Not strictly relevant, but if you're in the mood for code review then I'd love to learn better coding conventions.)
Thank you!
All of the sidebar components get painted at the top-left corner, while still showing/functioning on the sidebar;
JComponent is an abstract class that all Swing components extend from. It has no default painting logic.
Therefore invoking super.paintComponent(...) does not really do anything.
In particular it does not clear the background of the component before doing custom painting. This will result in the painting artifacts that you see.
Any time you extend JComponent your logic should be something something like:
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// clear background
g.setColor( getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
// do custom painting
g.setColor( getForeground() );
...
}
Note: this is the reason the many people override JPanel for simple custom painting as mentioned by Abra. The paintComponent(...) method of the JPanel will clear the background by default.
I made a few changes to the code you posted.
I changed class Grid such that it extends JPanel and not JComponent, since custom painting is usually done on a JPanel.
I added a instance member variable grid with type Grid, to class BuildGridGUI2 rather than creating one and sending it as a parameter to method makeSideMenu.
Here is your code with my modifications (and my preferred coding style). It simply solves your reported problem and nothing more.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BuildGridGUI2 {
static final int WIDTH_MIN = 100;
static final int WIDTH_MAX = 2000;
static final int WIDTH_INIT = 520;
static final int HEIGHT_MIN = 100;
static final int HEIGHT_MAX = 2000;
static final int HEIGHT_INIT = 300;
int widthOfFrames = 255;
int heightOfFrames = 255;
int numCols = 3;
int numFrames = 1;
Grid grid;
private final JComponent makeUI() {
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
grid = new Grid();
JComponent j = makeSideMenu();
grid.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.red),
grid.getBorder()));
j.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.red),
j.getBorder()));
p.add(j, BorderLayout.EAST);
p.add(grid, BorderLayout.WEST);
return p;
}
private final JComponent makeSideMenu() {
JPanel p = new JPanel();
JLabel numColsSelectorLabel = new JLabel("Frames Per Column");
JSpinner numColsSelectorField = new JSpinner();
JLabel widthLabel = new JLabel("Width per Frame (Pixels)");
JSpinner widthSpinner = new JSpinner();
JSlider widthSlider = new JSlider(WIDTH_MIN, WIDTH_MAX, WIDTH_INIT);
JLabel heightLabel = new JLabel("Height per Frame (Pixels)");
JSpinner heightSpinner = new JSpinner();
JSlider heightSlider = new JSlider(BuildGridGUI2.HEIGHT_MIN,
BuildGridGUI2.HEIGHT_MAX,
BuildGridGUI2.HEIGHT_INIT);
JButton confirmButton = new JButton("Confirm");
numColsSelectorField.setEditor(new JSpinner.NumberEditor(numColsSelectorField));
numColsSelectorField.setMaximumSize(numColsSelectorField.getPreferredSize());
widthSlider.setMajorTickSpacing(300);
widthSlider.setMinorTickSpacing(20);
widthSlider.setPaintTicks(true);
widthSlider.setPaintLabels(true);
widthSpinner.setEditor(new JSpinner.NumberEditor(widthSpinner));
widthSpinner.setPreferredSize(new Dimension(70, 30));
widthSpinner.setMaximumSize(widthSpinner.getPreferredSize());
heightSlider.setMajorTickSpacing(300);
heightSlider.setMinorTickSpacing(20);
heightSlider.setPaintTicks(true);
heightSlider.setPaintLabels(true);
heightSpinner.setEditor(new JSpinner.NumberEditor(heightSpinner));
heightSpinner.setPreferredSize(new Dimension(70, 30));
heightSpinner.setMaximumSize(heightSpinner.getPreferredSize());
confirmButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
widthOfFrames = 200;
grid.refresh();
}
});
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.setPreferredSize(new Dimension(300, 480));
p.setSize(new Dimension(300, 480));
numColsSelectorLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
numColsSelectorField.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSlider.setAlignmentX(Component.CENTER_ALIGNMENT);
confirmButton.setAlignmentX(Component.CENTER_ALIGNMENT);
widthLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
heightLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
widthSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
heightSpinner.setAlignmentX(Component.CENTER_ALIGNMENT);
p.add(Box.createRigidArea(new Dimension(300, 30)));
p.add(numColsSelectorLabel);
p.add(Box.createRigidArea(new Dimension(300, 5)));
p.add(numColsSelectorField);
p.add(Box.createRigidArea(new Dimension(300, 25)));
p.add(widthLabel);
p.add(Box.createRigidArea(new Dimension(300, 3)));
p.add(widthSpinner);
p.add(widthSlider);
p.add(Box.createRigidArea(new Dimension(300, 25)));
p.add(heightLabel);
p.add(Box.createRigidArea(new Dimension(300, 3)));
p.add(heightSpinner);
p.add(heightSlider);
p.add(Box.createRigidArea(new Dimension(300, 45)));
p.add(confirmButton);
return p;
}
private static void createAndShowGUI() {
BuildGridGUI2 b = new BuildGridGUI2();
JFrame mainFrame = new JFrame("Grid Builder");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(940, 480);
mainFrame.getContentPane().add(b.makeUI());
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
class Grid extends JPanel {
static final int CANVAS_WIDTH = 640;
static final int CANVAS_HEIGHT = 480;
private final double conversionRatio = 4.0 / 9.0; // 1080p scaled down to 480p preview
private int squareHeight, squareWidth, numColumns, numRows;
public Grid() {
setOpaque(true);
setSize(CANVAS_WIDTH, CANVAS_HEIGHT); // trying to get the size to work properly.
setPreferredSize(new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(CANVAS_WIDTH, CANVAS_HEIGHT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
updateVars();
int k;
for (k = 0; k < numColumns; k++) {
// #param: (x1, y1), (x2, y2)
g.drawLine(k * squareWidth, 0, k * squareWidth, CANVAS_HEIGHT);
}
for (k = 0; k < numRows; k++) {
g.drawLine(0, k * squareHeight, CANVAS_WIDTH, k * squareHeight);
}
}
public void refresh() {
// Attempting to set the repaint area to only the grid region
// because CTRL+F is free and parameters are not
repaint(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}
public void updateVars() {
this.squareWidth = (int) ((double) BuildGridGUI2.this.widthOfFrames * conversionRatio);
this.squareHeight = (int) ((double) BuildGridGUI2.this.heightOfFrames * conversionRatio);
this.numColumns = BuildGridGUI2.this.numCols;
this.numRows = (int) (((double) BuildGridGUI2.this.numFrames / numColumns) + 0.5);
}
}
}
One tip: Try to use specific sub-classes of JComponent rather than JComponent. For example, I suggest changing the return type of method makeSideMenu() to JPanel rather than JComponent since that method actually returns a JPanel.
I have a class that extends JFrame and I would like to add two JPanels to that JFrame: a text panel and a graphics panel. My text panel is a panel containing a label with text. My graphics panel will contain a graph (created using 2DGraphics). I'm adding the graphic panel to the left side (0,0) and the text panel to right side (1,0) using a gridbaglayout method. However, the graphic panel won't show up in the frame. I have tried numerous methods to try to get the panel to show up with no success.
import java.awt.*;
import javax.swing.*;
public class GraphicsTest extends JFrame {
private final JPanel textPanel;
private final JLabel textLabel;
public GraphicsTest() {
textPanel = new JPanel(new BorderLayout());
textLabel = new JLabel("Home label");
this.setBounds(180, 112, 1080, 675);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new GridBagLayout());
textPanel.add(textLabel, BorderLayout.NORTH);
addComponent(this, new GraphicPanel(), 0, 0, 1, 1, GridBagConstraints.CENTER);
addComponent(this, textPanel, 1, 0, 1, 1, GridBagConstraints.CENTER);
this.setVisible(true);
}
public class GraphicPanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void paintComponents(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
// drawing lots of shapes and lines, removed for readibility
}
}
public void addComponent(JFrame frame, JComponent component, int x, int y, int width, int height, int align) {
GridBagConstraints c = new GridBagConstraints();
c.gridx = x;
c.gridy = y;
c.gridwidth = width;
c.gridheight = height;
c.weightx = 100.0;
c.weighty = 100.0;
c.insets = new Insets(5, 0, 5, 0);
c.anchor = align;
c.fill = GridBagConstraints.NONE;
frame.add(component, c);
}
public static void main(String[] args) {
GraphicsTest gui = new GraphicsTest();
}
}
Works fine for me when I use a size like (200, 200).
GridbagLayout works such that if a component can't be displayed at its preferred size, then it will be displayed at its minimum size, which would be (0, 0).
So whether (700, 700) works will depend on your screen resolution. It also works at (700, 600) for my screen.
I question if you want to use a GridBadLayout. Maybe just use a BorderLayout. The custom painting panel would go in the CENTER so it will grow/shrink as the frame size changes. The other panel would go to the LINE_END, so it will remain at a fixed size.
I have to draw a JLabel with an onclick event on an drawn circle. The newly created JLabel should be placed placed very closed a line which is already created. I was trying to draw this JLabel at the middle position of the line. But problem is, even after setting fixed calculated coordinates(x,y), the JLabel is not drawn in the given location.(unlike g.drawLine() or g.drawOval()). My code is given below: need help to fix it.
public class ButtonExample extends JFrame{
JFrame frame;
JLabel label1, label2, label3;
private Shape myShape;
private int arrowAdded = 0;
public ButtonExample() {
super("Location test of JLabel and Graphics objects");
label1 = new JLabel("0,0");
//label2 = new JLabel("40,40");
label1.setBounds(0, 0, 50, 50);
label1.setBorder(BorderFactory.createLineBorder(Color.black));
//label2.setBounds(100, 100, 50, 50);
//label2.setBorder(BorderFactory.createLineBorder(Color.black));
add(label1);
//add(label2);
repaint();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
if (myShape.contains(me.getPoint())) {
arrowAdded = 1;
repaint();
}
}
});
setLayout(null);
setSize(1000,600);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1000, 600);
}
public void paint(Graphics g){
super.paint(g);
int startX = 100;
int startY = 100;
int endX = 180;
int endY = 120;
g.setColor(new Color(0, 255, 0));
myShape = new Ellipse2D.Double(startX, startY, 30, 30);
Graphics2D g2d = (Graphics2D) g;
g2d.draw(myShape);
g.drawLine(startX, startY, endX, endY);
int lX = (int)Math.abs(endX-startX)/2;
int lY = (int)Math.abs(endY-startY)/2;
if(endX>startX) {
lX = lX+startX;
}else {
lX = lX+endX;
}
if(endY>startY) {
lY = lY+startY;
}else {
lY = lY+endY;
}
if(arrowAdded == 1) {
label3 = new JLabel();
label3.setBounds(lX, lY, 20, 15);
label3.setBorder(BorderFactory.createLineBorder(Color.black));
add(label3);
g.drawRect(lX, lY, 20, 15);
}enter code here
}
public static void main(String[] args) {
new ButtonExample();
}
}
Don't override paint() on a JFrame!
The frame includes the title bar and borders, so you can't just paint at (0, 0). You would need your painting to be offset by the frame decorations.
Instead, custom painting should be done by overriding the paintComponent(...) method of a JPanel and then you add the panel to the frame. Now the offsets will be relative to the panel, so you can use (0, 0). Of course you would also add the label to the panel at your desired location.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Change your code
int lX = (int)Math.abs(endX-startX) / 2;
int lY = (int)Math.abs(endY-startY) / 2;
to
int lX = (endX-startX) / 2;
int lY = (endY-startY) / 2;
and your rectangle (left upper corner) will be placed in the middle position of the line (you can still set an offset if it´s too close :-) )
I was wondering why my button does not show up on the panel until AFTER I hover my mouse over where it would be? It also disappears again if I resize the window. The MainMenuScreen is just an image I use as my background image.
//MainMenu setup
JPanel card2 = new JPanel();
card2.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(2,2,2,2);
gbc.anchor = GridBagConstraints.CENTER;
MainMenuScreen mms = new MainMenuScreen();
mms.setLayout(new FlowLayout());
card2.add(mms);
card2.add(menuButton1, gbc);
Here is how I setup by background image.
public class MainMenuScreen extends JPanel{
private static final long serialVersionUID = 1L;
private BufferedImage background;
public MainMenuScreen() {
try {
background = ImageIO.read(new File("M&M Arcade.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth());
int y = (getHeight() - background.getHeight());
g.drawImage(background, x, y, this);
}
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.white);
}
}
The JButton doesn't show up beacuse you are using the same GridBagConstraints values for your MainMenuScreen component and your JButton menuButton1, i.e. they exist at the same location. Once the values of gbc are adjusted, the button will appear. Also better to consistently use the correct overloaded method of add when adding to a container with GridBagLayout.
Edit:
There have been numerous discussions on how to implement background images on JPanel containers. A notable one is Background Panel.
This question is related to my previous question How to generate Cartesian Coordinate (x,y) from GridBaglayout?
I have successfully get the coordinate of each pictures, however when I checked the coordinate through (System.out.println) and the placement of the images on the screen, it seems to be wrong. e.g. if on the screen it was obvious that the x point of the first picture is on cell 2 which is on coordinate of 20, but the program shows x=1.
Here is part of the code:
public Grid (){
setPreferredSize(new Dimension(600,600));
....
setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.weightx = 1d;
gc.weighty = 1d;
gc.insets = new Insets(0, 0, 0, 0);//top, left, bottom, and right
gc.fill = GridBagConstraints.BOTH;
JLabel[][] label = new JLabel[ROWS][COLS];
Random rand = new Random();
// fill the panel with labels
for (int i=0;i<IMAGES;i++){
ImageIcon icon = createImageIcon("myPics.jpg");
int r, c;
do{
//pick random cell which is empty
r = (int)Math.floor(Math.random() * ROWS);
c = (int)Math.floor(Math.random() * COLS);
} while (label[r][c]!=null);
//randomly scale the images
int x = rand.nextInt(50)+30;
int y = rand.nextInt(50)+30;
Image image = icon.getImage().getScaledInstance(x,y, Image.SCALE_SMOOTH);
icon.setImage(image);
JLabel lbl = new JLabel(icon); // Instantiate GUI components
gc.gridx = r;
gc.gridy = c;
add(lbl, gc); //add(component, constraintObj);
label[r][c] = lbl;
}
I checked the coordinate through this code:
Component[] components = getComponents();
for (Component component : components) {
System.out.println(component.getBounds());
}
You can use SwingUtilities convertPointToScreen() and convertPointFromScreen() to convert between screen and component coordinates.
Addendum: Here's a simple example I used when trying to understand how components move and resize under the influence of a layout manager.
public class MyPanel extends JPanel {
public MyPanel() {
super(new GridLayout(4, 4));
for (int i = 0; i < 16; i++) {
JPanel panel = new JPanel(new GridLayout());
panel.add(new CenterLabel());
this.add(panel);
}
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
private static class CenterLabel extends JLabel {
public CenterLabel() {
this.setHorizontalAlignment(JLabel.CENTER);
this.setVerticalAlignment(JLabel.CENTER);
this.setOpaque(true);
this.setBackground(Color.lightGray);
this.setBorder(BorderFactory.createLineBorder(Color.blue));
this.setPreferredSize(new Dimension(160, 100));
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int w = e.getComponent().getWidth();
int h = e.getComponent().getHeight();
CenterLabel.this.setText("[" + w/2 + "\u253C" + h/2 + "]");
}
});
}
}
}
if on the screen it was obvious that
the x point of the first picture is on
cell 2 which is on coordinate of 20,
but the program shows x=1.
The first image will have x/y coordinates of 0/0. The second images will have coordinates of 1/0. The X/Y values of offset from 0. Is that what you are talking about?
Or is your listener added to the image not the panel in which case you need to convert the image coordinates to the panel coordinates. Check our the SwingUtilities class for methods to do this.
If you need more help post your SSCCE.