How to draw an image over JPanels - java

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

Related

Image only paints within original bounds

I want to animate several jpgs in a JFrame, I'll show you some extracts:
My class constructor that extends JFrame
super(title);
setLayout(null);
setResizable(false);
setSize(Settings.windowWidth, Settings.windowHeight);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
My class constructor that extends JPanel
i = new ImageIcon(image).getImage();
setSize(i.getWidth(this),i.getHeight(this));
setBounds(x, y, i.getWidth(this), i.getHeight(this));
The overwritten method
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(i, getX(), getY(), null);
}
Yes, I know null layout isn't preferrable, but unless you have a better idea for absolute positioning I'll stick with it for now.
Above code does paint the image, starting at (x,y), but not completely.
For 50 and 100 it shows this:
Which is pretty much: It only paints the image within a 256x256 box (image dimensions) from 0,0, no matter where it has been relocated to.
Any advice, help, solutions, suggestions?
If you need more code, ask me, just don't feel like putting everything around it in here, too ;)
There is no need for custom painting:
Add the ImageIcon to a JLabel and add the JLabel to a JPanel
Change the location of the label on the panel when you want to animate it.
Or, if you do custom painting then there is no need for a null layout.
You override the getPreferredSize() method of the JPanel and add your panel to the frame.
Then in the paintComponent() method you can paint the image where every you want withing the bounds of the preferred size that you set.
My gut feeling is you don't understand how component painting actually works...
First, you do this...
i = new ImageIcon(image).getImage();
setSize(i.getWidth(this),i.getHeight(this));
setBounds(x, y, i.getWidth(this), i.getHeight(this));
Then you do this...
g.drawImage(i, getX(), getY(), null);
which seems to be painting the image at a offset position from the components origin, but since the component is sized to match the size of the image, the image is cropped at the component boundaries.
When a component is painted, the Graphics context's origin is set to the components location, meaning that 0x0 is now the components top/left corner.
You can test this by using setBorder(new LineBorder(Color.RED)), which will show you the physical bounds of the component
In your case, something like
g.drawImage(i, 0, 0, this);
In your case, you should be moving the component not the image.
Personally, I'd add the JPanel to the JFrame using a BorderLayout, then you can simply move the image anywhere within the context of the component itself. Remember to override getPreferredSize to return an appropriate size for your purposes so the frame can be packed around it more effectively.
It's tricky to do animation with components (not impossible, there's just a lot to take into account), generally it's just easier to paint directly to a canvas like a JPanel, but that's me
See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works

How to add multiple layers onto JPanel

I need some help with Java Swing components and its capabilities. I need to add a JPanel to a JFrame and paint an Ellipse2D on it. Onto the Ellipse2D I want to add another element, in my case it is a picture (right now I use an ImageIcon, maybe wrong). How can I achieve adding the Ellipse2D and the picture on the panel as shown in the image I attached?
The reason why I need the images separated is, because I need to change the filling color of the ellipse sometimes.
Thanks for any help.
What you need is to create a custom JPanel implementation and override paintComponent method.
Inside it, you just do:
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw ellipse here
// Draw your image here. It will be drawn on top of the ellipse.
}
This way, you can hold the ellipse fill color in the CustomPanel class, and just call repaint() method after you change the color.
your idea could be very good described (including code example) in the Oracles tutorial How to Decorate Components with the JLayer Class
notice JLayer is available only for Java7, but its based on (for Java6) JXLayer
you can use (I'm using) GlassPane too, with the same / similair output to the Swing GUI
EDIT
quite easy and nice output is by using OverlayLayout, there is possible to overlay J/Component(s) with Graphics e.g., a few examples
take the two images as image icons like
ImageIcon car=new ImageIcon("image path");
ImageIcon elipse=new ImageIcon("image path");
add those two image icons two label
JLabel carLabel=new JLabel(car);
JLabel ellipseLabel=new JLabel(ellipse);
and set the position of ellipse and car
carLabel.setBounds(0,0,50,50);
ellipseLabel.setBounds(10,10,50,50);

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).

Drawing between JPanels

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.

How to make canvas with Swing?

I'm trying to make a paint editor with Java in which I have a toolbar with the objects that I would like to paste in the canvas. I'm using Swing components to make the GUI, but when I looked for the way of making the canvas, I only found the class canvas from AWT.
Is there any way to make something similar to canvas with Swing? (for example, JPanel?) I have read that using the class canvas from AWT with a GUI made with swing won't work correctly, is that true?
In order to make a custom 'Canvas' in swing you usually write a subclass of a JPanel. Then, you must overwrite the protected paintComponent(Graphics g) method of JPanel.
In the paint method, you can call methods on the Graphics object to actually draw on the JPanel.
As always, the Java Tutorials have a great reference on this to get you started.
You'll probably want to make a subclass of JPanel and implement your own way of painting components you want to draw onto the panel.
The basic approach will probably be along the line of assigning a MouseListener to the subclass of JPanel, then implement painting functionality.
The basic idea may be something along the line of:
class MyCanvas extends JPanel implements MouseListener
{
Image img; // Contains the image to draw on MyCanvas
public MyCanvas()
{
// Initialize img here.
this.addMouseListener(this);
}
public void paintComponent(Graphics g)
{
// Draws the image to the canvas
g.drawImage(img, 0, 0, null);
}
public void mouseClicked(MouseEvent e)
{
int x = e.getX();
int y = e.getY();
Graphics g = img.getGraphics();
g.fillOval(x, y, 3, 3);
g.dispose();
}
// ... other MouseListener methods ... //
}
The above example is incomplete (and not tested -- it definitely won't compile), but it gives an idea about how to implement a MyCanvas class in which a user can click on and draw circles.
The img object is used to hold the image of the canvas. The paintComponent method is used to paint the img object to the canvas. In the mouseClicked method, the Graphics object associated with img is retrieved in order to fillOval onto the image.
Since one the requirements is to paste images onto the canvas, it may be a good idea to hold some Images that you want to paste into the canvas. Perhaps something along the line of:
Image[] myImages; // Used to store images to paint to screen.
Then, in the routine to paint the image onto img stored in MyCanvas:
g.drawImage(myImage[INDEX_OF_DESIRED_IMAGE], 0, 0, null);
By using the drawImage method of the Graphics object, other Images can be drawn onto Images.
As for the question on AWT and Swing, yes, it is true that you do not want to mix components from the AWT and Swing, as they differ in the way they render GUI components. AWT is based on heavyweight components, meaning they native windowing for painting the GUI, while Swing is based on lightweight components, meaning the GUI is drawn by Java itself without using native components.
A good guide on the difference of AWT and Swing is provided in Painting in AWT and Swing article from Sun.
Simply subclass JComponent.
JPanel is an inappropriate class. It is often suggested as it appears to have setOpaque(true) invoked on it automatically. It's actually the PL&F which does that, and whether or not it actually happens is implementation and vendor dependent.
Canvas is a heavyweight component. That is to say that it is controlled by the underlying windowing system. The result is that it will typically be drawn over the top of Swing components, without respect to z-order or clipping (putting it in a scroll pane will give odd behaviour).
You might want to look at the Minueto API. It is a very simple to use graphics api, and you can combine the Java event listening with it to provide your drawing capability.
http://minueto.cs.mcgill.ca/

Categories

Resources