Drawing on a jPanel that has sub-panels - java

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.

Related

Can't understand JPanel setBackground method behavior

JPanel.setBackground method does nothing (although opaque attribute is true) if super.paintComponent father method isn't being called.
I have read a lot of similar questions here about this issue, and in each one of them I've only found solutions without explanation that helped me to understand why setBackground method when written before adding the JPanel to JFrame changes the JPanel background color, while when the setBackground is written inside paintComponent nothing is changed (only when calling father's paintComponent method as already mentioned).
Is it somehow related to Graphics object?
I have tried to change JPanel's opaque attribute to true and using setBackground(COLOR.BLACK) in paintComponent() method I had overriden in a class that extends JPanel
paintComponent(Graphics g)
{
this.setOpaque(true);
this.setBackground(COLOR.BLACK);
}
I expect that the JPanel background color will be black
Instead, background color is the default one
Well, first of all if you're using paintComponent(Graphics g) method, the first line you need to have inside is: super.paintComponent(g) otherwise you're breaking the paint-chain.
This will allow the parent component to draw the default component, before any customizations you do to it. If you don't do it, well, is like having a drawing in a piece of paper, imagine a circle, then cutting that circle and then trying to paint the outside.
Here's a more in-depth answer to How does super.paintComponent(g) works
However I wouldn't write
this.setOpaque(true);
this.setBackground(COLOR.BLACK);
inside the paintComponent(...) method, as it gets called several times and you can't control when it will ever get called. I would put those lines in a constructor, unless you want to change it later in your program while it's being painted depending on the state of your program or a gradient maybe.
For this part:
why setBackground method when written before adding the JPanel to JFrame changes the JPanel background color
Honestly, I don't understand what you mean.
Why do you say that if i won't call super.paintComponent(),it will break the chain? It's still drawing all the shapes and lines i want using graphics object.
From the docs:
The JPanel has a UI delegate which performs the background painting for itself. You call it by using super.paintComponent(g) and we pass the Graphics component to prevent irrevocable changes such as Graphics.translate
Your JPanel knows how to paint its children, but requires some help to paint itself, and this help comes from its parent.
When I mentioned "break the paint chain" I didn't mean nothing would paint, but that you would get strange behaviors such as the one of the JPanel's background disappearing or not being set.
In addition,something weird happens if the argument i'm sending to setBackground method is a random color(using Random object). JPanel changing color very quickly although i'm not doing anything(not minimizing,not resizing,etc).Can you consider why?
As I said before, the paintComponent gets called several times and you don't have control over when it will be called, even moving your mouse or something else will trigger the panel to repaint.

Java - Repaint specific components

I'm learning Java as part of my degree but its very brief but what ever I do I like to make sure I atleast have some understanding.
Until now anything that I want displayed on the screen is put into the paintcomponent method of the JPanel.
However, I have drawn out some parts of my layout which never change, and only a certain thing in the middle rotates.
I have a timer which calls repaint().
If I correct in thinking that everything including the components that never change are being removed and then redrawn and the whole paintcomponent method is being run every time.
To me I feel like I should (or there must be) a way where the static stuff is moved out / is only drawn once, and only the parts that I specifically want to be redrawing should stay in the paintcomponent method?
Is this correct or am I not fully understanding something?
I am assuming that you are not adding GUI components in the paintComponent method, right?
Components are not "removed" during a repaint
Better to put the stable part of any image into a single background BufferedImage and draw the BufferedImage in the paintComponent method. This can make for more efficient painting.
Consider calling an overload of repaint(...) that sets a bounding rectangle for the region to be painted.

Why is paintComponent() continuously and asynchronously being called without explicit repaint() call?

So this question has two parts, which I think may be related, and it's mostly abstract. Briefly, here's what I'm doing:
I have a JFrame with a JPanel and some child JPanels each with 3 JButtons on it. I also created a JComponent called glassPanel for the JFrame (i.e. myJFrame.setGlassPane(glassPanel)), that allows me to paint over the JPanels and buttons.
(1) Essentially triggered by clicking all 3 buttons on a JPanel, glassPanel is set to Visible (which appears to then call paintComponent()). This is relates to the first question.
(2) In paintComponent() I draw and paint rectangles and images, using a double buffer, onto glassPanel. This is relates to the second question.
Here's my relevant GlassPanel class code (this is not an SSCCE because it is an abstract question for now):
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
public class GlassPanel extends JComponent {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
setDoubleBuffered(true);
Graphics2D g2 = (Graphics2D) g;
g2.drawRect(x,y,width,height);
g2.fillRect(x,y,width,height);
g2.drawImage(img, x, y, this);
}
}
By placing a System.out.print statement inside the paintComponent() method, I could tell it was being called continuously and also asynchronously. For how I think the call is made, see (1). Also, let's say I'm absolutely certain there is no call to repaint() anywhere in the code (I've checked many, many times). This is the basis of the first question.
The first time I click 3 buttons, all goes smoothly. The rectangle and images are both drawn immediately. However, when I click the next 3 buttons (at this point, glassPanel has already been setVisible(true) and the first rectangle and image are still on the screen, painted over the first 3 buttons), the second rectangle and image only load partially. When I click away from the JFrame and onto the Eclipse window that I've run the program from, the number of calls to paintComponent() rapidly increases by the same amount each time AND the partially loaded image(s) and rectangle(s) immediately and completely show up in the background JFrame. When I click back to the JFrame, the number of calls goes up again by an exact amount). This is the basis of the second question.
UPDATE: Here's something I read:
Also, when the GUI is covered by another window and then becomes
uncovered, the painting system invokes the paintComponent method with
the painting area equal to the newly uncovered area.
My questions are:
(1) why might paintComponent() be called so much without a repaint()? Or, a similar question, what might be calling paintComponent()?
UPDATE: After doing a little math, I strongly believe that it's being called by every component (all buttons and panels) continuously. But still, no call to repaint()...
(2) Why are the images loaded partially until i take focus from the JFrame window?
Note that I have tried many things: (a) creating my own doubleBuffer and using no double buffer (I know it's mostly for animation), (b) overriding and not overriding paintComponent(), (c) drawing and not drawing the image (the rectangle still takes time to load), (d) making absolutely sure there was no repaint(), (e) using and not using SwingUtilities.invokeLater(new Runnable() {
public void run() { //stuff});, (f) done an if statement to only setVisible(true) once.
I can try and past the SSCCE if I must, but I do think these are more abstract. Thanks!
Well, I think I've answered both questions. For the first, why paintComponent() was being called continuously, was that it was not actually being called continuously. It was being called by all the components when it first shows the GUI. When the Eclipse window covers it and then uncovers it, it gets called more times.
For the second, it has to do with the clipBounds of the Graphics2D object/thing. I found out how the clipBounds were changing for each paint call, so when I set the clip at the start of the paintComponent() method, the images show up immediately. (BTW, It looks GREAT!).
With a twist: after an image is displayed, each click of a button does something to the image. I haven't figured out what exactly, though. It almost looks like its repainting the same image over the old images.
So I have to figure out how to keep the old images but draw new ones when appropriate and only draw/add the new ones onto the glassPanel.
UPDATE: Calling repaint() immediately after each button is clicked helps a little. But it still causes the image to flicker somewhat, as if adding another layer, as the button is pressed, and then it returns to normal when the user lets go.

repaint() method causes a second, non-functional JMenuBar to appear?

I am creating a program in which the user can draw lines and erase them. I am able to create my window and create a functioning JMenuBar. When the drawing capability is not enabled, everything is fine. But when the drawing capability is enabled, a second JMenuBar is painted below the original. This menu bar is not functional, and sometimes when it is drawn, ALL of the graphics on the screen are repainted lower as well. Re-sizing the window in any way remedies these problem.
I would post pictures but, you know, I'm a new user and can't.
I have been able to narrow the problem down to a point in my paint() method. Need to know: When the mouse is dragged, the repaint() method is called. It uses my paint() method which calls the paintObject(Graphics g) method in my Line class. The JMenuBar error is appearing at the END of the paint method in the top class.
public void paint(Graphics g)//paint() in the top class
{
for(int i = 0; i < objs.size(); i++)//Loop that finds all Tool(Line) objects that
((Tools)objs.get(i)).paintObject(g);//have been drawn, and redraws them.
//This part of the code completes successfully with no error.
}//ERROR OCCURS HERE!!!!!!
I have no idea why this is happening. As a side note, there is also a JTextField on the screen that doesn't appear until it is highlighted. Thanks for at least reading this far!
Edit: I'll go on a limb, I'll bet you in fact are drawing directly on the top level window (JFrame or JApplet or JDialog...) and have added the mouse listener to this window as well -- hence the drawing of components translated down a bit.
The solution is to look at the tutorials first before attempting something fairly complex like this. They'll tell you how to draw correctly including the suggestions I've given in my comments, such as drawing in a JComponent or JPanel's paintComponent method and calling the super.paintComponent(g) method as the first method call of your paintComponent override.

Painting on JPanels

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.

Categories

Resources