问题
I have writen this code for playing music, the JSlider moves automatically forward with the progress of music, I have add change listener to JSlider for changing the music position with cursor, the problem is that when the nob of JSlider moves with the progress of music changeListener(); method is also invoked, this create hindrance in playing music, so therefore I want that the changeListener(); method should be invoked only when I move the nob of JSlider with cursor. Please tell how it can be done.
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class A implements ChangeListener {
Clip clip;
public A() throws Exception {
JFrame f = new JFrame();
f.setSize(800,400);
f.setVisible(true);
f.setLayout(null);
URL url = this.getClass().getClassLoader().getResource("jal pari.wav");
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioIn);
int x = (int)(clip.getMicrosecondLength()/1000000);
JSlider s = new JSlider(JSlider.HORIZONTAL,0,x,0);
s.addChangeListener(this);
s.setBounds(50,50,800,100);
f.add(s);
clip.start();
while( clip.getMicrosecondPosition()!=clip.getMicrosecondLength() ) {
s.setValue((int)(clip.getMicrosecondPosition()/1000000));
}
}
public static void main(String arg[]) throws Exception {
new A();
}
public void stateChanged(ChangeEvent e) {
JSlider js = (JSlider)e.getSource();
int v = js.getValue();
clip.setMicrosecondPosition(v*1000000);
}
}
回答1:
A stateChanged
event is raised whenever the underlying BoundedRangeModel
is changed. This could be for any number of reasons. The problem is, you don't know why and, generally, don't care.
In your case, you have two (main) situations that might change the model. The while-loop
and the user.
What you need is some way to alter the state in such away as to be able to either detect who is making the change or prevent the notification of the change under certain circumstances.
In this (simple) example, I use to simple flags to indicate where the updates are occurring and stop either modifying element from updating the model until the other has finished. This works here because I've used a Swing Timer
to perform the progression updates, this ensures that both the "timed" update and the user updates are occurring within the context of the same thread. This provides a level of protection, as it's impossible for the Timer
to try and change the state of the UI while the stateChanged
method is been run.
It also means that when a stateChanged
event is raised from the "time progress" code, it is ignored by our stateChanged
handler, so it won't mess with the current position of the Clip
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class MusicPlayer {
public static void main(String[] args) {
new MusicPlayer();
}
public MusicPlayer() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JSlider slider;
private Clip clip;
private Timer updateTimer;
private boolean timerUpdate = false;
private boolean userUpdate = false;
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
slider = new JSlider();
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(10);
slider.setPaintTicks(true);
slider.setValue(0);
add(slider);
updateTimer = new Timer(100, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (clip != null) {
if (!userUpdate) {
timerUpdate = true;
try {
long length = TimeUnit.NANOSECONDS.convert(clip.getMicrosecondLength(), TimeUnit.SECONDS);
long time = TimeUnit.NANOSECONDS.convert(clip.getMicrosecondPosition(), TimeUnit.SECONDS);
int progress = (int) Math.round(((double) time / (double) length) * 100d);
slider.setValue(progress);
} finally {
timerUpdate = false;
}
}
}
}
});
updateTimer.start();
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (!timerUpdate && !userUpdate) {
userUpdate = true;
try {
long length = clip.getMicrosecondLength();
int progress = slider.getValue();
long time = (long) (length * (progress / 100d));
clip.setMicrosecondPosition(time);
} finally {
userUpdate = false;
}
}
}
});
try {
File source = new File("\\...\\Kalimba.wav");
AudioInputStream audioIn = AudioSystem.getAudioInputStream(source);
clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException exp) {
exp.printStackTrace();
}
}
}
}
来源:https://stackoverflow.com/questions/24600159/change-listener-creates-hinderance-in-jslider