For a homework assignment I have to make a Java program that draws a red circle on a jframe when clicking a "start" button. When clicking the button, the method setSmallCircle is called. This does work, but inside this method I'm making a calling repaint(), but this doesn't seem to call the paintComponent method.
This is my code so far:
import java.awt.*;
import javax.swing.*;
class ReactionPanel extends JPanel {
Color color;
int size;
public void paintComponent(Graphics g){
System.out.println("paintcomp 1");
super.paintComponent(g);
System.out.println("paintcomp 2");
g.setColor(color);
g.fillOval(200, 200, size, size);
}
public void setSmallCircle(Color c){
color = c;
size = 10;
System.out.println("drawing");
repaint();
System.out.println("repaint called");
}
}
The method setSmallCircle(Color.red) is called by some other class. Does anyone know why the "repaint()" isn't drawing a red circle?
Any update to the painting of swing component should be inside EDT (eevent dispatch thread). However while experimenting following portion:
public void paintComponent(Graphics g){
System.out.println("paintcomp 1");
super.paintComponent(g);
g.setColor(color);
System.out.println(color); // print color as null
g.fillOval(20, 20, size, size); // printing size as 0
System.out.println(size);
}
updating color and size in setSmallCircle() is not taking effect !! paintComponent seems to keep using the old value, instead of updated value.
Related
I wrote a program that draws triangles on the screen. However only the first triangle is shown. How can I make multiple custom JComponents visible?
I already tried to create something like a draw() method but then I can't perform any actions on this object like i. e. I would like the color of the triangle to change whenever I click on it. For this I would need a MouseListener but it won't work with the draw() method.
View.java file:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class View extends JPanel {
public View()
{
setPreferredSize(new Dimension(300, 300));
add(new Triangle(20, 50, Color.red)); //this one will react to mouseClicked
add(new Triangle(100, 200, Color.pink)); //this one doesn't appear
}
public static void main(String []args)
{
JFrame frame = new JFrame("Trianlge test");
frame.add(new View());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Triangle p3 = new Triangle(60, 120, Color.blue); //this one won't react to mouseClicked()
p3.draw(g);
}
}
Triangle.java file:
package test;
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.geom.GeneralPath;
import javax.swing.JComponent;
public class Triangle extends JComponent implements MouseListener{
private int x,y;
private Color c;
public Triangle(int x, int y, Color c)
{
this.x = x;
this.y = y;
this.c = c;
setPreferredSize(new Dimension(100, 100));
addMouseListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
GeneralPath path = new GeneralPath();
g2d.setColor(c);
path.moveTo(x, y);
path.lineTo(x, y);
path.lineTo(x+50, y);
path.lineTo(x, y-50);
path.closePath();
g2d.fill(path);
repaint();
}
public void draw(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
GeneralPath path = new GeneralPath();
g2d.setColor(c);
path.moveTo(x, y);
path.lineTo(x, y);
path.lineTo(x+50, y);
path.lineTo(x, y-50);
path.closePath();
g2d.fill(path);
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
c = Color.cyan;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
JFrame frame = new JFrame();
First of all that statement in your View class is completely unnecessary. You would not create a JFrame instance in the constructor of a component. Also your code never references the variable which is a good indication it is not needed.
However, the main problem is your concept of creating custom components is wrong:
setPreferredSize(new Dimension(100, 100));
You attempt to set the preferred size of the component.
add(new Triangle(100, 200, Color.pink)); //this one doesn't appear
But then you attempt to do you custom painting at (100, 200) which is outside the size of the component. So the painting logic clipped at the size of component so you don't see anything being painted.
Custom painting should be done relative to (0, 0) of the component, not relative to the parent component.
If you you want to randomly position components on the parent panel then you need to:
set the parent panel to use a null layout
set the location of each component you add to the panel
set the size of each component you add to the panel.
basically you need to take over the functionality of the layout manager.
Other problems with your current painting code:
Don't invoke repaint() in a painting method. This will essentially cause an infinite painting loop. If you need animation you use a Swing Timer to schedule the animation.
Don't invoke paintComponent(...) directly. Swing will invoke paintComponent() when a component needs to be repainted.
However, I would suggest that if you want to paint Shapes on a panel, Then you forget about creating custom components. Instead you keep an ArrayList of the Shapes you want to paint and then in the paintComponent() method of the panel you iterate through the ArrayList to paint each shape.
For an example of this approach take a look at the Draw On Component example found in Custom Painting Approaches.
Note:
If you really want to be able to handle mouse events then you need to use a Shape object to represent your shapes to do proper hit detection. If you just display your shape as a component, then the mouse hit will be detected if you click anywhere in the rectangular area of the component, not just the triangle part that you actually paint. The Shape class has a contains(...) method you can use to determine if you actually click in the Shape or not.
Check out Playing With Shapes for more information on this concept.
Set a border to Triangle components like this:
public Triangle(int x, int y, Color c)
{
this.x = x;
this.y = y;
this.c = c;
setPreferredSize(new Dimension(100, 100));
addMouseListener(this);
// Set this border to see the boundaries of this component.
// When you are done, you may remove this.
setBorder(BorderFactory.createLineBorder(Color.black));
}
Then you can better understand the bounds of the components.
Pink triangle is not visible because it is outside component's boundary.
p3 triangle does not react to mouse clicks because it is just a drawing. Only components react to mouse and other events.
Notice that components are rectangle in shape. So, the mouse listener you have added works anywhere on the component; not only on the area of triangle.
You are drawing triangles in two ways in this program.
1. By adding Triangle components. (Like "add(new Triangle(20, 50, Color.red));")
2. By drawing p3 in paintComponent() method.
From software designing perspective, better to stick to one approach. Otherwise it can be confusing and error prone.
private void draw_shape() {
Graphics g = getGraphics();
g.drawLine(0, 0, 100, 100);
repaint();
}
In paint method only those graphics are drawn which is a part of paint method because of which
I wanted to draw shapes outside of paint method.
This code draws the line but it immediately disappeares, I don't understand why this is happening. please help
This doens't work because you are getting the current Graphics outside of the Swing repaint thread. Basically:
you get the current Graphics
you draw something on it
then you call repaint() that will call the paint() of the component thus discarding all you did
To make it work you should override the paint (paintComponent for Swing) method of your object:
#Override
public void paint(Graphics g) {
super.paint(g); // if you have children to the component
g.drawLine(..)
}
and then just call repaint() when something has been modified.
The line disappears because Swing (or AWT) will call paint(Graphics) or paintComponent(Graphics g) in order to pain the component.
What you need to do is to put your drawing logic on the paint(Graphics) or paintComponent(Graphics g) method. The latter is more advisable.
If you really need to draw things using another method, store an image as a class field and draw this image on the paint or paintComponent methods.
Because the paint method also paints stuff. You should not draw graphics outside the paint method. You should instead override the paint method, like this:
#Override public void paint (Graphics g) {
super.paint(g);
g.drawLine(0, 0, 100, 100);
}
Thanks for the help found the answer
BufferedImage image = (BufferedImage) createImage(300, 300);
image.getGraphics().drawLine(0, 0, 300, 300);
jLabel1.setIcon( new ImageIcon(image ));
I have a problem with put drawn circle into middle of Frame by using methods getWidth() and getHeight(). I tried something with Image package but no idea where to implement this methods:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Frame;
import java.awt.Image;
public class Circle extends Frame {
public Circle() {
setSize(400,400);
setLocationRelativeTo(null);
setVisible(true);
}
public Color() {
}
public void paint(Graphics g) {
g.setColor(Color.ORANGE);
g.fillOval(200, 200, 200, 200);
}
public static void main(String[] args) {
Circle c = new Circle();
c.paint(null);
}
}
Then I have to use method setColor(Color) and Color class constructor to make random color of this circle (after every run of this program). I opened Color constructor but there is an error :/
Better to extract all the paint functionality to a JComponent here to take full advantage of Swing's optimized paint model using paintComponent.
The Circle is actually a JFrame. Inside in its constructor, a new component is created which handles the painting of the circle. The Color constructor has been removed as this is invalid syntax.
The circle co-ordinates are start in the top left-hand corner and take the full available width & height for drawing.
Also would recommend using lightweight Swing components over old-style AWT component.
public class Circle extends JFrame {
public Circle() {
setSize(400, 400);
add(new CirclePanel());
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
Circle c = new Circle();
}
}
class CirclePanel extends JComponent {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.ORANGE);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
See: Painting in AWT and Swing
Simply call getWidth() and getHeight() from within the paint(...) method and use the results returned for your fillOval(...) parameters.
But having said that, it's a better idea to draw in a Canvas that is added to the Frame. And having said that, it's much better still to draw in the paintComponent(...) method of a JPanel that is added to the contentPane of a JFrame in a Swing application.
I am drawing onto a JPanel using getGraphics and the drawLine and fillOval commands but it is very temperamental when running the program. However, when I debug it it draws every time.
draw.drawPoints(drawing.getGraphics(), xCoord, yCoord);
Calls:
public void drawPoints (Graphics g, int x, int y){
g.setColor(Color.red);
g.fillOval(x, y, 5, 5);
}
edit: It wont always draw. Most of the time is stays blank.
I am drawing onto a JPanel using getGraphics
You should not draw stuff on the JPanel by getting a Graphics object from drawing.getGraphics().
Instead, you should override the paintComponent(Graphics g) method and do your painting there.
A simple example to get you started:
container.add(new JPanel() {
public void paintComponent(Graphics g) {
drawPoints(g, xCoord, yCoord);
}
});
You need to do that every time the object is repainted.
I created a new JApplet form in NetBeans:
public class UI extends javax.swing.JApplet {
//generated code...
}
And a JPanel in design mode named panou:
// Variables declaration - do not modify
private javax.swing.JPanel panou;
How do I get to draw a line on panou? I've been searching for this for 5 hours now so a code snippet and where to place it would be great. Using Graphics2D preferably.
Go to design mode
Right Click on the panel "panou"
Click "Costumize code"
In the dialog select in the first combobox "costum creation"
add after = new javax.swing.JPanel() this, so you see this:
panou = new javax.swing.JPanel(){
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Do the original draw
g.drawLine(10, 10, 60, 60); // Write here your coordinates
}
};
Make sure you import java.awt.Graphics.
The line that you will see is always one pixel thick. You can make it more "line" by doing the following:
Create this method:
public static final void setAntiAliasing(Graphics g, boolean yesno)
{
Object obj = yesno ? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF;
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, obj);
}
And add after super.paintComponent(g); (in your costum creation) this:
setAntiAlias(g, true);
Edit:
What you are doing wrong is: you paint the line once (by creating the frame).
When you paint the line the frame is also invisible. The first draw is happening when the frame becomes visible. The frame will be Repainted, so everything from the previous paint will disappear.
Always you resize the frame, everything will be repainted. So you have to make sure each time the panel is painted, the line also is painted.
To do custom painting in a JPanel, one would need to make a subclass of a JPanel, and then overload the paintComponent method:
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
// Perform custom painting here.
}
}
In the example above, the MyPanel class is a subclass of JPanel, which will perform whatever custom painting is written in the paintComponent method.
For more information on how to do custom painting in Swing components, Lesson: Performing Custom Painting from The Java Tutorials have some examples.
If one wants to do painting with Java2D (i.e. using Graphics2D) then one could do some painting on a BufferedImage first, then draw the contents of the BufferedImage onto the JPanel:
class MyPanel extends JPanel {
BufferedImage image;
public MyPanel() {
Graphics2D g = image.createGraphics();
// Do Java2D painting onto the BufferedImage.
}
public void paintComponent(Graphics g) {
// Draw the contents of the BufferedImage onto the panel.
g.drawImage(image, 0, 0, null);
}
}
Further reading:
Painting in AWT and Swing
Trail: 2D Graphics