I have defined a class called Stone to add graphical stones to a JPanel:
public class Stone {
private int x, y;
private Color color;
private static final int radius = 18;
Stone(Color color) {
this.color = color;
}
public Stone(int x, int y, Color color) {
this(color);
this.x = x;
this.y = y;
}
void draw(Graphics g) {
g.setColor(color);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
I want to draw them on a JPanel. Do I have to do this within the paint method of JPanel or is it possible to use the add method of JPanel?
A quick answer is that you should extend a JComponent (because you want to add it to JPanel) and override the paintComponent method (because you want some custom painting of your object).
The easiest thing to try is to make your Stone class extend JComponent, rename draw() to paintComponent() and add a Stone instance to your JPanel.
It depends on what you want to do with it on the screen. As the others mentioned you could inherit from JComponent, which could be a good choice if the user wants to interact with it in some way.
A more light-weight approach could be to implement the Shape interface, or provide a method getShape().
You could use the ShapeIcon I wrote some time ago, to add it to a JLabel:
http://softsmithy.sourceforge.net/lib/docs/api/org/softsmithy/lib/swing/icon/ShapeIcon.html
Then add the JLabel to the JPanel.
But maybe instead of adding individual Stone icons to individual JLabels, you want to draw an image first and show that in the JPanel?
If you could tell us more about your goals we could help you better.
Related
So I want to change the position of an image which I implemented as a JComponent after I click the corresponding button, however, it does not do anything when getting clicked.
I think the problem may lay inside my paintComponent method, since I want the image to be drawn in the center of my panel in the beginning.
The panel itself has a GridBagLayout, although I think that anchoring it in the center would not help either, since I want to move the image whenever the button is clicked and only want it to show up in the center of the panel when I open the frame...
For this button in particular I want my JComponent/image to move 50 pixels to the right.
Anyway here's what I did:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (this.getWidth() - img.getWidth(null))/2;
int y = (this.getHeight() - img.getHeight(null))/2;
g.drawImage(img, x, y, null);
}
#Override
public void actionPerformed(ActionEvent e) {
img.repaint(img.getX()+50, img.getY(), img.getHeight(), img.getWidth());
}
Currently, your paintComponent method always uses the same values for x and y, so it should come as no surprise that the image doesn’t move.
x and y are state that needs to be remembered. This is done by defining instance fields:
private int x;
private int y;
You can’t initialize them at construction time, because a component has a width and height of zero when it’s created (usually). Fortunately, there is a method which tells you when a component has been given a width and height; that method is addNotify, which you can and should override:
#Override
public void addNotify() {
super.addNotify();
x = (this.getWidth() - img.getWidth(this)) / 2;
y = (this.getHeight() - img.getHeight(this)) / 2;
}
Notice that you should pass this (your component instance) to any method which expects an ImageObserver argument, rather than null.
Now paintComponent doesn’t need to do any calculation. It just paints the image at whatever x and y happen to be:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, x, y, this);
}
And now that your coordinates are instance fields, you can change them directly:
#Override
public void actionPerformed(ActionEvent event) {
x += 50;
repaint();
}
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 have an assignment where I have to draw a circle in a panel and with that circle calculate the reaction time of the user when the circle changes size or color. I've got the paintComponent method. But now I have to call the method for the circles in another class and I don't know how to do that. Can someone please help me with this?
Here is my class where paintComponent is written:
public class ReactionPanel extends JPanel {
boolean setSmallCircle, setInitialCircle;
Color color = new Color (255,0,0); //color = red
Color c = new Color (255,255,0); //color = yellow
int size;
int x = 250;
int y = x;
public void paintComponent(Graphics g){
super.paintComponent(g);
if (setInitialCircle){
size = 50;
}
if (setSmallCircle) {
size = 50;
}
else {
size = 150;
}
g.drawOval(x,y,size,size);
g.fillOval(x,y,size,size);
}
void setInitialCircle(Graphics g, Color color){
g.setColor(color);
}
void setSmallCircle(Graphics g, Color c){
g.setColor(c);
}
void setBigCircle(Graphics g, Color c){
g.setColor(c);
}
}
Now I would need those (setInitialCircle etc) and call them in my main class ReactionExperiment like so:
void startTest(){
//draw red circle
}
How do I do this?
Thanks for the help!
I believe you want this ?
ReactionPanel reactionPanel = new ReactionPanel();
reactionPanel.setSmallCircle(x, x);
What this code does it instantiates ReactionPanel (it makes a new instance of it). As such you can then use its methods in another class.
I assume you have two classes, and you'd like to call a public function defined in one from the other. Since the method is not a static method, you'd have to instantiate an object for the class like -
ReactionPanel obj = new ReactionPanel();
then using this object, you can call any method defined in the first class, like
obj.paintComponent(g); // you'll have to define g first though
I am creating lines and other components and want them to respond like a Swing button events as a line would be clickable :
class CustomLine extends JComponent {
private int destx = 100;
private int desty = 100;
private int startx = 0;
private int starty = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(startx, starty, destx, desty);
}
}
how this works?
It would be a math problem. You need to find the equation of the line and then find the distance from point (mouse click).
The math is done for you, for example here
You will also need to figure out handling mouse events.
What about extending it from JButton?
Just overrive the paint-Method like you did with JComponent and use it like a normal Button.
I'm not sure, if you need to alter the Border, too. I'm not familiar with altering GUI-Elements, but maybe this should do the trick.
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.