2012-03-26 73 views
1

我正在用Java制作钢琴应用程序。这其中的功能之一,doClick不释放键直到循环结束

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

} 

编辑:

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

的:做了一些与穿线工作之后,这是我

通过将for循环在另一个线程,该键在合适的时间发布问题现在是键盘故障。键盘是使用分层窗格创建的,出于某种原因,当键被释放时,应该位于底部的图层到达顶部。当我将鼠标悬停在他们身上时,故障消失。有任何想法吗?

编辑2:我修正了毛刺。我只需要添加

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

doClick();

回答

1

你的方法存在的问题是你阻塞了事件线程。此线程负责用户输入,绘画和窗口更新。我的猜测是,现在doClick的超时被事件线程(看起来是逻辑)检查,所以它不会被释放,直到你的actionPerformed方法退出(所以事件线程可以继续其事件处理)。

解决此问题的方法是(如您已经提到的)将for循环移至另一个线程,并使用SwingUtilities.invokeLater调用doClick

+0

如果for循环处于不同的线程中,并且该线程内调用了doClick,为什么还需要使用invokeLater调用?其他线程中的所有内容都会自动异步发生,对吗? – gsingh2011 2012-03-26 16:35:18

+0

Java Swing不是线程安全的(就像大多数的GUI框架一样)。'invokeLater'是从其他线程更新Swing组件的首选方法,它只是确保您提供给'invokeLater'的'Runnable'在事件线程中执行。 – Neet 2012-03-27 15:48:02

+0

你可以看看我在底部做的编辑吗?我把循环放在'Runnable'中,尽管它现在可以工作,但键很差。使用invokeLater只会让问题变得更糟,因为不允许释放密钥。 – gsingh2011 2012-03-29 20:33:14