I searched on the internet if there is a proper way of making a piano in Java Swing. But either they had gaps between the black keys or they didn't explain how they've done it.
I tried using a JPanel with a null-layout and adding the white keys (Jpanels or Jbuttons) with a MouseListener first and then adding the black keys so they should be above the whites. The problem is that it isn't very elegant code and besides that, it doesn't work.
Does anyone know how to make a Piano in Java?
Here's my code:
package me.Trainer.Piano;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
import me.Trainer.Enums.Note;
public class PianoGraphics {
static volatile Note result = null;
public static JPanel getDrawnKeyboard() {
JPanel panel = new JPanel() {
private static final long serialVersionUID = 502433120279478947L;
Dimension lastFrame;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = this.getWidth();
int height = this.getHeight();
if (lastFrame != this.getSize()) {
this.removeAll();
JPanel white = new JPanel() {
private static final long serialVersionUID = 2350489085544800839L;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.LIGHT_GRAY);
g.drawRect(0, 0, this.getWidth(), this.getHeight());
};
};
white.setBackground(Color.WHITE);
white.setSize(width / 52, height);
for (int i = 0; i < 52; i++) {
Note note;
int oct = (int) i / 7;
switch(i % 7) {
case 0:
note = Note.values()[0 + (oct * 12)];
break;
case 1:
note = Note.values()[2 + (oct * 12)];
break;
case 2:
note = Note.values()[3 + (oct * 12)];
break;
case 3:
note = Note.values()[5 + (oct * 12)];
break;
case 4:
note = Note.values()[7 + (oct * 12)];
break;
case 5:
note = Note.values()[8 + (oct * 12)];
break;
case 6:
note = Note.values()[10 + (oct * 12)];
break;
default:
note = Note.C4;
}
white.setLocation(i * (width / 52), 0);
white.addMouseListener(new KeyboardMouseListener() {
Note n = note;
@Override
public void mouseReleased(MouseEvent e) {
white.setBackground(Color.WHITE);
result = null;
}
@Override
public void mouseClicked(MouseEvent e) {
white.setBackground(Color.LIGHT_GRAY);
result = n;
}
});
this.add(white);
}
JPanel black = new JPanel() {
private static final long serialVersionUID = 8445848892107864631L;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.DARK_GRAY);
g.drawRect(0, 0, this.getWidth(), this.getHeight());
};
};
black.setBackground(Color.BLACK);
black.setSize(width / 108, height / 3 * 2);
for (int i = 0; i < 7; i++) {
Note note = Note.values()[1 + (i*12)];
JPanel b = black;
b.setLocation(i*12*8 + 7, 0);
b.addMouseListener(new KeyboardMouseListener() {
public void mouseClicked(MouseEvent e) {
b.setBackground(Color.DARK_GRAY);
result = note;
};
public void mouseReleased(MouseEvent e) {
b.setBackground(Color.BLACK);
result = null;
System.out.println(note.name());
};
});
this.add(b);
JPanel b1 = black;
Note note1 = Note.values()[1 + (i*12)];
b1.setLocation(i*12*8 + 21, 0);
b1.addMouseListener(new KeyboardMouseListener() {
public void mouseClicked(MouseEvent e) {
b1.setBackground(Color.DARK_GRAY);
result = note1;
System.out.println(note1.name());
};
public void mouseReleased(MouseEvent e) {
b1.setBackground(Color.BLACK);
result = null;
};
});
this.add(b1);
JPanel b2 = black;
Note note2 = Note.values()[1 + (i*12)];
b2.setLocation(i*12*8 + 30, 0);
b2.addMouseListener(new KeyboardMouseListener() {
public void mouseClicked(MouseEvent e) {
b2.setBackground(Color.DARK_GRAY);
result = note2;
};
public void mouseReleased(MouseEvent e) {
b2.setBackground(Color.BLACK);
result = null;
};
});
this.add(b2);
JPanel b3 = black;
Note note3 = Note.values()[1 + (i*12)];
b3.setLocation(i*12*8 + 45, 0);
b3.addMouseListener(new KeyboardMouseListener() {
public void mouseClicked(MouseEvent e) {
b3.setBackground(Color.DARK_GRAY);
result = note3;
};
public void mouseReleased(MouseEvent e) {
b3.setBackground(Color.BLACK);
result = null;
};
});
this.add(b3);
JPanel b4 = black;
Note note4 = Note.values()[1 + (i*12)];
b4.setLocation(i*12*8 + 53, 0);
b4.addMouseListener(new KeyboardMouseListener() {
public void mouseClicked(MouseEvent e) {
b4.setBackground(Color.DARK_GRAY);
result = note4;
};
public void mouseReleased(MouseEvent e) {
b4.setBackground(Color.BLACK);
result = null;
};
});
this.add(b4);
}
}
lastFrame = this.getSize();
}
};
panel.setLayout(null);
return panel;
}
public static Note waitForNote() {
while (result == null) {}
Note note = result;
result = null;
return note;
}
}
class KeyboardMouseListener implements MouseListener {
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
}
And here's what I get: Nothing is clickable
You can use the Swing
Shapeinterfaces, in particularjava.awt.geom.Path2Dto draw arbitrary shapes and also do click testing. I once wrote a Swing MIDI piano using this:I think it would be quite difficult to post the full program because it's entangled with some of my utility classes, and you presumably have your own design you want to build anyway. But here is the source of the graphical "Keyboard" component, which has no dependencies:
I haven't looked at this code in years but here's the basic idea: The entire keyboard is one component. It generates a list of
Shapeobjects for the keys, and uses the shapes both for painting the keys and click testing (add yourMouseListenerandMouseMotionListenerwhich callgetKeyAtPoint). There are two advantages to doing the keyboard as one component, rather than separate buttons. One is that you can do completely arbitrary shape boundaries, rather than just rectangles. The other is that you can drag/glide the mouse straight along the keyboard (which doesn't work with separate buttons).