I am currently trying to use paintComponent to draw a square in the middle of my JFrame on top of a JPanel. The size of my frame is 600 x 600, but when I try to draw the square with Xcoordinate = 300 and Ycoordinate = 300 the square is not really even close to the middle of my Frame.
Am I doing something wrong? This is for a school project, so any tips would also be appreciated. Thanks in advance!
Write a complete working program that draws a square in the middle of the
frame.
When the user clicks on a square (left click), it then replaces it with 4
smaller
squares drawn in separate quadrants, each of which is a quarter of the size
of the original
square and has random color. If user clicks (right click) on any square, it
is removed/deleted from the frame.
If user remains inactive for 15 seconds ( (i.e., stops clicking), all squares should start moving away
from the center (any speed is fine, overlaps are fine but not preferred). If squares, hit the edges,
they are deleted again. If user presses 'S', the squares stop moving. The frame is cleared when user
presses 'Delete' key three times in quick succession (within 3 seconds) and you start all over again.
Test your program to draw unique patterns.
My frame class:
import javax.swing.*;
import java.awt.*;
public class SquareFrame extends JFrame {
private final int WIDTH = 600, HEIGHT = 600;
private final Dimension frameSize;
private SquarePanel panel;
public SquareFrame(){
panel = new SquarePanel();
frameSize = new Dimension(WIDTH,HEIGHT);
this.setTitle("Sqaures");
this.setSize(frameSize);
this.getContentPane().add(panel);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args){
SquareFrame frame = new SquareFrame();
}
}
My panel class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class SquarePanel extends JPanel implements MouseListener {
public SquarePanel(){
this.setBackground(Color.BLACK);
this.setFocusable(true);
this.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Square test = new Square();
g.setColor(Color.WHITE);
g.drawRect(test.x,test.y,test.width,test.height);
}
#Override
public void mouseClicked(MouseEvent e) { }
#Override
public void mousePressed(MouseEvent e) { }
#Override
public void mouseReleased(MouseEvent e) { }
#Override
public void mouseEntered(MouseEvent e) { }
#Override
public void mouseExited(MouseEvent e) { }
}
My square class:
import java.awt.*;
public class Square extends Rectangle {
public Square(){
this.height = 100;
this.width = 100;
this.x = 300;
this.y = 200;
}
}
Write a complete working program that draws a square in the middle of the
frame.
Well, I think that is a terrible requirement. The frame contains a title bar and 4 borders. As the frame shrinks in size you won't be able to paint the square on the title bar.
Custom painting is done on the panel that is added to the frame. So I think you should really be trying to center the panel in the bounds of the panel. You need to clarify the actual requirement.
public class Square extends Rectangle {
Why are you extending Rectangle?
If you need, the x, y, width, height information then you just use the getBounds() method of the Rectangle class to get the information.
However, you don't need the x/y values.
If you want to center the rectangle then you need to calculate the x/y values dynamically as you do your custom painting based on the current size of the panel.
What you need to know is the width/height of the rectangle you want to paint, so really you should be creating a Dimension object. Something like:
Dimension d = new Dimension(100, 100);
Then to center the rectangle in the panel you need to know the width of the panel and the width of the rectangle to calculate the location to do the painting of the rectangle:
int x = (getWidth() - d.getWidth()) / 2;
int y = ...
g.drawRect(x, y, d.width, d.height);
Related
I have a class called CharMove,in it are the paint(Graphics g) method, and some custom methods. The class should create a square,then move that square randomly around the screen. However,when I create two instances of this class in my World Class,only one square appears. First the square doesn't move but the new coord.'s are displayed,then after 5 runs the square begins to move randomly. I think the program is getting caught on the Graphics method because only one square is being created,when the CharMove class should be creating another instance of Graphics.I have searched online but can't find a way to create different instances of Graphics.Thanks in advance.
CharMove Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class CharMove extends JPanel {
int x = 250;
int y = 250;
public void paint(Graphics g) {
Graphics pane = (Graphics2D) g;
pane.setColor(Color.blue);
pane.fillRect(x, y, 10, 10);
}
public void movement(JFrame frame) {
for (int i=0;i<5;i++) {
try {
TimeUnit.SECONDS.sleep(1);
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int Getx(int a, JFrame frame) {
Random rn = new Random();
int xnum = rn.nextInt(10)-5;
a += xnum;
System.out.println("x:" + a);
return a;
}
public int Gety(int b, JFrame frame){
Random rn = new Random();
int ynum = rn.nextInt(10)-5;
b += ynum;
System.out.println("y:" + b);
return b;
}
}
World Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class World {
public static void main(String[] args) {
JFrame game = new JFrame();
game.setTitle("Matrix");
game.setSize(500, 500);;
game.getContentPane().setBackground(Color.white);
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setVisible(true);
CharMove char1 = new CharMove();
CharMove char2 = new CharMove();
game.add(char1);
game.add(char2);
char1.movement(game);
char2.movement(game);
}
}
In swing, all of your painting should be down in paintComponent(Graphics g) (rename your method)
To do animation, you should use a Swing Timer (w/ an ActionListener) to update the positions of your animated items. Once that's done, the timer should call repaint();
public void actionPerformed(ActionEvent ae) {
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
}
However,when I create two instances of this class in my World Class,only one square appears.
The default layout manager for a JFrame is a BorderLayout.
game.add(char1);
game.add(char2);
When you add components without specifying a constraint then both components are added to the CENTER. However, only one component can be added to the CENTER so only the last one added is displayed.
Try:
game.add(char1, BorderLayout.PAGE_START);
game.add(char2, BorderLayout.PAGE_END);
However when you do this the componens won't be displayed because they have a (0, 0) preferredSize. So you will also need to override the getPreferredSize() method of your CharMove class.
#Override
public Dimension getPreferredSize()
{
return new Dimension(300, 200);
}
Also, custom painting should be done in the paintComponent(...) method and you need to invoke super.paintComponent(...) at the start to clear the background.
The repaint() method in your movement() method should be on the panel, not the frame, since you are changing properties of the panel.
Each CharMove is essentially a JPanel which draws a single square of size 10 somewhere on itself when it is painted. You are adding two CharMove panels to the game JFrame (which in fact adds them to the default content pane, which has a subclassed BorderLayout). As you are not providing a layout constraints object, in fact both panels are being added to the BorderLayout.CENTER of the content pane, and the second is completely covering the first.
To correct this you should modify CharMove so that it paints all of the squares (eg by maintaining an array or some sort of collection of squares, and painting all of them in the paint method) and just add that one panel to the JFrame.
Apart from this issue, while you are animating the squares in the movement method you are blocking the Event Dispatch Thread, meaning that during the animation you won't be able to move any other windows or respond to any mouse clicks or other inputs. Take ControlAltDel's advice about using the Swing Timer for animation to correct this problem.
I'm adding a function in my game that takes a screenshot and saves it to a new image. I have no problems with the filepath or anything of that sort, but rather the Rectangle through which the screenshot is taken. If I make a new Rectangle, like such:
new Rectangle(0, 0, 500, 500);
then it creates a 500 x 500 Rectangle at the top left of the computer screen, not at the top left of the content pane. The content pane to which I am referring is much smaller than the screen and located in the center. Thanks for reading, any help would be appreciated.
For any component (which can be a JPanel, Container, Window, or basically anything), you can use this to get the Rectangle that will represent its bounds on the screen:
Rectangle getBoundsOnScreen( Component component ) {
Point topLeft = component.getLocationOnScreen();
Dimension size = component.getSize();
return new Rectangle( topLeft, size );
}
So if you wanted just the content pane of a JFrame:
getBoundsOnScreen( frame.getContentPane() );
For stuff like an entire JFrame, you can just do this:
frame.getBounds();
Look at the example below. I first create a RectangleComponent class, which extends the Rectangle class:
import javax.swing.*;
import java.awt.*;
public class RectangleComponent extends JComponent
{
Rectangle rectangle;
public RectangleComponent(int xspace,int yspace, int width, int height)
{
rectangle = new Rectangle(xspace, yspace, width, height);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.draw(rectangle);
}
}
Now we create a class that generates the main window, where you add the Rectangle component:
import javax.swing.*;
import java.awt.*;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
}
public static void main(String[] args) {
JFrame frame = new JFrame("Java Rulez");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100,100,600,600);
frame.getContentPane().setBackground(Color.YELLOW);
frame.add(new RectangleComponent(0, 0, 500, 500));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I'm new to java. I do have a java class but I want to get ahead. I really like it! This is my problem. I'm trying to draw the two paddles needed for the game. I did create 2 objects for them, and they both "Show" but the following happens:
Main Runner
import javax.swing.JFrame;
public class PongRunner {
public static void main (String[] args)
{
new PongRunner();
}
public PongRunner()
{
JFrame PongFrame= new JFrame();
PongFrame.setSize(800,600);
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
PongFrame.setLocationRelativeTo(null);
PongFrame.setTitle("My Pong Game");
PongFrame.setResizable(false);
PongFrame.setVisible(true);
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
PaddleA and PaddleB are both drawRect Graphics and draw a specific rectangle.
The graphics that I tell JFrame to add first are the only ones visible. The one under it doesnt get added into the Frame I wanted to know why,and how i can draw both paddles in the same JFrame... I'm reading my book and looking over the internet as much as possible, but no luck within these 2 days. Some help would be nice. I just started learning java and I think I'm making progress. Some tips would be nice too thanks :), specialy on actionListener since im going to have to use them to move the paddles!
SOURCE CODE OF BOTH PADDLES:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleA extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(70,200,20,100);
}
}
PaddleB:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class PaddleB extends JPanel implements ActionListener
{
private Timer timer;
public void timer()
{
timer = new Timer(25, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(500, 200, 20, 100);
}
}
As you've added the 2 paddles into the same BorderLayout.CENTER location of the main PongFrame, only the second paddle will be displayed.
This type of painting is more easily done using one Graphics object on one JComponent.
Swing components aren't really designed to be used like this. Each of your paddle JPanels is trying to paint a rectangle, but the container (the JFrame) and its layout manager will control the size and location of the JPanels. So the rectangle they paint may be outside their bounds, and won't appear at all.
Some Layout Managers will only show one of the JPanels if you add them one after another like this - see answer by Reimus.
To do animated graphics like this, you probably need to start with a single JComponent (e.g. a JPanel) that fills the JFrame, and paint both paddles as rectangles onto it.
This is because you add the first paddle and then add the second paddle over the first thus replacing them:
PongFrame.add(new PaddleB());
PongFrame.add(new PaddleA());
try using LayoutManagers here is just a quick sample:
PongFrame.getContentPane().add(new PaddleB(),BorderLayout.WEST);
PongFrame.getContentPane().add(new PaddleA(),BorderLayout.EAST);
also your g.fillRect(...); has x and y co-ordinates that are too large for the JFrame and thus are not displayed. Change it like so:
g.fillRect(0,0, 20, 100);
Not related though:
dont use JFrame.add() rather JFrame.getContentPane().add()
UPADTE:
As others have mentioned the logic for the game is a bit off. You should have a single drawing panel which is added to the JFrame and all drawing is done on the single JPanel using Graphics and not JPanels. Here is a small example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PongRunner {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PongRunner();
}
});
}
public PongRunner() {
JFrame PongFrame = new JFrame();
PongFrame.setTitle("My Pong Game");
PongFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PongFrame.setResizable(false);
PongFrame.setSize(400,400);
//PongFrame.setLocationRelativeTo(null);
PongFrame.getContentPane().add(new DrawingPanel());
PongFrame.setVisible(true);
}
}
class DrawingPanel extends JPanel {
Rect rect1, rect2;
public DrawingPanel() {
super(true);
rect1 = new Rect(80, 80, 20, 100, Color.yellow);
rect2 = new Rect(100, 200, 20, 100, Color.red);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(rect1.getColor());
g.fillRect(rect1.getX(), rect1.getY(), rect1.getWidth(), rect1.getHeight());
g.setColor(rect2.getColor());
g.fillRect(rect2.getX(), rect2.getY(), rect2.getWidth(), rect2.getHeight());
}
}
class Rect {
private Color color;
private int x, y, height, width;
Rect(int x, int y, int width, int height, Color color) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.color = color;
}
public Color getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
}
Please note this snippet is not perfect and merely demonstrates the stereotypical logic needed for drawing rectangles to form a game of sorts
I don't have time to try running your code, but try setting the size and position of the "paddle" objects, or set them as non-opaque. I believe that Swing tries to speed up rendering by not drawing components which are completely covered by another opaque component. It may not be drawing one of your paddles for that reason.
If you do set the size and position of your paddles, you may have to modify your paintComponent methods: if my memory is correct, I think the Graphics object which is passed in is clipped to the area of the component, so 0,0 would be the top-left corner of the component.
you shouldn't add your JPanels to your JFrame by the JFrame#add(Component) method. Better add them to the contentPane: frame.getContentPane().add(panel); You should although read about LayoutManagers. They can help you by positining Components in your Frame or mess things up if you use them incorrect.
I am trying to add/draw a single Graphics object to an existing JPanel. I am generating 10 initial Graphics objects randomly sized and place in the panel, but would like to add additional drawn objects one a time, randomly sized and placed like the initial 10.
Currently, the AddNewDrawItem class is not rendering the new Graphics object.
Thank you for input.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class Painter{
private DrawingPanel dp = new DrawingPanel();
//constructor
public Painter(){
buildGUI();
}
private void buildGUI(){
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.setTitle("Paint drawing demonstration");
JPanel headerPanel = new JPanel();
headerPanel.add(new JLabel("The drawing panel is below"));
JButton addNew = new JButton("Add New Graphic");
addNew.addActionListener(new addNewClickHandler());
headerPanel.add(addNew);
frame.add(BorderLayout.NORTH,headerPanel);
frame.add(BorderLayout.SOUTH,this.dp);
frame.pack();
frame.setVisible(true);
}
class DrawingPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.white);
int x, posx, posy, width, height;
for(x=0;x<=10;x++){
//even number differentiation
if(x % 2 == 0){
g.setColor(Color.red);
}else{
g.setColor(Color.blue);
}
Random rand = new Random();
posx = rand.nextInt(300);
posy = rand.nextInt(300);
width = rand.nextInt(40);
height = rand.nextInt(40);
//System.out.println("the ran x pos is: " + posx);
g.fillRect(posx, posy, width, height);
}//end for
}//end paintComponent
public Dimension getPreferredSize() {
return new Dimension(400,400);
}
}// end DrawingPanel
private class addNewClickHandler implements ActionListener{
public void actionPerformed(ActionEvent e){
System.out.print("in addNew_click_handler click handler");//trace debug
AddNewDrawItem newItem = new AddNewDrawItem();
newItem.repaint();
System.out.print("after repaint() in addNew_click_handler click handler");//trace debug
}
}
class AddNewDrawItem extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.white);
int posx, posy, width, height;
Random rand = new Random();
posx = rand.nextInt(300);
posy = rand.nextInt(300);
width = rand.nextInt(40);
height = rand.nextInt(40);
g.setColor(Color.cyan);
g.fillRect(posx, posy, width, height);
}//end paintComponent
}//end AddNewDrawItem
public static void main(String args[]){
new Painter();
}
}//end class Painter
You've got some problems with your code, one of which is that you've got program logic in your paintComponent method: the code randomly changes values that are displayed within this method, meaning your display will change when it repaints, whether you want it to or not. To see that this is so, try to resize your GUI and you'll see some psychedelic changes in the drawn red and blue rectangles.
Now as to your current problem, the solution to it is similar to the solution to the problem I describe above. I suggest...
that you create an ArrayList<Rectangle2D>,
that you create your random rectangles in your class's constructor so that they're created only once, and then place them in the ArrayList above.
that you iterate through this ArrayList in your JPanel's paintComponent method, drawing them as you go. This way paintComponent does nothing but paint which is as it should be,
that you create a MouseAdapter-derived object and add it as a MouseListener and MouseMotionListener to your DrawingPanel
that you use the listener above to create a new Rectangle2D object and when done add it to the ArrayList and call repaint on the DrawingPanel
that you activate the mouse adapter via your button's action listener.
I'll stop there, but I think you get the idea, and if you don't, please ask any questions you may have.
I am a high school student, and I am working on my final project for my computer science class. My assignment was to make a simple paint application, however I am having some problems. These include: the painted image being erased when the window is resized and that I can draw over the menu's in my window.
I believe I am drawing over the menu's because I am using the graphics object of the JFrame itself. However I can not find any alternatives. I have tried making separate components to draw in, and even using BufferedImage, but none of my attempts have been successful.
Here is my code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.*;
public class Paint implements MouseMotionListener{
private JFrame window;
private drawingComponent pComp;
private int xPos; //xPos of mouse;
private int yPos; //yPos of mouse;
private Color color; // new color
private JTextArea sizeBox;
private int brushShape = 0;
public static void main(String[] args) {
Paint paint = new Paint();
paint.ImplementGUI();
}
public Paint() {
this.pComp = new drawingComponent();
this.window = new JFrame("JPaint");
this.window.setSize(500, 500);
this.window.setVisible(true);
this.window.addMouseMotionListener(this);
this.window.add(this.pComp);
}
public void ImplementGUI() {
JMenuBar menu = new JMenuBar();
JMenu brush = new JMenu("Brush Settings");
JMenu brushShape = new JMenu("Shape");
brush.setSize(100,100);
brush.add(brushShape);
menu.add(brush);
menu.setSize(window.getWidth(), 20);
menu.setVisible(true);
this.sizeBox = new JTextArea("Brush Size:");
this.sizeBox.setText("20");
brush.add(this.sizeBox);
this.window.setJMenuBar(menu);
}
public void mouseDragged(MouseEvent pMouse) {
if (pMouse.isShiftDown() == true) {
this.pComp.paint(this.window.getGraphics(), pMouse.getX(), pMouse.getY(),
window.getBackground(), Integer.parseInt(sizeBox.getText()));
}
else {
this.pComp.paint(this.window.getGraphics(), pMouse.getX(), pMouse.getY(),
color, Integer.parseInt(sizeBox.getText()));
}
}
public void mouseMoved(MouseEvent pMouse) {
}
}
class drawingComponent extends JComponent {
public void paint(Graphics g, int x, int y, Color color, int size) {
g.setColor(color);
g.fillOval(x-(size/2), y-(size/2), size, size); // 10 is subtracted from
// coordinates so the mouse pointer is at the exact middle of the brush.
}
}
How can I fix this problem?
Never use the getGraphics() method to do painting. The effect is only temporary as you have noticed.
Custom painting is done by overriding the paintComponent() method, not the paint() method.
See Custom Painting Approaches for a couple of example of how to paint multiple objects on a panel.
camickr is right. When you resize the window, the ImplementGUI component is repainted, so ImplementGUI.paintComponent() will be invoked to repaint the whole surface of the component, and what you did paint within mouseDragged() will be lost.