Java 更改侦听器在JSlider中创建障碍

Java 更改侦听器在JSlider中创建障碍,java,swing,clip,jslider,Java,Swing,Clip,Jslider,我写了这个播放音乐的代码,JSlider随着音乐的进程自动向前移动,我在JSlider中添加了change listener,用光标改变音乐的位置,问题是当JSlider的nob随着音乐的进程而移动时changeListener();方法也被调用,这会在播放音乐时造成障碍,因此我希望changeListener();方法只应在使用光标移动JSlider的nob时调用。请告诉我怎么做 import java.net.URL; import javax.sound.sampled.AudioInpu

我写了这个播放音乐的代码,JSlider随着音乐的进程自动向前移动,我在JSlider中添加了change listener,用光标改变音乐的位置,问题是当JSlider的nob随着音乐的进程而移动时changeListener();方法也被调用,这会在播放音乐时造成障碍,因此我希望changeListener();方法只应在使用光标移动JSlider的nob时调用。请告诉我怎么做

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);
    }


}

只要基础
BoundedRangeModel
发生更改,就会引发
stateChanged
事件。这可能有很多原因。问题是,你不知道为什么,一般来说,你也不在乎

在您的案例中,有两种(主要)情况可能会改变模型。
while循环
和用户

您需要的是某种方式来改变状态,以便在某些情况下能够检测到谁在进行更改或阻止通知更改

在这个(简单的)示例中,我使用简单标志来指示更新发生的位置,并停止其中一个修改元素更新模型,直到另一个完成。这在这里起作用是因为我使用了Swing
计时器来执行进程更新,这确保了“定时”更新和用户更新都发生在同一线程的上下文中。这提供了一定程度的保护,因为在运行
stateChanged
方法时,
计时器不可能尝试更改UI的状态

这还意味着当从“时间进度”代码引发
stateChanged
事件时,我们的
stateChanged
处理程序会忽略它,因此它不会干扰
剪辑的当前位置

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();
            }
        }
    }

}

没有真正的方法来确定调用stateChanged方法的原因。在播放音乐时设置滑块的值时,可以暂时禁用侦听器。你也应该考虑使用Swing定时器来代替你的LO,这将减少CPU和EDT上的负载,并确保在EDIS上下文中完成更新。JSlider的任何类型的光标监听器??无法区分程序所做的更改和用户所做的更改之间的区别。本质上,它们都调用setValue…我改为使用带有鼠标听器的
JProgressBar
,作为一个名为DukeBox的玩家的轨迹“跳转到”点。它回避了使用
JSlider
的所有问题。