I do not have much familiar with graphics in Java, sorry. However, this is what I'm trying to do. I want to be able draw a couple of points on a canvas (JPanel here), and be able to redraw the points everytime the method (drawPoints) is invoked with a new set of parameters: double[]xs, double[]ys. Any chance I could do this without 'redrawing' the canvas? I can't even get the points to plot in the current state of the code.
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class PlotPoints extends JPanel {
double[] x;
double[] y;
public void paintComponent (Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
for (int i=0; i<x.length; i++){
g2d.fillOval((int)this.x[i],(int)this.y[i], 10, 10);
}
}
public void drawPoints(double[]xs, double[]ys){
JFrame frame = new JFrame("Points");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.x=xs.clone();
this.y=ys.clone();
frame.add(new PlotPoints());
frame.setSize(100, 100);//laptop display size
frame.setVisible(true);
}
}
Here is the other class that invokes the 'drawPoints' method from the PlotPoints Class. I got this code snippet from some StackOverflow Q&As, and tried to improvise on it to suit my needs. If a different structure is more suited, I'd be grateful for your sharing.
import java.lang.*;
public class MainClass {
double[] xcoords;
double[] ycoords;
public static void main(String[] args){
//create instances of classes
PlotPoints myPlots=new PlotPoints();
MainClass myMain=new MainClass();
//initialize coordinates
myMain.xcoords=new double[5];
myMain.ycoords=new double[5];
//put values into coordinates
for (int i=0; i<5; i++){
myMain.xcoords[i]=Math.random()*1000; //Random number
myMain.ycoords[i]=Math.random()*1000;
}
//Create a plotter. Plot
//to draw points defined by: (xcoords[i],ycoords[i])
myPlots.drawPoints(myMain.xcoords, myMain.ycoords);
//Please do this!
}
}
Any chance I could do this without 'redrawing' the canvas?
Sure. Draw them to a BufferedImage that is itself displayed in a JLabel. E.G. as seen in this answer.
But don't be too quick to go this way. Java-2D can animate thousands of graphics elements in a paint() method.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I'm trying to make a simple app that visualised sorting algorithms, but have gotten stuck trying to draw a rectangle (these will later represent each number in the array). I have tried alot of tutorials, and my code compiles, but it just makes an empty screen when ran. Have I missed something obvious?
import javax.swing.*;
import java.util.ArrayList;
import java.util.Random;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
class DrawRectangle extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.PINK);
Rectangle2D rect=new Rectangle2D.Double(50,50,200,200);
g2.draw(rect);
g2.fill(rect);
}
}
public class Sorter {
static int numElements = 20;
static int width = 800;
static int height = 500;
public void newList(){
}
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<Integer>();
Random rand = new Random();
for(int i = 0; i <= numElements; i++){
int randomNum = rand.nextInt(100);
nums.add(randomNum);
}
// Create J Frame
JFrame f=new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int arrWidth = width - 200;
int eachCol = arrWidth / nums.size();
for(int i = 0; i <= numElements; i++){
}
f.setSize(width,height);
f.setLayout(null);//using no layout managers
f.setVisible(true);//making the frame visible
DrawRectangle rec= new DrawRectangle();
f.add(rec);
f.add(new DrawRectangle());
f.repaint();
}
}
Usually, when you want to draw shapes, you define a class that extends javax.swing.JPanel (and not JComponent) and you override method paintComponent (as you have done).
The first line in the overridden paintComponent method should almost always be a call to the superclass method, i.e.
super.paintComponent(g);
As stated in Performing Custom Painting
in Swing, your GUI creation code should be placed on the Event Dispatch Thread (EDT)
There is no need to set the layout to null in your JFrame. There is also no need to explicitly call method repaint.
You should add all the components to the JFrame before calling method setVisible.
The following code displays a pink rectangle in a JFrame. Note that I removed the irrelevant code concerning generating random numbers since that does not affect your desired result of displaying a rectangle.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DrawRectangle extends JPanel {
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.PINK);
Rectangle2D rect=new Rectangle2D.Double(50,50,200,200);
g2.draw(rect);
g2.fill(rect);
}
}
public class Sorter {
static int width = 800;
static int height = 500;
private void createAndDisplayGui() {
JFrame f=new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new DrawRectangle());
f.setSize(width,height);
f.setLocationByPlatform(true);
f.setVisible(true);
}
public static void main(String[] args) {
final Sorter sorter = new Sorter();
EventQueue.invokeLater(() -> sorter.createAndDisplayGui());
}
}
Note that in the tutorial, Performing Custom Painting, they call method invokeLater of class SwingUtilities. That method merely invokes the method in class EventQueue so I just call the EventQueue method directly.
Here is a screen capture.
Your problem is that your DrawRectangle is never given any size (height & width). You could add
public Dimension getPreferredSize() {
return new Dimension(200,200);
}
to DrawRectangle and turn the layoutmanger back on (preferred solution). Or you could manually setSize/setBounds of both the DrawRectangle and the Frame.
I am having trouble with creating a stroked shape in BasicStroke Outline = new BasicStroke(10f, 50, 50);. The error I am currently getting is error:
can't find symbol canvas.setStroke(Outline) pointing to the dot.
I am new to constructors so any help would be great and the only thing I did similar to this was creating an instance of Scanner.
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Color; //sets color
import java.awt.BasicStroke;
import java.awt.Graphics2D;
public class ColoredOlypmicRings extends JFrame
{
//varriables go here
public void paint(Graphics canvas)
{
super.paint (canvas);
canvas.setColor(Color.green);
canvas.drawOval(100,100,100,100); //color green
canvas.setColor(Color.red);
canvas.drawOval(200,200,100,100); //color red
final BasicStroke Outline = new BasicStroke(10f, 50, 50);
canvas.setStroke(Outline);
canvas.drawOval(300,300,200,200);
}
public ColoredOlypmicRings()
{
setSize(600,400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
ColoredOlypmicRings guiWindow = new ColoredOlypmicRings();
guiWindow.setVisible(true);
}
}
The Graphics class can't handle Strokes and doesn't have methods for setting it as its API will tell you.
The Graphics2D class on the other hand can handle this class and should be used to handle it. So cast your Graphics object to a Graphics2D object.
e.g.,
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(....); // do it here after casting
}
Also check out the BasicStroke API as you're not using the constructor correctly, passing in incorrect parameters.
Other issues:
Don't draw directly in a JFrame or other top-level window.
Instead draw in the paintComnponent method of a JPanel that is displayed in the JFrame.
The three int BasicStroke constructor is being mis-used as the 2nd and 3rd parameters should be constants that represent the cap and join state of the Stroke object.
Here are two instances of the exact same code that draws a bunch of rectangles superimposed down a diagonal, except in the second instance there are parentheses around "i^2" for both the x and y values. I believe that the rectangles should begin at (0,0) in both cases, but in the first case they do not begin at the origin. Instead, they begin slightly off from the origin. Why is there a difference when the rectangles are added to the JFrame?
using
javax.swing.JComponent
javax.swing.JFrame
and
java.awt.Rectangle
for(int i=0;i<600;i++){
Rectangle rect1 = new Rectangle(20*i^2, 20*i^2, 50, 100);
g2.draw(rect1);
}
for(int i=0;i<600;i++){
Rectangle rect1 = new Rectangle(20*(i^2), 20*(i^2), 50, 100);
g2.draw(rect1);
}
My code is in two classes. Here is the main class:
import javax.swing.JFrame;
public class MainClass {
public static void main(String[] args){
JFrame window = new JFrame();
window.setSize(600,600);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingComponent drawing = new DrawingComponent();
window.add(drawing);
window.setVisible(true);
}
}
And here is the code for the DrawingComponent class that makes Rectangles to be added to the JFrame:
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class DrawingComponent extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
for(int i=0;i<600;i++){
Rectangle rect1 = new Rectangle(20*i^2, 20*i^2, 50, 100);
g2.draw(rect1);
}
}
}
The expression 20*i^2 is not the same as 20*(i^2).
Bitwise XOR operator ^ has lower precedence than multiplication * and so the two expressions will produce different results.
I am still trying to get a repaint() method to work in a separate class with a class that extends the JComponent. I have placed a couple of post on here and so far I haven't been able to get the code to work. I have gotten some good advice. I am placing below what I have so far.
Main Class 1:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
public class DDHGenericFrame extends JFrame {
private static final long serialVersionUID = 1L;
DDHGenericPanel d = new DDHGenericPanel(); /*User defined class that is above*/
public DDHGenericFrame() {
initUI();
}
public final void initUI() {
add(d);//Adds the panel to the JFrame
setSize(650,350);
setTitle("Lines");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
DDHGenericFrame ex = new DDHGenericFrame();
ex.setVisible(true);
}
}
Class 2:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
class DDHGenericPanel extends JPanel {
private static final long serialVersionUID = 1L;
public JButton aButton1;
public JButton aButton2;
public TestPane tPane = new TestPane();
DDHGenericPanel(){
System.out.println("DDH Generic JPanel");
aButton1 = new JButton();
aButton1.setText("Button 1");
aButton1.addActionListener(new myButtonActionListener1());
add(aButton1);
aButton2 = new JButton();
aButton2.setText("Button 2");
aButton2.addActionListener(new myButtonActionListener2());
add(aButton2);
System.out.println("Before the setDraw!!!");
tPane.setDraw();
System.out.println("After the setDraw!!!");
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("paintComponent of DDHGenericPanel.java");
}
}
class myButtonActionListener1 implements ActionListener {
public TestPane tPane = new TestPane();
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Button 1 -- Before the setDraw!!!");
tPane.setDraw();
System.out.println("Button 1 -- After the setDraw!!!");
}
}
class myButtonActionListener2 implements ActionListener {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Button1 clicked 2");
}
}
Class 3: (I had this one embedded in the same files as the class above -- it will be separate when I have the finished code)
/**
* This class will draw a cricle with the repaint method
* #author DDH
*/
class TestPane extends JComponent {
private static final long serialVersionUID = 1L;
private static final int LINE_THICKNESS = 4;
private static final int LINE_GAP = 10;
private Color lineColor = Color.red;
/**
* This method will draw the circle with coordinated (0,0)
* #param none
* #return none
*/
public void setDraw() {
repaint();//This should call the paintComponent() that is below and paint a circe but it does not for some reason.
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int radius = 10;
BufferedImage buffer = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint (RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Ellipse2D circle = new Ellipse2D.Float(0, 0, radius,radius);
Shape clip = g2d.getClip();
g2d.setClip(circle);
AffineTransform at = g2d.getTransform();
g2d.setTransform(AffineTransform.getRotateInstance(
Math.toRadians(45),
radius / 2, radius / 2));
int gap = LINE_GAP;
g2d.setColor(Color.WHITE);
g2d.fill(circle);
g2d.setColor(lineColor);
//g2d.setStroke(new BasicStroke(LINE_THICKNESS));
for (int index = 0; index < 10; index++) {
int x1 = index*gap-(LINE_THICKNESS/2);
int y1 = 0;
int x2 = index*gap+(LINE_THICKNESS/2);
int y2 = radius;
int width = x2 - x1;
int height = y2 - y1;
g2d.fillRect(x1, y1, width, height);
//g2d.drawLine(index * gap, 0, index * gap, getRadius());
}
g2d.setTransform(at);
g2d.setClip(clip);
g2d.dispose();
g.drawImage(buffer, 0, 0, this);
}
}
Frome what I have read and what people have posted this should work. Is there a way to force it to paint right away. Repaint() sometimes has a little bit of a delay. I want to use this as the start of a game and I have to be able to create an ArrayList of Circles and then repaint them immediately.
Currently this will only draw one circle in the top (0,0) coordinates.
Doug Deines Hauf
Is there a way to force it to paint right away.
It will paint right away as soon as the GUI is visible. There is nothing special that you need to do. There is no need for a setDraw() method. All components will automatically be painted when the GUI is displayed.
System.out.println("Before the setDraw!!!");
tPane.setDraw();
System.out.println("After the setDraw!!!");
That code does nothing. The GUI isn't visible yet so there is nothing to paint. There is no reason for you do invoke a repaint unless you actually change a property of a component on a visible GUI.
public void setDraw() {
repaint();
}
There is no reason to create a method that simply does a repaint(), get rid of this method. That is NOT what I suggested in your last posting. I said you create a method to change a property that will affect the outcome of the painting of the component.
I gave you an example, like when you use setForeground(), the method changes the Color of the text to be painted, so repaint() is automatically invoked when the color is changed.
Get rid of all the complex painting code in your paint component and then try to do a simple
graphics.drawString();
Don't be playing with rotations and clips (even I have problem with these concepts and if not done correctly you may not get anything painted) until you get something basic working. Then once you get that working you do something more complicated, one step at a time until you understand the basics. Don't write a complex program until you get something simple working.
Also, I don't know why you are attempting to draw from a buffered image. Just draw using the Graphics object that is passed into the paintComponent() method. There is no need to use a BufferedImage, Swing is already double buffered so you are just complicating your code.
Have you read the Custom Painting tutorial yet? It contains a working example.
Edit:
Having said all the above you still have two fundamental problems:
you don't add the component to the panel
the component doesn't have a preferred size so there is nothing to paint. You need to override the getPreferredSize() method to return a reasonable size for the component that you want to paint.
Even these two fixes don't solve the problem of your complex painting, but at least now I can get a simple drawstring(...) to work.
Write a program that fills the window with a larrge ellipse. The ellipse shoud touch the window boundaries, even if the window is resized.
I have the following code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
public class EllipseComponent extends JComponent {
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double ellipse = new Ellipse2D.Double(0,0,150,200);
g2.draw(ellipse);
g2.setColor(Color.red);
g2.fill(ellipse);
}
}
And the main class:
import javax.swing.JFrame;
public class EllipseViewer {
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(150, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
EllipseComponent component = new EllipseComponent();
frame.add(component);
frame.setVisible(true);
}
}
in your EllipseComponent you do:
Ellipse2D.Double ellipse = new Ellipse2D.Double(0,0,getWidth(),getHeight());
I'd also recommend the changes given by Hovercraft Full Of Eels. In this simple case it might not be an issue but as the paintComponent method grows in complexity you realy want as little as possible to be computed in the paintComponent method.
Do not resize components within paintComponent. In fact, do not create objects or do any program logic within this method. The method needs to be lean, fast as possible, do drawing, and that's it. You must understand that you do not have complete control over when or even if this method is called, and you certainly don't want to add code to it unnecessarily that may slow it down.
You should create your ellipse in the class's constructor. To resize it according to the JComponent's size and on change of size, use a ComponentListener.:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
public class EllipseComponent extends JComponent {
Ellipse2D ellipse = null;
public EllipseComponent {
ellipse = new Ellipse2D.Double(0,0,150,200);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// set the size of your ellipse here
// based on the component's width and height
}
});
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.draw(ellipse);
g2.setColor(Color.red);
g2.fill(ellipse);
}
}
Caveat: code not run nor tested