doClick不会释放键,直到循环结束

时间:2012-03-26 04:02:28

标签: java onclick click

我正在用Java制作钢琴应用程序。这是其中一项功能,

public void playOnce(int time) {
    play();
    doClick(time);
    stop();
}

public void play() {
    channel[0].noteOn(note, 60);
}

public void stop() {
    channel[0].noteOff(note);
}

如果有必要,我会提供一个最小的工作示例,但我想确保它不是一个明显的问题。问题是playOnce是在while循环中调用的。 playOnce在Key类中,每个Key都有不同的注释。在while循环的每次迭代中,在不同的键上调用playOnce。一旦所有键都被播放,它就会停止。

doClick方法正确按下了键,但是在所有键都被播放之前它不会被释放。事实上,在正在播放按键时,即使按下暂停按钮也无法执行任何操作。对于这个问题,我想我可以将整个循环放在不同的线程中,但我不认为这种解决方案会允许释放密钥。

编辑:是的,我发现我需要一个新线程才能让其他操作正常工作,但我仍然需要修复doClick()。这可能比我想象的要复杂得多,这是一个有效的例子,

Main.java

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;

public class Main implements ActionListener {

    final int WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT, BLACK_KEY_WIDTH,
            BLACK_KEY_HEIGHT;
    final int WIDTH;
    final JFileChooser fc;
    {
        WHITE_KEY_WIDTH = Key.WHITE_KEY_WIDTH;
        BLACK_KEY_WIDTH = Key.BLACK_KEY_WIDTH;
        WHITE_KEY_HEIGHT = Key.WHITE_KEY_HEIGHT;
        BLACK_KEY_HEIGHT = Key.BLACK_KEY_HEIGHT;
        WIDTH = 3 * (WHITE_KEY_WIDTH * 7) + WHITE_KEY_WIDTH;
        fc = new JFileChooser();
    }

    public static Key keys[] = new Key[48];
    private static int index = 0;
    private String prevText = "";

    JTextArea shabadEditor = null;
    JSpinner tempoControl;
    JFrame frame;
    File curFile;

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        frame = new JFrame();

        JPanel mainPanel = new JPanel();
        JPanel controlPanel = new JPanel();
        JLayeredPane pianoPanel = new JLayeredPane();

        mainPanel.setLayout(new GridBagLayout());

        JButton playButton = new JButton("Play");
        JButton pauseButton = new JButton("Pause");

        playButton.addActionListener(this);
        playButton.setActionCommand("play");

        pauseButton.addActionListener(this);
        pauseButton.setActionCommand("pause");

        SpinnerNumberModel model = new SpinnerNumberModel(1, 0, 2, .1);
        tempoControl = new JSpinner(model);
        JSpinner.NumberEditor editor = (JSpinner.NumberEditor) tempoControl
                .getEditor();
        DecimalFormat format = editor.getFormat();
        format.setMinimumFractionDigits(1);
        Dimension d = tempoControl.getPreferredSize();
        d.width = 40;
        tempoControl.setPreferredSize(d);

        GridBagConstraints c = new GridBagConstraints();
        // Construct each top level component
        controlPanel.add(playButton);
        controlPanel.add(pauseButton);
        controlPanel.add(tempoControl);
        shabadEditor = new JTextArea(20, 78);
        constructKeyboard(pianoPanel);

        // Add the piano panel and shabad editor to the window
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(controlPanel, c);

        c.gridx = 0;
        c.gridy = 1;
        c.weightx = 1.0;
        // c.weighty = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        pianoPanel
                .setPreferredSize(new Dimension(WIDTH - 18, WHITE_KEY_HEIGHT));
        mainPanel.add(pianoPanel, c);

        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(shabadEditor, c);
        frame.add(mainPanel);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH, WHITE_KEY_HEIGHT * 3 + 30);
        frame.setLocation(250, 60);
        frame.setVisible(true);
    }

    void constructKeyboard(Container panel) {
        int i = 0;
        int j = 0;

        for (int k = 0; k < 3; k++) {
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addWhiteKey(panel, i++);
            j++;
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            j++;
            addWhiteKey(panel, i++);
        }
    }

    void addWhiteKey(Container panel, int i) {
        WhiteKey b = new WhiteKey();
        b.setLocation(i++ * WHITE_KEY_WIDTH, 0);
        panel.add(b, 0, -1);
        keys[index++] = b;
    }

    void addBlackKey(Container panel, int factor) {
        BlackKey b = new BlackKey();
        b.setLocation(WHITE_KEY_WIDTH - BLACK_KEY_WIDTH / 2 + factor
                * WHITE_KEY_WIDTH, 0);
        panel.add(b, 1, -1);
        keys[index++] = b;
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        String action = arg0.getActionCommand();

        if (action.equals("play")) {
            System.out.println("working");
            for (int i = 0; i < 10; i++) {
                keys[i].playOnce(500);
            }
        }
    }
}

Key.java

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.JButton;

public class Key extends JButton implements MouseListener {

    private static final long serialVersionUID = 1L;

    public static final int WHITE_KEY_HEIGHT = 200;
    public static final int WHITE_KEY_WIDTH = 40;
    public static final int BLACK_KEY_WIDTH = 20;
    public static final int BLACK_KEY_HEIGHT = 120;

    private static int noteCount = 40;
    public int note;

    private static Synthesizer synth = null;

    static {
        try {
            synth = MidiSystem.getSynthesizer();
            synth.open();
        } catch (MidiUnavailableException e) {
            e.printStackTrace();
        }
    }
    MidiChannel channel[];

    public Key() {
        note = noteCount++;

        // Instrument[] instruments = synth.getAvailableInstruments();
        // for (Instrument instrument : instruments) {
        // System.out.println(instrument.getName());
        // System.out.println(instrument.getPatch().getBank());
        // System.out.println(instrument.getPatch().getProgram());
        // }

        channel = synth.getChannels();
        channel[0].programChange(20);
        addMouseListener(this);
    }

    public void playOnce(int time) {
        play();
        doClick(time);
        stop();
    }

    public void play() {
        channel[0].noteOn(note, 60);
    }

    public void stop() {
        channel[0].noteOff(note);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println(this.note);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent e) {
        play();

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        stop();
    }

}

BlackKey.java

import java.awt.Color;

public class BlackKey extends Key {

    private static final long serialVersionUID = 1L;

    public BlackKey() {
        super();
        setBackground(Color.BLACK);
        setSize(BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT);
    }
}

WhiteKey.java

import java.awt.Color;

public class WhiteKey extends Key {

    private static final long serialVersionUID = 1L;

    public WhiteKey() {
        super();
        setBackground(Color.WHITE);
        setSize(WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT);
    }

}

编辑:在完成一些线程工作之后,这就是我所拥有的

通过将for循环放在另一个线程中,键会在正确的时间释放:

@Override
    public void actionPerformed(ActionEvent arg0) {
        String action = arg0.getActionCommand();

        if (action.equals("play")) {
            System.out.println("working");
            new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        keys[i].playOnce(100);
                    }
                }
            }).start();
        }
    }
}

现在的问题是键盘故障。键盘是使用分层窗格创建的,由于某些原因,当释放键时,应该位于底部的图层将显示在顶部。当我将鼠标悬停在它们上面时,故障消失了。有什么想法吗?

EDIT2:我修复了故障。我只需要添加

     try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

在doClick();

之后

1 个答案:

答案 0 :(得分:1)

您的方法存在的问题是您正在阻止事件线程。该线程负责用户输入,绘画和窗口更新。我的猜测是,doClick的超时在事件线程中被检查(似乎是逻辑的),因此在你的actionPerformed方法退出之前它不会被释放(因此事件线程可以继续它的事件处理)。

解决此问题的方法是(如您所述)将for循环移至另一个主题并使用SwingUtilities.invokeLater致电doClick