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.
Related
Here is a piece of code :
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class QuitButton extends JPanel implements ActionListener
{
static JButton button = new JButton("Panic");
Color[] colors = new Color[9];
boolean pressed = false;
public QuitButton()
{
button.addActionListener(this);
colors[0] = Color.RED;
colors[1] = Color.BLUE;
colors[2] = Color.GREEN;
colors[3] = Color.YELLOW;
colors[4] = Color.BLACK;
colors[5] = Color.PINK;
colors[6] = Color.MAGENTA;
colors[7] = Color.ORANGE;
colors[8] = Color.CYAN;
}
#Override
public void actionPerformed(ActionEvent e)
{
pressed = true;
}
public static void main(String args[])
{
JFrame frame = new JFrame("Do NOT Panic!!");
QuitButton qb = new QuitButton();
frame.add(qb);
frame.add(button);
frame.setLayout(new FlowLayout());
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
//frame.pack();
button.requestFocus();
qb.gameLoop();
}
public void gameLoop()
{
while (true)
{
repaint();
try
{
Thread.sleep(200);
} catch (Exception e)
{
}
}
}
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if (pressed == false)
{
super.paint(g2d);
g2d.setColor(Color.GRAY);
g2d.fillRect(0, 0, 400, 400);
} else
{
super.paint(g2d);
Random r = new Random();
int min = 0;
int max = 8;
int index = r.nextInt(max - min) + min;
g2d.setColor(colors[index]);
g2d.fillRect(0, 0, 400, 400);
}
}
The purpose of this program: The rectangle should be grey before but when I click the panic button colours should start changing.
Please don't get confused with the name of the class which is QuitButton.
But my rectangle is not occupying the entire window. Instead I am getting a teeny tiny rectangle like this : http://g.recordit.co/xJAMiQu6fM.gif
I think it is because of the layout I am using and I haven't specified anywhere that the button will be on top. Probably that's why they are coming side by side. I am new to GUI creation and thank you for your help.
You seem to be making some guesses on how to do this, which is not a good way to learn to use a library. Your first step should be to check the relevant tutorials on this, most of which will be found here: Swing Info Since this appears to be homework, I'm not going to give you a code solution but rather suggestions on how to improve:
Override paintComponent, not paint since the latter gives double buffering and is less risky (less painting of borders and child component problems)
In your paintComponent override, be sure to call the super's paintComponent method first to clear "dirty" pixels.
Use a Swing Timer, not a while loop for your game loop. This will prevent your while loop from freezing the Swing event thread, a problem that can freeze your program. Google the tutorial as it is quite helpful.
Do your randomization within the ActionListener's code (here likely the ActionListener for your Swing Timer), not within the painting code. The painting code should not change the state of the object but rather should only display the object's state.
FlowLayout will respect a component's preferredSize, and your component's preferred size is 0,0 or close to it. Change this. Best to override public Dimension getPreferredSize() and return a Dimension that matches your Rectangle's size.
Avoid using "magic" numbers, such as for your rectangle's size, and instead use constants or fields.
Call repaint() within your Timer's ActionListener so the JVM knows to paint the component.
My program uses a Jframe, whenever I minimize the window and bring it back up, the program runs itself again. How can I cause it to not do this when minimized?
Code:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
import java.util.Random;
public class AB extends Component {
public void paint(Graphics g) {
Random r = new Random();
g.setColor(Color.black);
g.fillRect(r.nextInt(100), r.nextInt(100), 100, 100);
}
public static void main(String[] args) {
JFrame f = new JFrame("Load Image Sample");
f.getContentPane().setBackground(Color.white);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
f.setSize(dim.width, dim.height);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.add(new AB());
f.setVisible(true);
}
}
My guess: you have program logic present in your paint(...) or paintComponent(...) method override. If so, get the logic out of these methods, because as you're finding out, you do not have full control over when or even if they are called. The logic belongs elsewhere, perhaps in a Swing Timer, hard to say without code or a more detailed question.
If this doesn't help, then you'll need to show us the bugs in your code by creating and posting a minimal, runnable, example program as well as tell us more of the details of your program and its misbehavior.
After looking at your code, I see that my assumption/guess was correct: you are selecting your random numbers inside of the paint method, and so they will change with each repaint. You will want to
Create a class that extends JPanel.
Create your random numbers in this class's constructor, and use the random numbers to set class instance fields.
Override this class's paintComponent method
Don't forget to call the super's paintComponent method inside this method.
Use the numbers generated in the constructor in the paintComponent method override to draw your rectangle.
Place your JPanel into a JFrame.
e.g.,
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class RandomRect extends JPanel {
private static final int MAX_INT = 100;
private static final Color RECT_COLOR = Color.black;
private Random random = new Random();
private int randomX;
private int randomY;
public RandomRect() {
randomX = random.nextInt(MAX_INT);
randomY = random.nextInt(MAX_INT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
g.fillRect(randomX, randomY, MAX_INT, MAX_INT);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("RandomRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RandomRect());
//frame.pack();
// frame.setLocationRelativeTo(null);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
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.
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