I am a java beginner and for my first project I started building a Monopoly game.
I'm building the GUI in SWING using the Graphics method.
Two problems appeared which I can't find an answer to.
The first one is that it seems that I can't set the Background color to my JPanel which I had previously done the same way in another JPanel in the same project.
The second one is that I get a NullPointerException while trying to add an image.I managed to correct this error with a try/catch but it seems that the Graphics won't paint.Again I've used the same method to load and add images in a previous JPanel and it worked.
I should mention that my JFrame at the moment contains 3 elements everyone in there separate classes and are added via BorderLayout().
This is the code for the class that is creating problems:
public class MonopolyBoard extends JPanel{
Image atlantic;
MonopolyBoard() {
this.setBorder(new EtchedBorder());
this.setBackground(new Color( (80), (180), (210) )); //this code dosent work
//this throws exception without try catch
try{
ImageIcon a = new ImageIcon(this.getClass().getResource("../Card/Atlantic Ave.jpg"));
atlantic = a.getImage();
}
catch(NullPointerException e){}
}
public void paint(Graphics g){
}
Graphics2D g2 = (Graphics2D) g;
//this code should draw the image but it dosent
g2.drawImage(atlantic, 100, 100, null);
g.drawImage(atlantic, 100, 100, this);
};
}
You won't know unless you print the stacktrace inside the catch block. If the constructor, new ImageIcon(), isn't throwing the exception and instead is returning a null object, the next line, a.getImage(), will definitely cause a NPE because you can't call a method on a null object.
Instead of this
//this throws exception without try catch
try
{
ImageIcon a = new ImageIcon(this.getClass().getResource("../Card/AtlanticAve.jpg"));
atlantic = a.getImage();
}
catch(NullPointerException e){}
Try this
// the next line may be wrapped incorrectly due to MarkDown
ImageIcon a = new ImageIcon(this.getClass().getResource("../Card/AtlanticAve.jpg"));
if (a == null)
{
System.out.println("Can not find AtlanticAve.jpg");
return;
}
atlantic = a.getImage();
The line
// the next line may be wrapped incorrectly due to MarkDown
ImageIcon a = new ImageIcon(this.getClass().getResource("../Card/AtlanticAve.jpg"));
Basically, you need to start by seeing what might cause the constructor if ImageIcon to return a null object. That will get you on the right track. It might be something due to a failed getResource() call. A simple way of finding out would be to separate the above line into it's parts and give them their own result variables. It's messy and ineficient but that's how troubleshooting goes sometimes.
// using _var_ because I'm too lazy to look up the return types of the methods
var x1 = this.getClass().getResource("../Card/AtlanticAve.jpg");
if (x1 == null)
{
System.out.println("Can't find my resource");
}
You get the picture
I am very confused with your code but I think the problem is that your panel is not drawing!, your paint method it should be
#Override
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
//this code should draw the image but it dosent
g2.drawImage(atlantic, 100, 100, null);
g.drawImage(atlantic, 100, 100, this);
}
Setting the Background color does not have any effect because you override paint().
Paint is responsible for drawing the background.
You need to paint the background in your paint method:
public void paint(Graphics g){
// paint the background
g.fill(); // not sure about the parameters
// paint your image
g.drawImage(...);
}
Related
I have a simple JPanel that I'm using to display an image. I want some functionality where I'm able to pan the image by dragging it. I have something like this (note the code I have compiles and runs properly; the code below is heavily truncated to just get an idea of what I'm doing):
public class Data2DPanel extends JPanel {
public Data2DPanel(Data2D data) {
// Set image
this.image = Data2D.data2DToBufferedImage(data);
// Set mouse listener
Data2DMouseAdapter data2DMouseAdapter = new Data2DMouseAdapter();
this.addMouseListener(data2DMouseAdapter);
this.addMouseMotionListener(data2DMouseAdapter);
}
private class Data2DMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
switch (actionState) {
case PAN:
xOffset = xOffsetInit + e.getX()-xPosInit;
yOffset = yOffsetInit + e.getY()-yPosInit;
paintComponent(getGraphics());
break;
}
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// Paint image
g2.drawImage(image,x,y,width,height,this);
}
}
}
The problem is that there is a lot of flickering. I've checked stackoverflow/google and it seems a lot of the flickering problems are because people override the paint method instead of the paintComponent method. I've also checked isDoubleBuffered and it returns true. Intuitively, I feel maybe the mouseDragged method is updating too much for paintComponent() to keep up, but I figured double buffering should still prevent flickering from occuring. My question is if there's something inherently wrong with this approach and if there's a standard or elegant solution to this.
paintComponent(getGraphics()); should be repaint(). Compounded problems.
You never want to make a call to getGraphics() for custom painting. The only Graphics object used to paint should be the one provided in the paint method.
You should never call paintXxx to try and "force" a repaint of the component. You should call repaint() and allow the RepaintManager to handle all the update and paint cycle
I figured out a solution minutes after posting, but due to low reputation I couldn't delete the post
For the fun of it I decided to start working on something which might turn into a game at some point.
I'm trying to draw some circles and move them in a given direction currently. This causes flickering. It's very likely that I oversee something very basic but I can't figure out why it doesn't render smoothly.
My board class looks something like (removed what I deemed unnecessary):
public class Board extends Canvas implements Runnable {
public static void main(String[] args) {
Board board = new Board();
board.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame("Circles");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(board);
frame.pack();
frame.setVisible(true);
board.start();
}
#Override
public void run() {
while (running) {
process();
repaint();
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The paint method:
public void paint(Graphics g1) {
super.paint(g1);
Graphics2D g = (Graphics2D) g1;
for (Ball ball : Ball.BALLS) {
g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(), ball.getRadius());
}
}
My process method:
private void process() {
if (utilities.randInt(1, 100) < 10 && Ball.getBallCount() < 40) {
Ball.spawnNew(this);
}
for (Ball ball : Ball.BALLS) {
ball.move(this);
}
Ball.BALLS.removeAll(Ball.TO_REMOVE);
Ball.TO_REMOVE.clear();
}
The move method basically increments the x-value of the ball by a given value each time its called (moving it right).
Like I said, I'm unsure why it flickers so if you have any pointers please do tell.
Thanks!
This sounds like a case where you need to perform double-buffering, so that one copy of your canvas can remain shown while you are updating the other.
You're using AWT here, and I don't know how to implement double-buffering manually with AWT. However, if you're willing to use Swing here you can take advantage of automatic double-buffering. See the question about How to make canvas with Swing? as well as Oracle Technology Network's article on Painting in AWT and Swing.
The basic idea would be:
extend javax.swing.JPanel instead of Canvas (which means when you override paint(Graphics) you're now overriding it from javax.swing.JComponent instead of java.awt.Component)
create a constructor with super(true) to enable double-buffering.
Edit: Also, as iccthedral points out, you're better off overriding paintComponent(Graphics) and including a call to super.paintComponent(Graphics). See Difference between paint, paintComponent and paintComponents in Swing.
You need double buffering. To do this you need to create a BufferedImage and get the Graphics from it. Paint everything to the image, render the image on to the screen, then finally fill the image with a the background color or image to reset it.
Sample:
//one time instantiation
BufferedImage b = new BufferedImage(width, height, mode);
In paint(Graphics g):
Graphics buffer = b.getGraphics();
//render all of the stuff on to buffer
Graphics2D g = (Graphics2D) buffer;
for (Ball ball : Ball.BALLS) {
g.drawOval((int) ball.getLocation().getX(), (int) ball.getLocation().getY(), ball.getRadius(), ball.getRadius());
}
g1.drawImage(b, 0, 0, width, height, null);
g.setColor(Color.BLACK);
//reset the image
g.drawRect(0, 0, width, height);
g.dispose();
In a Java application, how is it possible to output audio in a paint function? I've tried this code:
public static void Player(String audioname){ //For audio
InputStream in = null;
try{
in = new FileInputStream (new File (audioname));
}
catch (FileNotFoundException e){
System.err.print("Audio file not found!");
}
try{
as = new AudioStream (in);
}
catch (IOException e){
System.err.print("AudioStream couldn't be created!");
}
}
////////////////////////////////////////////////////////////////
try{
Player(name);
AudioPlayer.player.start(as);
} catch(Exception f){
System.err.print("Audio couldn't be played!");
}
...however the player gets caught in the Exception f catch statement. I've also tried placing it in a different place (as a separate method that the paint calls but it still doesn't work. Any help?
Edit:
class playAudio implements Runnable{
public void run(){
try{
Player("countdown.wav");
AudioPlayer.player.start(as);
} catch(Exception f){
System.err.print("Audio couldn't be played!");
}
}
}
///////////////////////////////////////
public void paint(Graphics g){ //Draw function
Graphics2D g2d = (Graphics2D) g;
///////////////////////////////////
Thread audioThrd = new Thread(new playAudio());
audioThrd.start();
}
Here, I added a thread to play the audio file, but "Audio couldn't be played!" still shows. What am I doing wrong here?
No, never do audio inside of paint. Painting methods are for drawing and drawing only and should be blindingly fast. The slower your paint methods, the less responsive your program will seem to the users. Running audio will create a long-running process, and if this occurred within a paint method, your program's GUI would grind to a halt, not something you want to have happen.
Instead you want to play audio in a background thread off of the Swing event thread. A SwingWorker could work well, but so could any old garden variety thread.
Edit
Regarding this code:
public void paint(Graphics g){ //Draw function
Graphics2D g2d = (Graphics2D) g;
///////////////////////////////////
Thread audioThrd = new Thread(new playAudio());
audioThrd.start();
}
Again, don't do this. Please understand that you do not have control over when or even if* paint gets called since this is controlled by the JVM. You can suggest that it be called by calling repaint(), but this is not guaranteed to work, especially if repaint requests "stack" up. Note also that the JVM can call paint when you don't request it such as when the operating system notifies it that one of its windows is "dirty" and needs to be repainted.
So the bottom line is:
DON'T TRY TO PLAY MUSIC FROM WITHIN PAINT OR PAINTCOMPONENT
As for why your code is not playing music, have you implement my printStackTrace() recommendation yet?
Next we'll talk about why you should not do drawing inside of paint(Graphics g) but instead should do your drawing inside of your JComponent's paintComponent(Graphics g) method instead.
I am making a game canvas using swing and decided to use JTextField's for the input of a username and password into the panel.
I am buffering an image then rendering it onto the screen instead of drawing everything directly onto the panel real-time.
I have ran into a problem though, I paint a background and have set both of my text fields to opaque, but it seems that whenever I go to enter something into those text field's it flashes a black box where the JTextField is.
It happens in both of my username and password fields. Any idea of what the cause of this could be?
Other helpful information: Whenever I click on a text box, both of the components flash black where the first character would be.
EDIT -- I just noticed that the login button I have also flashes black when MOUSE_ENTERED and MOUSE_EXIT.
public class GamePanel extends JPanel implements Runnable {
public GamePanel(int width, int height) {
this.pWidth = width;
this.pHeight = height;
setController(new LoginGameController(this));
setPreferredSize( new Dimension(pWidth, pHeight));
setBackground(Color.BLACK);
setFocusable(true);
requestFocus(); // the JPanel now has focus, so receives key events
// create game components
addMouseListener(this);
addKeyListener(this);
setLayout(null);
startGame();
}
private void startGame()
// initialise and start the thread
{ if (animator == null) {
animator = new Thread(this);
animator.start();
}
}
public void run() {
while(true) {
gameUpdate();
if(getGraphics() != null){
gameRender(); // render the game to a buffer
paintScreen(); // draw the buffer on-screen
}
try {
Thread.sleep(28);
} catch (InterruptedException e) {}
}
}
private void paintScreen() {
Graphics2D g = (Graphics2D) getGraphics();
if ((g != null) && (img != null))
g.drawImage(img, 0, 0, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
private void gameRender() {
if(getWidth() > 0 && getHeight() > 0)
img = createImage(getWidth(), getHeight());
if(img != null) {
Graphics2D g = (Graphics2D) img.getGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0, 0, pWidth, pHeight);
getController().render(img);
paintComponents(img.getGraphics());
}
}
}
Here is text fields: (from a seperate class entirely calling to the GamePanel using getPanel()...)
//Setup Login fields
usernameTF = new JTextField();
usernameTF.setOpaque(false);
usernameTF.getCaret().setBlinkRate(0);
usernameTF.setForeground(Color.WHITE);
usernameTF.setBounds(USERNAME_FIELD);
usernameTF.setBorder(null);
getPanel().add(usernameTF);
passwordTF = new JPasswordField();
passwordTF.setOpaque(false);
passwordTF.getCaret().setBlinkRate(0);
passwordTF.setForeground(Color.WHITE);
passwordTF.setBounds(PASSWORD_FIELD);
passwordTF.setBorder(null);
getPanel().add(passwordTF);
loginBtn = new JButton();
loginBtn.setOpaque(false);
loginBtn.setBackground(null);
loginBtn.setBorder(null);
loginBtn.setBounds(LOGIN_BUTTON);
loginBtn.addMouseListener(getPanel());
getPanel().add(loginBtn);
Thanks!
The basic problem is, you circumventing Swings repaint process and not honoring the EDT when you up-date your graphics.
JComponent#getGraphics is a temporary/scratch buffer which will be re-draw on the next repaint. Also, if you didn't create the graphics, you shouldn't dispose it!
public void run() {
while(true) {
gameUpdate();
if(getGraphics() != null){
gameRender(); // render the game to a buffer
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
paintScreen(); // draw the buffer on-screen
}
});
} catch (Exception exp) {
exp.printStackTrace(); // please clean this up
}
}
try {
Thread.sleep(28);
} catch (InterruptedException e) {}
}
}
I don't know if this will fix it, but it can't hurt. (This WILL effect you FPS and you should be taking into consideration how long it took to paint and how long you want to wait)
Alternativly, rather then calling paintScreen(), you could call repaint (or have paintScreen do it) and override the paintComponent method to paint your buffer.
This would allow Swing to resume control over the paint process properly.
A window or component has at least three ways to repaint.
on demand from the operating system when it has lost a part of the window buffer.
on demand from a library component when it has changed its state.
manually by manipulating its own frame buffer.
normally, the following pathway is active:
The operating system may request a particular rectangle to be updated. This is picked up by the toolkit. Additionally, a component may signal its change to the toolkit as well through its repaint method. The toolkit keeps collecting an merging incoming repaint requests for a while then asks the window to repaint a particular rectangle. The default action for the window is to paint itself by calling its paintComponent method where it doesn't overlap with solid children and then paint its components recursively.
If you do your own rendering, the toolkit is doing it as well. Since you didn't override your paintComponent, when this method runs, it acts as default. The default action is to do nothing. The toolkit then picks up the empty frame buffer and paints the button (which doesn't paint its background) over it.
You can disable the toolkit's rendering process and do all rendering yourself (grab a buffer, paint into it, then submit). You can call the setIgnoreRepaint(true) method to ignore the requests by OS.
I have created a custom JButton where override the setIcon.
public class TestButton extends JButton {
public TestButton() {
super();
}
#Override
public void setIcon(Icon icon) {
super.setIcon(icon);
imgToBufferedImg(Toolkit.getDefaultToolkit().createImage("test.png"));
}
}
And here is the imgToBufferedImg method.
public BufferedImage imgToBufferedImg(Image image) {
if (image == null) {
return null;
}
if (image instanceof BufferedImage) {
return ((BufferedImage) image);
} else {
BufferedImage bufferedImage = new BufferedImage(
image.getWidth(null),
image.getHeight(null),
BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return bufferedImage;
}
}
I have added this component in Matisse, no problem, however, when i try to set the icon property of the button i get the error:
Failed to write the value to the property "icon"
The problem seems to come from the imgToBufferedImg since i can set the property if i remove the call to this method in setIcon. What is wrong with my image conversion method?
EDIT:
The following test succeeded:
try {
imgToBufferedImg(ImageIO.read(new FileInputStream("test.png")));
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
Also i just figured out that the problem is caused by:
((ImageIcon) icon).getImage();
Running this code when the UI is ready (e.g using a SwingUtilities.invokeLater) seems to work.
The problem might be in Toolkit#createImage(). ImageIO.read() might be better. Also, it looks like you're throwing away the result from imgToBufferedImg().
no reason why
create BufferedImage inside JButtons setIcon(), there you would be to set (for JButton) Icon, ImageIcon
this BufferedImage (should be Icon, ImageIcon) is create after is added to JButton
but
method could be BufferedImage to Icon, ImageIcon
whats wrong with JButton#setIcon()
you can use paintComponent() too
Thanks to the thrashed comment:
Toolkit "operations may be performed asynchronously." Your Image may be incomplete when you try to render it.
I was able to figure out what the problem was. Straight from the setIcon method, i requested the image from the icon:
((ImageIcon) icon).getImage()
But this image is definitively incomplete. Putin my logic within the event dispatching thread did the trick.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//requesting icon images here
}
}