I need learn the logic how can i change JPanel color which created own Painter method. I create a sample project for illustration;
Issue: Direct color changing code in button action not change anything.
Question 1) Is override the paintComponent method is proper way for paint he JPanel with Gradient colors while panel creation?
Question 2) How can i change the background color of this JPanel with other Gradient color or Direct color?
--CODE--
package tryingproject2;
import java.awt.Color;
import java.awt.GradientPaint;
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.JLabel;
import javax.swing.JPanel;
public class TryingProject2 {
public static void main(String[] args) {
class ImagePanel extends JPanel{
public void paintComponent( Graphics g ) {
Graphics2D g2d = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
Color color1;
Color color2;
color1 = new Color(223,130,24,255);
color2 = new Color(255,255,255,255);
GradientPaint gp = new GradientPaint(0, 0, color1, w, 0, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
}
JFrame frame = new JFrame();
frame.setLayout(null);
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel userPanel = new ImagePanel();
userPanel.setBounds(100, 40, 200, 200);
userPanel.setLayout(null);
JLabel newLabel = new JLabel("Sample Label");
newLabel.setBounds(50, 10, 100, 100);
userPanel.add(newLabel);
JButton button = new JButton("Change Color To Red");
button.setBounds(100, 300, 200, 40);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
userPanel.setBackground(Color.red);
userPanel.repaint();
System.out.println("Button Pressed.");
}
});
frame.add(userPanel);
frame.add(button);
frame.setVisible(true);
}
}
I rearranged some of your code to get rid of other problems.
Here's the GUI I created.
Here's the GUI after I left clicked the button.
I made the following changes to your code.
I moved all of the JFrame code into a run method, so I could get out of static methods and into object-oriented classes and methods as fast as possible.
I added a call to the SwingUtilitiles invokeLater method to ensure that Swing components are created and modified on the Event Dispatch thread.
I created a createMainPanel method to create the main panel. Instead of using ugly null layouts with pixel precision settings, I used a Swing layout, the BorderLayout, to position the components. This allows the user to expand the GUI to fill the screen, as well as the GUI fitting different computers with different screen sizes.
The ImagePanel class is a complete, first-class Java class. That means you can have class fields and class constructors. I provided a way to set the colors from outside the class. Set both colors to the same color if you don't want a gradient.
The paintComponent method of the ImagePanel class should start with a super call, to maintain the Swing Paint chain. The paintComponent method should do nothing but paint. Period. Full stop. Nothing else. I removed the code that had nothing to do with painting.
Looking in the actionPerformed method inside the createMainPanel method, you see how I change one of the gradient colors and perform a repaint. The action listener is the controller of your GUI. Only controller code should change the model (the colors in ImagePanel) or the view (ImagePanel and the JFrame). Always look for the model / view / controller pattern when creating a Swing GUI.
Here's the revised code.
package com.ggl.testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
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.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TryingProject2 implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TryingProject2());
}
#Override
public void run() {
JFrame frame = new JFrame("Color Gradient Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JPanel imageLabelPanel = new JPanel();
imageLabelPanel.setLayout(new BorderLayout());
final ImagePanel imagePanel = new ImagePanel(new Color(223, 130, 24,
255), new Color(255, 255, 255, 255));
imageLabelPanel.add(imagePanel, BorderLayout.CENTER);
JLabel newLabel = new JLabel("Sample Label");
newLabel.setHorizontalAlignment(JLabel.CENTER);
imageLabelPanel.add(newLabel, BorderLayout.SOUTH);
panel.add(imageLabelPanel, BorderLayout.CENTER);
JButton button = new JButton("Change Color To Red");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
imagePanel.setColor1(Color.RED);
imagePanel.repaint();
System.out.println("Button Pressed.");
}
});
panel.add(button, BorderLayout.SOUTH);
return panel;
}
public class ImagePanel extends JPanel {
private static final long serialVersionUID = 6970287820048941335L;
private Color color1;
private Color color2;
public ImagePanel(Color color1, Color color2) {
this.color1 = color1;
this.color2 = color2;
this.setPreferredSize(new Dimension(200, 200));
}
public void setColor1(Color color1) {
this.color1 = color1;
}
public void setColor2(Color color2) {
this.color2 = color2;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
GradientPaint gp = new GradientPaint(0, 0, color1, w, 0, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
}
}
Related
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 made my own BottomBar with a simple gradient extending JComponent and adjusting the paintComponent() method.
Then I add it to the SOUTH of my JFrame which uses BorderLayout.
Everything looks correct at the beginning.
When I resize the frame the BottomBar gets repainted and set to the new position correctly. The think is, it happens a few milliseconds to late, so that one can see the JFrame 's background for a second.
The funny thing is, that when I set the execution environment to Java-SE 1.6 it works... (instead of 1.7)
Also, Im running it on a mac, if that makes a difference.
Code - JButton Example
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String args[]){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Resize Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JButton(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
}
Code - BottomBar Example
Main:
public class Main {
public static void main(String args[]){
Frame window = new Frame();
window.setSize(500, 400);
window.setVisible(true);
}
}
Frame:
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class Frame extends JFrame{
private static final long serialVersionUID = 1L;
public Frame() {
setLayout( new BorderLayout() );
getContentPane().add( BorderLayout.SOUTH, new BottomBar() );
}
}
BottomBar
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
public class BottomBar extends JComponent {
private static final long serialVersionUID = 1L;
public BottomBar() {
setSize(200, 30);
setPreferredSize( new Dimension(200, 30) );
}
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
GradientPaint gradient = new GradientPaint(0, 0, new Color(185, 185, 185), 0, getHeight() , new Color(151, 151, 151) );
g2.setPaint(gradient);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setColor( new Color(64, 64, 64) );
g2.drawLine(0, 0, getWidth(), 0);
g2.setColor( new Color(215, 215, 215) );
g2.drawLine(0, 1, getWidth(), 1);
}
}
I am unable to reproduce the effect you describe on 1.6; you might try the sscce below on 1.7. Note, several suggestions for your example:
Avoid setXxxxSize(), as discussed here. If you just want a 30 pixel high bar in SOUTH, override getPreferredSize() as shown below. If you later decide to add components, you'll need to add a layout manager.
#Override
public Dimension getPreferredSize() {
return new Dimension(0, 30);
}
Use pack() to let the Window adopt the preferred sizes of the enclosed components. I've added an arbitrary size JPanel to the CENTER; resize the frame to see how the bar grows horizontally in SOUTH.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/a/13610367/230513 */
public class Main {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("BottomBar");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
}, BorderLayout.CENTER);
frame.add(new BottomBar(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
});
}
private static class BottomBar extends JComponent {
#Override
public Dimension getPreferredSize() {
return new Dimension(0, 30);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
GradientPaint gradient = new GradientPaint(
0, 0, new Color(185, 185, 185),
0, getHeight(), new Color(151, 151, 151));
g2.setPaint(gradient);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setColor(new Color(64, 64, 64));
g2.drawLine(0, 0, getWidth(), 0);
g2.setColor(new Color(215, 215, 215));
g2.drawLine(0, 1, getWidth(), 1);
}
}
}
I'm French so my English is quite bad but I have a real problem with java:
I'm trying to show an Image built manually with some fillRect & co.
It works. Next, I want to update this image as a function of the text I enter in the text field. And there is the problem: it doesn't change anything, and if I manually rescale the window of the JFrame, the image shows totally deformed or scaled badly.
I'm a beginner and I have difficulties with GUI, moreso when I want to couple it with Images.
Can you help me? Why doesn't it work as I want? This is my code below, a bit long but actually quite simple ! Thanks :)
I've changed it a bit, this is the 2e VERSION.
Now the problem s that I can't change a condition in order to modify the color of a simple rectangle, try "lol" in the entry field !
CODE VERSION 2
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.ImageIcon;
public class Fenetre extends JFrame {
private JFrame frame;
private JPanel container = new JPanel();
private JTextField jtf;
private JLabel label = new JLabel("Commandes ");
private JButton b = new JButton ("OK");
private Graphics graph;
private Image img;
private JLabel screen;
private boolean color;
/**
* Constructeur de l'objet
*/
public Fenetre(){
color = true;
frame = new JFrame();
frame.setTitle("Animation");
frame.setSize(1000, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
jtf = new JTextField();
jtf.setPreferredSize(new Dimension(800, 30));
b.addActionListener(new BoutonListener());
frame.setContentPane(top);
frame.setVisible(true);
paintComponent(graph);
screen = new JLabel( new ImageIcon(img));
top.add(screen);
top.add(label);
top.add(jtf);
top.add(b);
frame.setContentPane(top);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
if(jtf.getText().equals("lol")) lol();
System.out.println("Entry : " + jtf.getText());
}
}
public void paintComponent(Graphics g)
{
if(img == null) {
img = frame.createImage(1000,300);
g = img.getGraphics();
}
g.setColor(Color.white);
g.fillRect(0,0,1000,300);
if(color) g.setColor(Color.orange); else g.setColor(Color.blue);
g.fillRect(8,25,200,100);
g.setColor(Color.green);
g.drawString("Text",10,10);
}
public void lol()
{
if(color) color = false; else color = true;
}
}
PREVIOUS CODE
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.ParseException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.ImageIcon;
public class Fenetre extends JFrame {
private JPanel container = new JPanel();
private JTextField jtf;
private JLabel label = new JLabel("Commandes ");
private JButton b = new JButton ("OK");
private Graphics g;
private Image img;
private JLabel screen;
/**
* Constructeur de l'objet
*/
public Fenetre(){
this.setTitle("Animation");
this.setSize(1000, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
jtf = new JTextField();
jtf.setPreferredSize(new Dimension(800, 30));
b.addActionListener(new BoutonListener());
this.setContentPane(top);
this.setVisible(true);
paint(g);
screen = new JLabel( new ImageIcon(img));
top.add(screen);
top.add(label);
top.add(jtf);
top.add(b);
this.setContentPane(top);
}
class BoutonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.out.println("Entry : " + jtf.getText());
if(jtf.getText().equals("lol")) lol();
}
}
#Override
public void paint(Graphics g)
{
if(img == null) {
img = createImage(1000,300);
g = img.getGraphics();
}
g.setColor(Color.white);
g.fillRect(0,0,1000,300);
g.setColor(Color.orange);
g.fillRect(8,25,200,100);
g.setColor(Color.green);
g.drawString("Text",10,10);
}
#Override
public void update(Graphics g)
{
g.setColor(Color.blue);
g.fillRect(8,25,300,100);
}
public void lol()
{
g.setColor(Color.blue);
g.fillRect(8,25,200,100);
}
}
I see several problems in your code:
You are confusing your g member variable with the g parameter of the paint method. When lol is called, g is null and you get a NullPointerException
You should never grab a hold on Graphics (only in really rare cases). Instead you override properly paintComponent() and you use the Graphics parameter to draw what you want. When you want to update the component, you call repaint()
Don't override paint, but override paintComponent()
Don't override paint of JFrame. Use a dedicate component for that. No need to use a JLabel for that, a simple JComponent is enough.
Don't extend JFrame if you are not extending its functionality.
After adding components to the component hierarchy, call revalidate()
Fix those issues and come back with another question if you still have problems.
You should probably consider reading this tutorial and the few next steps. It will show you basic examples of things similar to what you are trying to do.
EDIT:
I took your V2 code and patched it as I could. This is very far from perfect but you should get the gist of how you can do this:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Fenetre extends JComponent {
private boolean color;
/**
* Constructeur de l'objet
*/
public Fenetre() {
color = true;
setPreferredSize(new Dimension(1000, 300));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(0, 0, 1000, 300);
if (color) {
g.setColor(Color.orange);
} else {
g.setColor(Color.blue);
}
g.fillRect(8, 25, 200, 100);
g.setColor(Color.green);
g.drawString("Text", 10, 10);
}
public void lol() {
if (color) {
color = false;
} else {
color = true;
}
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initUI();
}
});
}
protected static void initUI() {
JFrame frame = new JFrame();
frame.setTitle("Animation");
frame.setSize(1000, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
JPanel container = new JPanel();
final JTextField jtf = new JTextField();
final Fenetre fenetre = new Fenetre();
JLabel label = new JLabel("Commandes ");
JButton b = new JButton("OK");
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
jtf.setPreferredSize(new Dimension(800, 30));
class BoutonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (jtf.getText().equals("lol")) {
fenetre.lol();
}
System.out.println("Entry : " + jtf.getText());
}
}
b.addActionListener(new BoutonListener());
top.add(fenetre);
top.add(label);
top.add(jtf);
top.add(b);
top.revalidate();
frame.setContentPane(top);
frame.setVisible(true);
}
}
Your Swing graphics programming has several significant problems, and I urge you to go through the tutorials to learn how to do this better. For example, you are
calling the paint method directly -- something you should almost never do except in very special situations (this is not one of them)
Drawing directly in the JFrame's paint(...) method. Instead you will want to draw in the paintComponent(...) method override of a class derived from JComponent such as JPanel.
Calling update unnecessarily as if this were an AWT program. You don't do this in Swing unless you're changing the Look & Feel.
Again, take a look at the tutorials on this -- you will not regret doing this, trust me.
edit -- too slow! 1+ to Guillaume
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.
I want to draw a line in a JPanel.
This is my GUI and I want a line in the JPanel in white.
I find many examples but the problem is the how to use it.
In many exmples, always they draw in a JFrame that extends from a JPanel.
I want to add the Panel to the Frame and add some buttons to draw lines in many directions and use the X button in center to clean the JPanel.
This is the code of the interface:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.Color;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class circuit extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
circuit frame = new circuit();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public circuit() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 559, 332);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 21, 359, 255);
contentPane.add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
panel.setBackground(Color.WHITE);
JLabel label = new JLabel("New label");
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
/////////////
}
});
label.setIcon(new ImageIcon("C:\\Users\\achermen\\Desktop\\up.png"));
label.setBounds(447, 66, 46, 48);
contentPane.add(label);
JLabel label_1 = new JLabel("New label");
label_1.setIcon(new ImageIcon("C:\\Users\\achermen\\Desktop\\down.png"));
label_1.setBounds(447, 159, 46, 48);
contentPane.add(label_1);
JLabel label_2 = new JLabel("New label");
label_2.setIcon(new ImageIcon("C:\\Users\\achermen\\Desktop\\right.png"));
label_2.setBounds(495, 112, 46, 48);
contentPane.add(label_2);
JLabel label_3 = new JLabel("New label");
label_3.setIcon(new ImageIcon("C:\\Users\\achermen\\Desktop\\left.png"));
label_3.setBounds(398, 112, 46, 48);
contentPane.add(label_3);
JLabel label_4 = new JLabel("New label");
label_4.setIcon(new ImageIcon("C:\\Users\\achermen\\Desktop\\1303860240_list-remove.png"));
label_4.setBounds(447, 112, 46, 48);
contentPane.add(label_4);
}
}
This is the code to draw a line
public void paint(Graphics graphics)
{
graphics.drawLine(10, 20, 300, 310);
}
So how to use this lines ....
Thanks in advance.
Best regards,
Ali
It may be easier to draw lines using the following approach:
click to mark the first endpoint
drag to show the line in progress
release to mark the second endpoint
This related example may offer some additional guidance.
Addendum
The example below implements the outline above.
I've update the example to show how to use a panel of buttons to affect the drawing.
See also this related example that uses the Action interface with key bindings.
I've updated this example to use Key Bindings.
LinePanel.java
import java.awt.BasicStroke;
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.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/questions/6991648
* #see https://stackoverflow.com/questions/6887296
* #see https://stackoverflow.com/questions/5797965
*/
public class LinePanel extends JPanel {
private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;
public LinePanel() {
this.setPreferredSize(new Dimension(640, 480));
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(8,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
drawing = true;
p1 = e.getPoint();
p2 = p1;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
p2 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
p2 = e.getPoint();
repaint();
}
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 10;
public ControlPanel() {
this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code,
final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
});
ControlPanel.this.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(k, k.toString());
ControlPanel.this.getActionMap()
.put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() {
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.add(new ControlPanel(), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LinePanel().display();
}
});
}
}
Is this going to work like an etch-a-sketch? Then you need to track the current position of the point.
Point current = new Point(0, 0); //for example.
Then when the user clicks the buttons you can simply increment or decrement x and y accordingly.
On left arrow:
current.setX(current.getX() - INC);
where INC could be a variable that specifies the length of the distance to draw the line. Maybe 5? Always set a second point p1 to the previous location though.
It is always easier to create a class that extends Canvas or JPanel to draw on rather than draweing directly on the JFrame.
e.g.
public class Circuit extends JFrame {
Point p1, current;
JPanel drawPanel;
//your other declarations
public Circuit(){
super();
drawPanel = new DrawPanel();
p1 = new Point(0, 0);
current = new Point(0, 0);
add(drawPanel, BorderLayout.CENTER);
//your other code
}
class DrawingPanel extends JPanel{
public void paintComponent(Graphics g){
g.drawLine(p1.getX(), p1.getY(), current.getX(), current.getY());
}
}
//the rest of your code.
}
There is a simple answer for triggering graphics: e.g. The following code can be placed inside a click event and used for drawing a few simple objects on a jPanel. jPanel1 in this case was situated on one side of a tabbed jPanel7 and next to the triggering button. To do this in netbeans GUI, the code was placed inside the button action event. Once the usual errors appeared for not having the proper imports, right click on the code and click on "fix imports". Bingo, all is well :-) Warning: the setBackground command for the panel will override the graphics object. If you set the background color without using the graphics object, you will not see your objects!
Graphics g = jPanel1.getGraphics();
g.setColor(Color.blue);
g.drawLine( 0, 50, 20, 50);
g.setColor(Color.white);
g.fillRect(40, 50, 20, 20);
g.setColor(Color.blue);
g.drawRect(40, 50, 20, 20);
g.drawOval(80, 50, 20, 20);
g.setColor(Color.green);
g.fillOval(80, 50, 18, 18);
This restores your faith in true love :-)
The difficulty here is that any change or repaint will erase your effort. This approach is specifically discouraged by the Java founders. But in the current rendition of Netbeans Swing, where extending the jPanel is made difficult by locking code changes, this approach could be your only short term solution. A simple persistent graphic extension for the jPanel would be a most welcome addition to the current Netbeans Swing environment, a graphics panel. This would allow you to drag and drop the graphics panel and then get on with the event driven use of that panel. 40 other IDE's already have this, it seems Java has been slow to add this feature.