Draw in my JPanel calling a function in the "main" - java

I have an informatic project with JAVA langage for class and it's rated to graduate from high school.
Btw my program consists in drawing geometrical forms in the JPanel with clic control but I don't know how to put, in my main program, fonctions which draw figures as I want like drawSquare(x,y,length). For the moment the program show this window (it's a JFrame with my JPanel inside):
http://i.imgur.com/YetG3VB.png
I have a method readClick() which give to a Point point the coordinates point.x and point.y in the frame but I don't know how, in my main, I can do to call a graphical drawing.
"My problem is that I want to put in my main program something like
Point clic;
clic= Fenetre.readClick() ; // I have this method which wait me to clic on the frame
x = clic.x ; //and give to point the coordonates
y= clic.y; // point.x && point.y
clic = Fenetre.readClick();
a = clic.x ;
b = clic.y ;
"Function which draws rectangle for example"(x,y,a,b);
//It's this fonction I want to create and
// I want it to draw the rectangle in the JPanel
But I don't know how to make the function which adds a drawing in the JPanel "
My main program :
public class Geogebra {
#SuppressWarnings("unused")
public static void main(String args[]){
Fenetre fen = new Fenetre();
}}
My window class :
import java.awt.Color;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.SynchronousQueue;
import javax.swing.JFrame;
#SuppressWarnings({ "unused", "serial" })
public class Fenetre extends JFrame {
static Panneau component = null;
private final static SynchronousQueue<MouseEvent> clicks = new SynchronousQueue<MouseEvent>();
public Fenetre(){
JFrame frame = new JFrame();
component = new Panneau();
frame.setTitle("ISNOGEBRA");
frame.setSize(900, 700); // Taille initiale de la fenetre : 900 * 700
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(component);
frame.setVisible(true);
component.addMouseListener( new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clicks.offer(e);
}
});
}
public static MouseEvent readMouse() {
try {
return clicks.take();
}
catch (InterruptedException e) {
throw new AssertionError();
}
}
public static Point readClick() {
return readMouse().getPoint();
}
}
My JPanel class (the two class Mathwrapper and Menu just draw the interface, I'll show them if needed but it's really long) :
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.JPanel;
#SuppressWarnings({ "serial", "unused" })
public class Panneau extends JPanel implements MouseListener {
Menu menu;
MathWrapper mw;
public Panneau(){
super();
this.setBackground(Color.WHITE);
menu = new Menu(20, 20);
mw = MathWrapper.getInstance(0, 80);
this.setOpaque(true);
this.addMouseListener(this);
}
#Override
public void paintComponent(Graphics g){
menu.draw(g);
mw.draw(g);
}
public void mouseClicked(MouseEvent e) {
//That shows a blue square behind the icon in the menu I click on
//and draws a black one on the one which was selected
if((e.getX() > 20 && e.getX() < 60) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[0].toggleClicked();
menu.activeTool = 0;
}else if((e.getX() > 80 && e.getX() < 120) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[1].toggleClicked();
menu.activeTool = 1;
}else if((e.getX() > 140 && e.getX() < 180) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[2].toggleClicked();
menu.activeTool = 2;
}else if((e.getX() > 200 && e.getX() < 240) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[3].toggleClicked();
menu.activeTool = 3;
}else if((e.getX() > 260 && e.getX() < 300) && (e.getY()) > 20 && e.getY() < 60){
menu.btns[menu.activeTool].toggleClicked();
menu.btns[4].toggleClicked();
menu.activeTool = 4;
}
mw.setActiveTool(menu.activeTool);
this.repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
}
Please help me I've read plents of documents about JPanel and drawing but I still don't understand ..

If I understand your question, you basically want some function from which you can
wait for user interaction and get information about it
draw something to your window
(I'm ignoring right now that you want to do it from the main method, since I'm pretty sure you'll be happy to do it from another method as well)
Basically you are facing two obstacles in that project:
Drawing in most windowing systems is event driven, i. e. if the user puts the window to front, it has to know how to draw its current state
User interaction is also event driven, i. e. you will get callbacks when the user clicks.
The first obstacle is easy to solve: Create a BufferedImage somewhere that both your UI code and the function can access, and draw inside it. (BufferedImage has a createGraphics method you can use to draw inside the image's buffer). Then the paintComponent method just draws the image whenever somebody puts the window to foreground.
The second obstacle is a bit harder. Basically you'll need threading and synchronization for it. It used to be a lot of tweaking with wait and notify and synchronized calls up to Java 1.4, but since Java 1.5 the synchronization (for this special case) can be handled by BlockingQueue class. So your UI code waits (with MouseListener) for a mouse click and adds the coordinates (as a java.awt.Point) to BlockingQueue, and your UI code will just have to wait for the next point whenever it needs one. You will still have a bit of experience using multithreaded applications (so you should know how to start a Thread and that a Thread cannot interact directly with the UI), so depending on your experience of Java it may be a steep learning curve - but certainly doable.
EDIT: I see you are already using some kind of synchronization for the mouse events, so probably the only parts you still need is starting a thread and drawing to a BufferedImage instead of the real window itself.
EDIT2: Here is a very simplified example that shows you how you can draw to a BufferedImage from a second thread. It will ask for coordinates and colors from the console (standard input) and paint them to the image, which will show in the JPanel. It is your task to combine this example with what you already have to move the mouse position across etc.
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.util.Scanner;
public class BufferedImagePainting extends JFrame {
public static void main(String[] args) {
BufferedImage img = new BufferedImage(800, 600, BufferedImage.TYPE_3BYTE_BGR);
JPanel drawPanel = new DrawPanel(img);
new InteractionThread(img, drawPanel).start();
new BufferedImagePainting(drawPanel);
}
public BufferedImagePainting(final JPanel drawPanel) {
super();
setLayout(new GridLayout(1, 1));
add(drawPanel);
pack();
setVisible(true);
}
private static class DrawPanel extends JPanel {
private BufferedImage img;
public DrawPanel(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
}
private static class InteractionThread extends Thread {
private BufferedImage img;
private JPanel drawPanel;
public InteractionThread(BufferedImage img, JPanel drawPanel) {
this.img = img;
this.drawPanel = drawPanel;
}
#Override
public void run() {
#SuppressWarnings("resource")
Scanner s = new Scanner(System.in);
while (true) {
System.out.println("Enter a draw color and a fill color, separated by spaces");
System.out.println("Enter colors as 6-digit hex number, i. e. 000000 = black, ffffff = white");
Color drawColor = new Color(Integer.parseInt(s.next(), 16));
Color fillColor = new Color(Integer.parseInt(s.next(), 16));
System.out.println("Enter coordinates in form x y width height, separated by spaces");
int x = s.nextInt();
int y = s.nextInt();
int w = s.nextInt();
int h = s.nextInt();
Graphics g = img.createGraphics();
g.setColor(fillColor);
g.fillRect(x, y, w, h);
g.setColor(drawColor);
g.drawRect(x, y, w, h);
g.dispose();
drawPanel.repaint();
}
}
}
}

You must set visibility of your JFrame. Try this in your main class.
public class Geogebra {
#SuppressWarnings("unused")
public static void main(String args[]){
Fenetre fen = new Fenetre();
fen.setVisible(true);
}}

Related

How to rotate something upwards based on mouse position in Java

Good afternoon guys, I'm trying to rotate an polygon based on my mouse position, but I can't figure out how to rotate the polygon upwards based on my mouse y. I'm using MouseMotionListener. I've tried to do this until now:
public void mouseMoved(MouseEvent m){
int yantes= m.getY();
while (true){
int y = m.getY();
repaint();
if (y - yantes > 0){
rotation++;
if (rotation > 360) rotation = 0;
repaint();
break;
} else {
rotation--;
if (rotation < 0) rotation = 359;
repaint();
break;
}
}
}
The yantes variable tries to calculate the y before the move, and y the y in after the movement.
In your comment, you wrote:
I am using a book that used Java 6
Are you referring to the book Beginning Java SE 6 Game Programming, Third Edition ?
A java applet is a Container and so is a JPanel, so you can achieve the same results by extending class JPanel rather than extending Applet. That means you can write a regular java application without the need for HTML or a Web browser.
Instead of overriding method paint() in Applet, you need to override method paintComponent() in class JPanel.
The below code demonstrates rotating a square by moving the mouse. It rotates the square around the center point of the square. When you place the mouse inside the JPanel and move it to the left, the square rotates anti-clockwise. When you move the mouse to the right, the square rotates clockwise. If you move the mouse up and down, i.e. parallel to the y-axis, the square does not rotate.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class Rotating extends JPanel implements Runnable, MouseMotionListener {
private int theta;
private int lastX = Integer.MIN_VALUE;
private BasicStroke stroke;
private JFrame frame;
/**
* Creates and returns instance of this class.
*/
public Rotating() {
stroke = new BasicStroke(2.0f);
addMouseMotionListener(this);
setPreferredSize(new Dimension(600, 600));
}
/* Start 'MouseMotionListener' interface methods. */
#Override // javax.swing.event.MouseInputListener
public void mouseDragged(MouseEvent mousEvnt) {
// Do nothing.
}
#Override // javax.swing.event.MouseInputListener
public void mouseMoved(MouseEvent mousEvnt) {
int newX = mousEvnt.getX();
if (lastX == Integer.MIN_VALUE) {
lastX = newX;
}
if (newX < lastX) {
theta--;
if (theta < 0) {
theta = 359;
}
}
else if (newX > lastX) {
theta++;
if (theta > 360) {
theta = 0;
}
}
lastX = newX;
repaint();
}
/* End 'MouseMotionListener' interface methods. */
/* Start 'Runnable' interface methods. */
#Override
public void run() {
showGui();
}
/* End 'Runnable' interface methods. */
#Override // javax.swing.JComponent
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (g instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(theta), 300, 300);
g2d.setStroke(stroke);
g2d.drawRect(200, 200, 200, 200);
}
}
private void showGui() {
frame = new JFrame("Rotating");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Rotating());
}
}
Note that when overriding method paintComponent() you nearly always need to first call method paintComponent() in the super class.

Resizing a Path2D shape when user clicks and drags

So, I have a program that adds a square to a JPanel and lets the user drag the shape around the panel. What I want to do is be able to click on the bottom-right corner of the shape and resize it as the user drags it. I'm kind of stuck on how to do this. I know that as the user drags it will need to recalculate the rectangle's length and width to make the bottom right corner match where the mouse is. But how can I detect a click on the bottom right edge of the rectangle? Thanks for any help.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class UMLEditor {
public static void main(String[] args) {
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setTitle("UML Editior");
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
shapeList.addSquare(100, 100);
}
public void loadFile() {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
int r = chooser.showOpenDialog(this);
if (r == JFileChooser.APPROVE_OPTION) {
}
}
}
// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
int currentIndex;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void addSquare(int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
shapes.add(rect2);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.draw(shape);
}
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < shapes.size(); i++) {
if (shapes.get(i) != null
&& shapes.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
shapes.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
pressed = false;
}
}
}
I wrote a couple of things back in the day that might be helpful to you
To start, AreaManager (http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/shape/) This is kind of what you want, in that it's dealing with Shapes (Area's, actually). There's a dragger class which uses mouse drag, and a resizer class that uses the mouse wheel. But this isn't exactly the user interface you've described.
That user interface for doing changing the cursor and resizing based on the type of cursor and the mouse drag is in Draggable in http://sourceforge.net/p/tus/code/HEAD/tree/tjacobs/ui/drag/. Draggable works with Components that are contained in Containers with the layoutmanager turned off. But it should be not so complicated to adapt to your purposes

I am trying to display a JPEG image in java

I have an assignment that I am doing where I am supposed to implement and design an application that plays a game called catch the creature. Have the creature appear at a random location then disappear and reappear somewhere else. The goal is to "catch" the creature by clicking the creature with a mouse button. Record the number of times the creature is caught.
I need help just displaying the creature which is an JPEG of a pikachu, I have tried a few things but none of them work. Any help is appreciated thank you!
Main Code:
import javax.swing.*;
public class Catch_The_Creature
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Catch the Creature");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Creature panel = new Creature();
JOptionPane.showMessageDialog(frame, "Catch Pikachu!");
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
Creature Code:
import java.awt.*;
import java.util.Random;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Creature extends JPanel
{
private final int WIDTH = 400, HEIGHT = 300;
private final int DELAY=20, IMAGE_SIZE = 60;
private ImageIcon image;
private int pikachuX, pikachuY;
private int x, y;
private int catchCount=0;
private static Random generator = new Random();
private Timer time;
private ActionListener updater;
private JLabel countLabel;
public Creature()
{
image = new ImageIcon("image/pikachu.jpg");
time = new Timer(DELAY, updater);
addMouseListener ((MouseListener) new MouseClickedListener());
setBackground (Color.green);
setPreferredSize(new Dimension(1900,1000));
time.start();
}
public boolean point(int x, int y)
{
if (x == pikachuX && y == pikachuY)
{
catchCount++;
return true;
}
return false;
}
public int getCatchCount()
{
return catchCount;
}
private class MouseClickedListener extends MouseAdapter
{
public void mouseClicked(MouseEvent event)
{
point(event.getX(), event.getY());
}
}
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.drawImage(image.getImage(),WIDTH, HEIGHT, null);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}
public void actionPerformed(ActionEvent event)
{
time.setDelay(1000);
x += pikachuX;
y += pikachuY;
if (x <= 0 || x >= WIDTH-IMAGE_SIZE)
pikachuX = pikachuX * -1;
if (y <= 0 || y >= HEIGHT-IMAGE_SIZE)
pikachuY = pikachuY * -1;
repaint();
}
public void mouseEntered(MouseEvent arg0) {}
public void mouseExited(MouseEvent arg0) {}
public void mousePressed(MouseEvent arg0) {}
public void mouseReleased(MouseEvent arg0){}
}
It doesn't look like you ever add the ImageIcon to the panel or tell it to paint in the paintComponent() method.
First solution [Preferred]: Add ImageIcon to the panel. In the constructor
super.add(image);
Make sure you use the correct layout (probably a null or absolute layout) and that you update the coordinates of the ImageIcon itself, not just some member variables.
Second solution: Paint the ImageIcon in the paintComponent() method. This is probably discouraged because it goes against the general Swing principles.
Make sure your Image file is in the right directory. If you're running from netbeans or eclipse your file structure should look like this
ProjectRoot
src
bin
image
pikachu.jpeg
Since you are using "image/pikachu.png", you image filder should be a child of the project root folder as that's where the IDE will first search fore your file path
Edit: To draw image. Instead of using ImageIcon, use BufferedImage
try {
BufferedImage image = ImageIO.read("image/pikachu.jpeg");
} catch (Exception ex){
ex.printStackTrace();
}
public void paintComponent(Graphics page)
{
super.paintComponent(page);
page.drawImage(image, x, y, heightYouWant, widthYouWant, this);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}
All i needed to do was put values on where I wanted the picture to start at in the constructor.
public Creature()
{
image = new ImageIcon ("pikachu.png");
time = new Timer(DELAY, updater);
x = 0;
y = 50;
addMouseListener ((MouseListener) new MouseClickedListener());
setBackground (Color.green);
setPreferredSize(new Dimension(1900,1000));
time.start();
}
While still using an Image Icon and still paint the image in the paint component.
public void paintComponent(Graphics page)
{
super.paintComponent(page);
image.paintIcon (this, page, x, y);
page.drawString("Pikachus Captured: " + catchCount, 10, 35);
setFont(new Font("Arial", Font.BOLD,35));
}

java swing: in paintComponent method how to know what to repaint?

My component is bigger than the screen and parts of it are not shown (I will use scrollbars).
When I receive a call in paintComponent(g) how do I know what area should I paint?
I'm not sure if this is what you mean, but the problem is you will have to call repaint() on the JScrollPane each time you get a call in paintComponent(Graphics g) of the JPanel or else updates on the JPanel will not be visible in the JScrollPane.
Also I see you want to use JScrollBar (or maybe you confused the terminology)? I'd recommend a JScrollPane
I made a small example which is a JPanel with a grid that will change its colour every 2 seconds (Red to black and vice versa). The JPanel/Grid is larger then the JScrollPane; regardless we have to call repaint() on the JScrollPane instance or else the grid wont change colour:
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.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowUI();
}
});
}
private void createAndShowUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
frame.setPreferredSize(new Dimension(400, 400));
frame.pack();
frame.setVisible(true);
}
private void initComponents(JFrame frame) {
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(new Panel(800, 800, jsp));
frame.getContentPane().add(jsp);
}
}
class Panel extends JPanel {
private int across, down;
private Panel.Tile[][] tiles;
private Color color = Color.black;
private final JScrollPane jScrollPane;
public Panel(int width, int height, JScrollPane jScrollPane) {
this.setPreferredSize(new Dimension(width, height));
this.jScrollPane = jScrollPane;
createTiles();
changePanelColorTimer();//just something to do to check if its repaints fine
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < across; i++) {
for (int j = 0; j < down; j++) {
g.setColor(color);
for (int k = 0; k < 5; k++) {
g.drawRect(tiles[i][j].x + k, tiles[i][j].y + k, tiles[i][j].side - k * 2, tiles[i][j].side - 2 * k);
}
}
}
updateScrollPane();//refresh the pane after every paint
}
//calls repaint on the scrollPane instance
private void updateScrollPane() {
jScrollPane.repaint();
}
private void createTiles() {
across = 13;
down = 9;
tiles = new Panel.Tile[across][down];
for (int i = 0; i < across; i++) {
for (int j = 0; j < down; j++) {
tiles[i][j] = new Panel.Tile((i * 50), (j * 50), 50);
}
}
}
//change the color of the grid lines from black to red and vice versa every 2s
private void changePanelColorTimer() {
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (color == Color.black) {
color = Color.red;
} else {
color = Color.black;
}
}
});
timer.setInitialDelay(2000);
timer.start();
}
private class Tile {
int x, y, side;
public Tile(int inX, int inY, int inSide) {
x = inX;
y = inY;
side = inSide;
}
}
}
In the Panel class if we comment the line updateScrollPane(); in paintComponent(Graphics g) we wont see the grid change colour.
You can find out the area that actually has to be painted by querying the clip bounds of the Graphics object.
The JavaDoc seems to be a bit out-dated for this method: It says, that it may return a null clip. However, this is obviously never the case (and other Swing classes also rely on the clip never being null!).
The follwing MCVE illustrates the difference between using a the clip or painting the whole component:
It contains a JPanel with a size of 800x800 in a scroll pane. The panel paints a set of rectangles, and prints how many rectangles have been painted.
One can use the "Use clip bounds" checkbox to enable and disable using the clip. When the clip is used, only the visible area of the panel is repainted, and the number of rectangles is much lower. (Note that the test whether a rectangle has to be painted or not is rather simple here: It only performs an intersection test of the rectangle with the visible region. For a real application, one would directly use the clip bounds to find out which rectangles have to be painted).
This example also shows some of the tricky internals of scroll panes: When the blinking is switched off, and the scroll bars are moved, one can see that - although the whole visible area changes - only a tiny area actually has to be repainted (namely the area that has become visible due to the scrolling). The other part is simply moved as-it-is, by blitting the previous contents. This behavior can be modified with JViewport.html#setScrollMode.
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.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class PaintRegionTest
{
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PaintRegionPanel paintRegionPanel = new PaintRegionPanel();
paintRegionPanel.setPreferredSize(new Dimension(800, 800));
final Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
paintRegionPanel.changeColor();
}
});
timer.setInitialDelay(1000);
timer.start();
JScrollPane scrollPane = new JScrollPane(paintRegionPanel);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JPanel controlPanel = new JPanel(new FlowLayout());
final JCheckBox blinkCheckbox = new JCheckBox("Blink", true);
blinkCheckbox.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (blinkCheckbox.isSelected())
{
timer.start();
}
else
{
timer.stop();
}
}
});
controlPanel.add(blinkCheckbox);
final JCheckBox useClipCheckbox = new JCheckBox("Use clip bounds");
useClipCheckbox.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
paintRegionPanel.setUseClipBounds(
useClipCheckbox.isSelected());
}
});
controlPanel.add(useClipCheckbox);
frame.getContentPane().add(controlPanel, BorderLayout.SOUTH);
frame.setPreferredSize(new Dimension(400, 400));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class PaintRegionPanel extends JPanel
{
private Color color = Color.BLACK;
private boolean useClipBounds = false;
void setUseClipBounds(boolean useClipBounds)
{
this.useClipBounds = useClipBounds;
}
void changeColor()
{
if (color == Color.BLACK)
{
color = Color.RED;
}
else
{
color = Color.BLACK;
}
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(color);
Rectangle clipBounds = g.getClipBounds();
Rectangle ownBounds = new Rectangle(0,0,getWidth(),getHeight());
System.out.println("clipBounds: " + clipBounds);
System.out.println(" ownBounds: " + ownBounds);
Rectangle paintedRegion = null;
if (useClipBounds)
{
System.out.println("Using clipBounds");
paintedRegion = clipBounds;
}
else
{
System.out.println("Using ownBounds");
paintedRegion = ownBounds;
}
int counter = 0;
// This loop performs a a simple test see whether the objects
// have to be painted. In a real application, one would
// probably use the clip information to ONLY create the
// rectangles that actually have to be painted:
for (int x = 0; x < getWidth(); x += 20)
{
for (int y = 0; y < getHeight(); y += 20)
{
Rectangle r = new Rectangle(x + 5, y + 5, 10, 10);
if (r.intersects(paintedRegion))
{
g.fill(r);
counter++;
}
}
}
System.out.println("Painted "+counter+" rectangles ");
}
}
An aside: For many application cases, such an "optimization" should hardly be necessary. The painted elements are intersected against the clip anyhow, so one will probably not gain much performance. When "preparing" the elements to be painted is computationally expensive, one can consider this as one option. (In the example, "preparing" refers to creating the Rectangle instance, but there may be more complicated patterns). But in these cases, there may also be more elegant and easier solutions than manually checking the clip bounds.
All answers are wrong. So I decided to answer the question despide the fact that the question is two years old.
I believe that the correct answer is calling g.getClipBounds() inside of paintComponent(Graphics g) method. It will return the rectangle in the control's coordinate system of the area which is invalidated and must be redrawn.

Repaint leaves trail

I know that this isn't the first time that this question has been asked, but the responses haven't helped me much, so I am helping I am finally going to get my answer
I have made this little game where I have made a car drive around a track (using rectangles was mandatory). When I use the repaint() metod the rectangle representing the car repaints in the new location, but leaves a trail behind.
I have this code:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class MAP extends JFrame
{
//constant for the screen size and used for the drawing the terrain
final int WIDTH = 900, HEIGHT = 650;
//the URL and Img designed for the images
URL cLeft,cRight,cUp;
Image img1,img2,img3;
//these will keep track of each player’s speed:
double p1Speed =.5;
//these are ints that represent directions:
final int UP = 0, RIGHT = 1, DOWN = 2, LEFT = 3;
//these will keep track of the player’s directions (default = up)
int p1Direction = 0;
JPanel panel;
//draw the terrain
Rectangle left = new Rectangle(0,0,WIDTH/9,HEIGHT);
Rectangle right = new Rectangle((WIDTH/9)*9,0,WIDTH/9,HEIGHT);
Rectangle top = new Rectangle(0,0,WIDTH, HEIGHT/9);
Rectangle bottom = new Rectangle(0,(HEIGHT/9)*9,WIDTH,HEIGHT/9);
Rectangle center = new Rectangle((int)((WIDTH/9)*2.5),(int)((HEIGHT/9)*2.5),(int)((WIDTH/9)*5),(HEIGHT/9)*4);
//these obstacles will obstruct the path and make navigating harder
Rectangle obstacle = new
Rectangle(WIDTH/2,(int)((HEIGHT/9)*7),WIDTH/10,HEIGHT/9);
Rectangle obstacle2 = new
Rectangle(WIDTH/3,(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle3 = new
Rectangle(2*(WIDTH/3),(int)((HEIGHT/9)*5),WIDTH/10,HEIGHT/4);
Rectangle obstacle4 = new Rectangle(WIDTH/3,HEIGHT/9,WIDTH/30,HEIGHT/9);
Rectangle obstacle5 = new Rectangle(WIDTH/2,(int)((HEIGHT/9)*1.5),WIDTH/30,HEIGHT/4);
Rectangle finish = new Rectangle(WIDTH/9,(HEIGHT/2)-HEIGHT/9,(int)((WIDTH/9)*1.5),HEIGHT/70);
Rectangle lineO=new Rectangle(WIDTH/9,HEIGHT/2,(int)((WIDTH/9)*1.5)/2,HEIGHT/140);
Rectangle lineI = new Rectangle(((WIDTH/9)+((int)((WIDTH/9)*1.5)/2)),(HEIGHT/2)+(HEIGHT/10),(int)((WIDTH/9)*1.5)/2, HEIGHT/140);
//this is the rectangle for player 1’s (outer) car:
Rectangle p1 = new Rectangle(WIDTH/9,HEIGHT/2, WIDTH/30,WIDTH/30);
public MAP(){
super("Radical Racing");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setResizable(false);
setVisible(true);
setLocationRelativeTo(null);
//the following code creates the JFrame
try
{
cUp = this.getClass().getResource("carUp.jpg");
cLeft = this.getClass().getResource("carLeft.jpg");
cRight = this.getClass().getResource("carRight.jpg");
}catch(Exception e)
{}
//attach the URLs to the images
img1 = Toolkit.getDefaultToolkit().getImage(cUp);
img2 = Toolkit.getDefaultToolkit().getImage(cLeft);
img3 = Toolkit.getDefaultToolkit().getImage(cRight);
panel=new JPanel(){
public void paint(Graphics g)
{
//super.paint(g);
//draw the background for the racetrack
g.setColor(Color.DARK_GRAY);
g.fillRect(0,0,WIDTH,HEIGHT);
//when we draw, the border will be green
g.setColor(Color.GREEN);
//fill rectangle
g.fillRect(left.x,left.y,left.width,left.height);
g.fillRect(right.x,right.y,right.width,right.height);
g.fillRect(top.x,top.y,top.width,top.height);
g.fillRect(bottom.x,bottom.y,bottom.width,bottom.height);
g.fillRect(center.x,center.y,center.width,center.height);
g.fillRect(obstacle.x,obstacle.y,obstacle.width,obstacle.height);
g.fillRect(obstacle2.x,obstacle2.y,obstacle2.width,obstacle2.height);
g.fillRect(obstacle3.x,obstacle3.y,obstacle3.width,obstacle3.height);
g.fillRect(obstacle4.x,obstacle4.y,obstacle3.width,obstacle4.height);
g.fillRect(obstacle5.x,obstacle5.y,obstacle5.width,obstacle5.height);
//set the starting line color to white
g.setColor(Color.WHITE);
//now draw the starting line
g.fillRect(lineO.x,lineO.y,lineO.width,lineO.height);
g.fillRect(lineI.x,lineI.y,lineI.width,lineI.height);
//set the color of the finish line to yellow
g.setColor(Color.YELLOW);
//now draw the finish line
g.fillRect(finish.x,finish.y,finish.width,finish.height);
//set the color to blue for p1
g.setColor(Color.WHITE);
//now draw the actual player
g.fill3DRect(p1.x,p1.y,p1.width,p1.height,true);
//draw the images for the player
if(p1Direction==UP)
g.drawImage(img1,p1.x,p1.y,this);
if(p1Direction==LEFT)
g.drawImage(img2,p1.x,p1.y,this);
if(p1Direction==RIGHT)
g.drawImage(img3,p1.x,p1.y,this);
}
};
panel.setPreferredSize(new Dimension(950,600));
this.add(panel);
pack();
Move1 move=new Move1();
move.start();
}
private class Move1 extends Thread implements KeyListener
{
public void run()
{
//now, this should all be in an infinite loop, so the process
//repeats
addKeyListener(this);
while(true)
{
//now, put the code in a try block. This will let the
//program exit
//if there is an error.
try
{
//increase speed a bit
// if(p1Speed<=5)
// p1Speed+=.2;
//p1.y-=(int) p1Speed;
if(p1.intersects(left) || p1.intersects(right) || p1.intersects(top) ||
p1.intersects(bottom) || p1.intersects(obstacle) || p1.intersects(obstacle2)||
p1.intersects(obstacle3) || p1.intersects(obstacle4) || p1.intersects(obstacle5))
{
p1Speed = -5;
}
//if the car hits the center, do the same as above
//but make the speed -2.5.
if(p1.intersects(center))
{
p1Speed = -2.5;
}
//increase speed a bit
if(p1Speed<=5)
p1Speed+=.2;
//these will move the player based on direction
if(p1Direction==UP)
{
p1.y-=(int)p1Speed;
}
if(p1Direction==DOWN)
{
p1.y+=(int)p1Speed;
}
if(p1Direction==LEFT)
{
p1.x-=(int)p1Speed;
}
if(p1Direction==RIGHT)
{
p1.x+=(int)p1Speed;
}
panel.repaint();
//this delays the refresh rate:
Thread.sleep(200);
}
catch(Exception e)
{
//if there is an exception (an error), exit the loop.
break;
}
}
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent event) {
if(event.getKeyChar()=='a')
{
p1Direction = LEFT;
}
if(event.getKeyChar()=='s')
{
p1Direction = DOWN;
}
if(event.getKeyChar()=='d')
{
p1Direction = RIGHT;
}
if(event.getKeyChar()=='w')
{
p1Direction = UP;
}
}
}
//this starts the program by calling the constructor:
public static void main (String[ ] args)
{
new MAP();
}
}
Uncomment super.paint(g) [line 87] in your paint method.
It is responsible for clearing the canvas of any stale objects.
The reason is that in the line g.fillRect(0,0,WIDTH,HEIGHT); it actually is referring to java.awt.image.ImageObserver#WIDTH and java.awt.image.ImageObserver#HEIGHT instead of your instance variables. You can see this with any IDE.
Because of that the expected black background is painted only in a 1x2 pixel area in the top-left corner. And since the call to super.paint(g); is commented out, not even the gray background is repainted. As a result, old drawings of the car are not overdrawn.
The code needs to be changed to use MAP.this.WIDTH, or the WIDTH field needs to be renamed to something which does not conflict with the fields inherited from JPanel, or the field needs to be moved into the same JPanel to have higher precedence, or you could also use the getWidth() method from JPanel. And likewise for height.

Categories