Can't add Graphics into JPanel in Java - java

I'm writing the UI for the pet project I've been doing and I'm experimenting with java Graphics, drawing lines, shapes, and stuff. And, I've been trying all day to insert a simple line in a Jpanel but still haven't figured out what went wrong.
package thuake;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.geom.Line2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Main extends JFrame{
static Dimension DEFAULT_SIZE = new Dimension(530, 320);
static JFrame Frame1 = new JFrame();
static JScrollPane spanel = new JScrollPane();
static JPanel Panel1 = new JPanel();
static MenuBar menu = new MenuBar();
static Menu menusub1 = new Menu("Open");
public static void main(String[] args)
{
start();
}
public static void start (){
Frame1.setLayout(new FlowLayout(FlowLayout.CENTER,5,10));
spanel.add(new draw());
Frame1.add(spanel);
spanel.setBorder(BorderFactory.createLineBorder(Color.black));
spanel.setPreferredSize(new Dimension(500, 500));
Frame1.add(new JButton("ad"));
Frame1.add(new JButton("ad"));
Frame1.add(new JButton("ad"));
Frame1.add(new draw());
Frame1.setMenuBar(menu);
menu.add(menusub1);
Frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Frame1.pack();
spanel.setVisible(true);
Frame1.setVisible(true);
System.out.println();
}
static class draw extends Component {
public void paint(Graphics g) {
Graphics2D line = (Graphics2D)g;
line.drawLine(0, 0, 120, 120);
}
}
}

Here is a basic framework you should follow when you are doing graphics.
Key points are:
Don't extend JFrame, use a separate instance.
Override paintComponent() and not paint()
Start the process in the EDT. Do all swing stuff in the EDT.
Do not paint outside the EDT or with a graphics context that you retrieved yourself. Always use the one from paintComponent()
Set the width and height (dimension) for the panel and not the JFrame. The reason being that those dimensions on the JFrame include the thick borders. For the JPanel you get the full width.
At some point you can modify this and add additional components to the JFrame and/or JPanel. For now this should provde a basis for experimenting.
You should also read up on this and anything I omitted shown below. Check out the Java Tutorials for more on information on graphics and painting.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Template extends JPanel {
final static int height = 500;
final static int width = 500;
final static String title = "title";
JFrame frame = new JFrame(title);
public static void main(String[] args) {
// start on the EDT
SwingUtilities.invokeLater(() -> new Template().start());
}
public Template() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this); // add the panel
setPreferredSize(new Dimension(width, height));
frame.pack();
// center on screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // always do this
Graphics2D g2d = (Graphics2D) g.create();
// Optional. It averages the edges of a figure to give a smoothing effect
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// do something here.
g2d.setColor(Color.red);
g2d.fillRect(200,200,100,100);
g2d.dispose();
}
}

Related

setUndecorated() code is not working correctly

The program works fine, but when I add the setUndecorated code, the panel does not appear. Problem is solving when I minimize and reopen the program. I tried repaint() , but It's not working.
package testing;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class test extends JFrame{
static int width = 900;
static int height = 520;
JFrame frame;
JPanel panel;
JButton selectKey = new JButton("Select KeyIMG");
static BufferedImage bg;
class MyCanvas extends JComponent{
public void paint(Graphics g) {
try {
bg = ImageIO.read(new File("BGFILE"));
} catch (IOException e) {
e.printStackTrace();
}
g.setClip(0, 0, width, height);
g.drawImage(bg,0,0,width,height, this);
g.dispose();panel.repaint();
}
}
public test(){
super("Test");
setBounds(250, 100, width, height);
selectKey.setBounds(width/9,height/2,width/45*8,height/13);
getContentPane().add(new MyCanvas());setUndecorated(true);setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setBackground( new Color(0, 0, 0, 0));
panel.setLayout(null);
panel.add(selectKey);
add(panel);
}
public static void main(String...Args){
new test();
}
}
What's wrong ?
Issues
Overriding paint. It's highly discouraged to override paint, painting is a complex series of compounding methods which work together to produce a the final result. It's highly recommended that you override paintComponent instead
Not calling super.paint, see the previous comment. Unless you know exactly what you're doing and are prepared to take over the responsibility of the paint method, call it's super method, there only a very few use cases I'd consider it safe not to do this.
Using a alpha based color on an opaque component; panel.setBackground(new Color(0, 0, 0, 0));. This is bad idea. Swing only knows how to deal with opaque and transparent components, it doesn't know how it should paint components with alpha based colors. The API will simply ignore any components beneath it, which is likely one of the major causes of your problem
g.dispose(); don't ever dispose of a Graphics context you did not create or copy. Doing so can prevent other components from been painted
Don't call panel.repaint(); from within any paint method, painting paints the current state, it should never do anything to change it, doing so well put you into a spiral of CPU death as it begins to chew up all the CPU cycles, in fact, MyCanvas has no right to be modifying panel anyway and the way your code is set up, it could generate a NullPointerException
"Other" concerns
static BufferedImage bg; is worrisome. No body else has any need to deal with this variable, the only class which should be dealing with it is the MyCanvas class
g.setClip(0, 0, width, height); is pointless (and potentially dangerous), this has already been done before the paint method was called. This is made worse by the fact that you are not relying on the components actual size, which could cause the painting to overrun the visible bounds of the component
Extending from JFrame. You should avoid extending from top level containers, they are complex components to which you rarely add any new/reusable functionality to and they lock you into a single use case, much better to start with a JPanel and add that to whatever container you need
An example...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
class Background extends JComponent {
private BufferedImage bg;
public Background() {
try {
bg = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException e) {
e.printStackTrace();
}
setLayout(new BorderLayout());
}
#Override
public Dimension getPreferredSize() {
return bg == null ? super.getPreferredSize() : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(bg, 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
}
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setContentPane(new Background());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(new JLabel("This is a label, don't I look pretty"), gbc);
JButton selectKey = new JButton("Select KeyIMG");
panel.add(selectKey, gbc);
panel.setOpaque(false);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static void main(String... Args) {
new Test();
}
}

Drawing a graphic on Glass Pane

I am currently making a version of the board-game Khet and was planning on displaying the laser fired between turns using a Glass Pane, but for the life of me I can't get the drawings to show up at all. To save posting all of the code for the game I've posted the code only related to the glass pane as it's own separate application below. Can anyone tell me why the graphics aren't showing up?
package glasspane;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GlassPane {
JPanel panel, glass;
GlassPane() {
JFrame f = new JFrame("GlassPane");
panel = new JPanel();
JButton show = new JButton("Show glass");
JButton hide = new JButton("Hide glass");
show.addActionListener(e -> changeVisibility());
hide.addActionListener(e -> changeVisibility());
panel.add(show);
panel.add(hide);
glass = (JPanel) f.getGlassPane();
glass.add(new line());
glass.setVisible(false);
f.add(panel);
f.setSize(300, 300);
f.setVisible(true);
}
private void changeVisibility() {
glass.setVisible(!glass.isVisible());
panel.repaint();
}
#SuppressWarnings("serial")
private class line extends JComponent {
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Shape s = new Line2D.Float(0, 0, 300, 300);
g2.setColor(Color.GREEN);
g2.setStroke(new BasicStroke(20));
g2.draw(s);
System.out.println("doing something...");
}
}
public static void main(String[] args) {
new GlassPane();
}
}
Your line Component has zero size. Thats because JFrame.getGlassPane() returns a java.awt.Component that has no default layoutManager. So set a layoutManager for your glassPane should solve your problem.
glass = (JPanel) f.getGlassPane();
glass.setLayout(new BorderLayout());
glass.add(new line(), BorderLayout.CENTER);
glass.setVisible(false);
Now your line Compnent should be as big as glassPane
I did this and the line get showed up:
glass = (JPanel) f.getGlassPane();
glass.setLayout(new BorderLayout());
glass.add(new line());

Adding two JPanels to a JLayeredPane

I am trying to add a JPanel to a JLayeredPane with only partial success. I am able to see both of the panels separately, but not when I add them together. When I put them both together, only the DEFAULT_LAYER shows. Any thoughts would be great.
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ImageIcon;
import javax.swing.JLayeredPane;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Container;
import java.awt.Component;
import java.awt.Insets;
import java.util.ArrayList;
import java.awt.LayoutManager;
public class ImageEditorView
{
public static int dimensionWidth = 1280;
public static int dimensionHeight = 640;
private Color currentColor = Color.WHITE;
private JLayeredPane layeredPane;
private JPanel createEditPanel()
{
final CirclePanel editPanel = new CirclePanel();
//editPanel.setLayout(new CircleLayout(true));
editPanel.setBounds(50, 0, 150, 150);
editPanel.setOpaque(true);
return editPanel;
}
private JPanel createImageView()
{
JPanel imageView = new JPanel(new GridBagLayout());
imageView.setPreferredSize(new Dimension(dimensionWidth, dimensionHeight));
imageView.setBounds(0, 0, dimensionWidth, dimensionHeight);
imageView.setBackground(currentColor);
return imageView;
}
private void addToLayeredPane(JComponent component, int level)
{
//This prints out what is expected. values of 1 and 100 respectfully
System.out.println("The level has a value of " + level);
layeredPane.add(component, level);
}
public void createAndShowGUI()
{
JFrame f = new JFrame("ImageEditor");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
layeredPane = new JLayeredPane();
//Both createImageView and createEditPanel return with the type JPanel
addToLayeredPane(createImageView(), JLayeredPane.DEFAULT_LAYER);
addToLayeredPane(createEditPanel(), JLayeredPane.PALETTE_LAYER);
f.setSize(dimensionWidth, dimensionHeight);
f.setContentPane(layeredPane);
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
}
class CirclePanel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
g.drawOval(0, 0, g.getClipBounds().width, g.getClipBounds().height);
}
}
A JPanel is opaque by default. So you will only see the panel that has been added to the top layer. Try:
panel.setOpaque(false);
on the top panel.
If you need more help then post a proper SSCCE that demonstrate the problem. The code you posted does not compile and therefore is not executable.
Edit:
The problem is your addToLayeredPane(...) method. You are using a layer parameter of "int". It needs to be Integer.
Since you are adding the component using an int value the component is added to the panel normally and is painted in its ZOrder, which basically means the last component added is painted first so your "imageview" is painted on top of the "editview".
Also, you still are not using setOpaque(false) on the "editpanel" as I suggested. The code will only appear to work because you are NOT doing custom painting properly. You should always invoke super.paintComponent(g) at the start of your painting method. This will automatically paint the background which is why you need to make the panel transparent.

JLabel text gets overwritten on transparent bg

Im a newbie to java, Im trying to create an application like a desktop widget for which i have made the JPanel transparent. I have two JLabels on top of it one for holding an image and other for displaying time. I had a timer to update the time displayed in the JLabel. But With a transparent JPanel behind the jlabel's text gets overwritten instead of replacement. After Googling and Looking up on stackoverflow i tried many methods to override the paintcomponent method of the JLabel. But it didnt affect anything. Later I manually called the paintcomponent method inside the timer which worked out. But I feel its just a workaround. I need to know why the paintcomponent didnt get invoked and when it usually gets invoked.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.RepaintManager;
import javax.swing.SwingConstants;
import javax.swing.text.SimpleAttributeSet;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class WindowSample {
private JFrame frame;
MyLabel panel1;
// JLabel panel1;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
WindowSample window = new WindowSample();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public WindowSample() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setSize(dim);
frame.setBounds(0, 0, 500, 500);
frame.setBackground(new Color(0, 255, 0, 0));
frame.setUndecorated(true);
frame.setContentPane(new ContentPane());
frame.getContentPane().setBackground(Color.WHITE);
frame.getContentPane().setLayout(null);
// ImagePanel panel = new ImagePanel();
JLabel panel = new JLabel(
scale(new ImageIcon("Science Drops.png").getImage()));
panel.setBounds(0, 0, 200, 200);
panel1 = new MyLabel();
// panel1 = new JLabel();
panel1.setHorizontalAlignment(SwingConstants.CENTER);
panel1.setAlignmentX(SwingConstants.CENTER);
panel1.setFont(new Font("Calibiri",Font.BOLD,16));
panel1.setBounds(0, 205, 200, 50);
Timer n = new Timer();
panel1.setBackground(Color.white);
n.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
// this manual call to paintComponent did the trick. If i remove this line the text gets overwritten over itself for every second.
panel1.paintComponents(panel1.getGraphics());
panel1.setText(df.format(new Date()));
}
}, 1000, 1000);
frame.getContentPane().add(panel1);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#SuppressWarnings("serial")
public class MyLabel extends JLabel {
MyLabel() {
setOpaque(false);
}
#Override
public void paintComponents(Graphics arg0) {
Graphics2D g2d = (Graphics2D) arg0.create();
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.dispose();
super.paintComponents(arg0);
}
}
public class ContentPane extends JPanel {
public ContentPane() {
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f));
g2d.setColor(getBackground());
g2d.fill(getBounds());
g2d.dispose();
super.paintComponent(g);
}
}
public ImageIcon scale(Image src) {
int w = 200;
int h = 200;
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage dst = new BufferedImage(w, h, type);
Graphics2D g2 = dst.createGraphics();
g2.drawImage(src, 0, 0, w, h, frame);
g2.dispose();
return new ImageIcon(dst);
}
}
Read Backgrounds With Transparency for information on how transparency works and for some possible solutions.
Also, some other comments with your code:
Don't use a null layout. Swing was designed to be used with layout managers for to many reasons to list here.
Custom painting is done by overriding paintComponent() (no "s"). However, in your case I don't see any reason to do custom painting if you follow the advice in the link I provided above. I also don't think you need to do custom painting in your panel, but I don't totally understand what you are attempting to do.
Use javax.swing.Timer instead of java.util.Timer. Have a look at this tutorial from oracle about timers and swing.
You seem to be going about it the hard way...
labels are transparent by default.
labels support icons out of the box (include animated gifs ;))
null layouts are never a good idea, they might seem like a good idea, but you will spend more time correcting for funny little inconsistencies which be resolved using an appropriate layout manager...
java.util.Timer is not a suitable timer for Swing, instead you want to use javax.swing.Timer instead. It will trigger it's updates within the context of the EDT.
Based off what I think you want to do...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MyClock {
public static void main(String[] args) {
new MyClock();
}
public MyClock() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
final JLabel label = new JLabel(df.format(new Date()));
label.setIcon(new ImageIcon("Clock.png"));
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(df.format(new Date()));
}
});
timer.start();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setUndecorated(true);
frame.setBackground(new Color(0, 255, 0, 0));
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Take a look at How to use icons for more details about icon support in Swing.
You may also find Window#alwaysOnTop useful (remember, all frames lead to Window)
I can't believe there is still nobody who answered the right answer. Here's how you get away with this kind of problem :
Apply setOpaque(false) to your components, but also to all the parents.
It will prevent painting problems on your components with transparent backgrounds.

Need Help in understanding the unexpected output in swing code

I have developed an application to produce the following output:-
I overrided the JPanel's getPrefferredSize but why i am not observing any changes in the output when i change the size.Even if i set the size to 0,0 the diagram is not translated at all and I get the same output as shown above .
Here is my full code:-
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Skeleton extends JFrame{
Skeleton()
{
super("Donut");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
add(new Board());
setVisible(true);
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){public void run(){new Skeleton();}});
}
}
class Board extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2=(Graphics2D)g;
RenderingHints rh=new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
//the following statement has no noticeable effect.
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(rh);
Ellipse2D e=new Ellipse2D.Double(0, 0, 80, 130);
g2.setStroke(new BasicStroke(1));
g2.setColor(Color.RED);
for(int deg=0;deg<360;deg+=5)
{
AffineTransform at=AffineTransform.getTranslateInstance(getWidth()/2,getHeight()/2);
at.rotate(Math.toRadians(deg));
//returns a new Shape object after it has been undergone the 'at' transformation.
g2.draw(at.createTransformedShape(e));
}
}
public Dimension getPreferredSize()
{
return new Dimension(100,100);
}
}
Because of your layout manager. By default BorderLayout ignores preferred size but fills all available space. Use e.g. FlowLayout and yu will see the difference.

Categories

Resources