I have to following situation:
A JPanel is used a "drawing board" where the user can add blocks that have specific connection points which can be used to interconnect to other blocks (think Simulink or labView).
The blocks themselves are JPanel objects with buttons on them, that are added to the drawing board by the add() method after setting a null layout. The JPanels can be dragged around with the help of a MouseMotionListener.
To draw the connections, I override the drawing board paintComponent() method and call g.drawLine() (after a call to super.paintComponent). This works, but as soon as you move a block, the lines overlap each other and it turns into a mess.
Therefore I call drawingBoard.repaint() during the time a user moves a block. This has the effect that the lines are flickering visible during dragging and then disappear immediately.
Clearly, the drawing of the JPanels in the parent JPanel interferes with each other.
How can I solve this?
edit: Some snippets of the code:
The drawing board:
public void paintComponent(Graphics g){
g.clearRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
drawConnections(g);//Contains g.drawLine calls
}
The blocks are added to the drawing board with the JPanel.add() method. Below is the MouseMotionListener of such a "block" JPanel.
public void mouseDragged(MouseEvent e)
{
pt = SwingUtilities.convertPoint(movingPanel, e.getX(), e.getY(), movingPanel.getParent());
movingPanel.setBounds(pt.x - clickX, pt.y - clickY, movingPanel.getWidth(), movingPanel.getHeight());
e.consume();
movingPanel.getParent().repaint();
}
The block JPanel does not override paintComponent because no special drawing is necessary in it. It just contains some JLabels and JButtons. The buttons are used to create connections between blocks. The connection list is then used inside drawConnections mentioned above.
There's really not much more than this.
SOLVED:
Ok, as expected this was a very small detail.
In the line drawing code I used
Graphics2D g2 = (Graphics2D) this.getGraphics();
instead of
Graphics2D g2 = (Graphics2D) g;
I just noticed the references are not the same. D'oh
On approach might be to make the lines be JComponents that have been added to the panel and having them repaint themselves. This might also have the nice effect of isolating the line logic and paint calculation in a line class instead of having it on your drawing board.
If JDesktopPane is an acceptable "drawing board," you could try the approach shown here.
Related
I have a Battleship game in which I am trying to draw the ships, hits, and misses on the Grid object. Grid is an instance of JPanel with many Blocks. Blocks are JPanels also but are attributes of the Grid. The ships are being drawn on the Grid but under the Blocks. Is it possible to draw over the Blocks? I tried the Glass Pane and it didn't work too well. Any other solutions?
Is it possible to draw over the Blocks? I tried the Glass Pane and it
didn't work too well. Any other solutions?
Yes, a non-recommended but sometime useful approach is to use an extended JPanel and override the paint(Graphics g) function:
Class MyGridPane extends JPanel
{
#Override
public void paint(Graphics g) {
super.paint(g); // <----- don't forget to call this
// then anything you draw will appear above the child component's
Graphics2D g2d = (Graphics2D)g.create(); // cloning to work, it is safer aproach
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();// disposing the graphics object
}
}
Alternatively you may look into using JLayeredPane to work with a Panel containing your drawn image above the target panel(Grid)'s layer.
Check out How to Use Layered Pane
Blocks are JPanels also but are attributes of the Grid. The ships are
being drawn on the Grid but under the Blocks.
There's a sneaky way to achieve it, but you may need to change your base layout to GridBagLayout.
Basically, you want to add each Block to its own cell but GridBagLayout will allow you to add components that can expand columns and/or rows, allowing to add your ships that expand over Blocks.
This, of course, assumes you ships and effects are based on components
I am trying to draw several things inside a JPanel. I am drawing shapes (works), and I also want to fill the JPanel with a picture.
But paintComponent() only takes one argument. And this gets complicated when I have some extra code for drawing the shapes.
My paintComponent() method is currently like this:
public void paintComponent(Graphics g) {
g2 = (Graphics2D) g;
for (int i = 0; i < shapes.size(); i++) {
Shape s = (Shape) shapes.get(i);
if (s != null)
g2.draw(s);
}
}
I have searched around a lot and cannot find a way to do this.
Does anyone know how to do this, or maybe some workaround?
Just like your approach to drawing shapes, you need to maintain a reference to the images you want to draw within the class and reference them in much the same way.
The following are all examples of drawing images within paintComponent, on a verity of topics
BufferedImage in JFrame doesnt Show up
Scale the ImageIcon automatically to label size
Jpanel resize on repaint
Nb - I may be misreading this, but you should never be calling paintComponent yourself. It is called on your behalf by the repaint engine within Swing
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.
I'm writing a pretty simple game in Java, and I'm running into the issue of very serious flickering when I play the game as an applet in a browser. That is, all of my sprites, which are being painted on top of a background, are sometimes shown onscreen, but usually not - they repetitively flash onto the screen and then disappear. I've read that double buffering is probably the solution to this, but I'm having trouble implementing it correctly.
I'm using a JApplet as the container for a JPanel. This JPanel is the container onto which the sprites and game objects are painted - that is, in the JPanel's paintComonent method. In my JApplet, I'm using init, paint, and update override methods as follows:
Image offscreen;
Graphics bufferGraphics;
Dimension dim;
public void init(){
dim = getSize();
setBackground(Color.BLACK);
offscreen = createImage(dim.width,dim.height);
bufferGraphics = offscreen.getGraphics();
}
public void paint(Graphics g){
bufferGraphics.clearRect(0,0,dim.width,dim.height);
//here is my question - i"m not sure what I should print to bufferGraphics
g.drawImage(offscreen, 0, 0, this);
}
public void update(Graphics g){
paint(g);
}
The problem I'm running into is that, at the commented line, I'm unsure of what to do to get the current applet image printed to bufferGraphics. I read an example in which the sprite was painted straight to the JApplet, without using a JPanel. In light of that, my guess is that I'd need to paint the JPanel onto bufferGraphics at the commented line. Am I on the right track here? Any help is greatly appreciated; I just would like to know any way to do this properly.
Swing is double buffered by default, there is no need to do anything special.
Your problem is probably the painting code. The code you posted is used for AWT painting, NOT Swing painting.
Custom painting is done by overriding the paintComponent() method of a JPanel or JComponent. I suggest you start by reading the Swing tutorial on Custom Painting for a working example.
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.