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.
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 am trying to have a background image draw then have a character draw over it. My code was working until I added sleep do I didn't get 1500 fps.
package sylvyrfysh.screen;
import javax.swing.*;
import java.awt.*;
import game.infos.Information;
public class ImageLoadDraw extends JFrame{
/**
*
*/
private static final long serialVersionUID = 1L;
public static void main(){
DisplayMode dm=new DisplayMode(Information.sX,Information.sY,16,DisplayMode.REFRESH_RATE_UNKNOWN);
ImageLoadDraw i = new ImageLoadDraw();
i.run(dm);
}
public void run(DisplayMode dm){
setBackground(Color.PINK);
setForeground(Color.WHITE);
setFont(new Font("Arial",Font.PLAIN,24));
s=new Screen();
try{
loadpics();
s.setFullScreen(dm,this);
try{
Thread.sleep(10000);
}finally{
doRun=false;
s.restoreScreen();
}
}catch(Exception e){
e.printStackTrace();
}
}
private void loadpics() {
bg=new ImageIcon("src/sylvyrfysh/screen/maze_icon.png").getImage();
chara=new ImageIcon("src/sylvyrfysh/screen/char.png").getImage();
repaint();
}
public void paint(Graphics g){
if(g instanceof Graphics2D){
Graphics2D g2=(Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
while(true&&doRun){
g.drawImage(bg,0,0,null);
g.drawImage(bg,0,480,null);
g.drawImage(bg,360,0,null);
g.drawImage(bg,360,480,null);
g.drawImage(bg,720,0,null);
g.drawImage(bg,720,480,null);
g.drawImage(chara,imgX,imgY,null);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Screen s;
public static int imgX=0;
private int imgY=525;
private Image bg,chara;
private Boolean doRun=true;
}
Any ideas on what is causing it and how to fix it? Like I said, with no sleep it works fine.
Swing is an single threaded framework. That is all UI interactions and modifications are expected to be executed within the content of the Event Dispatching Thread, including repaint requests.
Anything that stops, blocks or otherwise prevents this thread from running will prevent the EDT from processing new events, including repaint requests. This will, essentially, make it appear that you program has hung (or stopped responding), because it has.
In your run method you are calling Thread.sleep. This is likely stopping the EDT from processing new events, but because you've actually called the method from the "main" thread, it might actually work, however...
In you paint method, you have an infinite loop AND a Thread.sleep call. These WILL stop the EDT from running, as paint is called from within the context of the EDT.
Painting won't always occur immediately, on some systems, until the paint method returns, it may not be pushed to the device for output, so, even the idea of looping within the paint, regardless of the fact that it will cause problems for the EDT, is a bad idea any way.
Unlike some other UI frameworks, you do not need to implement a "main-loop", Swing takes care of this for you.
Instead, in your paint method, you should simply paint what you need to paint for that cycle and exit.
Instead, you should do something like...
public void paint(Graphics g){
// Painting is a complex series of chained methods, failing to call super.paint
// to cause significant issues
super.paint(g);
// Graphics is guaranteed to be an instance of Graphics2D since I think 1.4
// You should create a copy, so any changes you make are not carried onto the
// next component, Graphics is shared between all the components being painted
// in this paint cycle.
Graphics2D g2=(Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(bg,0,0,null);
g2.drawImage(bg,0,480,null);
g2.drawImage(bg,360,0,null);
g2.drawImage(bg,360,480,null);
g2.drawImage(bg,720,0,null);
g2.drawImage(bg,720,480,null);
g2.drawImage(chara,imgX,imgY,null);
// If you create, you should dispose of it...
g2.dispose();
}
...instead
Instead of Thread.sleep you should be making use of something like javax.swing.Timer, for example...
s=new Screen();
try{
loadpics();
s.setFullScreen(dm,this);
Timer timer = new Timer(10000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
s.restoreScreen();
}
});
timer.setRepeats(false);
timer.start();
}catch(Exception e){
e.printStackTrace();
}
Take a look at Concurrency in Swing for more details.
You should also avoid overriding top level containers and especially overriding paint. Painting a complex series of chained method calls, each one performing a particular job, building up on top of each other to produce a final result.
Instead, you should start with a custom component of some kind, extending from JPanel for example, and override it's paintComponent method, making sure you call super.paintComponent before you do any of you own painting.
Take a look at Performing Custom Painting for more details
Well this one Thread.sleep(10000); turn off EVERYTHING for 10 seconds. This is NOT what you want. Even doing it for 30 milisec (which is ~30frames/sec) is not what you want, because it stops even input etc.
You should use Timer. It runs timing in another thread and it sleeps and wake up only that thread automatically for given number of milisec, so it does not affect your program and it can call it only after 30milisec for example.
Still to have good application, this timer should have low value and you should count how long passed through System.nanoTime() and for example repaint once each 30milisec, however read input each 5milisec etc.
I THINK->This is because you need to call the loadpics() parallel, try this
public void run(DisplayMode dm){
setBackground(Color.PINK);
setForeground(Color.WHITE);
setFont(new Font("Arial",Font.PLAIN,24));
s=new Screen();
loadpics();
s.setFullScreen(dm,this);
try{
new Thread(new Runnable() {
#Override
public void run() {try {Thread.sleep(1000);doRun=false;s.restoreScreen();} catch (Exception e) {}
}
}).start();
}
also setting the doRun as volatile.
private Boolean doRun=true;
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 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(...);
}
I'm learning Java by making a small game in a JApplet.
I got a little problem with my sprite's animation.
Here is the code :
this.sprite.setBounds(0,0,20,17);
this.sprite.setIcon(this.rangerDown);
for(int i = 0; i< 16;i++)
{
this.sprite.setBounds(this.sprite.getX(), this.sprite.getY()+1, 20, 17);
this.sprite.update(this.sprite.getGraphics());
try{
Thread.currentThread().sleep(100);
}catch(InterruptedException e){
}
}
It left some flicker during the animation. Once the animation end, the flicker disappears, but it's kind of ugly... I guess there is some step I missed.
I use this method because it gives the better result for now, but I would like to stay without AWT if possible, using Swing instead.
Any ideas how to get rid of the flicker?
Thanks for reading.
Screenshoot (Can't post images, sorry).
This is not a shadow. Its the border of your sprite. It just happens to be black and appears as a shadow. If you change the amount you shift your sprite (lets say by 50 pixels, not just 1) you will see what i mean.
To fix it what you need to do is to draw the background as well each time you update the location of your sprite. Although this will probably produce flickering.
The correct way to do it is to change the way you draw your objects. You need to override the paintComponent method of your panel and then simply call repaint each time you have updated the locations of your sprites.
EDIT:
See this code sample for basic usage. NOTE: This is NOT how you should write animation using Threads. I wrote that to show you what goes in the paintComponent method and wrote the animation Thread to show you that the "shadow" you mentioned is gone. NEVER have a non ending run loop in a thread :)
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
MyPanel c = new MyPanel();
f.getContentPane().add(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(350, 100);
f.setVisible(true);
}
}
class MyPanel extends JPanel {
int x = 0;
boolean toTheRight = true;
public MyPanel() {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
x = (toTheRight)?x+5:x-5;
if (x>300)
toTheRight = false;
if (x<0)
toTheRight = true;
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g.create();
g2.setPaint(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setPaint(Color.red);
g2.fillOval(x-2, 50, 4, 4);
}
}
The problem is double buffering.
In Applets:
Double buffering is done almost automatically. Call repaint() instead of paint in your method.
In Swing, there are many ways to do it. I usually go for the BufferStrategy route. When you're initializing your frame, do this:
JFrame frame;
... code to init frame here
frame.createBufferStrategy(2);
Then in your draw methods:
Graphics g = getBufferStrategy().getDrawGraphics();
..code to do drawing here...
g.dispose();
getBufferStrategy().show();