I cannot seem to get a rectangle to be displayed in the JFrame. Per the parameters of this project, I have to have this class implement the Icon interface. When I run the code as is, I get the JFrame, but nothing shows up inside. It should display a black square. I'm assuming the problem has something to do with how I am initializing the graphics instance variable. I don't have much experience working with GUI graphics, so I'm not entirely clear on how to do this correctly.
And yes, I do know that the getIconWidth and getIconHeight methods are redundant since I am using constants, but I have to have these methods in order to implement the interface.
public class MugDisplay extends JFrame implements Icon {
private int width;
private int height;
private JPanel panel;
private Graphics graphics;
private static final int ICON_WIDTH = 100;
private static final int ICON_HEIGHT = 100;
public MugDisplay() {
this.configureGui();
this.panel = new JPanel();
this.panel.setLayout(new BorderLayout());
this.add(this.panel, BorderLayout.CENTER);
this.graphics = this.getGraphics();
int xPos = (this.panel.getWidth() - this.getIconWidth()) / 2;
int yPos = (this.panel.getHeight() - this.getIconHeight()) / 2;
this.paintIcon(this.panel, this.graphics, xPos, yPos);
}
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fillRect(x, y, ICON_WIDTH, ICON_HEIGHT);
}
#Override
public int getIconWidth() {
return ICON_WIDTH;
}
#Override
public int getIconHeight() {
return ICON_HEIGHT;
}
private void configureGui() {
this.setPreferredSize(new Dimension(600, 600));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.getContentPane().setLayout(new BorderLayout());
this.pack();
this.setVisible(true);
}
}
In the interest of having a MCVE, here is the driver class that calls this class.
public class Main {
public static void main(String[] args) {
MugDisplay md = new MugDisplay();
md.setVisible(true);
}
}
this.graphics = this.getGraphics() is not how custom painting works in Swing. What you should do is create a panel, override its paintComponent method, make the call to super, and then do your painting. For instance:
panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int xPos = ...
paintIcon(this, g, xPos, yPos);
}
}
Calling getGraphics will provide you with a short-lived Graphics object that can soon become invalid, which is why you should opt for overriding paintComponent instead, which will always give you a usable Graphics object. See Performing Custom Painting.
On a separate note, it looks like you're calling setVisible(true) before you're finished adding the necessary components to the JFrame. To ensure that your components show up, call setVisible(true) after adding them all to the frame.
Related
I am currently working on a small coding project and I've run into an issue. I've looked over my past work and I can't seem to figure out why this program wont call the paint method. Currently I'm just trying to draw a circle to the frame.
The following creates the window and the object class for the simple circle I'm trying to draw.
public class Main {
public static void main(String[] args) {
final int WIDTH = 700, HEIGHT = 900;
JFrame frame = new JFrame("Physics Demo");
JPanel content = new JPanel();
content.setLayout(new GridLayout(1, 0, 0, 0));
Character ball = new Character(WIDTH, HEIGHT);
Timer changeFrame = new Timer (100, ball);
frameSetup(frame, content, WIDTH, HEIGHT, ball, changeFrame);
}
public static void frameSetup(JFrame frame, JPanel content, int WIDTH, int HEIGHT, Character ball, Timer changeFrame){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(content);
content.add(ball);
frame.addKeyListener(ball);
frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
changeFrame.start();
}
}
The class below is the object class, when I run the program I get a response from the console. Character triggers once (as it should) and the actionPreformed method runs on loop with the timer. For some reason it doesn't run the paint class.
public class Character extends JPanel implements ActionListener, KeyListener{
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT){
System.out.println("Character called upon... " + WIDTH);
}
public void characterObject(Graphics g, int WIDTH, int HEIGHT){
super.paint(g);
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I've been doing trial and error for a while now and I can't seem to figure it out so I'm using this as a last resort.
I can supply more information if needed.
Why are you calling super.paint from characterObject? This is not how custom painting works. You don't control the painting process, the API does
You need to override one of the methods called when the API want's the component to be repainted. As a general recommendation, this would the paintComponent method, for example
public class Character extends JPanel implements ActionListener, KeyListener {
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT) {
System.out.println("Character called upon... " + WIDTH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I would recommend having a read of Performing Custom Painting and Painting in Swing for more details about how painting actually works in Swing.
I'd also recommend having a look at How to use Key Bindings as a replacement for KeyListener, which will address you next obvious issue
You may also want to have a read of Java Coding Conventions, it will make it easier for other people to read your code and easier for you to read others.
You're passing the width and height to the Character constructor, but are ignoring them, I'd suggest you're going to need to assign those values to instance fields and use them within the paintComponent method
You should not call paint directly. It is called from the framework whenever repaint is needed. To force repaint just call 'repaint()'.
In case you call it from a timer you might need to put the call into the EDT that means the EventDispatchThread:
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
ball.repaint();
}
});
Oh and you really should override the paint method:
#Override
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
I am trying to draw a circle in the center of a window, and I can't seem to get it right, should be really easy! My understanding is that if you set a JPanel as the content pane of a JFrame, the default layout is a flowLayout and that drawing should start from the top left of the screen as 0,0. To try and figure out what's going on I drew a blue background filling the JPanel, but it seems to have a margin like so:
When the window gets smaller than the blue rectangle, the drawing starts to get clipped from the opposite side:
What's going on! Here is my code:
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[])
{
Test test = new Test();
test.Start();
}
public void Start()
{
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
this.setContentPane(circlePanel);
this.setVisible(true);
}
public class CirclePanel extends JPanel
{
private int radius = 200;
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getX(), getY(), diameter, diameter);
}
public int getX()
{
return (this.getWidth()/2) - radius;
}
public int getY()
{
return (this.getHeight()/2) - radius;
}
}
}
One big issue, you're unknowingly overriding two critical methods used by the layout managers to position components, the getX() and getY() methods, and thereby you're messing with the JPanel's placement.
So first and foremost, rename these methods so you don't accidentally move the JPanel.
Also, don't forget to call the super's paintComponent method, and avoid calling setSize(). Instead override getPreferredSize on your JPanel, and pack your JFrame.
e.g.,
public int getMyX() {
return myX;
}
public int getMyY() {
return myY;
}
For example
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame {
public static void main(String args[]) {
//!!
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Test test = new Test();
test.Start();
}
});
}
public void Start() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// this.setSize(500, 500);
CirclePanel circlePanel = new CirclePanel();
setContentPane(circlePanel);
pack();
setVisible(true);
}
public class CirclePanel extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private int radius = 200;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!!
g.setColor(Color.blue);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
int diameter = radius * 2;
g.fillOval(getMyX(), getMyY(), diameter, diameter);
}
//!!
public int getMyX() {
return (this.getWidth() / 2) - radius;
}
//!!
public int getMyY() {
return (this.getHeight() / 2) - radius;
}
//!!
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
}
The problem is that you're overriding getX() and getY(). Rename those to something else, and your code will work as expected. Also, it's a good idea to turn on compiler warnings for missing #Override annotations, and heed those warnings. (That would have notified the methods override superclass methods).
Try using this.add(circlePanel) instead if this.setContentPane(circlePanel) and set the size of the JPanel to be the same size as the JFrame
Class one:
public class Bar extends JComponent {
// instance variables - replace the example below with your own
private static boolean litUp = false;
private static boolean vertical = true;
private static int positionX;
private static int positionY;
private static int sizeX;
private static int sizeY;
private static Color color;
public void paintComponent(Graphics g) {
System.out.println("I am being called");
positionX = 50;
positionY = 30;
vertical = true;
if(vertical == true) {
sizeX = 10;
sizeY = 30;
if(litUp == true)
{
color = Color.red;
}
else
{
color = Color.black;
}
} else{
sizeX = 30;
sizeY = 10;
if(litUp == true)
{
color = Color.red;
}
else
{
color = Color.black;
}
}
g.fillRect(positionX, positionY, sizeX, sizeY);
g.setColor(color);
super.paintComponent(g);
}
}
Class Two:
public class TestingBar {
public static void main(String[] args) {
final int FRAME_WIDTH = 317;
final int FRAME_HEIGHT = 415;
//created frame and panel. Panel layout is taken away from the flow layout.
final JFrame myFrame = new JFrame();
myFrame.setTitle("Tester Window (v2)");
myFrame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
final JPanel myPanel = new JPanel();
myPanel.setLayout(null);
final Bar testBar = new Bar();
//myPanel.add(testBar);
myFrame.getContentPane().add(testBar);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myPanel.add(testBar);
}
}
This is just giving me an empty JFrame. And the print statement is never being utilized in the paintComponent method. I am seriously stumped.
PS: They won't let me post this without typing more, so I will talk about turtles. I don't understand how they survive out in the wild. They can't run, their shells really aren't that hard, they can only attack from the front, and if they are flipped over they are crippled for an indefinite amount of time. You think that natural selection would have worn them out of the pool by now.
Maybe they survived by being so darn adorable. I mean, just watch youtube videos of a baby turtle trying to eat a cherry tomato. It is the epitome of cute.
I think you should call super.paintComponent(g); at the beggining of the function. It probably paints over your rectangle.
You are adding testBar to your frame then again to your myPanel which removes it from the frame (a component can only have one parent). But since myPanel is not added to anything, it never gets called.
Commenting out this line will fix it, but you probably are intending to do something meaningful with your myPanel, so check your design.
//myPanel.add(testBar);
I turned your Bar class into a JPanel and ovrrode the paint method instead and it worked but the other comments are correct, you're not adding the panel to the frame, just the bar object:
//created frame and panel. Panel layout is taken away from the flow layout.
final JFrame myFrame = new JFrame();
myFrame.setTitle("Tester Window (v2)");
myFrame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
final Bar testBar = new Bar();
myFrame.getContentPane().add(testBar);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
and in bar:
public class Bar extends JPanel
{
//....
public void paint(Graphics g)
{
g.setColor(Color.black);
g.fillRect(20,20,20,20);
}
}
Call super.paintComponents first thing. You probably want to cast g to a Graphics2D object, too.
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Wrong order -- set the color, then draw the rectangle (using g2):
g2.setColor(color);
g2.fillRect(positionX, positionY, sizeX, sizeY);
I am trying to display a JComponent inside a JPanel.
I am using null layout because location of the components can be changed during runtime and I have to control them.
But the following code does not work. The JComponent only becomes visible on display if I explicity call the "paintComponent" method, which I think is not good practice.
My JComponent Class
public class MyIcon extends JComponent
{
private double xPos;
private double yPos;
private double radius = 30;
public MyIcon(double xPos, double yPos)
{
this.xPos = xPos;
this.yPos = yPos;
this.setBounds((int)xPos, (int)yPos, (int)radius, (int)radius);
this.setPreferredSize(new Dimension((int)radius, (int)radius ) );
}
public Dimension getPreferredSize()
{
return ( new Dimension( (int)radius, (int)radius ) );
}
public Dimension getMinimumSize()
{
return new Dimension((int)radius, (int)radius);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.drawOval( (int)xPos, (int)yPos, (int)radius, (int)radius);
g2d.fillOval((int)xPos, (int)yPos, (int)radius, (int)radius);
System.out.println("Icon.paintComponnet() called");
}
}
My Panel Class
public class MyPanel extends JPanel
{
private MyIcon myIcon;
private Graphics2D graphics;
private int width = 700;
private int height = 500;
public MyPanel()
{
myIcon = new MyIcon(20,30);
init();
}
private void init()
{
setLayout(null);
setBackground(Color.WHITE);
setPreferredSize( new Dimension(width, height) );
setBorder(BorderFactory.createLineBorder(Color.BLACK));
add( myIcon );
myIcon.repaint();
}
public void paintComponent(Graphics g)
{
graphics = (Graphics2D) g;
super.paintComponent(g);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
System.out.println("MyPanel.paintComponnet() called");
// why my Icon only gets painted if I explicitly call for it ?
//myIcon.paintComponent(g);
}
}
My Frame Class
public class Editor {
public static void main(String[] args)
{
Editor editor = new Editor();
}
private MyPanel myPanel;
private JFrame frame;
private JToolBar toolBar;
public Editor()
{
myPanel = new MyPanel();
init();
}
private void init()
{
frame = new JFrame("Editor");
Container content = frame.getContentPane();
frame.setSize(800, 600);
frame.setLayout(new BorderLayout(15,15));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
content.add(myPanel,BorderLayout.WEST);
frame.pack();
frame.setVisible(true);
}
}
I'd be use paintIcon() for Class that returns Icon Object
public void paintIcon(Component c, Graphics g, int x, int y) {
for example
Maybe a good approach would be that the Icon class would not extend JComponent but would be just a simple object.
Then, you rename the current Icon.paintComponent you have to something like drawIcon and call the drawIcon method from the MyPanel.paintComponent directly and passing the reference to the Graphics object.
This way you don't have to wonder about using a null layout and you can control the place to display the icon to by just using the Graphics(2d) APIs.
I want to know how to make a dot/pixel at a certain x,y co-ordinate on my JFrame.
Anyone know some simple code for this?
I have created a small example program:
public class Test extends JFrame {
public Test() {
this.setPreferredSize(new Dimension(400, 400));
this.pack();
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void paint(Graphics g) {
super.paint(g);
// define the position
int locX = 200;
int locY = 200;
// draw a line (there is no drawPoint..)
g.drawLine(locX, locY, locX, locY);
}
public static void main(String[] args) {
Test test = new Test();
}
}
You could also use the update or paintComponents method which would be much nicer. But then you have to make sure, that it gets called. If you have problems and it does not get called you could use the following solution: Why is paint()/paintComponent() never called?
Best compromise between simplicity and usefulness would probably be to extend JPanel, and override paintComponent( Graphics ). Then place that panel in your JFrame (with an appropriate layout. There are some usage notes here: http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/JComponent.html#paintComponent%28java.awt.Graphics%29
see
void update(Graphics g)
method of JFrame class.
graphics API ( like draw point, draw line, draw arc, etc ) are in Graphics class.
EDIT: http://www.javadb.com/drawing-a-line-using-java-2d-graphics-api
Ask yourself if your really want to extend JFrame or JPanel. If you decide that you don't then you could create a basic JComponent. You may have varying success with this depending on what layout manager you use.
public class PixelComponent extends JComponent
{
private Color color;
public PixelComponent(Color color)
{
super();
this.color = color;
}
public PixelComponent()
{
this(Color.BLACK);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, 1, 1);
}
}
Send the Graphics Reference and axis x and y to make a pixel:
private void doPixel(Graphics g, int x, int y){
g.fillRect(x, y, 1, 1);
}