I am trying to paint a rectangle using JPanel. The problem is, when I click on a menu item it should paint a new rectangle. BUT, when I do this, only part of it is painted. Here is what I mean:
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
does this:
but when I put the squares.addSquare(...) OUTSIDE of the ActionListener, it gives the correct shape (just not when I want it to)
squares.addSquare(10, 10, 100, 100);
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
//nothing on click...
}
});
and here is the correct rectangle:
any idea why it doesn't draw correctly when I put it in the ActionListener? Thanks.
Full Code
class UMLWindow extends JFrame {
Squares squares = new Squares();
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(squares);
//squares.addSquare(10, 10, 100, 100);
JMenuBar menubar = new JMenuBar();
JMenu file = new JMenu("File");
file.setMnemonic(KeyEvent.VK_F);
JMenu shapes = new JMenu("Shapes");
file.setMnemonic(KeyEvent.VK_F);
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
shapes.add(rectangleMenuItem);
menubar.add(shapes);
setJMenuBar(menubar);
setTitle("UML Editior");
setSize(300, 200);
setLocationRelativeTo(null);
}
}
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
private List<Rectangle> squares = new ArrayList<Rectangle>();
public void addSquare(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(x, y, width, height);
squares.add(rect);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
}
}
Swing uses a passive rendering engine, so it doesn't know when it should update/repaint a component unless you tell it.
Start by calling repaint from within your addSquare method...
public void addSquare(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(x, y, width, height);
squares.add(rect);
repaint();
}
This will make a request to the repaint manager informing it the current component needs to be repainted.
You should also be providing a sizing hint for your component, so layout managers to set it's size to 0x0.
Consider overriding the getPreferredSize method of the Squares class and return an appropriate default size...
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
This will also mean that you can call pack on the main window and it will "pack" itself around the content, very neat.
Also, don't call methods which will either change the state of the component or other components or schedule an additional repaint request, as this could setup an infinite loop of repaints which will drain your PC of resources....
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// This is bad...
//this.setOpaque(true);
// This is bad...
//this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
}
Things like opacity and color should be defined outside of the paint cycle. Also, components that extend from JPanel are opaque by default ;)
Related
I try to use swing and I have a litle problem that I fail to solve. That I want to do is simple: I just want to had to JPanel in a JFrame using BorderLayout.
The problem is my center panel is always placed above my North Jpanel. In fact whatever the size I give my north panel just have like 10 pixel, after the center pannel beggin (like on this image).
Note: when I put my second panel south the first panel have enough place to be drawn but even if it has more place the second one also take just 10 pixel which is not enough (like on this image).
this is my Plateau constructor class which extends JFrame:
public Plateau(){
super("arkanoid");
this.setLayout(new BorderLayout());
setFocusable(true);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().add(affich,BorderLayout.NORTH);
this.getContentPane().add(jeu, BorderLayout.CENTER);
setVisible(true);
this.setResizable(false);
this.setMinimumSize(new Dimension(700,800));
this.setLocationRelativeTo(null);
}
here a part of my panel placed in center (the rest is dvariable modification and drawing functions):
public class Jeu extends JPanel {
public Jeu(int score, int plateauX, int balleX, int balleY, boolean perdu){
super();
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D)g;
this.setSize(new Dimension(Width,Heigth));
}
}
and here is all my class supposed to be on north:
public class Affich extends JPanel {
public Affich() {
super();
this.setSize(100,100);
}
public void paint(Graphics g){
this.setSize(100,100);
g.drawOval(0, 0, 50, 50);
}
}
I hope I was clear enough
NEVER call setSize(...) or anything like it within a painting method. These methods should be for painting and painting only, and if you try to change size state, you can end up with a vicious cycle of endless calls -- set size which calls repaint which sets size, which calls repaint.
Instead:
Override the JPanel's paintComponent not paint, since paint is responsible for more than painting the JPanel, and overriding it can have unintended consequences on the JPanel's borders and child components.
Call the super's paintComponent within the override
Again, do only painting within a painting method
Don't set size but instead set preferred size and from code that is called once, and not within a painting method.
For example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
public class MyDrawing {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int affichW = 100;
int affichH = 100;
Affich affich = new Affich(affichW , affichH );
Jeu jeu = new Jeu();
frame.add(affich, BorderLayout.PAGE_START);
frame.add(jeu, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
#SuppressWarnings("serial")
class Jeu extends JPanel {
private static final int JEU_W = 600;
private static final int JEU_H = 450;
public Jeu(int score, int plateauX, int balleX, int balleY, boolean perdu) {
super();
setBorder(BorderFactory.createTitledBorder("Jeu"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
} else {
return new Dimension(JEU_W, JEU_H);
}
}
public Jeu() {
this(0, 0, 0, 0, false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw with g2 here
}
}
#SuppressWarnings("serial")
class Affich extends JPanel {
private int width = 0;
private int height = 0;
public Affich(int width, int height) {
super();
this.width = width;
this.height = height;
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
} else {
return new Dimension(width, height);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw smooth oval
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawOval(0, 0, 50, 50);
}
}
I am trying to add a button which will when pressed, clear the contents off the JPanel and return the panel back to its original set-up. How would i go about doing this? I have tried to revalidate, removeall etc but none have worked for me so far. Any suggestions on how i can do this? I will attach the code below, Help would be greatly appreciated.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class WindowBlind extends JFrame
implements ChangeListener, ActionListener {
private JSlider slider;
private int sliderValue = 0;
private JPanel panel;
private JButton open;
private JButton close;
private JButton exit;
private boolean clear;
public static void main(String[] args) {
WindowBlind applic = new WindowBlind();
applic.setLocation(100,100);
applic.setVisible(true);
} // main
public WindowBlind() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setTitle("WindowBlind");
setSize(300,300);
Container window = getContentPane();
window.setLayout(new FlowLayout());
panel = new JPanel() {
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintScreen(g);
} // paintComponent
};
panel.setPreferredSize(new Dimension(200, 200));
panel.setBackground(Color.white);
window.add(panel);
slider = new JSlider(JSlider.VERTICAL,0,100,0);
slider.setInverted(true); // 0 will be at top, not bottom, of vertical slider
window.add(slider);
slider.addChangeListener(this); // Register for slider events
JButton open = new JButton("Open Slider");
window.add(open);
open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
JButton close = new JButton("Close Slider");
window.add(close);
close.addActionListener(this);
JButton exit = new JButton("Exit Slider");
window.add(exit);
exit.addActionListener(this);
} // WindowBlind constructor
public void paintScreen(Graphics g) {
g.setColor(Color.cyan);
g.fillRect(70, 40, 60, 100); // The blue sky
g.setColor(Color.lightGray);
g.fillRect(70, 40, 60, sliderValue); // The blind, partially closed
g.setColor(Color.black);
g.drawRect(70, 40, 60, 100); // The window frame
} // paintScreen
// When the slider is adjusted, this method is called automatically
public void stateChanged(ChangeEvent e) {
sliderValue = slider.getValue(); // Fetch the slider's current setting
repaint(); // Force a screen refresh (paintComponent is called indirectly)
} // stateChanged
#Override
public void actionPerformed(ActionEvent e) {
}
}
I'm taking a HUGE guess and assuming you want to reset the slider to it's "default" state, which would suggest that you need to change the sliderValue, something like...
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sliderValue = 0;
slider.repaint();
}
});
A better solution would be to generate a self contained class which encapsulated all this functionality, for example...
public class SliderPane extends JPanel {
private double sliderValue;
public double getSliderValue() {
return sliderValue;
}
public void setSliderValue(double sliderValue) {
this.sliderValue = Math.max(Math.min(1.0, sliderValue), 0);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
g.setColor(Color.cyan);
g.fillRect(0, 0, width, height); // The blue sky
g.setColor(Color.lightGray);
g.fillRect(0, 0, width, (int)(sliderValue * height)); // The blind, partially closed
g.setColor(Color.black);
g.drawRect(0, 0, width, height); // The window frame
}
}
Then you could control the slider value through the setSliderValue method.
This also allows you to specify the slider value as percentage, meaning that the size of the component doesn't matter as the area filled is a percentage of the height
This is because you always call paintScreen from the panel's paintComponent method. I would suggest this midification:
panel = new JPanel() {
boolean drawMe = true;
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(drawMe)
paintScreen(g);
} // paintComponent
};
Whenever you want to clear the panel, do this:
panel.drawMe=false;
panel.invalidate();
I having trouble displaying the x,y coordinates of the mouse on JPopupMenu under mouse pointer. the reason that i use JlayerPane in the code is to be able adding cartesian coordinate system dynamically to the JPopupMenu like what you can see in the CAD application.
Does anyone know why it does not display while it paint correctly?
public void show popupwindow(JPanel parentPanel, int xLocation, int yLocation){
Box selectionBox = createSelectionBox();
JPopupMenu popupMenu = new JPopupMenu();
// create an instance of my custom mouse cursor label
XYMouseLabel mouseLabel = new XYMouseLabel();
mouseLabel.setBounds(0, 0, selectionBox.getWidth(), selectionBox.getHeight());
JPanel panel = new JPanel();
panel.setSize(new Dimension(500, 400));
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(400, 300));
layeredPane.setBorder(BorderFactory.createTitledBorder("Move the Mouse to get coordinate"));
layeredPane.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent me) {
super.mouseMoved(me);
mouseLabel.x = me.getX();
mouseLabel.y = me.getY();
mouseLabel.repaint();
}
});
layeredPane.add(mouseLabel , JLayeredPane.DRAG_LAYER);
layeredPane.add(selectionBox, 2, 0);
panel.add(layeredPane);
popupMenu.add(panel);
popupMenu.show(parentPanel , xLocation,yLocation)
}
public class XYMouseLabel extends JComponent {
public int x;
public int y;
public XYMouseLabel() {
this.setBackground(Color.green);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hier paint the cartesian coordinate system
String s = x + ", " + y;
g2.setColor(Color.red);
g2.drawString(s, x, y);
}
}
So, I have this simple program that allows you to click a JMenu item "New Rectangle" and it adds a shape on the center of the screen. My question is: how can I click-and-drag this around the window? I know I will need some type of Mouse Listener but I'm not sure exactly how to implement it.
public class SimpleDraw {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
// Display the window.
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Squares squares = new Squares();
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
}
public void addMenus() {
getContentPane().add(squares);
JMenuBar menubar = new JMenuBar();
JMenu shapes = new JMenu("Shapes");
JMenuItem rectangleMenuItem = new JMenuItem("New Rectangle");
rectangleMenuItem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
squares.addSquare(10, 10, 100, 100);
}
});
shapes.add(rectangleMenuItem);
menubar.add(shapes);
setJMenuBar(menubar);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
class Squares extends JPanel {
private static final long serialVersionUID = 1L;
private List<Rectangle> squares = new ArrayList<Rectangle>();
public void addSquare(int x, int y, int width, int height) {
Rectangle rect = new Rectangle(getWidth() / 2 - width / 2, getHeight()
/ 2 - height / 2, width, height);
squares.add(rect);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
repaint();
}
}
Maybe it can help you :
http://zetcode.com/tutorials/javaswingtutorial/resizablecomponent/
Disadvantage of code
In this code we do not layout manager. So if we have a component in center of jframe when this jframe is re-sizing this component maybe display in corner of jframe. You can solve this problem but it is a little complex. We can talk about this problem if you want.
I wrote a solution for this in this package here. You can see that there is an AreaDragger class, which sounds like what you are looking for
So I am testing out a JSlider for a bigger project and can't get it to work. The slider is supposed to adjust the size of a circle, and it's not working. I thought I might have an issue with the creation of the circle, and I am trying to use setFrame, and it's giving an error saying it's "undefined." Can anyone see why? Since it should take in either float or double as parameters. Or if you can see why it's not adjusting the size of the shape that would help a lot too... Here's what I have:
public class DrawShape extends JPanel{
private float width = 300;
private Shape circle = new Ellipse2D.Float(100, 20, width, 300);
public DrawShape() {
}
public DrawShape(float width) {
this.width = width;
}
public void setWidth(int w) {
this.width = w;
circle.setFrame(100, 20, width, 300);//This is where the error is
}
public void paintComponent (Graphics g) {
super.paintComponents(g);
Graphics2D graphics = (Graphics2D)g;
graphics.setColor(Color.black);
graphics.fill(circle);
}//end paintComponent
}//end class
Class with main:
public class SliderTest extends JFrame{
private static DrawShape circle = new DrawShape();
JSlider slider;
JLabel label;
public SliderTest() {
setLayout(new FlowLayout());
slider = new JSlider(JSlider.HORIZONTAL, 150, 450, 300);//orientation, min val, max value, starting val
slider.setMajorTickSpacing(50);//every 5 integers will be a new tick position
slider.setPaintTicks(true);
add(slider);
label = new JLabel("Current value 300");
add(label);
event e = new event();
slider.addChangeListener(e);;
}//end cons
public class event implements ChangeListener{
public void stateChanged(ChangeEvent e) {
JSlider slider = (JSlider)e.getSource();
int value = slider.getValue();
label.setText("Current Value " + value);
circle.setWidth(value);
repaint();
}//end stateChanged
}//end class event
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Circle");
frame.add(circle);
frame.setSize(500,400);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
JFrame frame1 = new SliderTest ();
frame1.setTitle("Toolbar");
frame1.setSize(300,200);
frame1.setLocation(200,100);
frame1.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame1.setVisible(true);
}
}
Shape does not have a setFrame method. RectangularShape does...
Instead of
private Shape circle = new Ellipse2D.Float(100, 20, width, 300);
You might try using...
private Ellipse2D circle = new Ellipse2D.Float(100, 20, width, 300);
instead...
Your public DrawShape(float width) { constructor is also wrong, as it does not actually do anything.
You should also consider overriding the getPreferredSize method so it can return the width of the shape as a part of the preferred size.
I'm not sure you actually need to maintain the width reference as you can ascertain this from the circle directly...IMHO
For Example
I've not tested this...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
public class DrawShape extends JPanel {
private final Ellipse2D circle = new Ellipse2D.Float(100, 20, 300, 300);
public DrawShape() {
}
public DrawShape(float width) {
circle.setFrame(100, 20, width, 300);
}
public void setWidth(int w) {
circle.setFrame(100, 20, w, 300);
revalidate();
}
#Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
size.width = circle.getBounds().width;
return size;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D graphics = (Graphics2D) g;
graphics.setColor(Color.black);
graphics.fill(circle);
}//end paintComponent
}//end class