Java - Swing - painting (adding another shapes on a JPanel) - java

class that makes a JFrame, adds a JPanel on it and draws a rectangle on the JPanel
class Frame {
JFrame frame;
myPanel panel;
void draw() {
frame = new JFrame ("qwertz");
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setSize(300,200);
panel = new myPanel();
panel.setLayout(null);
frame.add(panel);
myPanel.a = 50;
myPanel.b = 30;
}
void add() {
//
}}
second class is the JPanel that the first class uses
class myPanel extends JPanel {
static int a;
static int b;
public void paint(Graphics g) {
g.drawRect(a,a,b,b);
}}
what is the easiest way to add another rectangle on the panel ?
(I would like the code that adds it to be in the add() method in the first class, if its possible)

You don't want to call a method "add". Every Swing component has an add method.
Create a GUI model class that holds as many rectangles as you want to define.
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
public class RectangleModel {
private List<Rectangle> rectangles;
public RectangleModel() {
this.rectangles = new ArrayList<Rectangle>();
}
public void addRectangle(int x, int y, int width, int height) {
this.rectangles.add(new Rectangle(x, y, width, height));
}
public void addRectangle(Rectangle rectangle) {
this.rectangles.add(rectangle);
}
public void draw(Graphics g) {
for (Rectangle rectangle : rectangles) {
g.drawRect(rectangle.x, rectangle.y, rectangle.width,
rectangle.height);
}
}
}
Modify your JPanel so it looks like this:
class MyPanel extends JPanel {
private RectangleModel model;
public MyPanel(RectangleModel model) {
this.model = model;
this.setPreferredSize(new Dimension(300, 200));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
model.draw(g);
}}
}
Now, all your main class has to do is:
Execute SwingUtilities.invokeLater to put all of your GUI components on the Event Dispatch thread (EDT).
Create your GUI model.
Create your GUI frame class and panel class.
Add Rectangles to your GUI model.
Pack the JFrame.

Related

EDITED!! uncalled getter function causes the JPanel to reprint in the wrong location

This is quite bizarre, I have a gui program that allows user to update a J panel by detecting mouseclick
every time the mouse click is detected, the JPanel repaints itself. for some reason the repaint is off by like 30-40 pixels even though through testings,it shows that they are painting at the exact same coordinates. This problem is solved however after I minimize and then re-maximize the window .
the repaint is called in the after detecting mouse click in the same method
Edit: below is a minimum reproduction of that error
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
public class CenterPanel extends JPanel{
private int sideLength = 50;
private int x = 10;
private int y = 10;
public CenterPanel() {
setPreferredSize(new Dimension(x*sideLength,y*sideLength));
addMouseListener(new Mouse());
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
this.setBorder(new LineBorder(Color.BLACK,3));
try {
createCanvas(x,y,g,sideLength);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createCanvas(int x, int y, Graphics g, int sideLength) throws InterruptedException {
int coordX=0;
int coordY=0;
for(int i=0; i<x;i++) {
for(int j=0; j<x;j++) {
paintRectangle(g,Color.LIGHT_GRAY,coordX,coordY,sideLength-1,sideLength-1);
coordX=coordX+sideLength;
}
coordX=0;
coordY=coordY+sideLength;
}
}
private static void paintRectangle(Graphics g,Color color,int x, int y,int width,int height) throws InterruptedException {
g.setColor(color);
g.fillRect(x, y, width, height);
}
class Mouse extends MouseAdapter{
public void mouseClicked (MouseEvent e) {
repaint();
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
import javax.swing.*;
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class TestFrame extends JFrame implements MouseMotionListener, ActionListener{
private static Color currentColor = Color.lightGray;
private static Color defaultColor = Color.lightGray;
private CenterPanel centerPanel;
JButton red, yellow, white, pink, orange, magenta, light_gray, green, gray, dark_gray, cyan, blue, black;
public TestFrame() {
centerPanel=new CenterPanel();
gui();
}
private void gui() {
JPanel topPanel = new JPanel ();
topPanel.setBackground(Color.GREEN);
topPanel.setPreferredSize(new Dimension(50,50));
JPanel bottomPanel = new JPanel ();
bottomPanel.setBackground(Color.YELLOW);
bottomPanel.setPreferredSize(new Dimension(50,50));
JPanel leftPanel = new JPanel ();
leftPanel.setBackground(Color.RED);
leftPanel.setPreferredSize(new Dimension(50,50));
JPanel rightPanel = new JPanel ();
rightPanel.setBackground(Color.PINK);
rightPanel.setPreferredSize(new Dimension(50,50));
Container c = this.getContentPane();
c.setLayout(new BorderLayout());
c.add(centerPanel, BorderLayout.CENTER);
c.add(topPanel, BorderLayout.NORTH);
c.add(bottomPanel, BorderLayout.SOUTH);
c.add(leftPanel, BorderLayout.WEST);
c.add(rightPanel, BorderLayout.EAST);
this.setVisible(true);
pack();
}
public void actionPerformed(ActionEvent e) {
}
public static void main(String[] args) {
TestFrame t=new TestFrame();
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
}
Edit2: I performed another test by adding Jpanels to North South and West borderlayout locations and find out that the center borderlayout location (the one canvas is in) is misaligned and was overlapping with other Jpanels and was fixed after repaint. Which seems to be whats causing the change in Canvas location.
Edit3: photo of said experiment
Update: I changed setSize() of the CenterPanel Jpanel to setPreferredSize(). now the overlapping shows up without having to call repaint (mouse click).
**UPDATE!!! I have narrowed the problem down to the getter function in the CenterPanel **
public int getX() {
return x;
}
public int getY() {
return y;
}
these two. if I remove them the problem disappears. The problem is I still dont understand why the two getter function could cause this issue when they aren't even called???
You are overriding JComponents (the superclass of JPanel) getX() and getY().
When the position of your panel is determined, it will call those two getters and use them as the coordinates - in pixels.
Name your getters in a different way.
And enable your IDE's warning WRT missing #Override annotations.
Could you please provide a MyFrame class?
I tried with this code and it worked well:
import javax.swing.JFrame;
import java.awt.Color;
public class App extends JFrame {
public static void main(String[] args) {
App app = new App();
app.start();
}
public static Color getCurrentColor() {
return Color.GREEN.darker();
}
public void start() {
setSize(400, 400);
setVisible(true);
add(new Canvas());
}
public static Color getDefaultColor() {
return Color.BLUE;
}
}
Replace
super.paintComponents(g);
with
super.paintComponent(g);

paint() doesn't work after panel and frame was added

I am making kind of my paint that creates shapes, it worked fine until I added layers(panel and frame), now the shapes aren't visible on button press I assume it is under the layers?i tried using paintComponent and revalidate etc and still couldn't get it to appear
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
public boolean SquareCheck;
public boolean CircleCheck;
JButton buttonSquare;
JButton buttonCircle;
JFrame frame;
JPanel panel;
public void paint(Graphics g) {
if (SquareCheck) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
} else if (CircleCheck) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public void start() {
frame = new JFrame();
panel = new JPanel();
//setLayout(new BorderLayout());
buttonSquare = new JButton("■");
buttonCircle = new JButton("●");
buttonSquare.setPreferredSize(new Dimension(200, 20));
buttonCircle.setPreferredSize(new Dimension(200, 20));
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
//add(buttonCircle, BorderLayout.NORTH);
//add(buttonSquare, BorderLayout.SOUTH);
JToggleButton red = new JToggleButton();
panel.add(buttonCircle);
panel.add(buttonSquare);
frame.add(panel);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonSquare) {
SquareCheck = true;
} else if (e.getSource() == buttonCircle) {
CircleCheck = true;
}
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
Main2 x = new Main2();
x.start();
}
}
Basiclly the buttons changes the boolean then the repaint is called and based on the boolean it draws either a cirlce or a square,the code worked before adding frame and panel
Your paint method is a method of the Main2 class, but you never add a Main2 instance to the JFrame or to any component that goes into the JFrame, and so the Main2 instance will never be displayed, and the Swing painting manager will never call its paint method.
For starters, get rid of this variable, panel = new JPanel(); and every place you use panel, substitute this. This way you'll be working with a correct Main2 instance and adding it to the GUI.
Other issues:
You need to call the super's equivalent painting method in your override on its first line
Override paintComponent, not paint, and yes call super.paintComponenet(g); in this override
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
For safety's sake, add the #Override annotation above any method that you think may be overriding a parent method (such as paint), to make sure that you are doing it correctly.
For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
#SuppressWarnings("serial")
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
private JToggleButton buttonSquare;
private JToggleButton buttonCircle;
JFrame frame;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buttonSquare.isSelected()) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
}
if (buttonCircle.isSelected()) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public Main2() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buttonSquare = new JToggleButton("■");
buttonCircle = new JToggleButton("●");
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
this.add(buttonCircle);
this.add(buttonSquare);
frame.add(this);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
new Main2();
}
}
class MyShape {
public int x, y, width, length;
public MyShape(int x, int y, int width, int length) {
this.x = x;
this.y = y;
this.width = width;
this.length = length;
}
}

How can I pass an argument to paintComponent in order to call it in a different class?

In my main class I have the following code to load an image from my machine and display it on the frame to draw things on it:
public class ShowMap extends JPanel {
private static final int WIDTH = 1340;
private static final int HEIGHT = 613;
public void main(String args[]) {
JFrame frame = new JFrame("MAP");
frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));
frame.setMaximumSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = (JPanel)frame.getContentPane();
JLabel label = new JLabel();
label.setIcon(new ImageIcon("map.png"));
panel.add(label);
}
}
The image I am loading is a map where I would like to indicate the position of some objects by drawing points in the right coordinates. So it is important here to dictate to the DrawPoint class (below) what coordinates should get the point.
Also, I would greatly appreciate an explanation of how to erase a point that has been drawn.
My search led me to the following, but as soon as I add int coordx, int coordy to the arguments of the method, it is no more highlighted, and I don't know how to call this method in ShowMap while passing the coordinates as arguments.
public class DrawPoint extends JPanel {
private int coordx;
private int coordy;
public void paintComponent(Graphics g, int coordx, int coordy){
g.setColor(Color.BLACK);
g.fillOval(coordx,coordy,8,8);
}
}
Here is a demonstration of what MadProgrammer wrote in his comment : "should be changing a state variable of the component and then calling repaint" :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SwingTest extends JFrame {
private static final int SIZE = 300;
private DrawPoint drawPoint;
public SwingTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
drawPoint = new DrawPoint();
drawPoint.setPreferredSize(new Dimension(SIZE, SIZE));
add(drawPoint);
pack();
setVisible(true);
}
//demonstrate change in DrawPoint state
private void reDraw() {
Random rnd = new Random();
Timer timer = new Timer(1000, e -> { //periodically change coordinates and repaint
drawPoint.setCoordx(rnd.nextInt(SIZE));
drawPoint.setCoordy(rnd.nextInt(SIZE));
drawPoint.repaint();
});
timer.start();
}
public static void main(String[] args){
SwingUtilities.invokeLater(() -> new SwingTest().reDraw());
}
}
class DrawPoint extends JPanel {
private int coordx, coordy;
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(coordx,coordy,8,8);
}
//use setters to change the state
void setCoordy(int coordy) { this.coordy = coordy; }
void setCoordx(int coordx) {this.coordx = coordx;}
}

Java Drawing to a JPanel (debugging)

I'm trying to draw a basic object to a JPanel
although it doesn't seem to be working.
I'm certain I am doing something wrong with where the paint method
is being called
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class testGui {
static colors gc_colors;
static gui gc_gui;
public static void main(String[] args) {
gc_colors = new colors();
gc_gui = new gui();
gc_gui.cv_frame.setVisible(true);
}
public static class colors {
Color cv_ltGrey;
Color cv_mdGrey;
Color cv_dkGrey;
public colors() {
cv_ltGrey = Color.decode("#DDDDDD");
cv_mdGrey = Color.decode("#CCCCCC");
cv_dkGrey = Color.decode("#111111");
}
}
public static class gui {
JFrame cv_frame;
JPanel cv_panel;
JPanel cv_content;
public gui() {
cv_frame = new JFrame();
cv_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cv_frame.setTitle("Test GUI");
cv_frame.setSize(600, 400);
cv_frame.setLayout(new FlowLayout());
cv_panel = new JPanel();
cv_panel.setBackground(gc_colors.cv_ltGrey);
cv_panel.setPreferredSize(new Dimension(500, 300));
cv_frame.add(cv_panel);
cv_content = new content();
cv_panel.add(cv_content);
}
}
public static class content extends JPanel {
public void paint(Graphics graphic) {
super.paint(graphic);
draw(graphic);
}
public void update() {
repaint();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.setPaint(gc_colors.cv_ltGrey);
graphic2D.fillRect(10, 10, 100, 100);
}
}
}
I have a class for my gui which I am adding a JPanel to (a light grey one).
Which I am then trying to add my drawing to using a JPanel extended class
called content.
When I run it though it seems to create the grey JPanel which I want but
the drawing is just a tiny white square and I'm not sure why.
So, you content panel has a default preferred size of 0x0, FlowLayout honours the preferredSize of its components (with a little margin), hence the reason why you have a nice little small white rectangle.
What you need to do is override the getPreferredSize method of the content panel and return a suitable size, for example
public static class content extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
public void paint(Graphics graphic) {
super.paint(graphic);
draw(graphic);
}
public void update() {
repaint();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.setPaint(gc_colors.cv_ltGrey);
graphic2D.fillRect(10, 10, 100, 100);
}
}
I've decided to just leave out the second JPanel altogether.
It was too much of a hassle to put the JPanel inside of another JPanel
so instead I am only going to use a single JPanel
public static class gui {
JFrame cv_frame;
JPanel cv_panel;
JPanel cv_content;
public gui() {
cv_frame = new JFrame();
cv_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cv_frame.setTitle("Test GUI");
cv_frame.setSize(600, 400);
cv_frame.setLayout(new FlowLayout());
cv_content = new content();
cv_content.setBackground(gc_colors.cv_ltGrey);
cv_content.setPreferredSize(new Dimension(500, 300));
cv_frame.add(cv_content);
}
}

What will be the Main of this program in java |How do i call the line method in main?

package drawinglinebymethods;
import java.awt.*;
import javax.swing.JFrame;
public class DrawingLineByMethods extends Frame {
public JFrame f=new JFrame();
void fra_size()
{
f.setSize(450, 300);
}
void fra_visible()
{
f.setVisible(true);
}
void fra_title()
{
f.setTitle(" java frame");
}
void exit()
{
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void line(Graphics g) {
g.drawLine(10, 10, 20, 300);
}
public static void main(String[] args) {
DrawingLineByMethods obj = new DrawingLineByMethods();
obj.fra_size();
obj.fra_title();
obj.fra_visible();
obj.fra_exit();
obj.line(g); // <-- error here
}
}
Your question suggests that you are not yet clear on how graphics and drawing works in Swing GUI's. Some suggestions for you:
Don't have your class extend java.awt.Frame as this makes no sense. You're not creating an AWT Frame window and have no need of this.
Create a class that extends JPanel and draw in its paintComponent method as the Swing painting tutorials (link now added) show you.
You never call drawing code directly but rather it is indirectly called by the JVM.
If you want to draw from outside of the GUI itself, then draw to a BufferedImage, one that is then displayed within the GUI.
Don't use a Graphics object obtained by calling getGraphics() on a GUI component as the object thus obtained will not persist, and this risks you creating unstable graphics or worse -- throwing a NullPointerException.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
public LineDraw() {
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
If you want to add lines as the program runs, again, use a BufferedImage that is drawn within the JPanel's paintComponent method. When you need to add a new line to the GUI, extract the Graphics or Graphics2D object from the BufferedImage using getGraphics() or createGraphics() respectively (it's OK to do this), draw with this Graphics object, dispose of the Graphics object, and repaint the GUI. For example in the code below, I draw a new line when the button is pressed by adding code within the JButton's ActionListener (actually its AbstractAction which is similar to an ActionListener but more powerful):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
private BufferedImage img;
private int yDistance = 20;
private int deltaY = 10;
public LineDraw() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
add(new JButton(new DrawLineAction("Draw Line", KeyEvent.VK_D)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// draw the buffered image here
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
public void drawNewLines() {
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
yDistance += deltaY;
g2.drawLine(10, 10, yDistance, PREF_H);
g2.dispose();
repaint();
}
private class DrawLineAction extends AbstractAction {
public DrawLineAction(String name, int mnemonic) {
super(name); // give button its text
putValue(MNEMONIC_KEY, mnemonic); // alt-hot key for button
}
#Override
public void actionPerformed(ActionEvent e) {
drawNewLines();
}
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Line is a non-static method, the only way to call it from a static method (main here) is to have an instance of the class.

Categories