I have a java program which displays an ellipse on the screen and changes its direction through the use of the arrow keys. I constantly call repaint() on the ellipse using a while loop.
The ellipse moves, but the problem is that it leaves a trail of ellipses on its path. How do I make it so that it removes the old ellipses and then repaint the new one?
Code:
public void run(){
while (animator != null)
{
setBackground(Color.GREEN);
repaint();
// The direction works and the rest works fine.
player1.move(player1, player1.direction);
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
break;
}
}
}
// The paintComponent of my player1 is fine.
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D)g;
player1.paintComponent(g2, player1);
}
The problem is likely that your paintComponent(Graphics) overriden method is not calling super.paintComponent(g), which clears the canvas, among other things:
// Bad:
#Override
public void paintComponent(Graphics g) {
// paint the ellipse, etc.
}
// Good:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// paint the ellipse, etc.
}
Here is some basic code which does the very basics (without the use of a continuous while loop):
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class Ellipses extends JFrame {
public static void main(String[] args){
//Ensures application is not run on the main thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Ellipses myEllipses = new Ellipses();
myEllipses.init();
}
});
}
public Ellipses(){
//Set up the frame
this.setTitle("Ellipses Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.setSize(400, 400);
}
private Ellipse ellipse1;
public void init(){
Container contentPane = this.getContentPane();
//Create a new ellipse and add to the content pane
ellipse1 = new Ellipse();
contentPane.add(ellipse1);
//Add the keyListener to the contentPane
contentPane.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){
ellipse1.decreaseY();
}
if(e.getKeyCode() == KeyEvent.VK_DOWN){
ellipse1.increaseY();
}
if(e.getKeyCode() == KeyEvent.VK_LEFT){
ellipse1.decreaseX();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT){
ellipse1.increaseX();
}
//Repaint the ellipse
ellipse1.repaint();
}
});
//Request the focus so key presses can be detected
contentPane.setFocusable(true);
contentPane.requestFocus();
}
//Create an ellipse which can be drawn to the screen
public class Ellipse extends JComponent{
private int x , y; //Coordinates of the oval
public Ellipse(){
setCoordinates(100, 100);
}
public void setCoordinates(int x, int y){
this.x = x;
this.y = y;
}
public void increaseY(){
y+=10;
}
public void increaseX(){
x+=10;
}
public void decreaseY(){
y-=10;
}
public void decreaseX(){
x-=10;
}
public void paint(Graphics g){
//Ensures previous paint is cleared
super.paintComponents(g);
g.setColor(Color.RED);
g.fillOval(x, y, 100, 100);
}
}
}
You should draw the background Color before drawing the ellipse in the game loop :)
Also you can call repaint() , it actually calls update() to do
the dirty work of clearing the screen and calling paint().
When you use repaint the update method inherit
public void update(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, width, height);
g.setColor(getForeground());
paint(g)
}
so the background return to it's original color and the trail of ellipses will be removed
Related
My program has 3 java files, namely Frame, Dude(which contains the character) and Board(which implements the actionListener). My program is not throwing any error and the images(background and character) are rendering good. But the character is not moving forward.
import javax.swing.*;
public class Frame {
public static void main(String[] args){
JFrame frame= new JFrame("2D Game");
frame.add(new Board());
frame.setVisible(true);
frame.setSize(1200, 600);
}
}
import java.awt.*;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Dude {
int x, dx, y;
Image still;
public Dude(){
ImageIcon i = new ImageIcon("/home/amitabh/Pictures/man1.jpg");
still= i.getImage();
x=10;
y=172;
}
public void move(){
x=x+dx;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public Image getImage(){
return still;
}
public void keyPressed(KeyEvent e){
int key= e.getKeyCode();
if(key== KeyEvent.VK_LEFT);
dx= -1;
if(key== KeyEvent.VK_RIGHT);
dx= 1;
}
public void keyReleased(KeyEvent e){
int key= e.getKeyCode();
if(key==KeyEvent.VK_LEFT);
dx=0;
if(key==KeyEvent.VK_RIGHT);
dx=0;
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Board extends JPanel implements ActionListener{
Image img;
Timer time;
Dude p;
public Board(){
p= new Dude();
addKeyListener(new AL());
setFocusable(true);
ImageIcon i= new ImageIcon("/home/amitabh/Pictures/game1.png");
img= i.getImage();
time= new Timer(5,this);
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
p.move();
repaint();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d= (Graphics2D)g;
g2d.drawImage(img, 0,0, null);
g2d.drawImage(p.getImage(), p.getX(), p.getY(), null);
}
public class AL extends KeyAdapter{
public void keyReleased(KeyEvent e){
p.keyReleased(e);
}
public void KeyPressed(KeyEvent e){
p.keyPressed(e);
}
}
}
Start by talking a very close look at:
if (key == KeyEvent.VK_LEFT);
Does that look funny to you?
if (key == KeyEvent.VK_LEFT); // <--- What's the ; doing here?
Change it to be more like...
if (key == KeyEvent.VK_LEFT) {
dx = 0;
}
And, yes, this is why you're encouraged to use { ... }
Next, take a closer look at...
public void KeyPressed(KeyEvent e) {
See anything wrong there? Why does it start with an uppercase K, that's not the correct method signature
Change to something more like...
#Override
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
#Override
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
Yes, this is why you're encouraged to use #Override ;)
And finally change your paint method to paintComponent
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
You are encouraged to override paintComponent when performing custom painting, it tends to cause less issues
I'd also encorage you to have a look at the Key Bindings API and favour it over KeyListener as it provides better control of the focus level required to trigger the key events
I'd also encourage your to override getPreferredSize of the Board and return your preferred size from there, rather then setting the frame's size. The frame size includes the frame's decorations, so you content is smaller then the frame size
I have a panel which contains custom components that have been added to it. It paints correctly except when the components (which have their own mouselisteners) are being dragged it starts to paint weirdly.
Interestingly if I slightly re-size the parent panel it will now paint as intended. I know that the parent panel is being repainted through
super.paintComponent(g);
and a print statement inside the panels
paintComponent(Graphics g):
method. I tried revalidating when the component is dragged around (as the component is having its bounds re-set) it becomes invalidated. Still I am having no success and was wondering if anyone could help.
Is there some place where i am not repainting correctly that might be causing this behavior?
Also as an addendum I have a mouse listener on both the panel and its components, is there a way so that when a component is clicked the panel mouse listener also responds (beyond going back up to the parent class from the component)
Here is a working example of the issue I am having
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
public class testHome {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testHome window = new testHome();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public testHome() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new myPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
}
}
class myPanel extends JPanel {
MyComponent comp;
public myPanel() {
super(null);
comp = new MyComponent(5, 5);
this.add(comp);
revalidate();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Line2D.Double line = new Line2D.Double(10, 10, comp.getX(), comp.getY());
System.out.println(comp.getX() + " " + comp.getY());
g2d.draw(line);
}
}
class MyComponent extends JComponent implements MouseListener, MouseMotionListener {
int x;
int y;
int mx;
int my;
public MyComponent(int x, int y) {
this.setVisible(true);
this.setBounds(x, y, 15, 15);
this.x = x;
this.y = y;
addMouseMotionListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
g2d.fillRect(this.x, this.y, 15, 15);
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
System.out.println("dragging");
int dx = e.getX() - mx;
int dy = e.getY() - my;
this.setBounds(this.getX() + dx, this.getY() + dy, 15, 15);
}
#Override
public void mousePressed(MouseEvent e) {
mx = e.getX();
my = e.getY();
}
#Override
public void mouseMoved(MouseEvent e) {}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mouseReleased(MouseEvent e) {}
}
The parent panel does not actually get automatically repainted completely. paintComponent() is called, but if you check the clipping, for example by:
System.out.println(g.getClipBounds());
you'll see that only the area below the smaller component is painted. (The component is not opaque, so the parent component needs to paint the area below it). You need to call repaint() explicitly for the parent panel:
getParent().repaint();
(Or using the repaint() variants that specify the region, if the parent component is expensive to draw and can optimize partial draws).
Here's my simple code which draws oval near mouse cursor.Each time I click frame is repainted and only one oval can be draw at the time.I would like to know how to make each oval drawn on click to stay on frame.Thank you for each suggestion.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Buffer extends JPanel implements MouseListener{
public static JFrame frame;
public static boolean check;
public void paintComponent(Graphics g){
super.paintComponent(g);
if(check==true){
g.drawOval((int)MouseInfo.getPointerInfo().getLocation().getX(), (int)MouseInfo.getPointerInfo().getLocation().getY(), 10, 10);
}
}
public static void main(String args[]){
Buffer x=new Buffer();
x.setBackground(Color.cyan);
frame=new JFrame();
frame.setSize(500,500);
frame.addMouseListener(x);
frame.add(x);
frame.setVisible(true);
}
public void mouseClicked(MouseEvent e){
check=true;
repaint();
}
public void mouseEntered(MouseEvent arg0){}
public void mouseExited(MouseEvent arg0){}
public void mousePressed(MouseEvent arg0){}
public void mouseReleased(MouseEvent arg0){}
}
Make an ArrayList of objects representing the ovals. In paintComponent, draw each oval in the list. In the mouse listener, add an oval to the list. Here's an example:
public class Buffer extends JPanel implements MouseListener {
...
private List<Ellipse2D> ovals = new ArrayList<Ellipse2D>();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Ellipse2D oval : ovals)
g2d.draw(oval);
}
public void mouseClicked(MouseEvent e) {
ovals.add(new Ellipse2D.Double(e.getX(), e.getY(), 10, 10);
repaint();
}
}
I have a Java paint program, and I've got two problems to do with it. Both problems are relatively simple, and just regard how the mouse input is handled and how the image uses colors. Here's a photo of the app:
So here's my first problem:
As you can see, by the look of the app, there's a spray of dots on the paint area. Each of those dots is a mouseclick. The program does not recognize when a user is holding down the mouse button, so you have to click individually.
This is obviously counterproductive, user-unfriendly and unacceptable. Now, how I fix this, I'm not sure. I've tried using a permanent while (true) loop, but that does not work. How do I make it so that instead of having to click every time, each time the mouse is held down it sprays out dots?
The second problem is the color of the dots. As you can see, at the bottom, there are color buttons. These function, but there is a problem: Whenever I change the color, all the dots currently on the screen change color. The color is run by a variable called currentColor which is run by the actionListeners controlled by all the color buttons on the bottom panel. How do I make sure that colors already placed on the screen are not affected anymore?
I believe that all the code that can be fixed for these two problems lies in my custom JPanel which is used for the program to paint on. I'll post the entire class below, and if you have any other questions, please let me know.
int xCord, yCord;
public class PaintPanel extends JPanel implements MouseListener {
// default serial whatever...
private static final long serialVersionUID = -6514297510194472060L;
public PaintPanel() {
addMouseListener(this);
}
ArrayList<Point> points = new ArrayList<Point>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point point : points) {
g.setColor(currentColor);
g.fillOval(point.x, point.y, 12, 12);
}
repaint();
}
#Override
public void mouseClicked(MouseEvent m) {
}
#Override
public void mouseEntered(MouseEvent m) {
}
#Override
public void mouseExited(MouseEvent m) {
}
#Override
public void mousePressed(MouseEvent m) {
if (paintPanel.contains(m.getPoint())) {
points.add(m.getPoint());
xCord = m.getX();
yCord = m.getY();
System.out.println("x: " + xCord + " y: " + yCord);
}
}
#Override
public void mouseReleased(MouseEvent m) {
}
}
Painting in Swing is destructive.
That is to say, when Swing requests that a repaint occur on a component, the component is expected to clear what ever was previously paint and update itself.
The problem with your color issue is that you only ever have a single color specified.
A possible solution would be to paint to backing buffer (like BufferedImage) instead of relying on paintComponent.
Instead of repainting all the dots each time paintComponent is called, you would simply paint the BufferedImage instead.
As to your issue with the mouse, you need to implement a MouseMotionListener, this will allow you to detect when the mouse is dragged across the surface, painting a trail of dots
Update with very BASIC example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimplePaint04 {
public static void main(String[] args) {
new SimplePaint04();
}
public SimplePaint04() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private PaintPane paintPane;
public TestPane() {
setLayout(new BorderLayout());
add((paintPane = new PaintPane()));
add(new ColorsPane(paintPane), BorderLayout.SOUTH);
}
}
public class ColorsPane extends JPanel {
public ColorsPane(PaintPane paintPane) {
add(new JButton(new ColorAction(paintPane, "Red", Color.RED)));
add(new JButton(new ColorAction(paintPane, "Green", Color.GREEN)));
add(new JButton(new ColorAction(paintPane, "Blue", Color.BLUE)));
}
public class ColorAction extends AbstractAction {
private PaintPane paintPane;
private Color color;
private ColorAction(PaintPane paintPane, String name, Color color) {
putValue(NAME, name);
this.paintPane = paintPane;
this.color = color;
}
#Override
public void actionPerformed(ActionEvent e) {
paintPane.setForeground(color);
}
}
}
public class PaintPane extends JPanel {
private BufferedImage background;
public PaintPane() {
setBackground(Color.WHITE);
setForeground(Color.BLACK);
MouseAdapter handler = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
drawDot(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e) {
drawDot(e.getPoint());
}
};
addMouseListener(handler);
addMouseMotionListener(handler);
}
protected void drawDot(Point p) {
if (background == null) {
updateBuffer();;
}
if (background != null) {
Graphics2D g2d = background.createGraphics();
g2d.setColor(getForeground());
g2d.fillOval(p.x - 5, p.y - 5, 10, 10);
g2d.dispose();
}
repaint();
}
#Override
public void invalidate() {
super.invalidate();
updateBuffer();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
BufferedImage newBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newBuffer.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
if (background != null) {
g2d.drawImage(background, 0, 0, this);
}
g2d.dispose();
background = newBuffer;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background == null) {
updateBuffer();
}
g2d.drawImage(background, 0, 0, this);
g2d.dispose();
}
}
}
hi everyone im using netbeans 7.2.1
and was following a tutorial for keyboard input
this code should draw a circle in JFrame, which it does
but then should take arrow key input to move it, which it does not
thank you for your help :)
package gamefirstclass;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class GameFirstClass extends JFrame {
//Variables
int y, x;
//Double Buffer
private Image dbImage;
private Graphics dbg;
//Window Basics
public GameFirstClass() {
addKeyListener(new AL());
setTitle("Add window title");
setSize(800, 700);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x = 150;
y = 150;
}
// main
public static void main(String[] args) {
new GameFirstClass();
}
//Controls
public class AL extends KeyAdapter {
#Override
public void keyPressed(KeyEvent event) {
int keyCode = event.getKeyCode();
if (keyCode == event.VK_LEFT)
{
x--;
}
if (keyCode == event.VK_RIGHT)
{
x++;
}
if (keyCode == event.VK_UP)
{
y--;
}
if (keyCode == event.VK_DOWN)
{
y++;
}
}
#Override
public void keyReleased(KeyEvent event) {
}
}
//Double Buffer
#Override
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
g.fillOval(x, y, 15, 15);
repaint();
}
}
Okay, so there is one problem: your if statements!.
Basically, in your keyListener, the if statements don't execute because of the semicolon after them.
The blocks are all that remain, and the modifications to x are the same. Just remove those semicolons and I believe it will work.
Also, don't call repaint() from paintComponent(), because you are generating a lot of repaints for every time. I don't know if this matters, but you should call setFocusable(true) on your JFrame to make sure that the KeyListener works.
Good Luck!