Слушатель изменений создает помеху в JSlider

Я написал этот код для воспроизведения музыки, JSlider автоматически перемещается вперед с ходом музыки, я добавил прослушиватель изменений в JSlider для изменения музыкальной позиции с помощью курсора, проблема в том, что когда Nob JSlider перемещается с ходом музыки изменитьСлушатель(); метод также вызывается, это создает помехи в воспроизведении музыки, поэтому я хочу, чтобы changeListener(); метод должен вызываться только тогда, когда я перемещаю курсор JSlider. Подскажите пожалуйста как это можно сделать.

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


}

person user3808922    schedule 06.07.2014    source источник
comment
Не существует реального способа определить, почему был вызван метод stateChanged. Вы можете временно отключить прослушиватель, установив значение ползунка во время воспроизведения музыки. Вам также следует рассмотреть возможность использования Swing Timer вместо того, чтобы идти в туалет while, что снизит нагрузку на ЦП и EDT, а также обеспечит выполнение обновлений в контексте EDT.   -  person MadProgrammer    schedule 07.07.2014
comment
Есть ли прослушиватель курсора типа для JSlider???   -  person user3808922    schedule 07.07.2014
comment
Нет, как я уже сказал, невозможно отличить изменение, сделанное программой, от изменения, внесенного пользователем. По сути, они оба вызывают setValue...   -  person MadProgrammer    schedule 07.07.2014
comment
Вместо этого я адаптировал JProgressBar с MouseListener для использования в качестве точки перехода трека для плеера DukeBox. Он обходит стороной все проблемы с использованием JSlider.   -  person Andrew Thompson    schedule 07.07.2014


Ответы (1)


Событие stateChanged возникает всякий раз, когда изменяется базовое BoundedRangeModel. Это может быть по любому количеству причин. Проблема в том, что вы не знаете почему и, как правило, вам все равно.

В вашем случае у вас есть две (основные) ситуации, которые могут изменить модель. while-loop и пользователь.

Что вам нужно, так это какой-то способ изменить состояние таким образом, чтобы иметь возможность либо определить, кто вносит изменение, либо предотвратить уведомление об изменении при определенных обстоятельствах.

В этом (простом) примере я использую простые флаги, чтобы указать, где происходят обновления, и запретить модифицирующему элементу обновлять модель до тех пор, пока не завершится другой. Это работает здесь, потому что я использовал Swing Timer для выполнения обновлений прогрессии, это гарантирует, что как «временное» обновление, так и пользовательские обновления происходят в контексте одного и того же потока. Это обеспечивает определенный уровень защиты, поскольку Timer не может попытаться изменить состояние пользовательского интерфейса, пока выполняется метод stateChanged.

Это также означает, что когда событие stateChanged вызывается из кода "time progress", оно игнорируется нашим обработчиком stateChanged, поэтому оно не будет мешать текущей позиции 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();
            }
        }
    }

}
person MadProgrammer    schedule 07.07.2014