I was working a swing application that can draw graphs of maths function. I was using getGraghics functions but I have no idea how I remove and repaint them so I decided to override the paintComponent() method to implement what I was looking for
what I want to do is drawing function graphs in a panel after user click the button. but it seems paintCompnent() is not working. I have followed exactly on any existing tutorial and similar questions on stack overflow.but none of them working for me :( it just made no sense:(
Please help I have stuck on this problem for over a whole night:(
following were codes for drawing functions graphs but as it is not working so I only left the part of drawing coordinate system for testing and after that is the code of how I create an instance and try to add it to my panel in the main class
class drawfunction extends JPanel{
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.drawLine(0, 200, 400, 200);
g.drawLine(200,0 , 200, 400);
}
}
then is the code in main class
JPanel panel = new JPanel();
panel.setBounds(14, 104, 400, 400);
contentPane.add(panel);
panel.setBackground(Color.white);
JButton btnNewButton = new JButton("View the graph");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//a= Integer.parseInt(cofficient_a.getText());
//b= Integer.parseInt(cofficient_b.getText());
//c= Integer.parseInt(cofficient_c.getText());
//d= Integer.parseInt(cofficient_d.getText());
//e= Integer.parseInt(cofficient_e.getText());
drawfunction a=new drawfunction();
panel.add(a);
});
Can anyone please tell me what I should do to fix this. Thank you !!!!
Two basic things...
A component has a default preferred size of 0x0, so when adding it a container under the control of just about any layout manager will get it sized to 0x0 (or very close)
Swing is generally lazy, it won't update the UI when you add or remove components, that could impede performance, as the UI doesn't know the best to update the UI based on what you are doing, instead, you need to call revalidate and (most of the time) repaint to have the UI updated
For example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel center;
public TestPane() {
setLayout(new BorderLayout());
JButton btnNewButton = new JButton("View the graph");
center = new JPanel();
center.setPreferredSize(new Dimension(400, 400));
add(center);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
remove(center);
//a= Integer.parseInt(cofficient_a.getText());
//b= Integer.parseInt(cofficient_b.getText());
//c= Integer.parseInt(cofficient_c.getText());
//d= Integer.parseInt(cofficient_d.getText());
//e= Integer.parseInt(cofficient_e.getText());
center = new Drawfunction();
add(center);
revalidate();
repaint();
}
});
add(btnNewButton, BorderLayout.NORTH);
}
public class Drawfunction extends JPanel {
public Drawfunction() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.red);
g2d.drawLine(0, 200, 400, 200);
g2d.drawLine(200, 0, 200, 400);
g2d.dispose();
}
}
}
}
Related
To be more specific I edit the question by replacing it with some reproducible code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestStackOverflow {
public static void main(String[] args) {
new TestStackOverflow();
}
public TestStackOverflow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame window = new JFrame();
window.setSize(new Dimension(1000, 1000));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel header = new JPanel();
header.add(new JLabel("Hello, I'm the header"));
header.setBackground(Color.red);
window.add(header, BorderLayout.NORTH);
window.add(new TestPane());
window.setLocationRelativeTo(null);
window.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.blue);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(900, 0, 100, 100);
//System.out.println(getWidth());
}
}
}
This code shows the rectangle being drawn too far right. I saw with "getWidth" that the width is actually less then 1000, but I had defined with window.setSize(new Dimension(1000, 1000)) the size of the window. So, why is it just 984 instead of 1000. The swing documentation says that the component would take the remaining space and that would / should be in my case 1000.
image of wrong placed rectangle
You seem to want it in the corner of the whole window. In that case you need to override the frame's painting method. Demo obviously - I'm not suggesting you write it exactly like this:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestStackOverflow {
public static void main(String[] args) {
new TestStackOverflow();
}
public TestStackOverflow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame window = new JFrame() {
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawRect(900, 0, 100, 100);
// System.out.println(getWidth());
}
};
window.setSize(new Dimension(1000, 1000));
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel header = new JPanel();
header.add(new JLabel("Hello, I'm the header"));
header.setBackground(Color.red);
window.add(header, BorderLayout.NORTH);
window.add(new TestPane());
window.setLocationRelativeTo(null);
window.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.blue);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//g.drawRect(900, 0, 100, 100);
// System.out.println(getWidth());
}
}
}
I've done several inspection and done research on how to make a single panel transparent so that the image underneath it will show but has been unsuccessful with panels on a JTabbedPane. I have setOpaque(false) and setBackground(new Color(0,0,0,20)) on both the panels of the JTabbedPane and also did the same on the JTabbedPane itself. However, I still can't get the image on the back of the tabbed pane to show. What else am I missing here?
tabbePane.java
package tabbedpanetransparencypractice;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.*;
public class tabbedPane extends JTabbedPane{
JPanel tab1panel = new JPanel();
JPanel tab2panel = new JPanel();
public tabbedPane(){
this.setPreferredSize(new Dimension(400,400));
this.setBackground(new Color(0,0,0,20));
this.setOpaque(false);
tab1panel.setBackground(new Color(0,0,0,20));
tab2panel.setBackground(new Color(0,0,0,20));
tab1panel.setOpaque(false);
tab2panel.setOpaque(false);
this.addTab("Tab 1", tab1panel);
this.addTab("Tab 2", tab2panel);
}
}
topPanel.java
package tabbedpanetransparencypractice;
import javax.swing.*;
import java.awt.*;
public class topPanel extends JPanel{
Image myBG = new ImageIcon(getClass().getClassLoader().getResource("Assets/loginBg.jpg")).getImage();
#Override
public void paintComponent(Graphics g){
g.drawImage(myBG,0,0,getWidth(),getHeight(),this);
}
public topPanel(){
addTabbedPane();
}
public void addTabbedPane(){
tabbedPane tabbedpane = new tabbedPane();
this.add(tabbedpane);
}
}
frame.java
package tabbedpanetransparencypractice;
import java.awt.Dimension;
import javax.swing.*;
public class frame extends JFrame{
topPanel myPanel = new topPanel();
public frame(){
this.setPreferredSize(new Dimension(600,600));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.pack();
this.setLocationRelativeTo(null);
addComponents();
}
final void addComponents(){
this.setContentPane(myPanel);
}
}
main.java
package tabbedpanetransparencypractice;
public class main {
public static void main(String[] args) {
frame myFrame = new frame();
}
}
This is the output I get (I want the tab1 and tab2 to be transparent to reveal the bg image underneath)
I'd appreciate any help. Thanks.
First, Swing DOES NOT know how to deal with component's whose background colors are alpha based but which are opaque. Swing only knows how to deal with fully opaque and fully transparent components.
Using alpha based colors will generate weird and random paint artefacts. The simple answer is, you should never apply a alpha based color to a component's background (the only exception is JFrame - thanks Sun :P)
The primary solution is, fake it.
That is, make the component transparent (setOpaque(false)) and paint the background at a reduced alpha level. You can then use a alpha based color (because the component is no longer reliant on the color, as it's transparent), but I tend to just use a AlphaComposite as it's generally easier to control and manipulate.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
BackgroundPane bp = new BackgroundPane();
bp.setLayout(new BorderLayout());
bp.setBorder(new EmptyBorder(20, 20, 20, 20));
SeeThroughTabbedPane tp = new SeeThroughTabbedPane();
tp.setAlpha(0.5f);
tp.addTab("Tab 1", new TestPane("I be see through"));
tp.addTab("Tab 2", new TestPane("But you can't see me"));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(bp);
frame.add(tp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane(String text) {
setOpaque(false);
setLayout(new GridBagLayout());
JLabel label = new JLabel(text);
label.setForeground(Color.WHITE);
add(label);
}
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - bg.getWidth()) / 2;
int y = (getHeight() - bg.getHeight()) / 2;
g2d.drawImage(bg, x, y, this);
g2d.dispose();
}
}
}
public class SeeThroughTabbedPane extends JTabbedPane {
private float alpha;
public SeeThroughTabbedPane() {
setOpaque(false);
setAlpha(1f);
}
public void setAlpha(float value) {
if (alpha != value) {
float old = alpha;
this.alpha = value;
firePropertyChange("alpha", old, alpha);
repaint();
}
}
public float getAlpha() {
return alpha;
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getBackground());
g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
super.paintComponent(g);
}
}
}
Remember, anything you added to the JTabbedPane which is still opaque will remain so, not in my TestPane's constructor, I set the panel's opaque state to false
You might also like to take a look at Painting in AWT and Swing and Performing Custom Painting
You might be able to use UIManager.put("TabbedPane.contentAreaColor", new Color(255, 255, 0, 100));
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TransparentTabbedPaneTest {
public JComponent makeUI() {
Color bgc = new Color(110, 110, 0, 100);
Color fgc = new Color(255, 255, 0, 100);
UIManager.put("TabbedPane.shadow", fgc);
UIManager.put("TabbedPane.darkShadow", fgc);
UIManager.put("TabbedPane.light", fgc);
UIManager.put("TabbedPane.highlight", fgc);
UIManager.put("TabbedPane.tabAreaBackground", fgc);
UIManager.put("TabbedPane.unselectedBackground", fgc);
UIManager.put("TabbedPane.background", bgc);
UIManager.put("TabbedPane.foreground", Color.WHITE);
UIManager.put("TabbedPane.focus", fgc);
UIManager.put("TabbedPane.contentAreaColor", fgc);
UIManager.put("TabbedPane.selected", fgc);
UIManager.put("TabbedPane.selectHighlight", fgc);
UIManager.put("TabbedPane.borderHightlightColor", fgc);
JTabbedPane tabs = new JTabbedPane();
JPanel tab1panel = new JPanel();
tab1panel.setBackground(new Color(0, 220, 220, 50));
JPanel tab2panel = new JPanel();
tab2panel.setBackground(new Color(220, 0, 0, 50));
JPanel tab3panel = new JPanel();
tab3panel.setBackground(new Color(0, 0, 220, 50));
JCheckBox cb = new JCheckBox("setOpaque(false)");
cb.setOpaque(false);
tab3panel.add(cb);
tab3panel.add(new JCheckBox("setOpaque(true)"));
tabs.addTab("Tab 1", tab1panel);
tabs.addTab("Tab 2", tab2panel);
tabs.addTab("Tab 3", new AlphaContainer(tab3panel));
JPanel p = new JPanel(new BorderLayout()) {
private Image myBG = new ImageIcon(getClass().getResource("test.png")).getImage();
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(myBG, 0, 0, getWidth(), getHeight(), this);
}
};
p.add(tabs);
p.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TransparentTabbedPaneTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
//https://tips4java.wordpress.com/2009/05/31/backgrounds-with-transparency/
class AlphaContainer extends JComponent {
private JComponent component;
public AlphaContainer(JComponent component) {
this.component = component;
setLayout( new BorderLayout() );
setOpaque( false );
component.setOpaque( false );
add( component );
}
/**
* Paint the background using the background Color of the
* contained component
*/
#Override
public void paintComponent(Graphics g) {
g.setColor( component.getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
}
}
I have ran into a problem. The problem lies with adding multiple components to a JFrame, all within separate classes. I have to add the two components DrawBoard and QuestionBox into the JPanel 'panel' in the Board class. The DrawBoard and QuestionBox will both perform different functions.
The DrawBoard component should be 600x600 pixels, while the QuestionBox component should be 600x120 pixels. The DrawBoard is at the bottom and the QuestionBox sits at the top. I am not sure as to what layout to use.
When run I get this result.
Game class
package snake;
//This class is used to run the game.
public class Game {
/**
* #author HyperBlue
*/
public static Board board;
public static void main(String[] args) {
// TODO Auto-generated method stub
//Creates an object board from the Board() construct
board = new Board();
}
}
Board Class
public class Board implements ActionListener {
public DrawBoard drawBoard;
public QuestionBox questionBox;
public Timer ticker = new Timer(20, this);
public Board() {
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
JFrame frame = new JFrame("Snake");
frame.pack();
Insets insets = frame.getInsets();
JPanel container = new JPanel();
questionBox = new QuestionBox();
drawBoard = new DrawBoard();
container.setLayout(new BorderLayout());
container.add(questionBox, BorderLayout.NORTH);
container.add(drawBoard, BorderLayout.SOUTH);
frame.setMinimumSize(new Dimension(600+insets.left + insets.right, 720 +insets.bottom + insets.top));
frame.add(container);
//Sets the frame in middle of screen
frame.setLocation((dim.width / 2) - (frame.getWidth() / 2), (dim.height / 2) - (frame.getHeight() / 2));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
DrawBoard Class
package snake;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
//Warnings will not be thrown (are suppressed).
#SuppressWarnings("serial")
public class DrawBoard extends JPanel{
public static Color yellow = new Color(13816442);
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 0, 600, 600);
}
}
QuestionBox Class
package snake;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class QuestionBox extends JPanel{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 600, 120);
}
}
Each component should be responsible for managing it's own size, you should start by overriding getPreferredSize of the panels and returning the size you would like to use.
You should also not rely on magic numbers, but instead should use actual physical values, for example, instead of
g.fillRect(0, 0, 600, 120);
You should use...
g.fillRect(0, 0, getWidth(), getHeight());
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test1 {
public static void main(String[] args) {
new Test1();
}
public Test1() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
add(new DrawBoard());
add(new QuestionBox(), BorderLayout.SOUTH);
}
}
public static class DrawBoard extends JPanel {
public static Color yellow = new Color(13816442);
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 0, 600, 600);
}
}
public static class QuestionBox extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 120);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 600, 120);
}
}
}
You should also know that Toolkit.getDefaultToolkit().getScreenSize(); is not the most reliable method for determining the visible screen area, as it does not take into account various OS elements, like the task bar or dock, which can take up screen space.
this is my first post here. I am currently learning about basic GUI and Graphics in java. I'm still pretty new to the language, and it's my first language as well. As I learn java I like to experiment and play with the new tools I acquire from reading. As a result I wanted to make a window where the colors it is filled with will flash, currently, i wanted to stay simple and have it flash from orange to cyan and back. However, right now, all my program does is start white, and fill in with cyan and the text, and then stop changing whatsoever, and I'm not sure why this is.
Please point me in the right direction, thanks!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class ColorFlash extends JFrame{
private static class Display extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int h = 200; h > 25; h--){
g.setColor(Color.ORANGE);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.CYAN);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.ORANGE);
g.drawString("Hello world!", 30, 35);
g.drawOval(100, 100, 60, 60);
}
}
}
private static class ButtonHandler implements ActionListener{
public void actionPerformed(ActionEvent e){
System.exit(0);
}
}
public static void main (String [] args){
Display displayPanel = new Display();
JButton okButton = new JButton("OK");
ButtonHandler listener = new ButtonHandler();
okButton.addActionListener(listener);
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(displayPanel, BorderLayout.CENTER);
//content.add(okButton, BorderLayout.SOUTH);
//subContent.add(displayPanel, BorderLayout.SOUTH);
//content.add(okButton, BorderLayout.SOUTH);
content.add(okButton, BorderLayout.SOUTH);
JFrame window = new JFrame("GUI Test");
window.setContentPane(content);
window.setSize(500, 500);
window.setLocation(100,100);
window.setVisible(true);
}
}
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing
Basically, what's happening is whatever is painted last in your loop is what is actually been painted to the screen
What you need is some kind of timer/trigger that can change the color you want to paint with and repaint the component.
Take a look at How to use Swing Timers for more details
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestPaint {
private static class Display extends JPanel {
private Color[] colors = new Color[]{Color.ORANGE, Color.CYAN};
private int colorIndex;
public Display() {
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setBackground(colors[colorIndex % 2]);
colorIndex++;
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.ORANGE);
g.drawString("Hello world!", 30, 35);
g.drawOval(100, 100, 60, 60);
}
}
public static void main(String[] args) {
new TestPaint();
}
public TestPaint() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Display());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
You might also like to take a look at Initial Threads
In my application I need to draw grid lines just like those like Photoshop has - e.g, an user can drag lines over the document to help him align layers. Now, the problem is that I am able to draw such lines (it's just plain simple Java2D painting using Line2D), but I am not being able to keep such lines on top of everything else, because when children components draw themselves, my grid line is erased.
The program structure is something like this: JFrame -> JPanel -> JScrollPane -> JPanel -> [many others JPanels, which are like layers]
As a test, I added the draw code to JFrame, which correctly shows my Line2D instance on top of everything else. However, when I do anything in an child component that requires that child to repaint itself, the line that was drawn in the JFrame is erased.
I understand that this is the expected Swing behavior - that is, it will only repaint those areas that have changed. However, I am looking for some approach that continuously draws line grid lines on top of everything else.
The only way I was able to get it working was to use a Swing Timer that calls repaint() on my root component every 10ms, but it consumes a lot of CPU.
UPDATE
Working code of an example is below. Please mind that in my real application I have dozens of different components that could trigger a repaint(), and none of them have a reference to the component that does the grid line drawing (of course I can pass it to everyone, but that appears to be the latest option)
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GridTest extends JFrame {
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel();
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// When I call repaint() here, the paint() method of
// JFrame it's not called, thus resulting in part of the
// red line to be erased / overridden.
// In my real application application, I don't have
// easy access to the component that draws the lines
p.repaint();
}
});
b.setBounds(0, 150, 100, 30);
add(b);
pack();
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D)g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
gg.dispose();
}
}
if you want to paint over JComponents placed to the JScrollPane then you can paint to the JViewPort, example here
EDIT:
1) beacuse your code painted to the wrong Container, to the JFrame, sure is possible to paint to the JFrame, but you have to extract RootPane or GlassPane
2) you have to learn how to LayoutManagers works, I let your code with original Sizing, not nice and very bad
3) paint to the GlassPane or JViewPort
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import javax.swing.*;
public class GridTest extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D) g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
//gg.dispose();
}
};
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.repaint();
}
});
b.setBounds(0, 150, 100, 30);
add(b);
pack();
setVisible(true);
}
}
EDIT: 2, if you expecting single line, on the fixed Bounds
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class GridTest extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D) g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
gg.dispose();
}
};
JPanel p1 = new JPanel();
p1.setBorder(new LineBorder(Color.black,1));
JPanel p2 = new JPanel();
p2.setBorder(new LineBorder(Color.black,1));
JPanel p3 = new JPanel();
p3.setBorder(new LineBorder(Color.black,1));
p.setLayout(new GridLayout(3,0));
p.add(p1);
p.add(p2);
p.add(p3);
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p, BorderLayout.CENTER);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.repaint();
}
});
add(b, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
One possible solution is to override the JPanel's repaint method so that it calls the contentPane's repaint method instead. Another point is that you probably shouldn't draw grid lines directly in the JFrame but rather in its contentPane. Counter to what I usually recommend, I think you're better off overriding the either the contentPane's paint method (or that of some other containing JPanel), not its paintComponent method so that it can call after the children have been painted. For example:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class GridTest2 extends JPanel {
private static final Stroke LINE_STROKE = new BasicStroke(3f);
private boolean drawInPaintComponent = false;
public GridTest2() {
final JPanel panel = new JPanel() {
#Override
public void repaint() {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
JPanel contentPane = (JPanel) rootPane.getContentPane();
contentPane.repaint();
}
}
};
panel.setBackground(Color.white);
panel.setPreferredSize(new Dimension(100, 100));
JPanel biggerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
biggerPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 0, 0));
biggerPanel.setOpaque(false);
biggerPanel.add(panel);
JButton resetButton = new JButton(new AbstractAction("Reset") {
public void actionPerformed(ActionEvent arg0) {
panel.repaint();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(resetButton);
setLayout(new BorderLayout());
add(biggerPanel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawInPaintComponent ) {
drawRedLine(g);
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (!drawInPaintComponent ) {
drawRedLine(g);
}
}
private void drawRedLine(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(LINE_STROKE);
g2.setColor(Color.red);
g2.drawLine(0, 50, getWidth(), 50);
}
private static void createAndShowGui() {
GridTest2 mainPanel = new GridTest2();
JFrame frame = new JFrame("GridTest2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I know it's an old post, but I've had the same problem recently...
You should override paintChildren instead of paint or paintComponent.
From the JComponent.paint documentation:
Invoked by Swing to draw components. Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.
This method actually delegates the work of painting to three protected methods: paintComponent, paintBorder, and paintChildren. They're called in the order listed to ensure that children appear on top of component itself. Generally speaking, the component and its children should not paint in the insets area allocated to the border. Subclasses can just override this method, as always. A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.
So if you
#Override
protected void paintChildren(Graphics g){
super.paintChildren(g);
paintGrid(g);
}
the grid will be on top of your children components ^^
Assuming the parent frame already has a list of all the grid lines it has to draw, what you can do is get each child frame to draw its own personal bits of the lines. In pseudocode:
gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()
Swing uses a JLayeredPane within JFrames (and similar components). Using the layered pane, you can position paint-only components over your main content.
This code uses components placed within the JLayeredPane to position (and automatically repaint) arbitrary decorations above the main content of any component, thus obviating the need to override the paint() method of any given component.