I am having issues with the below program I have been working on. The program is supposed to take the users' input via a GUI and then use those to create and draw an object. After some testing I know that the object (either oval or rectangular) is being created. The issue I am running into is that the object is not being drawn within the panel. What am I doing wrong here and how can I get the draw method to be called. I am very new to the graphics methods and maybe making a simple mistake.
import java.awt.*;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
*
* Purpose: This GUI will take a users input to print a shape
*/
public class Test {
//Shape class that extends the base rectangle class
public abstract static class Shape extends Rectangle {
private static final long serialVersionUID = 1L;
// instance variables for shape class
private String color;
private Boolean solid;
private int count;
// Constructor for shape class
public Shape(Rectangle rectangle, String color, Boolean solid)
{
super(rectangle);
this.color = color;
this.solid = solid;
count++;
}
// Method that will give the number of shapes printed
public int getNoOfShapes() {
return count;
}
// method that will take the users color select and set the color
public void setColor(Graphics g) {
if(color.equalsIgnoreCase("black"))
g.setColor(Color.BLACK);
else if(color.equalsIgnoreCase("red"))
g.setColor(Color.RED);
else if(color.equalsIgnoreCase("orange"))
g.setColor(Color.ORANGE);
else if(color.equalsIgnoreCase("yellow"))
g.setColor(Color.YELLOW);
else if(color.equalsIgnoreCase("green"))
g.setColor(Color.GREEN);
else if(color.equalsIgnoreCase("blue"))
g.setColor(Color.BLUE);
else if(color.equalsIgnoreCase("magenta"))
g.setColor(Color.MAGENTA);
}
// return shape type is solid or hollow
public Boolean getSolid() {
return solid;
}
// draw method
public abstract void draw(Graphics g);
}
//Oval subclass
public static class Oval extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for the oval class
public Oval(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Draw method for the oval
#Override
public void draw(Graphics g) {
// Draw a hollow oval shape
if(super.getSolid() == false)
g.drawOval((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
//Draw a solid oaval shape
else if(super.getSolid() == true)
g.fillOval((int)getX(),(int) getY(), (int)getWidth(), (int)getHeight());
}
}
//Rectangular subclass
public static class Rectangular extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for Rectangular class
public Rectangular(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Method to draw a rectangle
#Override
public void draw(Graphics g) {
// Will draw a hollow rectangle
if(super.getSolid() == false)
g.drawRect((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
//Will draw a solid rectangle
else if(super.getSolid() == true)
g.fillRect((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
}
}
//Drawing class with GUI inside
public static class Drawing extends JPanel {
private static final long serialVersionUID = 1L;
// create Shape
private Shape shape1=null;
public void drawShape(Shape shape) throws OutsideBounds{
try {
if(shape.intersects(270,20,200,200))
throw new OutsideBounds("This shape will not fit into the drawing box");
}catch(OutsideBounds o) {
JOptionPane.showMessageDialog(null, o.getMessage());
}catch(Exception e) {
JOptionPane.showMessageDialog(null, "Please only enter integers ");
}
}
// Method to paint shape
#Override
public void paintComponent(Graphics g) {
try {
// print count of shapes
super.paintComponents(g);
shape1.setColor(g);
shape1.draw(g);
repaint();
}catch(Exception e) {}
}
}
public static class GUI{
// Instance variables for the GUI
// private JFrame window;
private JLabel shape;
private JLabel fillType;
private JLabel color;
private JLabel width;
private JLabel height;
private JLabel xCoordinate;
private JLabel yCoordinate;
private JLabel printCount;
private JComboBox<String> cshape;
private JComboBox<String> cfillType;
private JComboBox<String> ccolor;
private JTextField widthField;
private JTextField heightField;
private JTextField xField;
private JTextField yField;
private JButton draw;
private JButton clear;
private JButton exit;
private JPanel panel;
// Constructor that will initiate GUI variables
public GUI() {
JFrame window = new JFrame();
window.setSize(500,350); // set size fo frame
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setLayout(null); // set no layout
window.setLocationRelativeTo(null); // centered frame
window.setTitle("Geometric Drawing"); // title
shape=new JLabel("Shape Type");
fillType=new JLabel("Fill Type");
color=new JLabel("Color");
width=new JLabel("Width");
height=new JLabel("Height");
xCoordinate=new JLabel("x coordinate");
yCoordinate=new JLabel("y coordinate");
printCount=new JLabel("1");
cshape=new JComboBox<>(new String[] {"Oval","Rectangle"});
cfillType=new JComboBox<>(new String[] {"Solid","Hollow"});
ccolor=new JComboBox<>(new String[] {"Black","Red","Orange","Yellow","Green","Blue","Magenta"});
widthField=new JTextField();
heightField=new JTextField();
xField=new JTextField();
yField=new JTextField();
draw=new JButton("Draw");
clear = new JButton("clear");
exit = new JButton("exit");
panel=new JPanel();
// set border with title to panel
panel.setBorder(BorderFactory.createTitledBorder("Shape Drawing"));
// adding all parts to the window
window.add(shape);
window.add(fillType);
window.add(color);
window.add(width);
window.add(height);
window.add(xCoordinate);
window.add(yCoordinate);
window.add(printCount);
window.add(cshape);
window.add(cfillType);
window.add(ccolor);
window.add(widthField);
window.add(heightField);
window.add( xField);
window.add(yField);
window.add(draw);
window.add(clear);
window.add(exit);
window.add(panel);
// set positions within the window
shape.setBounds(20, 20, 100, 30);
fillType.setBounds(20, 55, 100, 30);
color.setBounds(20, 90, 100, 30);
width.setBounds(20, 125, 100, 30);
height.setBounds(20, 160, 100, 30);
xCoordinate.setBounds(20, 195, 100, 30);
yCoordinate.setBounds(20, 230, 100, 30);
printCount.setBounds(285, 40, 20, 30);
cshape.setBounds(140, 20, 100, 30);
cfillType.setBounds(140, 55, 100, 30);
ccolor.setBounds(140, 90, 100, 30);
widthField.setBounds(140, 125, 100, 30);
heightField.setBounds(140, 160, 100, 30);
xField.setBounds(140, 195, 100, 30);
yField.setBounds(140, 230, 100, 30);
draw.setBounds(230, 280, 80, 30);
clear.setBounds(120, 280,80,30);
exit.setBounds(330, 280,80,30);
panel.setBounds(270, 20, 200, 200);
// adding action listener to draw button
// draw.addActionListener((ActionListener) this);
window.setVisible(true); // make frame visible
draw.addActionListener(
new java.awt.event.ActionListener(){
public void actionPerformed(java.awt.event.ActionEvent evt){
try {
Drawing d1 = new Drawing();
Shape shape;
String color;
int xCoord = 0, yCoord = 0, width = 0, height = 0;
//Point accounting for the needed shift
xCoord = Integer.parseInt(xField.getText() + 270);
yCoord = Integer.parseInt(yField.getText() + 50);
width = Integer.parseInt(widthField.getText());
height = Integer.parseInt(heightField.getText());
Rectangle r1 = new Rectangle (xCoord, yCoord, width, height);
// Point p = new Point(shapeX+270, shapeY+50);
//setting dimensions based off user input
//Dimension d = new Dimension(shapeWidth, shapeHeight);
if(((String) cshape.getSelectedItem()).equalsIgnoreCase("oval")){
shape = new Oval(r1, (String)ccolor.getSelectedItem(), true);
System.out.print("creating oval");
}
else{
shape = new Rectangular(r1,(String)ccolor.getSelectedItem(), true);}
d1.drawShape(shape);
window.repaint(); // call paint() method
}catch(NumberFormatException e) {
JOptionPane.showMessageDialog(null, "Please only enter integers ");
} catch (OutsideBounds ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
//Action Listener for clear button
clear.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//Clearing all text fields
widthField.setText("");
heightField.setText("");
xField.setText("");
yField.setText("");
}
});
//Action Liistener for exit button
exit.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//message to the user
JOptionPane.showMessageDialog(null,"Program is ending");
window.dispose();}
});
}
}
// Out of bounds class
public static class OutsideBounds extends Exception {
private static final long serialVersionUID = 1L;
// parameterized constructor
public OutsideBounds(String errorMessage) {
// call super class parameterized constructor
super(errorMessage);
}
}
public static void main(String[] args) {
GUI G1 = new GUI(); // Creating GUI
}
}```
There are a number small problems, which are combining to produce a larger problem.
null layouts are just a bad idea. Take the time to learn how to use and mix appropriate layouts
Don't use String as a carrier of information if you can avoid it. It's too easy to screw up. In you case, learn how to make use of a ListCellRenderer to customise the JComoboBox so it can carry Color as it's base value
There should be a single instance of Drawing which should render ALL the shapes. You then add each new shape to it. Instead of trying to create new instances for each individual shape.
In order for something to be painted, it must be added to something which can be painted. This means that the Drawing panel needs to be added to your frame at some point (or a contained which is directly or indirectly contained within your frame).
This is a very basic re-work of your code. It will generate a oval or rectangle at a random location with a random color. I'll leave you to fill in the rest of your requirements around it
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class Test {
//Shape class that extends the base rectangle class
public abstract static class Shape extends Rectangle {
private static final long serialVersionUID = 1L;
// instance variables for shape class
private String color;
private Boolean solid;
private int count;
// Constructor for shape class
public Shape(Rectangle rectangle, String color, Boolean solid) {
super(rectangle);
this.color = color;
this.solid = solid;
count++;
}
// Method that will give the number of shapes printed
public int getNoOfShapes() {
return count;
}
// method that will take the users color select and set the color
public void setColor(Graphics g) {
if (color.equalsIgnoreCase("black")) {
g.setColor(Color.BLACK);
} else if (color.equalsIgnoreCase("red")) {
g.setColor(Color.RED);
} else if (color.equalsIgnoreCase("orange")) {
g.setColor(Color.ORANGE);
} else if (color.equalsIgnoreCase("yellow")) {
g.setColor(Color.YELLOW);
} else if (color.equalsIgnoreCase("green")) {
g.setColor(Color.GREEN);
} else if (color.equalsIgnoreCase("blue")) {
g.setColor(Color.BLUE);
} else if (color.equalsIgnoreCase("magenta")) {
g.setColor(Color.MAGENTA);
}
}
// return shape type is solid or hollow
public Boolean getSolid() {
return solid;
}
// draw method
public abstract void draw(Graphics g);
}
//Oval subclass
public static class Oval extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for the oval class
public Oval(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Draw method for the oval
#Override
public void draw(Graphics g) {
// Draw a hollow oval shape
if (super.getSolid() == false) {
g.drawOval((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
} //Draw a solid oaval shape
else if (super.getSolid() == true) {
g.fillOval((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
}
}
}
//Rectangular subclass
public static class Rectangular extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for Rectangular class
public Rectangular(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Method to draw a rectangle
#Override
public void draw(Graphics g) {
// Will draw a hollow rectangle
if (super.getSolid() == false) {
g.drawRect((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
} //Will draw a solid rectangle
else if (super.getSolid() == true) {
g.fillRect((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
}
}
}
//Drawing class with GUI inside
public static class Drawing extends JPanel {
private List<Shape> shapes;
private List<String> colors;
private final Random rnd = new Random();
public Drawing() {
shapes = new ArrayList<>(25);
colors = new ArrayList<>();
colors.add("black");
colors.add("red");
colors.add("orange");
colors.add("yellow");
colors.add("green");
colors.add("blue");
colors.add("magenta");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Rectangle randomBounds() {
int minSize = 10;
int avaliableWidth = getWidth() - minSize;
int avaliableHeight = getHeight() - minSize;
int width = Math.max(minSize, rnd.nextInt(avaliableWidth / 4));
int height = Math.max(minSize, rnd.nextInt(avaliableHeight / 4));
int x = rnd.nextInt(avaliableWidth - width);
int y = rnd.nextInt(avaliableHeight - height);
Rectangle bounds = new Rectangle(x, y, width, height);
return bounds;
}
protected String randomColor() {
Collections.shuffle(colors);
String color = colors.get(0);
return color;
}
public void addCircle() {
shapes.add(new Oval(randomBounds(), randomColor(), rnd.nextBoolean()));
repaint();
}
public void addRectangle() {
shapes.add(new Rectangular(randomBounds(), randomColor(), rnd.nextBoolean()));
repaint();
}
// Method to paint shape
#Override
public void paintComponent(Graphics g) {
// print count of shapes
super.paintComponents(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.setColor(g2d);
shape.draw(g2d);
g2d.dispose();
}
}
}
public static class GUI {
private Drawing drawing;
// Constructor that will initiate GUI variables
public GUI() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setTitle("Geometric Drawing"); // title
JPanel controls = new JPanel(new GridLayout(0, 1));
controls.add(makeButton("Circle"));
controls.add(makeButton("Rectangle"));
drawing = new Drawing();
window.add(controls, BorderLayout.WEST);
window.add(drawing);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true); // make frame visible
}
protected JButton makeButton(String name) {
JButton btn = new JButton(name);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ("Circle".equals(e.getActionCommand())) {
drawing.addCircle();
} else if ("Rectangle".equals(e.getActionCommand())) {
drawing.addRectangle();
}
}
});
return btn;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
GUI G1 = new GUI(); // Creating GUI
}
});
}
}
}
Take a look at:
Laying Out Components Within a Container
How to Use Combo Boxes
for more details
Related
This is my 1st question here. I'm trying to build a White Page adjustable by zoom. It's inside a JScrollPane, so the size of the JScrollPane's ScrollBars are adjustable in the Dimension of that JPanel.
I want to adjust the size of those ScrollBars as the Size of the page (variables width and height in the code) + 2 borderSize, so the full size is equal the Page + margin of a borderSize around it. It works if zoom = 1.0.
If zoom < 1.0, the scroll bar is smaller than the Page and cut a piece of it. If zoom > 1 the Dimension size is way bigger than the page, leaving a huger border on its right and down corners, bigger than the borderSize.
How do I do this?
PS: I'm started learning java by myself, in the Quarantine last year, never had a teacher, just the internet, so any critics or suggestions, please, tell me.
Here's the JPanel's code:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.JSlider;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
public class Main2 {
private MyPanel mp = new MyPanel();
private JFrame frame;
private JSlider zoomSlider = new JSlider();
private JLabel zoomLabel = new JLabel("Zoom: XXX");
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main2 window = new Main2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Main2() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 619, 403);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpringLayout springLayout = new SpringLayout();
frame.getContentPane().setLayout(springLayout);
springLayout.putConstraint(SpringLayout.SOUTH, zoomSlider, 40, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, zoomSlider, -115, SpringLayout.EAST, frame.getContentPane());
zoomSlider.setValue(100);
zoomSlider.setSnapToTicks(true);
zoomSlider.setPaintTicks(true);
zoomSlider.setMaximum(200);
zoomSlider.setMinorTickSpacing(5);
zoomSlider.setMinimum(5);
springLayout.putConstraint(SpringLayout.NORTH, zoomSlider, 0, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomSlider, 0, SpringLayout.WEST, frame.getContentPane());
frame.getContentPane().add(zoomSlider);
JScrollPane scrollPane = new JScrollPane(mp);
springLayout.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, zoomSlider);
springLayout.putConstraint(SpringLayout.WEST, scrollPane, 10, SpringLayout.WEST, frame.getContentPane());
springLayout.putConstraint(SpringLayout.SOUTH, scrollPane, -10, SpringLayout.SOUTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, frame.getContentPane());
frame.getContentPane().add(scrollPane);
springLayout.putConstraint(SpringLayout.NORTH, zoomLabel, 10, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomLabel, 6, SpringLayout.EAST, zoomSlider);
frame.getContentPane().add(zoomLabel);
frame.addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent arg0) {
// TODO Auto-generated method stub
mp.draw();
}
});
zoomSlider.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
});
mp.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getPreciseWheelRotation() < 0) {
setZoom(zoomSlider.getValue()- 5);
} else {
setZoom(zoomSlider.getValue()+ 5);
}
// zoom += e.getPreciseWheelRotation();
if (mp.getZoom()*100 < 10) {
setZoom(10);
}
mp.draw();
}
});
AdjustmentListener adj = new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
setZoom(zoomSlider.getValue());
mp.draw();
}
};
scrollPane.getVerticalScrollBar().addAdjustmentListener(adj);
scrollPane.getHorizontalScrollBar().addAdjustmentListener(adj);
}
public void setZoom(int n) {
mp.setZoom(n);
zoomSlider.setValue(n);
zoomLabel.setText("Zoom: "+mp.getZoom()+"x");
}
}
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
int a, b;
String temp;
Float x, y;
x=(getZoom()*width); //Size of the page adjusted by zoom
y=(getZoom()*height);
temp = x.toString();
String temp1[] = temp.split("\\."); // converted to string to convert it to int
a = Integer.valueOf(temp1[0])+2*borderSize; //that value + 2 BorderSize
temp = y.toString();
String temp2[] = temp.split("\\.");
b = Integer.valueOf(temp2[0])+2*borderSize;
return new Dimension (a,b);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d = putZoom(g2d);
g2d.setColor(Color.WHITE);
g2d.fillRect(this.getX(),this.getY(), width, height);
g2d.setColor(Color.BLACK);
g2d.drawRect(this.getX()+borderSize,this.getY()+borderSize,width-2*borderSize,height-2*borderSize);
g2d.dispose();
}
public Graphics2D putZoom(Graphics2D g) {
AffineTransform at = new AffineTransform();
at.translate(borderSize,borderSize); // put the page a borderSize from the upper-left corner
at.scale(getZoom(),getZoom()); //adjust the page as zoom
Graphics2D g2d = g;
g2d.setTransform(at);
return g2d;
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {
return Float.valueOf(zoom)/100;
}
public void setZoom(int zom) { //this method is to update Zoom from the main
zoom=zom;
String zoomheight []= (String.valueOf(getZoom()*height)).split("\\.");
heightz = Integer.valueOf(zoomheight[0]);
String zoomwidth []= (String.valueOf(getZoom()*width)).split("\\.");
widthz = Integer.valueOf(zoomwidth[0]);
}
public int getZoomInt() {
return this.zoom;
}
}
Zoom(values from 0.1 to 2.0).
How can i improve this? Also, i have no idea how to update the JScrollPane's scrollbars together with the zoom.Thanks for the help.
UPDATE: i've created a minimal reproducible exemple.
Introduction
I started working on this before you updated your question. I used a zoom percentage rather than a zoom factor.
I created the following GUI and set the initial state to 30 percent.
I made the inner JPanel a checkerboard so you can more easily see the zoom. I modified your initial values so the inner JPanel would represent an 8 1/2 x 11 piece of paper at 50 pixels per inch.
Here's the same GUI at 100 percent.
Here's the same GUI at 10 percent.
Explanation
I created a JFrame and a control JPanel to hold the JSlider. I used a GridLayout to create the control JPanel.
I created an inner JPanel to hold the drawing and a display JPanel that holds the JScrollPane. I made the display JPanel proportionate to the size of the inner JPanel so I wouldn't have any stretching issues.
Getting the GUI to revalidate / repaint turned out to be the biggest challenge. I wound up having to invalidate the JScrollPane, both JScrollBars, and the display JPanel. I also had to reset the JScrollBars to zero each time I changed the zoom percentage.
Code
Here's the complete runnable code. I made all of the classes inner classes so I could post this as one code block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ZoomJPanelGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ZoomJPanelGUI());
}
private int zoomPercentage = 30;
private DisplayPanel displayPanel;
private JFrame frame;
#Override
public void run() {
frame = new JFrame("Zoom JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createControlPanel(), BorderLayout.BEFORE_FIRST_LINE);
this.displayPanel = new DisplayPanel(zoomPercentage);
frame.add(displayPanel.getPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createControlPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
JLabel label = new JLabel("Zoom Percentage");
label.setFont(panel.getFont().deriveFont(Font.BOLD, 24f));
panel.add(label);
JSlider slider = new JSlider(
JSlider.HORIZONTAL, 10, 100, zoomPercentage);
slider.setFont(panel.getFont().deriveFont(16f));
slider.setMajorTickSpacing(30);
slider.setMinorTickSpacing(5);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent event) {
JSlider slider = (JSlider) event.getSource();
if (!slider.getValueIsAdjusting()) {
zoomPercentage = (int) slider.getValue();
displayPanel.setZoomPercentage(zoomPercentage);
displayPanel.repaint();
frame.pack();
}
}
});
panel.add(slider);
return panel;
}
public class DisplayPanel {
private InnerPanel innerPanel;
private final JPanel panel;
private JScrollPane scrollPane;
private int zoomPercentage;
public DisplayPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
this.panel = createDisplayPanel();
}
private JPanel createDisplayPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
this.innerPanel = new InnerPanel(zoomPercentage);
scrollPane = new JScrollPane(innerPanel);
scrollPane.setPreferredSize(new Dimension(475, 600));
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
innerPanel.setZoomPercentage(zoomPercentage);
}
public JPanel getPanel() {
return panel;
}
public void repaint() {
innerPanel.repaint();
scrollPane.invalidate();
JScrollBar hScrollBar = scrollPane.getHorizontalScrollBar();
JScrollBar vScrollBar = scrollPane.getVerticalScrollBar();
hScrollBar.setValue(0);
vScrollBar.setValue(0);
hScrollBar.invalidate();
vScrollBar.invalidate();
panel.invalidate();
}
}
public class InnerPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int maximumBorderSize = 25;
private int maximumCellSize = 50;
private int maximumHeight = 5500;
private int maximumWidth = 4250;
private int zoomPercentage;
public InnerPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
#Override
public Dimension getPreferredSize() {
int width = maximumWidth * zoomPercentage / 100;
int height = maximumHeight * zoomPercentage / 100;
return new Dimension(width, height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int borderSize = maximumBorderSize * zoomPercentage / 100;
paintBackground(g2d);
paintBorder(g2d, borderSize);
paintCheckerboard(g2d, borderSize);
}
private void paintBackground(Graphics2D g2d) {
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
private void paintBorder(Graphics2D g2d, int borderSize) {
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3f));
g2d.drawRect(borderSize, borderSize, getWidth() - 2 * borderSize,
getHeight() - 2 * borderSize);
}
private void paintCheckerboard(Graphics2D g2d, int borderSize) {
int cellSize = maximumCellSize * zoomPercentage / 100;
int width = maximumWidth - maximumBorderSize * 2 - 2;
int height = maximumHeight - maximumBorderSize * 2 - 2;
int cellWidth = width / maximumCellSize;
int cellHeight = height / maximumCellSize;
boolean isBlue = true;
int x = borderSize;
int y = borderSize;
int heightRemainder = height - cellHeight * cellSize;
for (int i = 0; i < cellHeight; i++) {
int widthRemainder = width - cellWidth * cellSize;
for (int j = 0; j < cellWidth; j++) {
if (isBlue) {
g2d.setColor(Color.BLUE);
} else {
g2d.setColor(Color.YELLOW);
}
isBlue = !isBlue;
g2d.fillRect(x, y, cellSize, cellSize);
x += cellSize;
if (widthRemainder > 0) {
x++;
widthRemainder--;
}
}
// isBlue = !isBlue;
x = borderSize;
y += cellSize;
if (heightRemainder > 0) {
y++;
heightRemainder--;
}
}
}
}
}
I've finally did it. Started by not using transformation for scaling it, but making a new draw with the size zoomed, adapting all sizes in the method setSizes(), and adjusting the Dimension by those sizes.
(Just changed this class)
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
int maxHeight, maxWidth; //max size of draw
int maxAreaHeight, maxAreaWidth; //max size of area
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
setSizes();
return new Dimension (maxAreaWidth,maxAreaHeight);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d= createBase(g2d);
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {return Float.valueOf(zoom)/100;}
public void setZoom(int zom) {zoom=zom;}
public int getZoomInt() {return this.zoom;}
public void setSizes () {
widthz= width*getZoomInt()/100;
heightz=height*getZoomInt()/100;
maxHeight = heightz+2*borderSize;
maxWidth = widthz +2*borderSize;
maxAreaHeight = this.getY()+maxHeight;
maxAreaWidth = this.getX()+maxWidth;
if (this.getSize() != new Dimension(maxAreaWidth, maxAreaHeight)) {
this.setSize(maxAreaWidth, maxAreaHeight);
}
}
public Graphics2D createBase(Graphics2D g2d) {
Graphics2D g = g2d;
setSizes();
g.setColor(Color.WHITE);
g.fillRect(this.getX()+borderSize,this.getY()+borderSize, widthz, heightz);
g.setColor(Color.BLACK);
g.drawRect(this.getX()+borderSize+borderSize*zoom/100,this.getY()+borderSize+borderSize*zoom/100,widthz-2*borderSize*zoom/100,heightz-2*borderSize*zoom/100);
return g;
}
}
Thanks for all the help.
I am attempting to use jsliders to allow a user to pinpoint the origin of a circle to be drawn on a canvas. I am using a button to show and hide the circle. I am using paint on an inner jpanel so that paint will not write over components. However, the coordinates inside the jpanel are different than the coordinates for the entire frame. So, it is very difficult for me to get the coordinates of the jslider and then translate it to the jpanel to draw the circle. Is there an easy way to figure this out without a ton of guess and check? I am also using the custom layout miglayout. I have included the code for my GUI class as well as my custom JPanel I made so I could mess with the paint method.
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
x = new JSlider(JSlider.HORIZONTAL,650,325);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650,0));
y = new JSlider(JSlider.HORIZONTAL,650,325);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension (0,600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if(!bstate) {
int positionx = x.getValue() - 80;
int positiony = y.getValue();
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
} else {
Color transparent = new Color(0,0,0,0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600,600));
innerpanel.setBorder(blackline);
outerpanel.add(x,"wrap");
outerpanel.add(y,"split 2");
outerpanel.add(innerpanel);
outerpanel.add(state,"wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
public class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed (ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
Your problem is you are trying to make a one-to-one mapping between the value of the JSlider and the coordinate in your CustomPanel. You should use the JSlider value as a percentage, i.e. minimum value zero and maximum value 100. If you want the circle to appear in the middle of the CustomPanel so you place both JSliders in their mid-points, i.e. both at 50%. Then you calculate 50% of the corresponding dimension to get the coordinate. If the width of CustomPanel is 600, then 50% of 600 is 300 so positionx needs to be 300.
The only thing I changed in your code is the calculation of positionx and positiony.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.Border;
import net.miginfocom.swing.MigLayout;
public class CircleGUI extends JFrame implements ActionListener {
private MigLayout layout = new MigLayout();
private CustomPanel innerpanel;
private JSlider x,y;
private JColorChooser colorpick;
private JButton state;
private boolean bstate;
CircleGUI() {
initialize();
}
private void initialize() {
Border blackline = BorderFactory.createLineBorder(Color.black);
bstate = false;
// x = new JSlider(JSlider.HORIZONTAL, 650, 325);
x = new JSlider(0, 100, 10);
x.setPaintTicks(true);
x.setPaintLabels(true);
x.setPreferredSize(new Dimension(650, 0));
// y = new JSlider(JSlider.HORIZONTAL, 650, 325);
y = new JSlider(0, 100, 10);
y.setPaintTicks(true);
y.setPaintLabels(true);
y.setInverted(true);
y.setOrientation(JSlider.VERTICAL);
y.setPreferredSize(new Dimension(0, 600));
colorpick = new JColorChooser();
state = new JButton("Show");
state.addActionListener(e -> {
if (!bstate) {
int positionx = Math.round(x.getValue() / 100.0f * innerpanel.getSize().width) - 50;
int positiony = Math.round(y.getValue() / 100.0f * innerpanel.getSize().height) - 50;
Color c = colorpick.getColor();
innerpanel.setColor(c);
innerpanel.setX(positionx);
innerpanel.setY(positiony);
innerpanel.repaint();
state.setText("Hide");
bstate = true;
}
else {
Color transparent = new Color(0, 0, 0, 0);
innerpanel.setColor(transparent);
innerpanel.repaint();
state.setText("Show");
bstate = false;
}
});
JPanel outerpanel = new JPanel(layout);
innerpanel = new CustomPanel();
innerpanel.setPreferredSize(new Dimension(600, 600));
innerpanel.setBorder(blackline);
outerpanel.add(x, "wrap");
outerpanel.add(y, "split 2");
outerpanel.add(innerpanel);
outerpanel.add(state, "wrap");
outerpanel.add(colorpick);
this.setSize(1000, 1000);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(outerpanel);
}
#Override
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
CircleGUI cg = new CircleGUI();
cg.setVisible(true);
});
}
}
class CustomPanel extends JPanel implements ActionListener {
private Color c;
private int x;
private int y;
public CustomPanel() {
c = null;
}
#Override
public void actionPerformed(ActionEvent e) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(c);
g2.fill(new Ellipse2D.Double(x, y, 100, 100));
}
public void setColor(Color c) {
this.c = c;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
I want to accomplish something very similar to the image a Rectangle whit a Selector Line.
Basically, I have a Rectangle and I want to have a selector line all around it.
For that, I wanted to create an additional JComponent.
At the moment I can only draw the Rectangle. How could I get the parentPanel JPanel inside the Rectangle class, so that I could add the selector?
public class TestPanel extends JFrame {
public class Rectangle extends JComponent {
public Rectangle(){
setBounds(x1, y1, x2, y2);
JPanel Selector = new JPanel();
//Adds Selector to parentPanel within Rectangle
//setBounds(x1-1, y1-1, x2+1, y2+1)
//!Problem parent is initially null! cant even a use property
//Life hacks?
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(0, 0, getWidth(), getHeight());
}
}
public TestPanel() {
Rectangle Rectangle = new Rectangle();
JPanel parentFrame = new JPanel();
parentFrame.add(Rectangle);
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new TestPanel();
}
}
If I try to add the selector inside the rectangle, it will get out of the drawing area. If I resize the drawing area, it won't be scalable for later development.
If possible I would avoid dual binding like:
public TestPanel() {
Rectangle Rectangle = new Rectangle();
JPanel parentPanel = new JPanel();
parentPanel.add(Rectangle);
Rectangle.addParent(parentPanel)
...
}
Again, I'm not 100% clear on what you're trying to achieve. If what you wish to create is a user-created dashed line, one that can change with mouse press/drag/release, then you don't need to create a new component but rather use a MouseAdapter as a MouseListener and MouseMotionListener, all to help you create the Rectangle, and then simply draw the Rectangle with a dashed line using an appropriate Stroke, as per this answer.
For example, something like would create a dashed line that is user-selectable:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class SelectorPanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final Stroke DASHED_STROKE = new BasicStroke(2, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL, 0, new float[] { 5 }, 0);
private static final Color DASHED_COLOR = Color.LIGHT_GRAY;
private Rectangle rectangle = null;
public SelectorPanel() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
private class MyMouse extends MouseAdapter {
private Point p1 = null;
#Override
public void mousePressed(MouseEvent e) {
p1 = e.getPoint();
rectangle = null;
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
createRectangle(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
createRectangle(e);
p1 = null;
}
}
private void createRectangle(MouseEvent e) {
Point p2 = e.getPoint();
int x = Math.min(p1.x, p2.x);
int y = Math.min(p1.y, p2.y);
int width = Math.abs(p1.x - p2.x);
int height = Math.abs(p1.y - p2.y);
rectangle = new Rectangle(x, y, width, height);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (rectangle != null) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(DASHED_COLOR);
g2.setStroke(DASHED_STROKE);
g2.draw(rectangle);
g2.dispose();
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
SelectorPanel mainPanel = new SelectorPanel();
JFrame frame = new JFrame("SelectorPanel");
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());
}
}
So, I have multiple objects of the class Square, which is the subclass of JButton. I have an instance of the class Board, which contains a few instances of Square. What I want to do is when I press one of the buttons (squares), draw a shape (a circle) on top of it. For doing that, I have a boolean variable in the Square class, namely isClicked, that basically decides what has to be drawn in the paintComponent method.
The problem is that buttons start to behave in a weird way when I have a few of them. Surprisingly, if there is only one of them, there is no problem at all. At first, I had thought the problem might be related to threads, however, I put the main code into invokeLater method and that did not help at all.
I saw a solution using BufferedImage, but I would like to see if there is any possibility to solve the problem doing it my way.
Sorry for possibly not perfect English.
Square class:
public class Square extends JButton implements ActionListener {
private int number;
private boolean isClicked;
public Square(int x) {
number = x;
isClicked = false;
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if (!isClicked) {
super.paintComponent(g);
} else {
System.out.println("EXECUTED for: " + number);
g2d.drawOval(this.getX(), this.getY(), 100, 100);
}
}
#Override
public void actionPerformed(ActionEvent e) {
isClicked = !isClicked;
System.out.println(isClicked + " " + number);
repaint();
}
}
Board class:
public class Board extends JPanel {
private static final int BOARD_WIDTH = (int) (TicTacToe.WIDTH * 0.7);
private static final int VERTICAL_LINE_LENGTH = (int) (TicTacToe.WIDTH * 0.5);
private static final int HORIZONTAL_LINE_LENGTH = (int) (TicTacToe.HEIGHT * 0.8);
private static final int STROKE_WIDTH = 5;
private Square[] squares;
public Board() {
}
public void addButtons() {
squares = new Square[9];
for (int i = 0; i < 3; i++) {
Square square = new Square(i);
square.setPreferredSize(new Dimension(30, 30));
square.addActionListener(square);
this.add(square);
squares[i] = square;
((GridLayout)this.getLayout()).setHgap(30);
((GridLayout)this.getLayout()).setVgap(30);
}
}
public Square[] getButtons() {
return squares;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(STROKE_WIDTH));
// Horiztontal lines
g2d.drawLine(0, TicTacToe.HEIGHT / 3,
BOARD_WIDTH, TicTacToe.HEIGHT / 3);
g2d.drawLine(0, 2 * TicTacToe.HEIGHT / 3,
BOARD_WIDTH, 2 * TicTacToe.HEIGHT / 3);
// Vertical lines
g2d.drawLine(BOARD_WIDTH / 3, 0, BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
g2d.drawLine(2 * BOARD_WIDTH / 3, 0, 2 * BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
}
}
Main method:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Board board = new Board();
board.setPreferredSize(new Dimension((int) (WIDTH * 0.7), HEIGHT));
board.setLayout(new GridLayout(3, 3));
board.addButtons();
GameOptions opt = new GameOptions();
opt.setPreferredSize(new Dimension((int) (WIDTH * 0.3), HEIGHT));
JFrame frame = new JFrame("Tic Tac Toe");
frame.setLayout(new FlowLayout());
frame.add(board);
frame.add(opt);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
Your use of getX() and getY() on the button's drawing code is completely wrong and does not belong. These methods return the location of the button relative to its container, and so while this might work for a button located at the upper left, it will fail for anything else since you'll end up drawing somewhere far away from the button itself, and so many of your drawings will never show.
You'd be much better off not extending JButton but instead simply swapping ImageIcons that display what you want drawn on the JButton. This is much simpler and much more idiot-proof. You set the button's icon by calling .setIcon(myImageIcon) on it, passing in the icon of choice.
But if you absolutely wanted to draw on the button, you'd do so without using getX() or getY(). You'd also probably want to use a JToggleButton as the parent class, since you're toggling state. For example, my MCVE:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawButtonPanel extends JPanel {
private static final int SIDE = 3;
private static final int GAP = 5;
private static final Color BG = Color.BLACK;
public DrawButtonPanel() {
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
setBackground(BG);
for (int i = 0; i < SIDE * SIDE; i++) {
// add(new DrawButton1());
DrawButton2 drawButton2 = new DrawButton2(i);
AbstractButton button = drawButton2.getButton();
add(button);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawButtonPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawButton2 {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
private AbstractButton button = new JToggleButton();
private int index;
public DrawButton2(int index) {
this.index = index;
button.setBorderPainted(false);
button.setBorder(null);
button.setIcon(createPlainIcon());
button.setSelectedIcon(createSelectedIcon());
button.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println("Index: " + index);
}
}
});
}
public int getIndex() {
return index;
}
private Icon createPlainIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.dispose();
return new ImageIcon(img);
}
private Icon createSelectedIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
g2d.setColor(BG);
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, PREF_W - 2 * GAP, PREF_H - 2 * GAP);
g2d.dispose();
return new ImageIcon(img);
}
public AbstractButton getButton() {
return button;
}
}
#SuppressWarnings("serial")
class DrawButton1 extends JToggleButton {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
if (!isSelected()) {
super.paintComponent(g);
} else {
g2d.setColor(BG);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
}
g2d.dispose(); // since we created a new one
}
}
Edited to show how to do this with JToggleButton and icons.
I have a 3x3 check board-like image rendered on a JPanel which is added onto a JFrame. Then I have 9 more JPanels (1 on top of each square) and on click something needs to be drawn on the corresponding square. My problem is that it only works for the top-left square. The rest of the drawings seem to be drawn below the checkboard image. So if I comment out the part that loads the checkboard image,and click as if they were there then the drawings appear correctly. I get the same result with a layered pane. Absolute positioning is used and the coordinates seem to be correct since if I remove the checkboard image then the drawings appear where they should and the drawings do not occupy more than a square.
My code is structured as follows:
'main' class creates the frame and adds an instance of another class which extends JPanel and which also draws the checkboard image using paintComponent(Graphics g).
'main' class has also 9 instances added of a class that extends JPanel and draws something on a mouse click using paintComponent(Graphics g). Each instance is placed on top of a square
Please note that because I was going to do it with just Rectangles I named the second class Rectangles but it is rectangualar JPanels not java Rectangle instances
Code:
public class Main3
{
private JFrame frame=new JFrame("");
private Rectangles rect00=new Rectangles(0,0,129,129);
private Rectangles rect01=new Rectangles(136,0,129,129);
private Rectangles rect02=new Rectangles(268,0,129,129);
private Rectangles rect10=new Rectangles(0,136,129,129);
private Rectangles rect11=new Rectangles(134,136,129,129);
private Rectangles rect12=new Rectangles(269,137,129,129);
private Rectangles rect20=new Rectangles(0,270,129,129);
private Rectangles rect21=new Rectangles(136,269,129,129);
private Rectangles rect22=new Rectangles(269,270,129,129);
public void Display()
{
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(null);
frame.setSize(600,400);
sub inter=new sub();
inter.setLayout(null);
inter.setBounds(0,0,600,400);
inter.setSize(600,400);
rect00.setBounds(rect00.getX(),rect00.getY(),rect00.getWidth(),rect00.getHeight());
rect01.setBounds(rect01.getX(),rect01.getY(),rect01.getWidth(),rect01.getHeight());
rect02.setBounds(rect02.getX(),rect02.getY(),rect02.getWidth(),rect02.getHeight());
rect10.setBounds(rect10.getX(),rect10.getY(),rect10.getWidth(),rect10.getHeight());
rect11.setBounds(rect11.getX(),rect11.getY(),rect11.getWidth(),rect11.getHeight());
rect12.setBounds(rect12.getX(),rect12.getY(),rect12.getWidth(),rect12.getHeight());
rect20.setBounds(rect20.getX(),rect20.getY(),rect20.getWidth(),rect20.getHeight());
rect21.setBounds(rect21.getX(),rect21.getY(),rect21.getWidth(),rect21.getHeight());
rect22.setBounds(rect22.getX(),rect22.getY(),rect22.getWidth(),rect22.getHeight());
rect00.setOpaque(false);
rect01.setOpaque(false);
rect02.setOpaque(false);
rect10.setOpaque(false);
rect11.setOpaque(false);
rect12.setOpaque(false);
rect20.setOpaque(false);
rect21.setOpaque(false);
rect22.setOpaque(false);
inter.add(rect00);
inter.add(rect01);
inter.add(rect02);
inter.add(rect10);
inter.add(rect11);
inter.add(rect12);
inter.add(rect20);
inter.add(rect21);
inter.add(rect22);
frame.add(inter);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String args[])
{
new main().Display();
}
private class sub extends JPanel
{
private BufferedImage image;
public sub ()
{
try
{
image=ImageIO.read(new File("image.jpg"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(600,400));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
}
}
This is the other class
public class Rectangles extends JPanel implements MouseListener
{
private int Posx;
private int Posy;
private int width;
private int height;
private boolean selected=false;
public Rectangles(int Posx,int Posy,int width,int height)
{
this.Posx=Posx;
this.Posy=Posy;
this.width=width;
this.height=height;
this.addMouseListener(this);
}
#Override
protected void paintComponent(Graphics g)
{
if(selected==true)
{
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(new Color(250, 235, 215));
g2.drawRect(Posx,Posy,width,height);
Graphics2D g3=(Graphics2D)g;
g2.setColor(new Color(0,0,0));
g3.setStroke(new BasicStroke(20));
g3.drawLine(Posx,Posy,Posx+width,Posy+height);
g3.drawLine(Posx+width,Posy,Posx,Posy+height);
}
}
public int getX()
{
return Posx;
}
public int getY()
{
return Posy;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public void setSelected()
{
selected=true;
}
#Override
public void mouseClicked(MouseEvent arg0)
{
}
#Override
public void mouseEntered(MouseEvent arg0)
{
}
public void mouseExited(MouseEvent arg0)
{
}
#Override
public void mousePressed(MouseEvent arg0)
{
}
#Override
public void mouseReleased(MouseEvent arg0)
{
selected=true;
repaint();
}
}
1) You dont honor the components paint chain.
As per java docs for paintComponent(Graphics g):
Further, if you do not invoker super's implementation you must honour
the opaque property, that is if this component is opaque, you must
completely fill in the background in a non-opaque color. If you do not
honor the opaque property you will likely see visual artifacts.
2) super.paintComponent would in most cases be the first call in the method.
3) But there is more, your cast to Graphics2D twice, that should not be done:
Graphics2D g2 = (Graphics2D) g;
...
Graphics2D g3=(Graphics2D)g;
omit the g3 its not needed you already have casted to a Graphics2D object
4) Another problem lies here in sub class. You do this in your main code:
inter.add(rect00);
inter.add(rect01);
...
but in inter which is your variable name for the instance of sub class you only have:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
Thus it will only draw a single image no matter how many rectangles you add!
Also dont do
g2.drawLine(Posx, Posy, Posx + width, Posy + height); rather
g2.drawLine(0, 0, Posx + width, Posy + height); as the JPanel has been added at co-ordinates x and y on its container, when you draw on the JPanel we want to start at the top left i.e 0,0, changing the value would move the image further down on its conatiner
See fixed code here:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
private JFrame frame = new JFrame("");
private Rectangles rect00 = new Rectangles(0, 0, 129, 129);
private Rectangles rect01 = new Rectangles(136, 0, 129, 129);
private Rectangles rect02 = new Rectangles(268, 0, 129, 129);
private Rectangles rect10 = new Rectangles(0, 136, 129, 129);
private Rectangles rect11 = new Rectangles(134, 136, 129, 129);
private Rectangles rect12 = new Rectangles(269, 137, 129, 129);
private Rectangles rect20 = new Rectangles(0, 270, 129, 129);
private Rectangles rect21 = new Rectangles(136, 269, 129, 129);
private Rectangles rect22 = new Rectangles(269, 270, 129, 129);
public void Display() {
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(null);
frame.setSize(600, 400);
sub inter = new sub();
inter.setLayout(null);
inter.setBounds(0, 0, 600, 400);
inter.setSize(600, 400);
rect00.setBounds(rect00.getX(), rect00.getY(), rect00.getWidth(), rect00.getHeight());
rect01.setBounds(rect01.getX(), rect01.getY(), rect01.getWidth(), rect01.getHeight());
rect02.setBounds(rect02.getX(), rect02.getY(), rect02.getWidth(), rect02.getHeight());
rect10.setBounds(rect10.getX(), rect10.getY(), rect10.getWidth(), rect10.getHeight());
rect11.setBounds(rect11.getX(), rect11.getY(), rect11.getWidth(), rect11.getHeight());
rect12.setBounds(rect12.getX(), rect12.getY(), rect12.getWidth(), rect12.getHeight());
rect20.setBounds(rect20.getX(), rect20.getY(), rect20.getWidth(), rect20.getHeight());
rect21.setBounds(rect21.getX(), rect21.getY(), rect21.getWidth(), rect21.getHeight());
rect22.setBounds(rect22.getX(), rect22.getY(), rect22.getWidth(), rect22.getHeight());
rect00.setOpaque(false);
rect01.setOpaque(false);
rect02.setOpaque(false);
rect10.setOpaque(false);
rect11.setOpaque(false);
rect12.setOpaque(false);
rect20.setOpaque(false);
rect21.setOpaque(false);
rect22.setOpaque(false);
inter.addPanel(rect00);
inter.addPanel(rect01);
inter.addPanel(rect02);
inter.addPanel(rect10);
inter.addPanel(rect11);
inter.addPanel(rect12);
inter.addPanel(rect20);
inter.addPanel(rect21);
inter.addPanel(rect22);
frame.add(inter);
frame.setResizable(false);
frame.setVisible(true);
}
public static void main(String args[]) {
new Test().Display();
}
private class sub extends JPanel {
private BufferedImage image;
private ArrayList<Rectangles> rects = new ArrayList<>();
public sub() {
try {
image = ImageIO.read(new File("c:/image.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(600, 400));
}
void addPanel(Rectangles r) {
rects.add(r);
add(r);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Rectangles r : rects) {
g.drawImage(image, r.getX(), r.getY(), null);
}
}
}
}
class Rectangles extends JPanel implements MouseListener {
private int Posx;
private int Posy;
private int width;
private int height;
private boolean selected = false;
public Rectangles(int Posx, int Posy, int width, int height) {
this.Posx = Posx;
this.Posy = Posy;
this.width = width;
this.height = height;
this.addMouseListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (selected == true) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(250, 235, 215));
g2.drawRect(0,0, width, height);
g2.setColor(new Color(0, 0, 0));
g2.setStroke(new BasicStroke(20));
g2.drawLine(0,0, width,height);
g2.drawLine(getWidth(),0, 0, height);
}
}
public int getX() {
return Posx;
}
public int getY() {
return Posy;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void setSelected() {
selected = true;
}
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
selected = true;
repaint();
}
}
A few other pointers:
Dont use Absolute/Null layout. A GridLayout or GridBagLayout would suit your needs fine. (see here for more.)
Dont do JFrame#setSize(...); rather use Correct LayoutManager and call pack() on JFrame before setting it visible.
Dont call setSize on your Rectangles instances, simply override getPreferredSize like you did with sub panel??
No need for implementing MouseListener, just use MouseAdapter thus giving you the freedom to choose which methods to override and not just override all.
Have a read on Concurrency in Swing especailly Event-Dispatch-Thread