I'm new to Java Swing and I'm writting a program, into which user types few X's and Y's and then the program shows on x/y grid those points. I drew the grid with Graphics2D, but now the problem is with drawing those points. I'm adding every X and Y to separate ArrayList and then after clicking the button 'Draw' it should draw all of them, but I don't really know how. I've searched few topics and tutorials, but still can't get into it.
Here is the code of my DrawPoints class
import javax.swing.*;
import java.awt.*;
public class DrawPoints extends JPanel {
private int x;
private int y;
public DrawPoints(int x, int y) {
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(x*20, y*20, 5, 5); //20 is the difference between every point on my grid
}
}
And here is part of my actionListener function
if (event.getActionCommand().equals("Draw")) { //Button is called 'Draw'
for (int i = 0; i < dataX.size(); i++) { //dataX is an arrayList with all X's
DrawPoints drawPoints = new DrawPoints(dataX.get(i), dataY.get(i)); //dataY is an arrayList with all Y's
points.add(drawPoints); //points is a JPanel added in the main frame
}
}
I'm adding every X and Y to separate ArrayList and then after clicking the button 'Draw' it should draw all of them,
That approach is wrong.
All the data needed to paint the points should be part of the DrawPoints class:
The `ArrayList should be an instance variable of the class.
You would create a method like addPoint(...) in the DrawPoints class. This method would simply take the point and add it to the ArrayList and then invoke repaint() to make sure the point is painted on the panel.
The paintComponent(…) method would then be changed to iterate through all the pointes in the ArrayList and then paint each point.
I'm writting a program, into which user types few X's and Y's
So as the user type the x/y points you would invoke the addPoint(…) method of your DrawPoints class.
You will also need to override the getPreferredSize() method of your DrawPaoints class to give your panel a size so a layout manager can be used.
Check out the Draw On Component example found in Custom Painting Approaches for a working example that uses this approach. The code presented there is more complicated then you will need because it allows the user to use the mouse to create the rectangles to be painted.
Related
I am currently working on a test driven interactive 2D fluid dynamics simulation for my Bachelor thesis. The basic idea is that the user can draw shapes on his screen and the program will simulate how a fluid will flow around these shapes.
So far I have only started with the painting process and I have already run into a little problem.
First, here is a little UML diagram that shows my project structure so far
As you can see, I have created a Painter class that can visit several shapes and will call one of the abstract paint methods depending on the shape type. Then there is the SwingPainter class which inherits from the Painter class and implements the three paint methods as well as the clear method.
Here is the code for my SwingPainter:
public class SwingPainter extends Painter {
private final GraphicPanel graphicPanel;
public SwingPainter(GraphicPanel graphicPanel) {
this.graphicPanel = graphicPanel;
}
private Graphics getGraphics() {
return graphicPanel.getGraphics();
}
#Override
protected void paintLine(Point start, Point end) {
getGraphics().drawLine(start.getX(), start.getY(), end.getX(), end.getY());
}
#Override
protected void paintRectangle(Point start, Point end) {
int minX = start.getX() < end.getX() ? start.getX() : end.getX();
int minY = start.getY() < end.getY() ? start.getY() : end.getY();
int width = Math.abs(start.getX() - end.getX());
int height = Math.abs(start.getY() - end.getY());
getGraphics().drawRect(minX, minY, width, height);
}
#Override
protected void paintCircle(Point center, double radius) {
int minX = center.getX() - (int) radius;
int minY = center.getY() - (int) radius;
int diameter = (int) radius * 2;
getGraphics().drawOval(minX, minY, diameter, diameter);
}
#Override
public void clear() {
graphicPanel.paintComponent(getGraphics());
}
}
So my problem is that the GraphicPanelPresenter(see UML diagram) is responsible for passing the Painter/Visitor to the shapes when the user left clicks or moves the mouse(the second point of a line will follow the cursor for example). That means the actual painting is done outside of Swing's paint method. As a result I have encountered some flickering while painting and I was wondering if anyone knew how to fix that without throwing the whole painter/visitor functionality out the window(since there are other features concerning the shapes that still have to be implemented and could easily be handled by simply passing them another type of visitor).
Well, that was a rather lengthy description for a rather small problem, sorry for the wall of text, but I would be glad for any kind of hint!
You need to do all the of painting in the various Swing paint methods that are designed to be overridden for custom painting (such as paintComponent). Only Swing will know when it's done drawing, or when a new frame needs to be drawn to.
What it really seems like you need to do, is to have your GraphicsPanel have a reference to the object that holds your shape's states, and then draw the graphics that represent that state in paintComponent.
This should also simplify your class relationship diagram as well, since within GraphicsPanel you can call the state object's methods from your listeners to change the state (I would choose different names to make the state object not aware that the changes are based on UI interactions, so if right click rotates, make the method called rotate instead of handleRightClick).
I have a very simple problem. I am learning Java, and was given an assignment to draw a car. I did this all in one class that extends JPanel, and did the drawing within paintComponent().
I realize this is poor object-oriented programming, and decided to try to subclass some of the parts to rectify this situation.
I tried to create a class that draws wheels, but was unsuccessful.
Essentially, I wanted to be able to do this:
Main Class extends JPanel
paintComponent{
Wheel leftWheel = new Wheel(0, 50, 100);
this.add(leftWheel);
}
This should draw a wheel at the point (0, 50) within the JPanel, and have a diameter of 100.
However, i'm unsure how i'm supposed to control the positioning in the JPanel. When I do this, the wheel in drawn at the top center of my window. This is what my wheel class looks like:
public class Wheel extends JComponent {
private int x, y, diameter;
private boolean clockwise;
Wheel(int x, int y, int size, boolean cw)
{
this.setPreferredSize(new Dimension(size, size));
this.x = x;
this.y = y;
diameter = size;
clockwise = cw;
repaint();
}
public void paintComponent(Graphics canvas)
{
super.paintComponent(canvas);
canvas.setColor(Color.gray);
canvas.fillOval(x,y,diameter,diameter);
}
}
The x and y should be where it appears on the parent window, however, this is not the case in the following code (located in the parent class that extends JFrame):
Wheel leftWheel = new Wheel(0,0,WHEEL_DIAMETER,true);
this.add(leftWheel);
The wheel doesn't draw at the top left of my window, it draws in the center of my window at the top. Am I doing something incorrectly? Sorry if I don't follow some Java conventions, I don't have any experience yet. Is this how I should be handling the drawing of the wheel, or is there a more accepted practice for doing this type of drawing?
For example, in my JPanel class, if I add the following code:
Wheel x = new Wheel(50,60,75,true);
this.add(x);
I get a frame sized 75x75 in which a wheel (sized 75x75) is drawn at the point (50,60) within that frame, not within the parent JPanel's coordinate system. The result is a circle that gets clipped and I only see the top left of the circle. That image is displayed at the top center of my JPanel
I understand how to draw the wheel, and move it within itself, but how do I position the wheel on the JPanel??
Your constructor has a small bug,
Wheel(int x, int y, int size, boolean cw) {
this.setPreferredSize(new Dimension(size, size));
diameter = size;
clockwise = cw;
repaint();
}
You forgot to store x and y. I think you wanted,
Wheel(int x, int y, int size, boolean cw) {
this.x = x;
this.y = y;
this.setPreferredSize(new Dimension(size, size));
diameter = size;
clockwise = cw;
repaint();
}
Because your x and y are 0 if you don't set them.
Could you explain how to control it's location within the JPanel, not within itself please?
The default LayoutManager for a JPanel is a FlowLayout so the component will always be positioned based on the rules of the layout manager.
If you want to add components to a random location then you need to use a null layout. But when you use a null layout you are then responsible for setting the size and location of the component. So, in reality the custom painting should always be done at (0, 0) in your custom component.
Instead of adding multiple JPanels to create the vehicle I would simply use one class that extends JPanel and create multiple methods to create things such as wheels etc. to be called from within the overridden paintComponent method. You can pass the new method a reference of your graphics object, create a copy of your graphics object using g.create(), or use getGraphics() from inside the method itself. Inside the method to create a wheel you then are able to calculate it's position by using the panel's dimensions and place it properly.
An alternative would be to define and return shapes in other methods and simply draw them using the graphics object in paintComponent().
I am having trouble doing a rather simple task of taking in the diameter of a circle and then drawing it. Here is my code so far.
import javax.swing.*;
import java.awt.Graphics;
public class Shapes extends JFrame
{
double diameter;
public Shapes()
{
setSize(600,600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void getDiameter()
{
String input = JOptionPane.showInputDialog("What is the diameter of the circle?");
diameter = Double.parseDouble(input);
Shapes gui = new Shapes();
gui.setVisible(true);
}
public static void main(String[] args)
{
Shapes app = new Shapes();
app.getDiameter();
}
public void paint(Graphics canvas)
{
canvas.drawOval(50, 50, (int)diameter, (int)diameter);
}
}
When I run it, it brings up the Jframe window, but nothing is drawn, so I'm guessing the value of diameter is never passed to the paint method. Can someone help me get this to work? Thanks.
Your program is creating two Shapes objects actually, one of which has the diameter field set correctly but is not being displayed, and the other, which retains diameter's default value of 0 and which is displayed.
Suggestions:
Don't draw directly in a JFrame, but rather in the paintComponent(Graphics g) method override of a JPanel that is held by and displayed in the JFrame. There are many reasons for this, but for one, since the paint(...) method is not only responsible for painting a component but also its borders and children, this will prevent you from causing problems when paint(...) tries to paint a GUI's children and borders. It also will help your animations (which surely you will be doing soon) to be smooth given Swing component's default use of double buffering.
Always call the super.paintComponent(g) method within your JPanel's paintComponent override. This will allow Swing to erase images that need to be erased.
Don't create two Shapes objects, but rather only one. This will simplify things greatly and will allow you to set the diameter value of the one and only object of importance.
After changing the diameter field's value, call repaint on your GUI so that the displayed JPanel's paintComponent will be called.
We've just learned how to create our own class, and this particular assignment we had to work with graphics. We had to draw a crayon, and then create a test program where there are 5 crayons lined up next to one another (so we just change the color and x,y of each one). I know how to change the color and x,y coords, but my question is...
how do I 'print' each crayon? Yes, it's an applet and yes I know I need a .html file. But what exactly goes in the test program in order for the crayon to show up when I run the .html file? I've run non-applets before in test programs using System.out.println, but never any graphics. Would it just be System.out.println(Crayon); ?
Also, how do I get multiple crayons? I'm assuming it's Crayon crayons = new Crayon;, and then the next one might be 'Crayon crayons2 = new Crayons;`? I'm not sure.
The x,y coordinates need to be modified w/each crayon, but the UML for the assignment told me not to make them instance variables but instead to put it in 'public void paint (Graphics g, int x, int y)'. What I have so far for the test program (may or may not be correct):
import javax.swing.JApplet;
import java.awt.*;
public class BoxOfCrayons extends JApplet {
Crayon first = new Crayon (Color.red, 50, 250)
Start by having a read through 2D Graphics.
Basically, you will need to create some kind of list of Cryons. This can either be a Collection or array, depending on what you know. I would personally use a ArrayList as it's flexible and easy to use, but doesn't suffer from the same constraints as an array.
Next, create you're self a custom component (ie BoxOfCryons) which extends from JPanel. Override this classes paintComponent method. Within this method, run through the list of Cryons and paint each one, incrementing the x offset by the width of the last Cryon.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = 0;
int y = 0;
for (Crayon crayon : cryons) {
crayon.paint(g2d, x, y);
x += crayon.getWidth();
}
g2d.dispose();
}
Create yourself a new class that extends from JApplet. In it's init method, set the applets layout manager to BorderLayout and add an instance of the BoxOfCryons class to it.
I'm trying to make a tower of hanoi solver which simply solves the hanoi without any mouse events. The problem is when I move the rectangle the original remains, even after I repaint. I've searched the net and tried changing the code around but nonthing worked. I am using a JFrame with a JPanel inside of it if that changes anything.
I have my disk class here which is just a rectangle with colour.
class Disk extends Rectangle {
Color diskColour;
public Disk(int a, int b, int c, int d, Color colour) {
x = a;
y = b;
width = c;
height = d;
diskColour = colour;
}
public Color getColour() {
return diskColour;
}
public void paintSquare(Graphics g) {
repaint();
g.setColor(diskColour);
g.fillRect(x, y, width, height);
repaint();
}
}
Here is my code where I actually call the paintSquare method:
public void simpleMoveDisk(Disk[] disks, int n, Graphics g) {
disks[n].setLocation(30,25);
disks[n].paintSquare(g);
repaint();
}
The paintSquare method paints the disk, while the setLocation method changes its coordinates.
When this runs the rectangle occurs in the new location, however the old one still remains. Any help is appreciated, thanks in advance.
You are calling repaint() in several places and you shouldn't be.
Have your the top level class that is doing the painting, call the paintSquare method and any other method that is needed. Those methods should not be calling repaint().
Also your simple move disk is really strange in the fact that it passes an array of Disks, an index, and a graphics object. Instead make it just take in a Disk. Just pass it the one out of the array that is needed to be updated. Then let whatever class that calls simpleMoveDisk, separately make a call to repaint instead of trying to paint and update the model in the same method.