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
Related
I have a JFrame which contains a single panel.
In the panel I use the paintComponent method to resize its elements according the size of Jframe. The elements of the JPanel are an image as a background and 4 JLabel that cointains 4 ImageIcon and work like buttons. The method paintComponent of Jpanel is like below
public class MyPanel extends JPanel
{
//Declarations
private BufferedImage backGround;
public MyPanel()
{
//Some code here
}
public void paintComponent(Graphics graphics)
{
super.paintComponent(graphics);
Graphics2D graphics2d = (Graphics2D) graphics;
if(backGround != null)
{
graphics2d.drawImage(backGround, 0, 0, getWidth(), getHeight(), this);
}
/* This code is repeated 4 times because I have 4 labels */
label1.setSize(getWidth()/7 , getHeight()/10);
label1.setLocation(getWidth()/2 - getWidth()/14 , getHeight()/3 );
image1 = button1.getScaledInstance(label1.getWidth(), label1.getHeight(),
Image.SCALE_SMOOTH);
label1.setIcon(new ImageIcon(image1));
}
}
The frame has just a simple method , add(myPanel) so I did not write it here.
When I run the application , it takes me around 300 MB of ram and around 30% of CPU (Inter core i5-6200U) , which is quite unsual for me , expecially the amount of CPU. What is causing my application to take so much resources and is there any way I can reduce it ?
Whenever you repaint your component you change your labels' dimensions and create resources (the Image and the ImageIcon derived from it) and assign them as a new icon. These are changes to visible parts of your application and hence must cause repainting the components in question. Basically your paintComponent method
causes a repaint every time it is called effectively creating an endless loop and
is very heavyweight because it allocates expensive resources.
Both of these points are pretty bad ideas. Your paintComponent method should do just what the name suggests, i.e. painting the component. All actions that cause a repaint (changing icons or text, adding or removing components from the tree etc.) must not occur in it.
See also:
The API documentation on paintComponent(Graphics)
Painting in AWT and Swing
EDIT: When you want to resize components dependent on the size of other components create a ComponentListener and add it to the component you want to depend on by calling addComponentListener(ComponentListener). The ComponentListener instance will then have its componentResized(ComponentEvent) method called whenever the size changes.
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.
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.
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/