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
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);
}
}
For a school project I have to create a simple canvas on which I have to draw random circles. For this I am using a for loop where I kept spawning circles. To keep my PC from crashing, I used the Thread.sleep(20) method. It worked well but had one flaw: it is unable to close itself using the close button.
public class CanvasDraw extends Canvas {
private Random rand = new Random();
public void paint(Graphics graph){
setBackground(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
for(int i = 0; i<9999; i++){
graph.setColor(new Color(rand.nextInt(255),
rand.nextInt(255), rand.nextInt(255)));
graph.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
CanvasDraw drawing = new CanvasDraw();
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.getContentPane().add(drawing);
}
}
How can I fix this?
In your code you paint 10000 ovals each time paint is invoked (which can be quite often). This will clog the event dispatch thread. Paint your ovals on a BufferedImage in your main, and paint that instead. Also extend JComponent instead of Canvas and overwrite paintComponent instead of paint.
Here you go:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CircleDraw {
public static void main(String[] args) throws InterruptedException {
Random rand = new Random();
BufferedImage bufferedImage = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
Graphics g = bufferedImage.getGraphics();
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
for (int i = 0; i < 9999; i++) {
g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
g.fillOval(rand.nextInt(480), rand.nextInt(480), 120, 120);
}
JComponent component = new JComponent() {
#Override
protected void paintComponent(Graphics g) {
g.drawImage(bufferedImage, 0, 0, null);
}
};
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(component);
frame.setSize(bufferedImage.getWidth(), bufferedImage.getHeight());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Try running your spawning circles loop to run on separate thread.
Don't run it on MAIN thread!!
I created a simple circle animation using Java Swing. Here's what the GUI looks like.
The circle repaints every second with a different color and a different circle radius. The drawing area is 420 x 420 pixels. The color varies randomly. The circle radius varies randomly from 42 pixels to 140 pixels.
The JFrame is created in the run method of the CircleAnimation class.
The drawing panel Is created in the DrawingPanel class. All of the custom painting happens in the paintComponent method of the DrawingPanel class.
The animation is set up as a java.swing.Timer after the JFrame is created. The Animation class holds the ActionListener for the Timer. The actionPerformed method creates a new random color and circle radius.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class CircleAnimation implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleAnimation());
}
#Override
public void run() {
JFrame frame = new JFrame("Circle Animation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
Timer timer = new Timer(1000, new Animation(drawingPanel));
timer.setInitialDelay(1000);
timer.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int radius;
private Color color;
private Random random;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(420, 420));
this.random = new Random();
setRandomColorAndRadius();
}
public void setRandomColorAndRadius() {
this.color = new Color(getRandom(0, 255),
getRandom(0, 255), getRandom(0, 255));
int start = getPreferredSize().width / 10;
int limit = getPreferredSize().width / 3;
this.radius = getRandom(start, limit);
}
private int getRandom(int start, int limit) {
return random.nextInt(limit - start) + start;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() / 2 - radius;
int y = getHeight() / 2 - radius;
int width = radius + radius;
int height = width;
g.setColor(color);
g.fillOval(x, y, width, height);
}
}
public class Animation implements ActionListener {
private DrawingPanel drawingPanel;
public Animation(DrawingPanel drawingPanel) {
this.drawingPanel = drawingPanel;
}
#Override
public void actionPerformed(ActionEvent event) {
drawingPanel.setRandomColorAndRadius();
drawingPanel.repaint();
}
}
}
I want to draw a disc that changes colors twice a second. The disk is drawn on a DrawPanel which extends a JPanel and in the main method the DrawPanel is added to a frame.
For the colorchanging I use a timer which works when I'm trying to change the background of the DrawPanel in the main method (what i commented out).
Can someone tell me why it doesn't work for the Graphics g object or any other suggestions?
I just copied the code from the main method and added it into the paintComponent() method, but here it doesn't work.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class DrawPanel extends JPanel{
public GridBagLayout gbl;
//position and dimension
int x = 0, y = 0, width = 200, height = 200;
public DrawPanel(){
repaint();
}
public DrawPanel(GridBagLayout gridBagLayout) {
this.gbl = gridBagLayout;
}
public void paintComponent(Graphics g){
//Overwriting of old picture
super.paintComponent(g);
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Random gen = new Random();
Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
//Draw color disk
g.setColor(color);
g.fillArc(x, y, width, height, 0, 360);
}
};
Timer t = new Timer(500, action);
t.setRepeats(true);
t.setInitialDelay(0);
t.start();
//Draw boundary of circle
g.setColor(Color.BLACK);
g.drawArc(x, y, width, height, 0, 360);
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setSize(300, 300);
final DrawPanel panel = new DrawPanel();
panel.setOpaque(true);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// ActionListener action = new ActionListener() {
// #Override
// public void actionPerformed(ActionEvent e) {
// Random gen = new Random();
// Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
// panel.setBackground(color);
// }
// };
//
// Timer t = new Timer(500, action);
// t.setRepeats(true);
// t.setInitialDelay(0);
// t.start();
}
}
The Graphics object is transient, so you should not cache it even if the compiler allows that. Instead establish the timer in the constructor of the class, set the BG of the panel, then call for a repaint. E.G.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class DrawPanel extends JPanel {
Random gen = new Random();
//position and dimension
int x = 0, y = 0, width = 200, height = 200;
Color drawColor = Color.BLACK;
public DrawPanel() {
repaint();
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(gen.nextInt(256), gen.nextInt(256), gen.nextInt(256));
//Draw color disk
drawColor = color;
DrawPanel.this.repaint();
}
};
Timer t = new Timer(500, action);
t.setRepeats(true);
t.setInitialDelay(0);
t.start();
}
#Override
public void paintComponent(Graphics g) {
//Overwriting of old picture
super.paintComponent(g);
//Draw boundary of circle
g.setColor(drawColor);
g.drawArc(x, y, width, height, 0, 360);
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setSize(300, 300);
final DrawPanel panel = new DrawPanel();
panel.setOpaque(true);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Graphics objects are only valid for that one draw
It would be better to instead to tell the JPanel to change it's current color (with a variable) and then tell it to repaint
Add variable discColor to your JPanel
Change your drawing code to not use timers and instead make it simple, just draw the disc based off of discColor
With a timer, update the discColor variable and then call the panel's repaint() method
I have this inner class:
private class Plats extends JComponent{
private String namn;
Plats(int x, int y, String n){
namn=n;
setBounds(x-10, y-10, 150, 40);
setPreferredSize(new Dimension(20, 20));
setMinimumSize(new Dimension(20, 20));
setMaximumSize(new Dimension(20, 20));
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(0, 0, 20, 20);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString(namn, 0, 34);
}
public boolean contains(int x, int y){
return x<20 && x>0 && y<20 && y>0;
}
}
I want to set the bounds of the component to the width of the string that the constructor gets, but I can only get it to work if I do it inside the paintComponent method since I need the graphics object. It feels wrong to do it in the paintComponent method since every time the component has to be repainted it will set the bounds again and I only want to do it once when it is created.
Suggestions how I can solve this? Or should I just do it in the paintComponent anyway?, it works but I doesnt feel like a nice solution :( ?
Please don't use AbsoluteLayout, only if is there really important reason, use proper LayoutManager,
your JComponent should be returns PreferredSize (notice PreferredSize isnot accepted by all of standard or custom LayoutManagers) to its parent or container, for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
CustomComponents cc = new CustomComponents();
/*cc.addComponentListener(new java.awt.event.ComponentAdapter() {
#Override
public void componentResized(ComponentEvent event) {
setSize(Math.min(getPreferredSize().width, getWidth()),
Math.min(getPreferredSize().height, getHeight()));
}
});*/
add(cc, BorderLayout.CENTER);
CustomComponents cc1 = new CustomComponents();
add(cc1, BorderLayout.EAST);
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
//setMaximumSize(getMaximumSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(800, 600);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}
Even without a graphics context, you can use either TextLayout or FontRenderContext to find the bounds; the two are compared here. Because you extend JComponent, your component should override the getXxxSize() methods, as shown in mKorbel's answer.
You can use the Font#getStringBounds(String str, FontRenderContext frc) to get a bounding rectangle of the string in the form of the said Font. From there you can return the width and height.
Font Java 1.6 API
I am extending JFrame like this:
public GameFrame() {
this.setBounds(30, 30, 500, 500);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
initializeSquares();
}
private void initializeSquares(){
for(int i = 0; i < 5; i++){
this.getContentPane().add(new Square(i*10, i*10, 100, 100));
}
this.setVisible(true);
}
However, only one square is being drawn on the screen, does anybody know why?
also My Square class looks like this:
private int x;
private int y;
private int width;
private int height;
public Square(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(x, y, width, height);
}
The JFrame's contentPane uses BorderLayout by default. When you add a Square to it, it gets added by default BorderLayout.CENTER and covers up any previously added Squares. You will want to read up on all the layout managers available to Swing GUI's.
For e.g., start here: Laying Out Components within a Container
But having said this, I would do things differently. I would create just one JPanel and make it able to paint multiple squares. For example something like so:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class GameFrame extends JFrame {
public GameFrame() {
super("Game Frame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Squares squares = new Squares();
getContentPane().add(squares);
for (int i = 0; i < 15; i++) {
squares.addSquare(i * 10, i * 10, 100, 100);
}
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new GameFrame();
}
}
class Squares extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
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
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rect : squares) {
g2.draw(rect);
}
}
}
Fo absolute positioning, call setLayout(null). Then the icons will be drawed at the position returned by their getLocation() method, so you might want to call the setLocation() first.
Even I was having an issue while displaying multiple rectangles on a jframe with absolute layout i.e layout set to null.
JFrame frame = new JFrame("hello");
frame.setLayout(null);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
frame.getContentPane().setBackground(Color.red);
frame.getContentPane().add(new Square(10,10,100,100));//My rectangle class is similar to the Square mentioned in the question and it extends JComponent.
Still I was getting issues as the rectangle was not displayed.Unless you explicitly set the bounds on the Square(JComponent) ,it will not work.Even though the Square has the location passed in the constructor ,setbounds only fixed the issue.So the below fixed the issue and this issue is for an absolute layout jframe.
Square square = new Square(10,10,100,100);
square.setBounds(10,10,100,100);
frame.getContentPane().add(square);