Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
currently I am creating a custom JFrame with a custom JPanel. Rendering the JFrame works without flickering, but when I am rendering a custom `JPanel the components flicker. My code:
My code for painting the JFrame:
public void paint(Graphics g) {
Dimension d = getSize();
Graphics og = g;
g = b.getGraphics();
//background
g.setColor(SConstants.ct.FRAME_BACKGROUND());
g.fillRect(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), d.width - SConstants.ct.FRAME_FRAME_WIDTH(), d.height - SConstants.ct.FRAME_FRAME_WIDTH());
//frame
if(isFocused()){
g.setColor(SConstants.ct.FRAME_FOCUSED_FRAME());
}else{
g.setColor(SConstants.ct.FRAME_UNFOCUSED_FRAME());
}
//top
g.fillRect(0, 0, d.width, SConstants.ct.FRAME_FRAME_HEIGHT());
//left
g.fillRect(0, 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);
//bottom
g.fillRect(0, d.height - SConstants.ct.FRAME_FRAME_WIDTH(), d.width, SConstants.ct.FRAME_FRAME_WIDTH());
//right
g.fillRect(d.width - SConstants.ct.FRAME_FRAME_WIDTH(), 0, SConstants.ct.FRAME_FRAME_WIDTH(), d.height);
//icon
if(getIconImage() != null)
g.drawImage(getIconImage(), 15, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - getIconImage().getHeight(null) / 2, null);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(SConstants.ct.FRAME_FONT());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLACK);
g2d.drawString(getTitle(), d.width / 2 - g2d.getFontMetrics().stringWidth(getTitle()) / 2, SConstants.ct.FRAME_FRAME_HEIGHT() / 2 - g2d.getFontMetrics().getHeight() / 2 + g2d.getFontMetrics().getAscent());
getContentPane().paint(og);
og.drawImage(b, 0, 0, null);
}
I am setting the contentPane like this:
getRootPane().setLayout(null);
SPanel cp = new SPanel();
cp.setBounds(SConstants.ct.FRAME_FRAME_WIDTH(), SConstants.ct.FRAME_FRAME_HEIGHT(), getWidth() - SConstants.ct.FRAME_FRAME_WIDTH() * 2, getHeight() - SConstants.ct.FRAME_FRAME_HEIGHT() - SConstants.ct.FRAME_FRAME_WIDTH());
setContentPane(cp);
The SPanel is like this:
public void paint(Graphics g) {
paintComponent(g);
}
protected void paintComponent(Graphics g) {
//paintComponents(g);
for(Component c : getComponents())
c.paint(g);
}
I am rendering a SLabel like this:
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawString(text, getX() + SConstants.ct.LABEL_SPACING_HORIZONTAL(), getY());
}
public Dimension getPreferredSize() {
Rectangle2D d = getGraphics().getFontMetrics(SConstants.ct.LABEL_FONT()).getStringBounds(text, getGraphics());
int w = (int)(d.getWidth() + SConstants.ct.LABEL_SPACING_HORIZONTAL() * 2);
int h = (int)(d.getHeight() + SConstants.ct.LABEL_SPACING_VERTICAL() * 2);
return new Dimension(w, h);
}
Runnable example: https://drive.google.com/file/d/0B8czlxc22-xqOHlPdUZxV2dkMjQ/edit?usp=sharing
Anyone know why the SLabel is flickering?
You don't seem to have an understanding of how Swing works or how painting in Swing/AWT works...
SMain...
This...
public class SMain {
public static void main(String[] args) throws Exception {
//...
while(true){
sf.repaint();
Thread.sleep((int)(1000.0 / 24.0));
}
}
}
Is a bad idea...Swing uses a passive rendering model, meaning that updates occur when they are required, seen as you don't seem to be making changes to the state of the UI, this is just adding more pressure onto your system.
It could also, if called properly, prevent your UI from updating ever.
SFrame...
getRootPane().setLayout(null);
Why? Just why? The JRootPane has it's own layout management process that ensures that the contentPane is provided all the space within the containing frame minus space for the menu bar if provided, which you've not provided so, yeah, default implementation would allow the contentPane to occupy all the available space...
setSize may be called a number of times in a row, it would be better to simply invalidate b (setting it to null) and create it only when required, (when paint is called), but this begs the question of why?
Why not just allow the content pane to do this for you...
pack would automatically resize the contentPane, if you would actually let the JRootPane do it's job...
paint does a number of important jobs and you've circumvented most of them, this will cause you no end of problems and weridness....
Case in point, you don't translate the Graphics context before you try and paint the content pane, this means that it's possible for the contents of the content paint to paint over what you have painted (ie the title).
You would be better of using...
The content pane as it is, adding the title to the BorderLayout.NORTH position and the remaining content, wrapped in another container to the BorderLayout.CENTER position of default contentPane
Implementing your own JRootPane to provide the layout you want, using separate components for the title and content
Implementing your own Look and Feel delegate for the JFrame and painting the borders yourself
Using a EmptyBorder on the contentPane to provide space for the title...
Just about anything else then what you have done...
Beware, MouseListener and MouseMotionListener may be blocked by components higher in the container hierarchy which implement either or both of those interfaces
SPanel...
paint does a lot of important work, including, but not limited to, painting the component, border and components (as well as implementing double buffering), but you've decided to circumvent them all and do your own thing...
paintComponent is suppose to paint the background of the component, not it's children. This is already done by paint and paintChildren and paintComponents
A child component may also be painted independently of it's parent...
SComponent...
Beware, Component is a heavyweight (AWT) component and it's painting requirements are different from those of lightweight (Swing) components, and in fact, you should avoid adding heavy weight components to light weight containers....
Component already defines paint(Graphics)...
Heavyweight components are also not double buffered by default...this means that there updates can cause flickering...hence your problem...
"But I've double buffered the frame"...sorry to disappoint you, but components can be and are typically, painted independently of their parents, so they may not be painted as part of the parents update...
Conclusions...
You don't seem to have an understanding of how painting works in AWT/Swing. Take a look at Painting in AWT and Swing for more details...
You also don't seem to understand of the differences between Swing and AWT. While Swing sits on top of AWT, it has it's own framework requirements which differ from AWT and some significate ways...
You seem to be trying to implement you own framework...why, I have no idea, obviously the last 20+ years of development on Swing and AWT aren't good enough or something...
You might consider having a look at Modifying the Look and Feel and using the UI delegate API to implement what you are trying to do...
You have two choices, you can either, learn to understand how the current framework (Swing/AWT) works and work within it to achieve your goals or spend the rest of your life chasing weirdness, unexplainable problems and bugs.
You could also consider having a look at JavaFX which allows you to modify the look and feel via CSS...
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 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
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 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.