Painting on JPanels - java

I'm writing a program that will be just a simple shape maker, I'm supposed to my main panel, ShapeMakerPanel, along with 2 panels on that one, controlPanel(which holds buttons for choosing the shape and clearing it, etc) and drawingArea (where the shapes are actually drawn), but no matter what I do, I can't get the paint to show up on drawingArea. If I just use paintComponent and comment out add(drawingArea) then the drawing stuff works, but on the bottom layer, how can I paint on the drawingArea Jpanel. Also, when I do draw the shapes, I can only have one at a time, because every time I start a new one, the panel is wiped clean`
super.paintComponent(g);
g.setColor(penColor);
if (p1 != null && p2 != null)
{
if (shapeChoice.getSelectedItem().equals("Line"))
{
Line line = new Line(p1.x, p1.y, p2.x, p2.y);
line.draw(g);
}
}
I know its the super.paintComponent(g) messing me up, but without that, as I drag the mouse, it draws hundreds of lines.
If you were wondering about the Line class, we had to make a class for each shape we drew, the draw() method just uses the coordinates of the line and puts them into drawLine().

Don't override paintComponent() in ShapeMakerPanel; override it in drawingArea's class (if drawingArea is a plain JPanel, then create a new subclass of JPanel). In general you need to subclass the component on which you're going to paint.

Also, when I do draw the shapes, I can only have one at a time, because every time I start a new one, the panel is wiped clean`
See Custom Painting Approaches for a couple of ways to solve this.

Related

Passing a JPanel to a method to draw rectangles

I have a class with a draw method that accepts a JPanel as an argument.
The first line in the draw method is:
Graphics g = p.getGraphics();
Where (p is the the jPanel passed as an argument to the draw(Jpanel P) method).
I want to draw different rectangles in different colors (based on conditional statements). So i use the
g.setColor(Color.RED) // or another color
Then i draw the rectangle using
g.fillrect(x,y,xsize,ysize).
When i do
System.out.println("color is " + g.getColor().toString());
I see the colors changing, but i don't see the rectangles appearing on the JPanel. This JPanel is an object inside my class that extends from JFrame. What do i need to do to see the rectangles on my JPanel?
You should not paint by obtaining the Graphics object from the JPanel. What you should do is to subclass JPanel and override paintComponent, check this.
The reason why is because the Graphics instance is created everytime your Panel is painted, and you have no control about it, because its parent (the JFrame) may decide when to do so. So you should never make any assumption about the Graphics instance, and you must include your painting logic in the paintComponent method.
The reason why you are not seeing your rectangles is because you are painting them with either an old Graphics instance, or because in the next rePaint they are getting erased because is not the order it is supposed to be (those calls must be in the paintComponent method).
JPanel doesn't know that its canvas is updated, so you see nothing on the screen. The proper way is to override JPanel's method onPaintComponent and draw inside this method. This way, after you call JPanel.repaint() you'll see the stuff you've drawn.

Adding multiple graphics to a single JPanel

I am attempting to add 3 graphics, two of which have to move(presumably through each other, moving on a single axis) and only the lattermost one added to the panel shows.
In other words, I have many panels added to my JFrame, and in the biggest panel I have put in graphics objects using specific coordinates. It may be more helpful to show the code.
//the gameArea is the referred-to JPanel, above this code
TankOne tank1 = new TankOne(Color.GREEN);
TankTwo tank2 = new TankTwo(Color.MAGENTA);
FieldBar fieldb = new FieldBar(Color.getHSBColor((float) Math.random(),(float) Math.random(),(float) Math.random()));
JPanel tank1panel = new JPanel();
JPanel tank2panel = new JPanel();
tank1panel.add(tank1);
tank2panel.add(tank2);
gameArea.add(tank1panel);
gameArea.add(tank2panel);
gameArea.add(fieldb);
//repaint code here
As you can see, it is a tank game.
The one that shows up on the GUI is the last gameArea.add. [Here it would be fieldb, a bar that shows the game floor].
The attempt made here is me trying to put two new panels into the gameArea Jpanel and then placing the two tank objects inside those. Still no dice.
Is there a workaround for this that would allow me to move the tanks by pixels?
I have seen some answers include switching to gridlayout, but I don't think that will help seeing as the extra Panels did nothing.
Thank you for your answers.
I'm guessing that each tank JPanel shows an image of a single tank (? not 100% sure). Some suggestions, but please do tell me if my assumptions are way off base:
Keep the logic and view well separated a la M-V-C.
Do all graphics in a single JPanel, perhaps called a DrawingPanel, in its paintComponent(...) method.
The Background image will be a BufferedImage that is draw in the DrawingPanel's paintComponent(...) method.
The a tank itself would not be represented by a JPanel but would be its own small BufferedImage sprite, again draw in the paintComponent method, but drawn after the background image.
Each tank would need at least 4 sprites, one for each direction.
You may need more sprites if your tanks will move along a diagonal.
And a separate sprite/image for the turret.
You would move a sprite's drawn position in response to changes of it's model representation's position.
Same thing for turret rotation.

Drawing on a jPanel that has sub-panels

I have a jPanel, which contains a number of sub-panels that can be dragged around. What I want to do, is to draw lines connecting some of those sub-panels together.
However, while it seems like this should be simple, it's proven very frustrating. The best I've gotten, is to override the paintComponent function in the original jPanel as such:
panCharDisplay = new javax.swing.JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintLines(g);
}
};
And then draw the lines as such:
public void paintLines(Graphics g) {
g.setColor(Color.BLUE);
for (Character c : characters) {
if (c.female && c.spouse != null) {
g.drawLine(c.display.getX(), c.display.getY(), c.spouse.display.getX(), c.spouse.display.getY());
}
}
}
This works in a sense, in that it does technically draw the lines, in the right place, the right color, and so on, but only if I scroll away from where the line should be, and then scroll back. Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before. The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
I assume the reason for this is that I'm drawing the lines at the wrong time, and need to draw them after drawing the sub-panels, and also make sure that they are re-drawn every time the panels are dragged around.
Is there another place I can put in an override to make the lines show up more consistently? Another method I tried, was to make a class that extends JPanel, and try to use that to handle the drawing, but I couldn't get it to work at all.
Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before.
You probably need to invoke repaint() on the panel's parent as you drag the panel around.
The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
You should be able to override either the paint() or paintChildren() method instead of the paintComponent() method. Whichever method you override make sure to invoke super.XXX() first so that the default painting is done before you attempt to draw your lines.
Personally I like the lines painted below the component as is demonstrated in trashgod's GraphPanel example. The example does custom painting for the shapes, but I would guess the logic would be similar for the components.
You can get this JConnector project and use as is or adapt the sources as you need.

Painting over the top of components in Swing?

I have a JPanel added to a JViewport, and the panel has several other panels added to it. I'm trying to implement a dragging selection, where you can select more than one component by dragging the mouse. The only problem I'm facing is that the selection rectangle is being painted behind the components added to the main JPanel. How can I paint over the top of them?
My structure is as follows: JFrame -> ContentPane -> JLayeredPane -> JScrollPane -> JPanel -> JPanel [].
Design draft for college assignment:
As you can see, the rectangle is behind the other panels.
This is what I'm already doing (on a much simpler level obviously), and Swing paints the rectangle underneath the components added to it.
This is one case where you should override the paint() method of the panel and not the paintComponent() method. Then the custom painting will be done AFTER all the child components have been painted.
Use a Layered Pane:
http://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
This allows you to create overlapping components.
Use a glass pane to handle the drag painting, and possibly events as well:
http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
hot really sure what do you really needed and final effect, maybe is there two another ways painting to
1) GlassPane
2) Viewport
you can put that together, carrefully Insets to the visible Rectanle
Without seeing your actual code, it is difficult to say what you are doing wrong. However, I can still say what I would do:
Create a JPanel that represents the whole area where you want to draw, which — of course — contains every component.
Override that panel its paintComponents(Graphics) like this (EDITED, notice the s is now the last character from the method name):
#Override
public void paintComponents(Graphics g)
{ // ^
super.paintComponents(g);
// Draw your selection rectangle:
g.setColor(Color.RED);
g.drawRectangle(selectionRectangle);
}
Okay, this is what I've decided to do in the end:
I'm not sure if this is the best way to do it, but it seems to work okay.
Note: Using MigLayout.
In the constructor of the JPanel lying underneath the colored blocks.
...
this.add(new JPanel() {
#Override
public boolean isOpaque() {
return false;
}
#Override
public void paintComponent(Graphics g) {
if (dragShape != null) {
g.setColor(Colors.SECONDARY);
g.setStroke(new BasicStroke(2));
g.draw(dragShape);
}
}
}, "pos 0 0, width 100%, height 100%", 0);
...
Custom painting on top of Swing components is facilitated by JLayeredPane. This article describes an abstract base class that facilitates overpainting specific areas (like selection rectangles or component bounds).

Clearing JPanel

I am making a program in which there is a square that changes its x and y positions when a key is pressed. The square moves but the the old square is still there. How do I remove/clear everything from a panel before I repaint it? Calling removeAll had no effect.
Presumably your code includes custom paintComponent() logic. The key thing to observe is what does your panel look like when you do not override paintComponent()? An empty (or cleared) panel:
Thus the solution is to invoke the paintComponent() method of the parent type on the panel, before executing your custom paintComponent() logic:
public class CustomPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // first draw a clear/empty panel
// then draw using your custom logic.
}
}
I think this should work.
g.clearRect (0, 0, panel.getWidth(), panel.getHeight());
Also, you could keep the old location of the square and just clear that rather than clear the whole background.

Categories

Resources