資訊
閱讀了幾個相關的問題后,我想我在這里有一些獨特的情況。
我正在構建一個 Java swing 應用程式來幫助鼓手制作簡單的速記歌曲圖表。有一個對話框,用戶可以在其中“鍵入”節奏,將其錄制到 MIDI 序列中,然后處理成指法譜或樂譜。這旨在與歌曲的短片段一起使用。
設定
這個想法是,當系結的 JButton 在記錄序列時觸發它們的動作時,它們將生成一個帶有時間資訊的 MidiMessage。我還希望這些按鈕能夠直觀地表明它們已被激活。
系結的鍵當前使用我實作的鍵系結正確觸發(同時按鍵除外)...
問題
將同時的按鍵注冊為一個事件很重要——這里的時間很重要。
因此,例如,如果用戶同時按下H(踩镲)和S(小軍鼓),它將在小節中的同一位置注冊為齊聲打擊。
我曾嘗試使用類似于此的 KeyListener 實作:
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
enum UserAction {
CRASH_HIT, RIDE_HIT, HI_HAT_HIT, RACK_TOM_HIT, SNARE_HIT, FLOOR_TOM_HIT, KICK_DRUM_HIT;
}
public interface Observer {
public void didActivateAction(UserAction action);
public void didDeactivateAction(UserAction action);
}
private Map<UserAction, JLabel> labels;
private Set<UserAction> activeActions = new TreeSet<>();
private final Set<UserAction> allActions = new TreeSet<>(Arrays.asList(UserAction.values()));
public TestPane() {
labels = new HashMap<>();
for (UserAction action : UserAction.values()) {
JLabel label = new JLabel(action.name());
label.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY), new EmptyBorder(8, 8, 8, 8)));
label.setOpaque(true);
add(label);
labels.put(action, label);
}
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
Observer observer = new Observer() {
@Override
public void didActivateAction(UserAction action) {
if (activeActions.contains(action)) {
// We don't want to deal with "repeated" key events
return;
}
activeActions.add(action);
// I could update the labels here, but this is a deliberate
// example of how to decouple the action from the state
// so the actions can be dealt with in as a single unit
// of work, you can also take into consideratoin any
// relationships which different inputs might have as well
updateUIState();
}
@Override
public void didDeactivateAction(UserAction action) {
activeActions.remove(action);
updateUIState();
}
};
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, false), "pressed-crashHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, true), "released-crashHit");
actionMap.put("pressed-crashHit", new InputAction(UserAction.CRASH_HIT, true, observer));
actionMap.put("released-crashHit", new InputAction(UserAction.CRASH_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0, false), "pressed-rideHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0, true), "released-rideHit");
actionMap.put("pressed-rideHit", new InputAction(UserAction.RIDE_HIT, true, observer));
actionMap.put("released-rideHit", new InputAction(UserAction.RIDE_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_H, 0, false), "pressed-hihatHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_H, 0, true), "released-hihatHit");
actionMap.put("pressed-hihatHit", new InputAction(UserAction.HI_HAT_HIT, true, observer));
actionMap.put("released-hihatHit", new InputAction(UserAction.HI_HAT_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0, false), "pressed-racktomHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0, true), "released-racktomHit");
actionMap.put("pressed-racktomHit", new InputAction(UserAction.RACK_TOM_HIT, true, observer));
actionMap.put("released-racktomHit", new InputAction(UserAction.RACK_TOM_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "pressed-snareHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "released-snareHit");
actionMap.put("pressed-snareHit", new InputAction(UserAction.SNARE_HIT, true, observer));
actionMap.put("released-snareHit", new InputAction(UserAction.SNARE_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F, 0, false), "pressed-floortomHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F, 0, true), "released-floortomHit");
actionMap.put("pressed-floortomHit", new InputAction(UserAction.FLOOR_TOM_HIT, true, observer));
actionMap.put("released-floortomHit", new InputAction(UserAction.FLOOR_TOM_HIT, false, observer));
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "pressed-kickdrumHit");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "released-kickdrumHit");
actionMap.put("pressed-kickdrumHit", new InputAction(UserAction.KICK_DRUM_HIT, true, observer));
actionMap.put("released-kickdrumHit", new InputAction(UserAction.KICK_DRUM_HIT, false, observer));
}
protected void updateUIState() {
Set<UserAction> inactiveActions = new TreeSet<>(allActions);
inactiveActions.removeAll(activeActions);
for (UserAction action : inactiveActions) {
JLabel label = labels.get(action);
label.setBackground(null);
label.setForeground(Color.BLACK);
}
for (UserAction action : activeActions) {
JLabel label = labels.get(action);
label.setBackground(Color.BLUE);
label.setForeground(Color.WHITE);
}
}
// This could act as a base class, from which other, more dedicated
// implementations could be built, which did focused jobs, for example
// protected class ActivateCrashHit extends InputAction {
// public ActivateCrashHit(Observer observer) {
// super(UserAction.CRASH_HIT, true, observer);
// }
// // Override actionPerformed
// }
protected class InputAction extends AbstractAction {
private UserAction action;
private boolean activated;
private Observer observer;
public InputAction(UserAction action, boolean activated, Observer observer) {
this.action = action;
this.activated = activated;
this.observer = observer;
}
@Override
public void actionPerformed(ActionEvent e) {
// This could perform other actions, but the intention of the
// observer is provide an oppurunity for the interested party
// to also make some kind of update, to allow the user to
// see that that action occured
if (activated) {
observer.didActivateAction(action);
} else {
observer.didDeactivateAction(action);
}
}
}
}
}
您還應該注意,某些鍵盤存在硬體限制,這限制了可以在任何時候按下的同時鍵的數量,盡管說實話,我發現很難按此示例中的所有鍵一次大大地
uj5u.com熱心網友回復:
我個人會使用 KeyListener 介面來跟蹤用戶鍵入的內容,然后只需在 List 中注冊已按下(不釋放)的鍵,然后在釋放任何鍵后收集它們。
確保僅將尚未出現的鍵添加到串列中,因為在用戶按住鍵時會多次觸發 keyPressed 事件。
我還創建了一個小樣本來給你這個想法。
public class MyClass extends JFrame implements KeyListener {
private JTextArea textArea;
private List<Character> listKeys;
public MyClass() {
setTitle("test");
listKeys = new ArrayList<>();
textArea = new JTextArea();
textArea.addKeyListener(this);
setLayout(new BorderLayout());
add(textArea, BorderLayout.CENTER);
setLocation(50, 50);
setSize(500, 500);
setVisible(true);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if (!listKeys.contains(e.getKeyChar())) {
listKeys.add(e.getKeyChar());
}
}
@Override
public void keyReleased(KeyEvent e) {
if (listKeys.isEmpty()) {
return;
}
if (listKeys.size() > 1) {
System.out.print("The key combination ");
} else {
System.out.print("The key ");
}
for (Character c : listKeys) {
System.out.print(c " ");
}
System.out.println("has been entered");
listKeys.clear();
}
public static void main(String[] args) {
new MyClass();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/450060.html
上一篇:如何使用屬性設定注釋值
下一篇:它不會畫線
