Jframe not repainting issue - java

I have a program which is simple in function. On start, it creates a random circle which it places in the window/frame. When that circle is clicked, it should dissappear, and spawn a new circle elsewhere. the issue is, my program does this, but you see all the past circles unless you minimize/reopen the window. I cannot get it to repaint without my help... and I do NOT know why. Here is my code.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
public class Core extends JFrame implements MouseListener{
public static ArrayList<Ellipse2D> list = new ArrayList<Ellipse2D>();
Random r = new Random();
public Ellipse2D spawn(){
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
while(x<75||x>this.getWidth()-150){
x = r.nextInt(this.getWidth());
}
while(y<75||y>this.getHeight()-150){
y = r.nextInt(this.getHeight());
}
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public void mouseClicked(MouseEvent me) {
// Save the coordinates of the click lke this.
if(list.get(0).contains(me.getPoint())){
System.out.println("CLICKED SHAPE!");
list.clear();
list.add(spawn());
}
revalidate();
repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
#Override
public void paint(Graphics g) {
if(list.size()==0){
System.out.println("oops");
}
if(!list.isEmpty()){
System.out.println("DRAW");
int x =(int) list.get(0).getX();
int y =(int) list.get(0).getY();
int width = (int) list.get(0).getWidth();
int height = (int) list.get(0).getHeight();
g.setColor(Color.red);
g.drawOval(x,y, width, height);
}
}
public Core(){
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addMouseListener(this);
list.add(spawn());
setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Core();
}
});
}
}

Your code works perfectly fine for me, however...
Don't override paint of top level containers like JFrame, JFrame contains a JRootPane, which contains a contentPane and may also have a visible glassPane, all of which can overpaint what is painted within the paint method. As a general rule, you shouldn't extend from JFrame (or other top level containers), you are locking yourself into a single use case, reducing the re-usability of your component and you're not actually any new functionality to the class. Instead, use a JPanel and override it's paintComponent method
Call super.paint before doing any custom painting. If, however, you use a JPanel, call super.paintComponent. Painting is performed by a series of methods which are chained together to generate the final output. See Painting in AWT and Swing and Performing Custom Painting for more details
Consider using something like x = r.nextInt(this.getWidth() - 150) + 75; and y = r.nextInt(this.getWidth() - 150) + 75; instead of your while loops, I think you might find them safer to use
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Core {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Core();
}
});
}
public Core() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CorePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CorePane extends JPanel {
private ArrayList<Ellipse2D> list = new ArrayList<Ellipse2D>();
private Random r = new Random();
public Ellipse2D spawn() {
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
x = r.nextInt(this.getWidth() - 150) + 75;
y = r.nextInt(this.getWidth() - 150) + 75;
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public CorePane() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent me) {
// Save the coordinates of the click lke this.
if (list.get(0).contains(me.getPoint())) {
list.clear();
list.add(spawn());
}
revalidate();
repaint();
}
});
}
#Override
public void invalidate() {
super.invalidate();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (list.isEmpty()) {
list.add(spawn());
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (!list.isEmpty()) {
for (Ellipse2D ellipse : list) {
g2d.setColor(Color.red);
g2d.draw(ellipse);
}
}
g2d.dispose();
}
}
}
Or, based on what I believe you're trying to do, you could simply do something like...
public class CorePane extends JPanel {
private Random r = new Random();
private Ellipse2D ellipse;
public Ellipse2D spawn() {
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
x = r.nextInt(this.getWidth() - 150) + 75;
y = r.nextInt(this.getWidth() - 150) + 75;
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public CorePane() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent me) {
if (ellipse != null && ellipse.contains(me.getPoint())) {
ellipse = spawn();
}
revalidate();
repaint();
}
});
}
#Override
public void invalidate() {
super.invalidate();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
spawn();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (ellipse != null) {
g2d.setColor(Color.red);
g2d.draw(ellipse);
}
g2d.dispose();
}
}

Related

Filling bar (progress bar) JApplet paint() Thread()

Vertical bars should be filling to the height of the applet. When the top is reached, a new bar should start filling next to the previous. Problem: When the new bar starts filling the previous paint() /bar is cleared
img how it is: http://bayimg.com/DAEoeaagm
img how it should be: http://bayimg.com/dAeOgAaGm
the code:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;
public class fillingbar extends JApplet implements Runnable{
int shifting=0,filling=0;
public void init()
{
Thread t= new Thread(this);
t.start();
setSize(400,250);
}
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.GREEN);
g.fillRect(shifting,getHeight()-filling,20,filling);
g.setColor(Color.BLACK);
g.drawRect(shifting, getHeight()-filling, 20, filling);
}
public void run()
{
while(true)
{
repaint();
try{
if(shifting<getWidth())
{
if(filling<getHeight())
filling+=10;
else {
shifting+=20;
filling=0;
}
}
Thread.sleep(50);
}catch(Exception E){
System.out.println("Exception caught");
}
}
}
}
You only draw one rectangle in your paint method, and so it makes sense that only one will show.
If you need to draw more, do so, using a for loop that loops through perhaps a Rectangle ArrayList<Rectangle>.
Another way is to make shifting local and do a bit of simple math inside paintComponent to see what to draw and where. For instance, draw your completed bars inside of a for loop, for (int i = 0; i < filling / getHeight(); i++) {, and your yet to be completed bar up to filling % getHeight().
You should not draw directly within a JApplet but rather in the paintComponent method of a JPanel.
A Swing Timer is easier to use than a thread (for me at least), and can be safer.
For example, this can be created by the code below:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
#SuppressWarnings("serial")
public class FillingBar2 extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
FillingBarPanel fillingBarPanel = new FillingBarPanel();
add(fillingBarPanel);
add(new JButton(new StartAction(fillingBarPanel)), BorderLayout.PAGE_END);
setSize(getPreferredSize());
}
});
} catch (InvocationTargetException | InterruptedException e) {
System.err.println("Big Problems");
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class StartAction extends AbstractAction {
private FillingBarPanel fillingBarPanel;
public StartAction(FillingBarPanel fillingBarPanel) {
super("Start");
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
this.fillingBarPanel = fillingBarPanel;
}
#Override
public void actionPerformed(ActionEvent evt) {
fillingBarPanel.start();
}
}
#SuppressWarnings("serial")
class FillingBarPanel extends JPanel {
private static final int BAR_WIDTH = 20;
private static final int TIMER_DELAY = 100;
private static final int PREF_W = 400;
private static final int PREF_H = 250;
private int filling = 0;
private Timer timer;
public FillingBarPanel() {
timer = new Timer(TIMER_DELAY, new TimerListener());
}
public void start() {
if (timer != null && !timer.isRunning()) {
timer.start();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int shifting = 0;
for (int i = 0; i < filling / getHeight(); i++) {
shifting = i * BAR_WIDTH;
g.setColor(Color.GREEN);
g.fillRect(shifting, 0, BAR_WIDTH, getHeight());
g.setColor(Color.BLACK);
g.drawRect(shifting, 0, BAR_WIDTH, getHeight());
}
shifting = BAR_WIDTH * (filling / getHeight());
g.setColor(Color.GREEN);
g.fillRect(shifting, getHeight() - (filling % getHeight()), BAR_WIDTH, getHeight());
g.setColor(Color.BLACK);
g.drawRect(shifting, getHeight() - (filling % getHeight()), BAR_WIDTH, getHeight());
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent evt) {
filling += 10;
repaint();
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}

mouseMoved Event not working

I've been trying to work this code, it's like when you hover over the start button it should change its color to gray, but whenever i hover over it. nothing happens, can somebody tell me why? i didn't get any error and it seems like my mousemoved listener isn't recognized by the compiler, sorry for my english. I haven't finish it yet but here is the code:
class Contents extends JFrame implements Runnable {
private Image dbi;
private Graphics dbg;
private boolean isStarted, isHovered;
private int x,y,xDir,yDir,bx,by,timer,life,my,mx,mhx,mhy;
private Rectangle startgame = new Rectangle(80,100,150,40);
Contents()
{
super();
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if(isStarted)
setSize(600,600);
else
{
setSize(300,300);
setBackground(Color.BLUE);
}
setLocationRelativeTo(null);
isStarted = false;
isHovered = false;
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
mx = e.getX();
my = e.getY();
if(mx > startgame.x && mx < startgame.x+startgame.width &&
my > startgame.y && my < startgame.y+startgame.height)
{
isStarted = true;
}
}
public void mouseMoved(MouseEvent e)
{
mhx = e.getX();
mhy = e.getY();
if(mhx > startgame.x && mhx < startgame.x+startgame.width &&
mhy > startgame.y && mhy < startgame.y+startgame.height)
isHovered = true;
else
isHovered = false;
}
});
}
public void paint(Graphics g)
{
dbi = createImage(getWidth(), getHeight());
dbg = dbi.getGraphics();
draw(dbg);
g.drawImage(dbi,0,0,this);
repaint();
}
public void draw(Graphics g)
{
if(!isStarted)
{
if(!isHovered)
g.setColor(Color.GRAY);
else
g.setColor(Color.GREEN);
g.fillRect(startgame.x, startgame.y, startgame.width, startgame.height);
g.setFont(new Font("Serif",Font.BOLD,24));
g.setColor(Color.WHITE);
g.drawString("Start game", startgame.x+20, startgame.y+25);
g.drawString(String.format("hoverx: %d hovery: %d",mhx,mhy), 50,200);
}
else
{
}
}
public void run()
{
} }
public class Game {
public static void main(String[] args)
{
Contents c = new Contents();
} }
Just use Rectangle.contains(Point) to check if the point from the MouseEvent is inside the Rectangle. Here is an example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PaintedButton extends JPanel {
private static final Color HOVER_COLOR = Color.BLUE;
private static final Color NON_HOVER_COLOR = Color.GREEN;
private static final Rectangle2D RECTANGLE = new Rectangle2D.Double(50, 50,
200, 100);
private Color color = NON_HOVER_COLOR;
public PaintedButton() {
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (RECTANGLE.contains(p)) {
color = HOVER_COLOR;
} else {
color = NON_HOVER_COLOR;
}
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(color);
g2.fill(RECTANGLE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PaintedButton());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Java applet repaint a moving circle

I've just moved over from Pygame so Java 2D in an applet is a little new to me, especially when it comes to repainting the screen. In pygame you can simply do display.fill([1,1,1]) but how do I do this in an applet in Java? I understand the use of repaint() but that doesn't clear the screen - any moving object is not 'removed' from the screen so you just get a long line of painted circles.
Here's my code that I've been testing with:
package circles;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
public class circles extends Applet implements Runnable {
private static final long serialVersionUID = -6945236773451552299L;
static Random r = new Random();
String msg = "Click to play!";
static int w = 800, h = 800;
int[] txtPos = { (w/2)-50,(h/2)-50 };
int[] radiusRange = { 5,25 };
int[] circles;
static int[] posRange;
int x = 0, y = 0;
int radius = 0;
int cursorRadius = 10;
boolean game = false;
public static int[] pos() {
int side = r.nextInt(5-1)+1;
switch(side) {
case 1:
posRange = new int[]{ 1,r.nextInt(w),r.nextInt((h+40)-h)+h,r.nextInt(270-90)+90 };
break;
case 2:
posRange = new int[]{ 2,r.nextInt((w+40)-w)+w,r.nextInt(h),r.nextInt(270-90)+90 };
break;
case 3:
posRange = new int[]{ 3,r.nextInt(w),r.nextInt(40)-40,r.nextInt(180) };
break;
case 4:
posRange = new int[]{ 4,r.nextInt(40)-40,r.nextInt(h),r.nextInt(180) };
break;
}
System.out.println(side);
return posRange;
}
public void start() {
setSize(500,500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics e) {
Graphics2D g = (Graphics2D) e;
if(System.currentTimeMillis()%113==0) {
x+=1;
y+=1;
}
g.setColor(Color.BLUE);
g.fillOval(x,y,20,20);
repaint();
}
}
You need to call super.paint(g); in your paint method, as to not leave paint artifacts.
Never call repaint() from inside the paint method
Don't explicitly call paint, as you do in update(), when you mean to call reapaint()
just update the x and y values from inside the update() method, then call repaint()
You don't need to take a Graphics argument in update()
You need to call update() somewhere repeatedly in a loop, as it updates the x and y and reapint()s
If your class is going to be a Runnable, then you should put some code in the run() method. That's probably where you should have your loop
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class circles extends Applet implements Runnable {
int x = 0, y = 0;
public void start() {
setSize(500, 500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
while (true) {
try {
update();
Thread.sleep(50);
} catch (InterruptedException ex) {
}
}
}
public void update() {
x += 5;
y += 6;
repaint();
}
public void paint(Graphics e) {
super.paint(e);
Graphics2D g = (Graphics2D) e;
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
}
Side Notes
Why use Applets in the first place. If you must, why use AWT Applet and not Swing JApplet? Time for an upgrade.
Here's how I'd redo the whole thing in Swing, using a Swing Timer instead of a loop and Thread.sleep, as you should be doing.
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.SwingUtilities;
import javax.swing.Timer;
public class Circle extends JPanel{
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Circle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
See How to use Swing Timers
See Create GUIs with Swing
Here's more advanced example for you to look at and ponder.
UPDATE
"Problem is, that's a JPANEL application. I specifically want to make an applet easily usable on a web page. "
You can still use it. Just use the JPanel. Take out the main method, and instead of Applet, use a JApplet and just add the JPanel to your applet. Easy as that.
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.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CircleApplet extends JApplet {
#Override
public void init() {
add(new Circle());
}
public class Circle extends JPanel {
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
}
}

How to initialize Graphics g?

I want to display a GameOver image in a pacman game after lives are over. But I call the paintGameOverScreen(Graphics g) and then I need to initialize g. Is there any other way to do this?
This is my Lives class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Lives{
private int lives;
public Lives() {
lives = 1;
}
public void removeLife() {
lives--;
if(lives==0){
System.out.println("END GAME");
paintGameOverScreen(g);
System.exit(0);
}
}
public void paintGameOverScreen(Graphics g) {
ImageIcon i = new ImageIcon("src\image");
Image image = i.getImage();
int x = 0;
int y = 0;
g.drawImage(image, x, y, 100,100,null);
}
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(5*20, 25*20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6*20, 26*20);
}
}
You never call paint() or paintComponent() yourself, you always go through repaint() which will take care of setting up the appropriate Graphics
Just to show what #mKorbel is referring to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Lives extends JPanel {
private int lives;
private ImageIcon gameOverImage;
public Lives() {
try {
gameOverImage = new ImageIcon(new URL("http://imgup.motion-twin.com/dinorpg/0/f/77acf80b_989624.jpg"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lives = 5;
}
public void removeLife() {
if (lives > 0) {
lives--;
System.out.println("Left lives: " + lives);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lives > 0) {
System.out.println("Still have " + lives + " lives");
g.setColor(Color.WHITE);
g.fillRect(5 * 20, 25 * 20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6 * 20, 26 * 20);
} else if (gameOverImage != null) {
System.out.println("Game over");
int x = (getWidth() - gameOverImage.getIconWidth()) / 2;
int y = (getHeight() - gameOverImage.getIconHeight()) / 2;
g.drawImage(gameOverImage.getImage(), x, y, gameOverImage.getIconWidth(), gameOverImage.getIconHeight(), this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame(Lives.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Lives lives = new Lives();
frame.add(lives);
frame.pack();
frame.setVisible(true);
// Dummy timer that reduces the lives every second. For demo purposes only of course
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lives.removeLife();
}
});
t.start();
}
});
}
}
for public void paint(Graphics g) { is there missed container,
JPanel (in some cases JComponent) could be container for todays Java
have to use paintComponent instead of paint()
inside paintComponent you can to flag for paintGameOverScreen, then there paint only BufferedImage
prepare all Objects before, as local variable, do not load any FileIO (load images) inside paint(), paintComponent()
Here is how I did:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class InitializeGraphics
{
static BufferedImage buffer = null;
static int height = 10;
static int width = 10;
static Graphics2D g2;
public InitializeGraphics() {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2 = buffer.createGraphics();
g2.fillOval(2, 2, 2, 2);
g2.dispose();
}
protected void paintComponent(Graphics g) {
int x = 0;
int y = 0;
g.drawImage(buffer, x, y, width, height, null);
}
public Graphics2D getGraphics(){
return g2;
}
}
Then somewhere:
InitializeGraphics instance=new InitializeGraphics();
Graphics2D gG2 = instance.getGraphics();

Swing components drawing inside each other

This is my fourth question here in about two weeks... I think I have a lot to learn.
Anyway, I'm hoping the sample I've prepared below will explain the problem better than I can, but since I changed all of my components to Swing rather than AWT, they've all been drawing every component in the panel inside themselves whenever I call repaint().
This code is essentially my program stripped down as much as I can without destroying the problem completely. I've stuck a couple of my custom listbox components in there to demonstrate the issue, and made the border red to make it slightly easier to see what's drawing where.
Test.java:
package test;
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
public class Test extends JFrame
{
public static void main(String[] args)
{
Test test1 = new Test();
}
public Test()
{
super("Test Frame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(800, 480);
setLocationRelativeTo(null);
loadTestScreen();
}
public void loadTestScreen()
{
TestScreen newTestScreen = new TestScreen();
newTestScreen.setSize(new Dimension(getWidth() - getInsets().left - getInsets().right, getHeight() - getInsets().top - getInsets().bottom));
setContentPane(newTestScreen);
}
}
TestScreen.java:
package test;
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.util.*;
import java.net.URI;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class TestScreen extends JPanel implements ComponentListener, MouseListener
{
int border, LT;
public TestScreen()
{
setLayout(null);
setOpaque(true);
setBackground(Color.WHITE);
addComponentListener(this);
border = 8;
LT = 4;
///////////////////////////////////////////////////////////
ArrayList<String> testList = new ArrayList<String>();
testList.add("1");
testList.add("2");
testList.add("3");
testList.add("4");
testList.add("5");
testList.add("6");
add(new ListBox(testList), 0);
add(new ListBox(testList), 1);
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke((float)(LT)));
g2d.setColor(new Color(255, 0, 0));
g2d.drawRoundRect(border, border, getWidth() - border - border, getHeight() - border - border, border + border, border + border);
}
public void componentHidden(ComponentEvent e){}
public void componentShown(ComponentEvent e){}
public void componentMoved(ComponentEvent e)
{
componentResized(e);
}
public void componentResized(ComponentEvent e)
{
getComponent(0).setLocation(20, 20);
getComponent(0).setSize(100, 100);
getComponent(1).setLocation(200, 200);
getComponent(1).setSize(150, 150);
repaint();
}
public void mouseEntered(MouseEvent e)
{
repaint();
}
public void mouseExited(MouseEvent e)
{
repaint();
}
public void mouseReleased(MouseEvent e){} public void mouseDragged(MouseEvent e){} public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
repaint();
}
}
ListBox.java:
package test;
import java.beans.*;
import java.util.*;
import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeSupport;
import javax.swing.*;
public class ListBox extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener
{
public ArrayList<String> data;
int border, LT;
int selectedIndex = 0;
int mousedIndex = -1;
public int viewedHeight = 0;
int itemHeight;
int numberOfDisplayedItems;
Font miniFont;
FontMetrics miniMetrics;
private final PropertyChangeSupport pcs = new PropertyChangeSupport( this );
public ListBox(ArrayList<String> list)
{
setVisible(true);
setOpaque(true);
border = 8;
LT = 4;
addMouseListener(this);
addMouseMotionListener(this);
addComponentListener(this);
addMouseWheelListener(this);
data = list;
miniFont = new Font("Calibri", Font.PLAIN, 15);
miniMetrics = getFontMetrics(miniFont);
itemHeight = miniMetrics.getAscent() + miniMetrics.getDescent() + border + border;
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(new Color(93, 138, 168));
g2d.setStroke(new BasicStroke((float)LT / 2));
g2d.setClip(-(LT / 2), -(LT / 2), getWidth() + LT, getHeight() + LT);
int cumulativeDist = -viewedHeight;
for (int i = 0; i < data.size(); i++)
{
if (selectedIndex == i)
g2d.fillRect(0, cumulativeDist, getWidth(), itemHeight);
cumulativeDist += itemHeight;
g2d.drawLine(0, cumulativeDist, getWidth(), cumulativeDist);
}
g2d.drawRect(0, 0, getWidth(), getHeight());
g2d.setFont(miniFont);
g2d.setColor(new Color(42, 60, 76));
cumulativeDist = -viewedHeight + border + miniMetrics.getAscent();
for (int i = 0; i < data.size(); i++)
{
if (mousedIndex == i){
g2d.drawString(data.get(i), border + border / 2, cumulativeDist);
} else {
g2d.drawString(data.get(i), border, cumulativeDist);
}
cumulativeDist += itemHeight;
}
}
public String getSelectedItem()
{
return data.get(selectedIndex);
}
public void mouseReleased(MouseEvent e){} public void mouseEntered(MouseEvent e){} public void mouseDragged(MouseEvent e){}public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
int old = selectedIndex;
int mouseHeight = viewedHeight + e.getY();
int ID = mouseHeight / itemHeight;
System.out.println(mouseHeight / itemHeight);
selectedIndex = ID;
pcs.firePropertyChange("selectedIndex", old, selectedIndex);
}
public void componentHidden(ComponentEvent e){} public void componentShown(ComponentEvent e){} public void componentMoved(ComponentEvent e){}
public void componentResized(ComponentEvent e)
{
numberOfDisplayedItems = (int)((getHeight() / itemHeight) + 0.5);
repaint();
}
public void mouseWheelMoved(MouseWheelEvent e)
{
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
{
if (e.getUnitsToScroll() > 0)
{
viewedHeight += itemHeight;
}else{
viewedHeight -= itemHeight;
}
if (viewedHeight > (data.size() * itemHeight) - getHeight())
viewedHeight = (data.size() * itemHeight) - getHeight();
if (viewedHeight < 0)
{
viewedHeight = 0;
}
mouseMoved((MouseEvent)e);
repaint();
}
}
public void mouseMoved(MouseEvent e)
{
int mouseHeight = viewedHeight + e.getY();
int ID = mouseHeight / itemHeight;
mousedIndex = ID;
repaint();
}
public void mouseExited(MouseEvent e)
{
mousedIndex = -1;
repaint();
}
}
Thanks, and if you find any other glaring issues in my programming, please don't hesitate to tell me :P
The immediate problem—painting artifacts in ListBox—is caused by not honoring the opaque property. Using true promises to paint all of the bits, but your paintComponent() fails to do so.
Here are a few more suggestions:
Don't neglect the event dispatch thread.
Do use event listeners, but also consider the available adapters, which have convenient null implementations.
Don't reinvent existing components, and override paintComponent() only as a last resort.
Do learn to use layout managers and borders.
Yes there is much to learn, but this kind of experimentation is invaluable.
You should call setVisible(true); only once you've set up the component, at the end of your constructor.

Categories

Resources