I'm development a countdown for a program, the time is saved successfully, but when i restart the program the label load the time but not resume. What do i need to make for resume the timer?
This is for a program that needs a software licence activation
private Timer t;
private int M = 5, S = 60;
File Save = new File("C:\\Users\\User\\Downloads\\Countdown.txt");
private ActionListener accion = new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
S--;
if( S == -1){
M = M - 1;
S = 60;
}if( M == -1){
M = 5;
S = S - 0;
}if( M == 1 && S == 1){
Lbl_Warning.setText("Your'e Software Licence Soon Expire");
}if( M == 0 && S == 1){
Lbl_Warning.setText("Your'e Software Licence Expired");
M = 0; S = 0;
t.stop();
}
Update();
}
};
public Crono() {
initComponents();
this.setLocationRelativeTo(null);
t = new Timer(1000, acciones);
t.start();
}
public void Save(){
try{
if(!Save.exists()){
Save.createNewFile();
}{
BufferedWriter BW = new BufferedWriter(new FileWriter(Save.getAbsolutePath()));
BW.write(Lbl_Cronometro.getText());
BW.close();
}
} catch (IOException ex) {
ex.getMessage();
}
}
public void Charge(){
try {
if (Save.exists()){
Scanner sc = new Scanner(Save);
Lbl_Crono.setText(sc.nextLine());
sc.close();
}
} catch (FileNotFoundException ex) {
ex.getMessage();
}
}
private void Update() {
String time = (M+" : "+S);
Lbl_Crono.setText(time);
}
private void Lbl_ExitMouseClicked(java.awt.event.MouseEvent evt) {
if(t.isRunning())
{
t.stop();
Save();
System.exit(0);
}
Update();
}
I expect when I open again the JFrame, charge the saved time of the countdown and start.
Related
I'm writing a program that constantly pings a server. I wrote the code to check it once and put the ping in a JLabel and put it in a method called setPing().
Here is my code
private void formWindowOpened(java.awt.event.WindowEvent evt) {
setPing();
}
That worked but only did it once, so I did:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
for(;;){
setPing();
}
}
But this doesn't even work for the first time.
I didnt put the setPing method because it was too long so here it is:
public String setPing(){
Runtime runtime = Runtime.getRuntime();
try{
Process process = runtime.exec("ping lol.garena.com");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
int i = 0;
i = line.indexOf("Average");
if(i > 0){
String finalPing = "";
line.toCharArray();
try
{
finalPing = "";
for(int x = i; x < i + 17; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException e)
{
try
{
finalPing = "";
for(int x = i; x < i + 16; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException f)
{
try
{
finalPing = "";
for(int x = i; x < i + 15; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException g){}
}
}
String final1Ping = finalPing.replaceAll("[^0-9]", "");
return final1Ping;
}
}
}catch(IOException e){
}
return "";
}
UPDATE
Just in case this is important, Im using netbeans. I created a form and put this code in the formWindowOpened evt instead of calling it in main:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(1000, timerListener);
timer.start();
jLabel1.setText(label.getText());
timer.stop();
// TODO add your handling code here:
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime("lol.garena.com");
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public JComponent getUI() {
return label;
}
public static int pingTime(String hostnameOrIP) {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
Use a Swing Timer for repeating tasks & a SwingWorker for long running tasks. E.G. of both below - it uses a Timer to repeatedly perform a 'long running' task (a ping) in a SwingWorker.
See Concurrency in Swing for more details on the Event Dispatch Thread and doing long running or repeating tasks in a GUI.
This code combines a long running task ('pinging' a server) using SwingWorker invoked from a repeating task (updating the JLabel repeatedly with the times) using a Swing based Timer.
import java.awt.event.*;
import javax.swing.*;
import java.net.Socket;
public class LabelUpdateUsingTimer {
static String hostnameOrIP = "stackoverflow.com";
int delay = 5000;
JLabel label = new JLabel("0000");
LabelUpdateUsingTimer() {
label.setFont(label.getFont().deriveFont(120f));
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(delay, timerListener);
timer.start();
JOptionPane.showMessageDialog(
null, label, hostnameOrIP, JOptionPane.INFORMATION_MESSAGE);
timer.stop();
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime();
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public static int pingTime() {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (Exception weTried) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception weTried) {}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new LabelUpdateUsingTimer();
}
};
SwingUtilities.invokeLater(r);
}
}
You could use a Thread. The problem is you are blocking the main thread, thereby blocking your program. To get around this, start a background Thread to update components repeatedly.
(Note: you need to update GUI components on the EDT, so use SwingUtilities.invokeLater)
(new Thread((new Runnable(){
#Override
public void run(){
while(true){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
refToJLabel.setText(Math.random());
}
});
}
}
}))).start();
I'm writing a program that constantly pings a server. I wrote the code to check it once and put the ping in a JLabel and put it in a method called setPing().
Here is my code
private void formWindowOpened(java.awt.event.WindowEvent evt) {
setPing();
}
That worked but only did it once, so I did:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
for(;;){
setPing();
}
}
But this doesn't even work for the first time.
I didnt put the setPing method because it was too long so here it is:
public String setPing(){
Runtime runtime = Runtime.getRuntime();
try{
Process process = runtime.exec("ping lol.garena.com");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
int i = 0;
i = line.indexOf("Average");
if(i > 0){
String finalPing = "";
line.toCharArray();
try
{
finalPing = "";
for(int x = i; x < i + 17; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException e)
{
try
{
finalPing = "";
for(int x = i; x < i + 16; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException f)
{
try
{
finalPing = "";
for(int x = i; x < i + 15; x++)
{
finalPing = finalPing + (line.charAt(x));
}
}catch(IndexOutOfBoundsException g){}
}
}
String final1Ping = finalPing.replaceAll("[^0-9]", "");
return final1Ping;
}
}
}catch(IOException e){
}
return "";
}
UPDATE
Just in case this is important, Im using netbeans. I created a form and put this code in the formWindowOpened evt instead of calling it in main:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(1000, timerListener);
timer.start();
jLabel1.setText(label.getText());
timer.stop();
// TODO add your handling code here:
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime("lol.garena.com");
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public JComponent getUI() {
return label;
}
public static int pingTime(String hostnameOrIP) {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
Use a Swing Timer for repeating tasks & a SwingWorker for long running tasks. E.G. of both below - it uses a Timer to repeatedly perform a 'long running' task (a ping) in a SwingWorker.
See Concurrency in Swing for more details on the Event Dispatch Thread and doing long running or repeating tasks in a GUI.
This code combines a long running task ('pinging' a server) using SwingWorker invoked from a repeating task (updating the JLabel repeatedly with the times) using a Swing based Timer.
import java.awt.event.*;
import javax.swing.*;
import java.net.Socket;
public class LabelUpdateUsingTimer {
static String hostnameOrIP = "stackoverflow.com";
int delay = 5000;
JLabel label = new JLabel("0000");
LabelUpdateUsingTimer() {
label.setFont(label.getFont().deriveFont(120f));
ActionListener timerListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PingWorker().execute();
}
};
Timer timer = new Timer(delay, timerListener);
timer.start();
JOptionPane.showMessageDialog(
null, label, hostnameOrIP, JOptionPane.INFORMATION_MESSAGE);
timer.stop();
}
class PingWorker extends SwingWorker {
int time;
#Override
protected Object doInBackground() throws Exception {
time = pingTime();
return new Integer(time);
}
#Override
protected void done() {
label.setText("" + time);
}
};
public static int pingTime() {
Socket socket = null;
long start = System.currentTimeMillis();
try {
socket = new Socket(hostnameOrIP, 80);
} catch (Exception weTried) {
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception weTried) {}
}
}
long end = System.currentTimeMillis();
return (int) (end - start);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new LabelUpdateUsingTimer();
}
};
SwingUtilities.invokeLater(r);
}
}
You could use a Thread. The problem is you are blocking the main thread, thereby blocking your program. To get around this, start a background Thread to update components repeatedly.
(Note: you need to update GUI components on the EDT, so use SwingUtilities.invokeLater)
(new Thread((new Runnable(){
#Override
public void run(){
while(true){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
refToJLabel.setText(Math.random());
}
});
}
}
}))).start();
I am trying to build an audio player with an integrated JSlider, which updates the interface every microsecond.
In order to do so I am using the following:
sliderTime.setMinimum(0);
sliderTime.setMaximum((int) audioClip.getMicrosecondPosition(););
I have the feeling that this is not the best implementation out there (any suggestions to improve it is highly appreciated)
By the way, the issue I am facing is that for the first second the JSlider does not update.
Please find MCVE below:
It plays only wav uncompressed files
Main
public class Main
{
public static void main(final String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
PlayerView pw = new PlayerView();
Border border = new EmptyBorder(15,15,15,15);
pw.setBorder(border);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(pw, BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
AudioPlayer
public class AudioPlayer implements LineListener
{
private SimpleDateFormat dateFormater = new SimpleDateFormat("HH:mm:ss.SSS");
private TimeZone timeZone = Calendar.getInstance().getTimeZone();
public static final int REWIND_IN_MICROSECONDS = 3000000;
public static final int FORWARD_IN_MICROSECONDS = 3000000;
private boolean playCompleted;
private boolean isStopped;
private boolean isPaused;
private boolean isRewinded;
private boolean isForwarded;
private Clip audioClip;
public Clip getAudioClip()
{
return audioClip;
}
public void load(String audioFilePath) throws UnsupportedAudioFileException, IOException, LineUnavailableException
{
File encodedFile = new File(audioFilePath);
AudioInputStream pcmStream = AudioSystem.getAudioInputStream(encodedFile);
AudioFormat format =pcmStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
audioClip.open(pcmStream);
}
public long getClipMicroSecondLength()
{
return audioClip.getMicrosecondLength();
}
public long getClipMicroSecondPosition()
{
return audioClip.getMicrosecondPosition();
}
public String getClipLengthString()
{
long yourmilliseconds = audioClip.getMicrosecondLength() / 1_000;
Date resultdate = new Date(yourmilliseconds);
dateFormater.setTimeZone(TimeZone.getTimeZone(timeZone.getDisplayName(false, TimeZone.SHORT)));
return dateFormater.format(resultdate);
}
public void play() throws IOException
{
audioClip.start();
playCompleted = false;
isStopped = false;
while (!playCompleted)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException ex)
{
if (isStopped)
{
audioClip.stop();
break;
}
else if (isPaused)
{
audioClip.stop();
}
else if (isRewinded)
{
if( audioClip.getMicrosecondPosition() <= REWIND_IN_MICROSECONDS)
{
audioClip.setMicrosecondPosition(0);
isRewinded =false;
}
else
{
audioClip.setMicrosecondPosition(audioClip.getMicrosecondPosition() - REWIND_IN_MICROSECONDS);
isRewinded =false;
}
}
else if (isForwarded)
{
if((audioClip.getMicrosecondLength() - audioClip.getMicrosecondPosition()) >= FORWARD_IN_MICROSECONDS)
{
audioClip.setMicrosecondPosition(audioClip.getMicrosecondPosition() + FORWARD_IN_MICROSECONDS);
isForwarded =false;
}
else
{
audioClip.stop();
isForwarded =false;
}
}
else
{
audioClip.start();
}
}
}
audioClip.close();
}
public void stop()
{
isStopped = true;
}
public void pause()
{
isPaused = true;
}
public void resume()
{
isPaused = false;
}
public void rewind()
{
isRewinded = true;
}
public void forward()
{
isForwarded = true;
}
#Override
public void update(LineEvent event)
{
Type type = event.getType();
if (type == Type.STOP)
{
if (isStopped || !isPaused)
{
playCompleted = true;
}
}
}
}
PlayingTimer
public class PlayingTimer extends Thread
{
private SimpleDateFormat dateFormater = new SimpleDateFormat("HH:mm:ss.SSS");
private TimeZone timeZone = Calendar.getInstance().getTimeZone();
private boolean isRunning = false;
private boolean isPause = false;
private boolean isReset = false;
private boolean isRewinded = false;
private boolean isForwarded = false;
private long startTime;
private long pauseTime;
private long rewindTime;
private long forwardTime;
private JLabel labelRecordTime;
private JSlider slider;
private Clip audioClip;
public void setAudioClip(Clip audioClip)
{
this.audioClip = audioClip;
}
public PlayingTimer(JLabel labelRecordTime, JSlider slider)
{
this.labelRecordTime = labelRecordTime;
this.slider = slider;
dateFormater.setTimeZone(TimeZone.getTimeZone(timeZone.getDisplayName(false, TimeZone.SHORT)));
}
public void run()
{
isRunning = true;
startTime = System.currentTimeMillis();
while (isRunning)
{
try
{
Thread.sleep(30);
if (!isPause)
{
if (audioClip != null && audioClip.isRunning())
{
long currentMicros = audioClip.getMicrosecondPosition();
// Compute the progress as a value between 0.0 and 1.0
double progress =
(double)currentMicros / audioClip.getMicrosecondLength();
// Compute the slider value to indicate the progress
final int sliderValue = (int)(progress * slider.getMaximum());
// Update the slider with the new value, on the Event Dispatch Thread
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
labelRecordTime.setText(toTimeString());
slider.setValue(sliderValue);
}
});
}
}
else
{
pauseTime += 30;
}
}
catch (InterruptedException ex)
{
if (isReset)
{
slider.setValue(0);
labelRecordTime.setText("00:00:00.000");
isRunning = false;
break;
}
if (isRewinded)
{
if( audioClip.getMicrosecondPosition() <= AudioPlayer.REWIND_IN_MICROSECONDS)
{
//go back to start
rewindTime += audioClip.getMicrosecondPosition() / 1_000;
}
else
{
rewindTime += 3000;
}
isRewinded =false;
}
if (isForwarded)
{
if((audioClip.getMicrosecondLength()- audioClip.getMicrosecondPosition()) <= AudioPlayer.FORWARD_IN_MICROSECONDS)
{
forwardTime -= (audioClip.getMicrosecondLength()- audioClip.getMicrosecondPosition())/1_000;
}
else
{
forwardTime -= 3000;
}
isForwarded=false;
}
}
}
}
public void reset()
{
isReset = true;
isRunning = false;
}
public void rewind()
{
isRewinded = true;
}
public void forward()
{
isForwarded = true;
}
public void pauseTimer()
{
isPause = true;
}
public void resumeTimer()
{
isPause = false;
}
private String toTimeString()
{
long now = System.currentTimeMillis();
Date resultdate = new Date(now - startTime - pauseTime - rewindTime - forwardTime);
return dateFormater.format(resultdate);
}
}
PlayerView
public class PlayerView extends JPanel implements ActionListener
{
private static final int BUTTON_HEIGTH =60;
private static final int BUTTON_WIDTH =120;
private AudioPlayer player = new AudioPlayer();
private Thread playbackThread;
private PlayingTimer timer;
private boolean isPlaying = false;
private boolean isPause = false;
private String audioFilePath;
private String lastOpenPath;
private JLabel labelFileName;
private JLabel labelTimeCounter;
private JLabel labelDuration;
private JButton buttonOpen;
private JButton buttonPlay;
private JButton buttonPause;
private JButton buttonRewind;
private JButton buttonForward;
private JSlider sliderTime;
private Dimension buttonDimension = new Dimension(BUTTON_WIDTH,BUTTON_HEIGTH);
public PlayerView()
{
setLayout(new BorderLayout());
labelFileName = new JLabel("File Loaded:");
labelTimeCounter = new JLabel("00:00:00.000");
labelDuration = new JLabel("00:00:00.000");
sliderTime = new JSlider(0, 1000, 0);;
sliderTime.setValue(0);
sliderTime.setEnabled(false);
buttonOpen = new JButton("Open");
buttonOpen.setPreferredSize(buttonDimension);
buttonOpen.addActionListener(this);
buttonPlay = new JButton("Play");
buttonPlay.setEnabled(false);
buttonPlay.setPreferredSize(buttonDimension);
buttonPlay.addActionListener(this);
buttonPause = new JButton("Pause");
buttonPause.setEnabled(false);
buttonPause.setPreferredSize(buttonDimension);
buttonPause.addActionListener(this);
buttonRewind = new JButton("Rewind");
buttonRewind.setEnabled(false);
buttonRewind.setPreferredSize(buttonDimension);
buttonRewind.addActionListener(this);
buttonForward= new JButton("Forward");
buttonForward.setEnabled(false);
buttonForward.setPreferredSize(buttonDimension);
buttonForward.addActionListener(this);
init();
}
public void enableButtonPlay()
{
buttonPlay.setEnabled(true);
}
#Override
public void actionPerformed(ActionEvent event)
{
Object source = event.getSource();
if (source instanceof JButton)
{
JButton button = (JButton) source;
if (button == buttonOpen)
{
openFile();
}
else if (button == buttonPlay)
{
if (!isPlaying)
{
playBack();
}
else
{
stopPlaying();
}
}
else if (button == buttonPause)
{
if (!isPause)
{
pausePlaying();
}
else
{
resumePlaying();
}
}
else if (button == buttonRewind)
{
if (!isPause)
{
rewind();
}
}
else if (button == buttonForward)
{
if (!isPause)
{
forward();
}
}
}
}
public void openFile(String path)
{
audioFilePath = path ;
if (isPlaying || isPause)
{
stopPlaying();
while (player.getAudioClip().isRunning())
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
playBack();
}
private void openFile()
{
JFileChooser fileChooser = null;
if (lastOpenPath != null && !lastOpenPath.equals(""))
{
fileChooser = new JFileChooser(lastOpenPath);
}
else
{
fileChooser = new JFileChooser();
}
FileFilter wavFilter = new FileFilter()
{
#Override
public String getDescription()
{
return "Sound file (*.WAV)";
}
#Override
public boolean accept(File file)
{
if (file.isDirectory())
{
return true;
}
else
{
return file.getName().toLowerCase().endsWith(".wav");
}
}
};
fileChooser.setFileFilter(wavFilter);
fileChooser.setDialogTitle("Open Audio File");
fileChooser.setAcceptAllFileFilterUsed(false);
int userChoice = fileChooser.showOpenDialog(this);
if (userChoice == JFileChooser.APPROVE_OPTION)
{
audioFilePath = fileChooser.getSelectedFile().getAbsolutePath();
lastOpenPath = fileChooser.getSelectedFile().getParent();
if (isPlaying || isPause)
{
stopPlaying();
while (player.getAudioClip().isRunning())
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
}
}
playBack();
}
}
private void playBack()
{
timer = new PlayingTimer(labelTimeCounter, sliderTime);
timer.start();
isPlaying = true;
playbackThread = new Thread(new Runnable()
{
#Override
public void run()
{
try
{
buttonPlay.setText("Stop");
buttonPlay.setEnabled(true);
buttonRewind.setEnabled(true);
buttonForward.setEnabled(true);
buttonPause.setText("Pause");
buttonPause.setEnabled(true);
player.load(audioFilePath);
timer.setAudioClip(player.getAudioClip());
labelFileName.setText("Playing File: " + ((File)new File(audioFilePath)).getName());
sliderTime.setMinimum(0);
sliderTime.setMaximum((int)player.getClipMicroSecondLength());
labelDuration.setText(player.getClipLengthString());
player.play();
labelFileName.setText("File Loaded: " + ((File)new File(audioFilePath)).getName());
resetControls();
}
catch (UnsupportedAudioFileException ex)
{
JOptionPane.showMessageDialog(
PlayerView.this,
"The audio format is unsupported!",
"Error",
JOptionPane.ERROR_MESSAGE);
resetControls();
}
catch (LineUnavailableException ex)
{
JOptionPane.showMessageDialog(
PlayerView.this,
"Could not play the audio file because line is unavailable!",
"Error",
JOptionPane.ERROR_MESSAGE);
resetControls();
}
catch (IOException ex)
{
JOptionPane.showMessageDialog(
PlayerView.this,
"I/O error while playing the audio file!",
"Error",
JOptionPane.ERROR_MESSAGE);
resetControls();
}
}
});
playbackThread.start();
}
private void stopPlaying()
{
isPause = false;
buttonPause.setText(" Pause ");
buttonPause.setEnabled(false);
buttonRewind.setEnabled(false);
buttonForward.setEnabled(false);
timer.reset();
timer.interrupt();
player.stop();
playbackThread.interrupt();
}
private void pausePlaying()
{
labelFileName.setText("File Loaded: " + ((File)new File(audioFilePath)).getName());
buttonRewind.setEnabled(false);
buttonForward.setEnabled(false);
buttonPause.setText("Resume");
isPause = true;
player.pause();
timer.pauseTimer();
playbackThread.interrupt();
}
private void resumePlaying()
{
labelFileName.setText("Playing File: " + ((File)new File(audioFilePath)).getName());
buttonPause.setText(" Pause ");
buttonRewind.setEnabled(true);
buttonForward.setEnabled(true);
isPause = false;
player.resume();
timer.resumeTimer();
playbackThread.interrupt();
}
private void rewind()
{
player.rewind();
timer.rewind();
timer.interrupt();
playbackThread.interrupt();
}
private void forward()
{
player.forward();
timer.forward();
timer.interrupt();
playbackThread.interrupt();
}
private void resetControls()
{
timer.reset();
timer.interrupt();
isPlaying = false;
buttonPlay.setText("Play");
buttonPause.setEnabled(false);
buttonRewind.setEnabled(false);
buttonForward.setEnabled(false);
}
private void init()
{
add(labelFileName, BorderLayout.NORTH);
add(labelTimeCounter, BorderLayout.WEST);
add(labelDuration, BorderLayout.EAST);
add(sliderTime, BorderLayout.CENTER);
JPanel buttonContainer =new JPanel();
add(buttonContainer, BorderLayout.SOUTH);
buttonContainer.add(buttonOpen);
buttonContainer.add(buttonPlay);
buttonContainer.add(buttonPause);
buttonContainer.add(buttonRewind);
buttonContainer.add(buttonForward);
}
}
Okay, so, the issue with Clip. Here is an MCVE that, from the way you've described the problem, may reproduce it:
class TestFramePosition {
public static void main(String[] a) throws Exception {
File file = new File(a.length > 0 ? a[0] : "path/to/file.extension");
AudioInputStream ais = AudioSystem.getAudioInputStream(file);
final Clip clip = AudioSystem.getClip();
clip.open(ais);
clip.start();
new Thread(new Runnable() {
#Override
public void run() {
while(clip.isRunning()) {
try {
System.out.println(clip.getMicrosecondPosition());
Thread.sleep(1000 / 10);
} catch(InterruptedException ignored) {}
}
}
}).start();
System.in.read();
System.exit(0);
}
}
I was unable to reproduce it on OSX 10.6.8 and Windows XP, but you may run that code to see if it does on your particular platform.
So, the issue here is that, as I said in comments, since sound playback is dependent on platform-specific stuff, classes like Clip will have varied implementations. These will behave slightly differently.
For example, I found that when a Clip is done playing, the Clip on my Mac computer (a com.sun.media.sound.MixerClip) returns 0 for the position, while the Clip on my Windows computer (a com.sun.media.sound.DirectAudioDevice$DirectClip) returns the maximum value for the position. Just another small example of implementations being programmed differently.
The issue is that the contract for these methods is defined a little vaguely but, specifically, it is defined by 'the number of sample frames captured by, or rendered from, the line since it was opened'. This means it may not accurately represent the playback position, rather it is the amount of data read and written.
I did spend awhile yesterday perusing JDK source code but I was unable to find anything that would point towards the behavior you are seeing.
Anyway, what it comes down to is whether you are OK with slightly anomalous behavioral differences from platform to platform. What you are seeing may be a bug and if the above MCVE reproduces it, you may report it; however I would not personally expect it to get fixed in any timely manner because this is a section of the JDK that does not get a lot of attention. Also it is gradually being superseded by JavaFX.
Some other things:
You are sharing state between threads without synchronization. This leads to memory errors. You should read the concurrency tutorials, specifically synchronization.
You should always cap frame rate when working with Swing. Swing will not paint at 1000FPS, it will merge repaints aggressively. Updating the slider at this rate was just flooding the EDT.
You may use SourceDataLine because it gives you much greater control over the buffering behavior. The downside is that you have to basically reimplement the functionality of Clip.
Here is an MCVE demonstrating a playback loop to power a JSlider.
This example doesn't demonstrate seeking. Also since, AudioInputStream does not generally support mark operations, seeking backwards is a bit of a hassle. A backwards seek process is:
Stop the current playback and discard it.
Create a new AudioInputStream and seek forwards.
Start the new playback.
Also, if you are planning to use the JSlider to seek, you will probably run in to an issue where calling setValue on a JSlider will cause it to fire a ChangeEvent. So you can't update the slider's value programmatically and also listen to it without rejiggering it. This is really a Q&A itself so if you experience this problem I recommend you ask a new question.
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.io.File;
import java.io.IOException;
public class PlaybackSlider implements Runnable, ActionListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PlaybackSlider());
}
JButton open;
JButton play;
JSlider slider;
JLabel label;
File file;
PlaybackLoop player;
#Override
public void run() {
JFrame frame = new JFrame("Playback Slider");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel(new BorderLayout()) {
#Override
public Dimension getPreferredSize() {
Dimension pref = super.getPreferredSize();
pref.width = 480;
return pref;
}
};
slider = new JSlider(JSlider.HORIZONTAL, 0, 1000, 0);
content.add(slider, BorderLayout.CENTER);
JToolBar bar = new JToolBar(JToolBar.HORIZONTAL);
bar.setFloatable(false);
content.add(bar, BorderLayout.SOUTH);
open = new JButton("Open");
play = new JButton("Play");
open.addActionListener(this);
play.addActionListener(this);
label = new JLabel("");
bar.add(open);
bar.add(new JLabel(" "));
bar.add(play);
bar.add(new JLabel(" "));
bar.add(label);
frame.setContentPane(content);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent ae) {
Object source = ae.getSource();
if(source == open) {
File f = getFile();
if(f != null) {
file = f;
label.setText(file.getName());
stop();
}
}
if(source == play) {
if(file != null) {
if(player != null) {
stop();
} else {
start();
}
}
}
}
File getFile() {
JFileChooser diag = new JFileChooser();
int choice = diag.showOpenDialog(null);
if(choice == JFileChooser.APPROVE_OPTION) {
return diag.getSelectedFile();
} else {
return null;
}
}
void start() {
try {
player = new PlaybackLoop(file);
new Thread(player).start();
play.setText("Stop");
} catch(Exception e) {
player = null;
showError("the file couldn't be played", e);
}
}
void stop() {
if(player != null) {
player.stop();
}
}
void showError(String msg, Throwable cause) {
JOptionPane.showMessageDialog(null,
"There was an error because " + msg +
(cause == null ? "." : "\n(" + cause + ").")
);
}
class PlaybackLoop implements Runnable {
AudioInputStream in;
SourceDataLine line;
AudioFormat fmt;
int bufferSize;
boolean stopped;
PlaybackLoop(File file) throws Exception {
try {
in = AudioSystem.getAudioInputStream(file);
fmt = in.getFormat();
bufferSize = (int)(fmt.getFrameSize() * (fmt.getSampleRate() / 15));
line = AudioSystem.getSourceDataLine(fmt);
line.open(fmt, bufferSize);
} catch(Exception e) {
if(in != null)
in.close();
if(line != null)
line.close();
throw e;
}
}
void stop() {
synchronized(this) {
this.stopped = true;
}
}
#Override
public void run() {
line.start();
byte[] buf = new byte[bufferSize];
try {
try {
int b;
long elapsed = 0;
long total = in.getFrameLength();
for(;;) {
synchronized(this) {
if(stopped) {
break;
}
}
b = in.read(buf, 0, buf.length);
if(b < 0) {
break;
}
elapsed += b / fmt.getFrameSize();
updateSlider(elapsed, total);
line.write(buf, 0, b);
}
} finally {
line.close();
in.close();
}
} catch(IOException e) {
e.printStackTrace(System.err);
showError("there was a problem during playback", e);
}
endOnEDT();
}
void updateSlider(double elapsed, double total) {
final double amt = elapsed / total;
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
slider.setValue((int)Math.round(slider.getMaximum() * amt));
}
});
}
void endOnEDT() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
player = null;
slider.setValue(0);
play.setText("Play");
}
});
}
}
}
I assume you are wanting to use the JSlider as a progress bar and that at the moment you are setting the max value, the current position is at the end of the audioClip. (Are you dealing with Clip or AudioClip? AudioClip doesn't have a way to read its position AFAIK.) If you are using Clip, it would be safer to set the max with audioClip.getMicrosecondLength().
Since the audio has to play on a different thread than the one where the JSlider is being updated, I'd recommend making your audioClip a volatile variable. That might help with cross-thread weirdness that sometimes occurs.
Thread.sleep(1) at best can only update every millisecond. On some systems (older Windows) the method's reliance on the system clock means the actual updates are as slow as 16 millis apart. But updating the JSlider at more than 60 fps is probably moot. Screen monitors often are set to 60Hz, and there's only so much the human eye can take in.
Also there is only so much the ear can discern in terms of timing. For example, it is hard to tell if two percussive events happen at the same time if there is less than a couple milliseconds difference.
There are several issues with your code.
As Phil Freihofner pointed out, the sleep(1) and the treatment of the isRunning and isPause fields look highly dubious. To some extent, this is unrelated to your actual question, but worth noting here, because it may also cause problems later.
Regardless of that, the approach that Zoran Regvart showed is basically the way to go. The code in the given form may have suffered from some rounding issues. However, the general idea for cases like this is always the same:
You have a source interval [minA...maxA]
You have a target interval [minB...maxB]
You want a mapping between the two
In this case, it's a good practice to normalize the intervals. That is, to map the value from the source interval to a value between 0.0 and 1.0, and then map this normalized value to the target interval.
In the most generic form, this can be written as
long minA = ...
long maxA = ...
long a = ... // The current value in the source interval
int minB = ...
int maxB = ...
int b; // The value to compute in the target interval
// Map the first value to a value between 0.0 and 1.0
double normalized = (double)(a - minA)/(maxA-minA);
b = (int)(minB + normalized * (maxB - minB));
Fortunately, your "min" values are all zero here, so it's a bit simpler. Here is a MCVE (with some dummy classes). The most relevant part is the updateSlider method at the bottom.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class SliderMappingTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JButton startButton = new JButton("Start");
final JSlider progressSlider = new JSlider(0, 1000, 0);
startButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
startButton.setEnabled(false);
SliderMappingDummyAudioClip audioClip =
new SliderMappingDummyAudioClip();
SliderMappingDummyPlayer player =
new SliderMappingDummyPlayer(progressSlider, audioClip);
player.start();
}
});
f.getContentPane().setLayout(new GridLayout());
f.getContentPane().add(startButton);
f.getContentPane().add(progressSlider);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class SliderMappingDummyAudioClip
{
private long startMicros;
void start()
{
startMicros = System.nanoTime() / 1000L;
}
long getMicrosecondLength()
{
// 10 seconds
return 10L * 1000L * 1000L;
}
long getMicrosecondPosition()
{
return (System.nanoTime() / 1000L) - startMicros;
}
public boolean isRunning()
{
return getMicrosecondPosition() <= getMicrosecondLength();
}
}
class SliderMappingDummyPlayer
{
private final SliderMappingDummyAudioClip audioClip;
private final JSlider slider;
SliderMappingDummyPlayer(
JSlider slider,
SliderMappingDummyAudioClip audioClip)
{
this.slider = slider;
this.audioClip = audioClip;
}
void start()
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
doRun();
}
});
t.setDaemon(true);
t.start();
}
private void doRun()
{
audioClip.start();
while (audioClip.isRunning())
{
updateSlider();
try
{
Thread.sleep(30);
}
catch (InterruptedException ex)
{
Thread.currentThread().interrupt();
return;
}
}
}
private void updateSlider()
{
long currentMicros = audioClip.getMicrosecondPosition();
// Compute the progress as a value between 0.0 and 1.0
double progress =
(double)currentMicros / audioClip.getMicrosecondLength();
// Compute the slider value to indicate the progress
final int sliderValue = (int)(progress * slider.getMaximum());
System.out.println("update "+progress);
// Update the slider with the new value, on the Event Dispatch Thread
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
slider.setValue(sliderValue);
}
});
}
}
Are you sure you want to set the maximum to current position?
How about mapping longs to ints by division:
long coefficient = clip.getMicrosecondLength() / Integer.MAX_VALUE;
slider.setMinimum(0);
slider.setMaximum((int) (clip.getMicrosecondLength() / coefficient));
...
slider.setValue((int) (clip.getMicrosecondPosition() / coefficient));
I have two classes (Sampling and Stacker). The Sampling class (my Main class) is extends JFrame and has a JButton with an ActionListener to open the Stacker class.
The problem is when the button is clicked, the Stacker class will open but only a frame without any components. When I switch the main method into the Stacker class, the program works fine. What is the problem?
Here is the code:
The Sampling class:
public class Sampling extends JFrame implements ActionListener
{
private JButton openStacker;
Stacker st;
public Sampling()
{
setSize(300,300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
dispose();
st = new Stacker();
}
public static void main (String args[])
{
new Sampling();
}
}
The Stacker game class:
public class Stacker extends JFrame implements KeyListener
{
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5,5};
int layer = 19;
int deltax[] = {0,0};
boolean press = false;
boolean forward = true;
boolean start = true;
public Stacker()
{
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400,580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton [m][n];
setLayout(new GridLayout(n,m));
for (int y = 0;y<n;y++)
{
for (int x = 0;x<m;x++)
{
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
this.addKeyListener(this);
this.setVisible(true);
go();
}
public void go()
{
int tmp = 0;
Component temporaryLostComponent = null;
do{
if (forward == true)
{
forward();
} else {
back();
}
if (deltax[1] == 10-length[1])
{
forward = false;
} else if (deltax[1] == 0)
{
forward = true;
}
draw();
try
{
Thread.sleep((long) time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}while(press == false);
if (layer>12)
{
time= 150-(iteration*iteration*2-iteration);
} else
{
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0)
{
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
repeat();
}
last = deltax[1];
start = false;
go();
}
public int check()
{
if (start == true)
{
return length[1];
}
else if (last<deltax[1])
{
if (deltax[1]+length[1]-1 <= last+length[0]-1)
{
return length[1];
}
else
{
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
}
else if (last>deltax[1])
{
return length[1]-Math.abs(deltax[1]-last);
}
else
{
return length[1];
}
}
public void forward()
{
deltax[0] = deltax[1];
deltax[1]++;
}
public void back()
{
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw()
{
for (int x = 0;x<length[1];x++)
{
b[x+deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0;x<length[1];x++)
{
b[x+deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat()
{
if(JOptionPane.showConfirmDialog(null, "PLAY AGAIN?","WARNING",JOptionPane.YES_NO_OPTION)== JOptionPane.YES_OPTION)
{
dispose();
new Stacker();
}else{
System.exit(0);
}
}
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
press = true;
}
}
public void keyReleased(KeyEvent arg0)
{
}
public void keyTyped(KeyEvent arg0)
{
}
}
Just to put all my comments into an answer, and give you somewhere to start with:
Comment 1:
Take out go(); see that happens. I tested it and it will work. If you leave it there, even the frame's close button is jammed. You're blocking the edt with the while->Thread.sleep junk. You'll want to do some refactoring. You're code it hard to follow and I have no idea what you're trying to do, so I didn't even attempt it
Comment 2:
If you're wondering why it works when you just run the main from the Stacker class, it's probably because you are running it outside the EDT,
public static void main(String[] args) { new Stacker(); }. What happens when you click the button, that action is performed within the EDT, and hence your new Stacker() will be run on the EDT. In which case the EDT gets blocked by your while loop. If you try run the program from the Stacker class, but wrap it in a SwingUtilities.invokeLater, you will also notice the program fails to work. Swing programs should be run on the EDT though.
Comment 2: Read the first few sections on Concurrency with Swing
So what you can do is use a Swing Timer (which operates on the EDT) for the game loop. What I did was refactor your code a bit. It doesn't operate the way you want it to yet, only because I didn't really understand the logic of your code. So I couldn't get it to work. What I did though, is put some of the logic into the Timer.
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
And when the go() method is called, it just starts the timer by calling timer.start(). Basically what you need to know about the timer, is that every tick (the milliseconds you pass it), the actionPerformed will be called. So you can update the game state in that method, just like you did in the while loop each iteration.
Take some time to go over How to Use Swing Timers
To get the game working properly, you still need to make some adjustments, but this should give you a head start.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Sampling extends JFrame implements ActionListener {
private JButton openStacker;
Stacker st;
public Sampling() {
setSize(300, 300);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
setLocationRelativeTo(null);
openStacker = new JButton("Start Stacker!");
add(openStacker);
openStacker.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
dispose();
st = new Stacker();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Sampling();
}
});
}
}
class Stacker extends JFrame implements KeyListener {
int iteration = 1;
double time = 200;
int last = 0;
int m = 10;
int n = 20;
JButton b[][];
int length[] = {5, 5};
int layer = 19;
int deltax[] = {0, 0};
boolean press = false;
boolean forward = true;
boolean start = true;
Timer timer = new Timer((int)time, new ActionListener(){
public void actionPerformed(ActionEvent event) {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10 - length[1]) {
forward = false;
} else if (deltax[1] == 0) {
forward = true;
}
draw();
}
});
public Stacker() {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(400, 580);
this.setUndecorated(false);
this.setLocationRelativeTo(null);
b = new JButton[m][n];
setLayout(new GridLayout(n, m));
for (int y = 0; y < n; y++) {
for (int x = 0; x < m; x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.DARK_GRAY);
add(b[x][y]);
b[x][y].setEnabled(false);
}//end inner for
}
this.setFocusable(true);
this.pack();
JPanel panel = (JPanel)getContentPane();
panel.addKeyListener(this);
this.setVisible(true);
panel.requestFocusInWindow();
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
timer.start();
if (layer > 12) {
time = 150 - (iteration * iteration * 2 - iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
repeat();
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line " + (18 - layer) + "!");
repeat();
}
last = deltax[1];
start = false;
//go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last < deltax[1]) {
if (deltax[1] + length[1] - 1 <= last + length[0] - 1) {
return length[1];
} else {
return length[1] - Math.abs((deltax[1] + length[1]) - (last + length[0]));
}
} else if (last > deltax[1]) {
return length[1] - Math.abs(deltax[1] - last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0; x < length[1]; x++) {
b[x + deltax[0]][layer].setBackground(Color.DARK_GRAY);
}
for (int x = 0; x < length[1]; x++) {
b[x + deltax[1]][layer].setBackground(Color.CYAN);
}
}
public void repeat() {
if (JOptionPane.showConfirmDialog(null, "PLAY AGAIN?", "WARNING", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
dispose();
new Stacker();
} else {
System.exit(0);
}
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("Pressed");
press = true;
}
}
public void keyReleased(KeyEvent arg0) {
}
public void keyTyped(KeyEvent arg0) {
}
}
Notice the SwingUtilities.invokeLater in the main. That's how you can start up the program on the EDT. The link on Concurrency In Swing will give you more information.
I expected to see close to 200,000 lines (196,608) of output from the loop in this code. It only prints one line. Can anyone spot the blunder?
import java.awt.*;
import java.util.*;
import javax.swing.*;
class SwingWorkerUnicodeTest {
private String[] fontNameArray;
private JLabel output = new JLabel("Processing..");
private JProgressBar progressBar = new JProgressBar();
class CodePointDetailWorker extends SwingWorker<Object, Object> {
private ArrayList<Character.UnicodeBlock> unicodeBlockNames;
private ArrayList<Character.UnicodeScript> unicodeScripts;
private int[] glyphCount = new int[fontNameArray.length];
public CodePointDetailWorker() {
progressBar.setVisible(true);
Arrays.fill(glyphCount, 0);
}
#Override
protected Void doInBackground() throws Exception {
// Check for for the first 3 planes. The next 11 are unassigned
int pS = 3*65536;
for (int kk = 0; kk < pS; kk++) {
System.out.println("doInBackground " + kk + " " + pS);
doForEveryCodePoint(kk);
}
return null;
}
#Override
public void done() {
output.setText("Done!");
}
private final void doForEveryCodePoint(final int codePoint) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(codePoint);
if (block != null && !unicodeBlockNames.contains(block)) {
unicodeBlockNames.add(block);
}
Character.UnicodeScript us = Character.UnicodeScript.of(codePoint);
if (us == null || us.toString() == null) {
} else {
if (!unicodeScripts.contains(us)) {
unicodeScripts.add(us);
}
}
// fonts - test for points in all 6 defined blocks.
for (int ii = 0; ii < fontNameArray.length; ii++) {
Font f = new Font(fontNameArray[ii], Font.PLAIN, 16);
if (f.canDisplay(codePoint)) {
glyphCount[ii]++;
}
}
}
}
public SwingWorkerUnicodeTest() {
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
fontNameArray = ge.getAvailableFontFamilyNames();
JPanel gui = new JPanel(new BorderLayout());
gui.add(progressBar, BorderLayout.CENTER);
gui.add(output, BorderLayout.PAGE_END);
CodePointDetailWorker cpdw = new CodePointDetailWorker();
cpdw.execute();
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new SwingWorkerUnicodeTest();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Edit
Fixed code, based on the advice of the first 2 answers.
It now both implements the overridden method that reports errors, but initializes the arrays for ..much output and shows progress in the progress bar.
import java.awt.*;
import java.util.*;
import javax.swing.*;
class SwingWorkerUnicodeTest {
private JLabel output = new JLabel("Processing..");
// Check for for the first 3 planes. The next 11 are unassigned
int pS = 3 * 65536;
private JProgressBar progressBar = new JProgressBar(0, pS);
class CodePointDetailWorker extends SwingWorker<Object, Object> {
private ArrayList<Character.UnicodeBlock> unicodeBlockNames;
private ArrayList<Character.UnicodeScript> unicodeScripts;
private int[] glyphCount;
private String[] fontNameArray;
public CodePointDetailWorker(String[] fontNameArray) {
this.fontNameArray = fontNameArray;
progressBar.setVisible(true);
glyphCount = new int[fontNameArray.length];
Arrays.fill(glyphCount, 0);
unicodeBlockNames = new ArrayList<Character.UnicodeBlock>();
unicodeScripts = new ArrayList<Character.UnicodeScript>();
}
#Override
protected Void doInBackground() throws Exception {
for (int kk = 0; kk < pS; kk++) {
if (kk % 500 == 0) {
progressBar.setValue(kk);
}
doForEveryCodePoint(kk);
}
progressBar.setValue(0);
return null;
}
#Override
public void done() {
try {
get();
output.setText("Done!");
} catch (Exception ex) {
ex.printStackTrace();
output.setText("Bad: " + ex.getMessage());
}
}
private final void doForEveryCodePoint(final int codePoint) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(codePoint);
if (block != null && !unicodeBlockNames.contains(block)) {
unicodeBlockNames.add(block);
}
Character.UnicodeScript us = Character.UnicodeScript.of(codePoint);
if (us == null || us.toString() == null) {
} else {
if (!unicodeScripts.contains(us)) {
unicodeScripts.add(us);
}
}
// fonts - test for points in all 6 defined blocks.
for (int ii = 0; ii < fontNameArray.length; ii++) {
Font f = new Font(fontNameArray[ii], Font.PLAIN, 16);
if (f.canDisplay(codePoint)) {
glyphCount[ii]++;
}
}
}
}
public SwingWorkerUnicodeTest(String[] names) {
JPanel gui = new JPanel(new BorderLayout());
gui.add(progressBar, BorderLayout.CENTER);
gui.add(output, BorderLayout.PAGE_END);
CodePointDetailWorker cpdw = new CodePointDetailWorker(names);
cpdw.execute();
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = ge.getAvailableFontFamilyNames();
new SwingWorkerUnicodeTest(fontNames);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
There is, likely, an Exception being thrown within the doInBackground method, which is, obviously, causing it to exit.
In your done method, even if it returns nothing, you should call get to ensure that nothing went wrong during the execution of the doInBackground method
#Override
public void done() {
try {
get();
output.setText("Done!");
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
output.setText("Bad: " + ex.getMessage());
}
}
You're not initializing your ArrayLists
public CodePointDetailWorker() {
unicodeBlockNames = new ArrayList<>();
unicodeScripts = new ArrayList<>();
...
}
so a NPE is silently being thrown in the SwingWorker once this statement is encountered
if (block != null && !unicodeBlockNames.contains(block)) {
^---null