I am trying to draw some simple shapes onto a JPanel but am having some difficulty. Apologies if this question appears to have been answered before but the other answers don't seem to help.
I followed a simple tutorial and was successful in drawing some basic shapes onto a JFrame, but when I moved the code how it was into a new class which extends JPanel, nothing appears on screen.
public class TestingGraphics extends JFrame{
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
new TestingGraphics();
}
public TestingGraphics(){
this.setSize(1000,1000);
this.setPreferredSize(new Dimension(1000,1000));
this.setTitle("Drawing tings");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(new TestingPanelGraphics(), BorderLayout.CENTER);
this.setVisible(true);
}
public class TestingPanelGraphics extends JPanel {
public TestingPanelGraphics(){
this.setSize(1000,1000);
this.setPreferredSize(new Dimension(1000,1000));
this.add(new DrawStuff(), BorderLayout.CENTER);
revalidate();
repaint();
this.setVisible(true); //probably not necessary
}
private class DrawStuff extends JComponent{
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D graph2 = (Graphics2D) g;
graph2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Shape rootRect = new Rectangle2D.Float(100, 100, 1000, 500);
graph2.setColor(Color.BLACK);
graph2.draw(rootRect);
}
I've tried setting preferred size, and revalidating and repainting.
I've added a call to super.paintComponent, although neither of those two were necessary when I was drawing straight onto a JFrame.
I ensured I was overriding paintComponent and changed it from public to protected. All following advice from similar threads, but nothing seems to work. I've stepped through in debugger mode and ensured it goes into the right method, and even watched it go into the paintManager, but still nothing appears on the window.
You've forget to set the appropriate layout manager.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestingGraphics extends JFrame{
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
new TestingGraphics();
}
public TestingGraphics(){
this.setSize(1000,1000);
this.setPreferredSize(new Dimension(1000,1000));
this.setTitle("Drawing tings");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(new TestingPanelGraphics(), BorderLayout.CENTER);
this.setVisible(true);
}
public class TestingPanelGraphics extends JPanel {
public TestingPanelGraphics(){
setLayout(new BorderLayout());
this.setPreferredSize(new Dimension(1000,1000));
this.add(new DrawStuff(), BorderLayout.CENTER);
revalidate();
repaint();
this.setVisible(true); //probably not necessary
}
private class DrawStuff extends JComponent{
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D graph2 = (Graphics2D) g;
graph2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Shape rootRect = new Rectangle2D.Float(100, 100, 1000, 500);
graph2.setColor(Color.BLACK);
graph2.draw(rootRect);
}
}
}
}
When you extends JFrame your default layout is the BorderLayout, but when you extends JPanel your default layout is the FlowLayout.
Related
I'm trying to create a super simple component, and it's not appearing.
Component class:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Player extends JComponent{
public Player()
{
}
public void paint(Graphics g)
{
g.setColor(Color.green);
g.fillRect(40,40,150,150);
}
}
Panel Class im adding it to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JPanel;
public class Game extends JPanel{
public Game()
{
this.setBackground(Color.yellow);
this.setPreferredSize(new Dimension(500,500));
Player p = new Player();
this.add(p);
}
}
And the JFrame:
import javax.swing.JFrame;
public class Launcher {
public static void main(String[] args) {
JFrame frame = new JFrame("Key Collector Demo");
frame.add(new Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
}
The only thing showing up is the yellow background.
JFrame and JPanel are working fine; this problem consistently happens to me when building jcomponents. What am I missing?
Any help would be greatly appreciated!
Coordinates that you are using to draw a component are defined in the space of that component.
If you do this:
public void paint(Graphics g)
{
g.setColor(Color.green);
System.out.println(getSize());
g.fillRect(40,40,150,150);
}
You will see that at the moment it is attempted to get drawn its size is 1x1. So drawing it from 40,40 obviously takes it out of the visible area of the component.
Now change the method to:
public void paint(Graphics g)
{
g.setColor(Color.green);
System.out.println(getSize());
setSize(45, 45);
g.fillRect(40,40,150,150);
}
Now you can see small filled rectangle. This is because you forced the size to be 45x45 and now there are 5 pixels to show in the space of the component while the remaining part is still out of the area.
Do not assume the Player panel to have a fixed size.
For a first test your paint method could look like this:
public void paint(Graphics g) {
g.setColor(Color.green);
g.fillRect(0,0,getWidth(),getHeight());
}
The next problem is that the component probably has no size or position. Add a LayoutManager to your panel and add the component:
public Game() {
this.setBackground(Color.yellow);
this.setPreferredSize(new Dimension(500,500));
this.setLayout(new BorderLayout());
Player p = new Player();
this.add(p, BorderLayout.CENTER);
}
With that, your player might take the full space and you see green instead of yellow. Play around with the LayoutManager and the player's preferredSize.
To provide some background info that might help, I am creating the game pong and I decided to add an escape/pause menu (when you press escape on the keyboard a menu pops up with some settings), I looked around and found that the best way to do this is to use JLayeredPane and add another JPanel on top. However, when I added my 'painter' class to the JLayeredPane, the paint(Graphics g) method stopped getting called (it worked fine when I just added it to the JFrame).
import javax.swing.*;
public class Game extends JFrame {
public static Painter painter;
public Game() {
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane lp = getLayeredPane();
painter = new Painter();
add(lp);
}
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);
painter.repaint();
}
}
And here is my Painter class
import java.awt.*;
import javax.swing.*;
public class Painter extends JPanel {
public void paint(Graphics g) {
System.out.println("Working");
super.paint(g);
}
}
Instead of add(lp);, I originally tried lp.add(painter); in which case the paint method never got called. By doing add(lp) I get an IllegalArgumentException for adding container's parent to itself.
Here is mcve of using LayeredPane :
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Game extends JFrame {
public Game() {
setSize(250, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane lp = getLayeredPane();
//assign layout manager. otherwise you need to set content
//bounds (not recommended)
lp.setLayout(new BorderLayout());
Painter painter = new Painter();
lp.add(painter,1);
}
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);;
}
}
class Painter extends JPanel {
#Override
protected void paintComponent(Graphics g) { //do not override paint
super.paintComponent(g);
g.drawString("Working",50,50);
}
}
I have drawn a line using the below function:
public void paint(Graphics p) {
super.paint(p);
p.drawLine(600, 200, 580, 250);
}
I am wondering is there a way that I can delete this line?
Then is it possible to call this function in the main() method of a program?
You can use a flag to know whether the line is displaying or not.
As I said before you need to build your GUI towards JPanels and not JFrames. Also overriding paintComponent and not paint method.
For example, the following program displays a line or hides it when you click the JButton, adapt that logic to your own program with your own conditions.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LineDrawer {
private JFrame frame;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new LineDrawer()::createAndShowGui); //Put our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
MyPane pane = new MyPane(); //Create an instance of our custom JPanel class
button = new JButton("Hide/Show");
button.addActionListener(e -> {
pane.setShowLine(!pane.isShowLine()); //Change the state of the flag to its inverse: true -> false / false -> true
});
frame.add(pane);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Our custom class that handles painting.
#SuppressWarnings("serial")
class MyPane extends JPanel {
private boolean showLine;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (showLine) { //If true, show line
g2d.draw(new Line2D.Double(50, 50, 100, 50));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300); //For the size of our JPanel
}
public boolean isShowLine() {
return showLine;
}
public void setShowLine(boolean showLine) {
this.showLine = showLine;
this.repaint(); //Everytime we set a new state to showLine, repaint to make the changes visible
}
}
}
I can't post a GIF right now, but the program itself works. Btw the above code is called a Minimal, Complete and Verifiable Example and on your next questions you're encouraged to post one in order to get specific, faster and better answers to your questions.
In the below example, how can I get the JPanel to take up all of the JFrame? I set the preferred size to 800x420 but it only actually fills 792x391.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BSTest extends JFrame {
BufferStrategy bs;
DrawPanel panel = new DrawPanel();
public BSTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout()); // edited line
setVisible(true);
setSize(800,420);
setLocationRelativeTo(null);
setIgnoreRepaint(true);
createBufferStrategy(2);
bs = getBufferStrategy();
panel.setIgnoreRepaint(true);
panel.setPreferredSize(new Dimension(800,420));
add(panel, BorderLayout.CENTER); // edited line
panel.drawStuff();
}
public class DrawPanel extends JPanel {
public void drawStuff() {
while(true) {
try {
Graphics2D g = (Graphics2D)bs.getDrawGraphics();
g.setColor(Color.BLACK);
System.out.println("W:"+getSize().width+", H:"+getSize().height);
g.fillRect(0,0,getSize().width,getSize().height);
bs.show();
g.dispose();
Thread.sleep(20);
} catch (Exception e) { System.exit(0); }
}
}
}
public static void main(String[] args) {
BSTest bst = new BSTest();
}
}
If you are having only one panel in frame and nothing else then try this:
Set BorderLayout in frame.
Add panel in frame with BorderLayout.CENTER
May be this is happening because of while loop in JPanel.(Not sure why? finding actual reason. Will update when find it.) If you replace it with paintComponent(g) method all works fine:
public BSTest() {
//--- your code as it is
add(panel, BorderLayout.CENTER);
//-- removed panel.drawStuff();
}
public class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
System.out.println("W:" + getSize().width + ", H:" + getSize().height);
g2d.fillRect(0, 0, getSize().width, getSize().height);
}
}
//your code as it is.
Here's an alternative using pack instead.
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PackExample extends JFrame {
public PackExample(){
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(800,600));
panel.setBackground(Color.green);
add(panel);
pack();
setVisible(true);
}
public static void main(String[] args){
new PackExample();
}
}
This took me forever to figure out but its actually the simplest code ever.
Just create a parent panel and pass GridLayout then add your child panel like this.
JPanel parentPanel= new JPanel(new GridLyout());
JPanel childPanel= new JPanel();
parentPanel.add(childPanel);
If you want to fill the JFrame with the whole of JPanel you need to setUndecorated to true i.e. frame.setUndecorated(true);. But now you have to worry about your MAXIMIZE< MINIMIZE, and CLOSE Buttons, towards the top right side(Windows Operating System)
Im trying to add a JScrollpane to my JPanel. The problem is that the scrollpane doesn't recognize that my drawing is outside the frame. So how do I add the JScrollpane correctly?
Main class:
public MainFrame() extends JFrame{
public MainFrame() {
Container container = getContentPane();
container(new BorderLayout());
container.add(new JScrollPane(new Drawing()));
setSize(1280,720);
setVisible(true);
}
Drawing class:
public class Drawing() extends JPanel {
#Override
protected void paintComponent(Graphics g) {
g.drawLine(10, 100, 30000, 10);
}
}
There are a couple of errors in your code, let's step through each of them:
You're extending JFrame, and you should avoid it, see: Extends JFrame vs. creating it inside the program for more information about it. You're actually not changing its behavior so it's not needed to extend it.
For your JScrollPane to show the whole line, you need to change your window's size to be the same size of your line (as shown in this answer by #MadProgrammer).
Related to point 2, avoid the use of setSize(...) and instead override getPreferredSize(): See Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? for more information
You forgot to call super.paintComponent(...) method in your paintComponent() method.
Related to points 2, 3, you need to call pack() so Swing calculates the best preferred size for your component.
See this example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LongDraw {
private JFrame frame;
private Drawing drawing;
public static void main(String[] args) {
SwingUtilities.invokeLater(new LongDraw()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
drawing = new Drawing();
JScrollPane scroll = new JScrollPane(drawing);
frame.add(scroll);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class Drawing extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(10, 100, 3000, 10);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(3000, 500);
}
}
}
Which produces something similar to this: