I need to create a virtual piano containing four octaves using JLayeredPane inside a JScrollPane such that one octave is shown initially, and can be scrolled horizontally to see the other octaves. My code displays only one octave and does not show the scrollbar and the other octaves. What is the problem with the following code?
class PianoLayout extends JScrollPane
{
public PianoLayout()
{
initComponents();
}
private void initComponents()
{
JLayeredPane layer = new JLayeredPane();
//ScrollableLayeredPane layer = new ScrollableLayeredPane();
layer.setSize(1120,150);
JButton[] keys = new JButton[48];
int keyIndex = 0, i;
for(i=0;i<28;i++)
{
keys[keyIndex] = createWhiteKey(i);
layer.add(keys[keyIndex], 0, -1);
keyIndex+=1;
if(i%7!=2 && i%7!=6)
{
keys[keyIndex] = createBlackKey(i);
layer.add(keys[keyIndex], 1, -1);
keyIndex+=1;
}
}
this.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.setViewportView(layer);
setSize(280, 150);
setLocation(110,100);
}
private JButton createWhiteKey(int i)
{
JButton whiteKey = new JButton();
whiteKey.setBackground(Color.WHITE);
whiteKey.setLocation(i*40,0);
whiteKey.setSize(40, 150);
return whiteKey;
}
private JButton createBlackKey(int i)
{
JButton blackKey = new JButton();
blackKey.setBackground(Color.BLACK);
blackKey.setLocation(25 + i*40,0);
blackKey.setSize(30, 90);
return blackKey;
}
}
public class VirtualPiano
{
public static void main(String[] args)
{
JPanel panel = new JPanel(null);
JFrame mainFrame = new JFrame();
PianoLayout pianoLayout = new PianoLayout();
mainFrame.add(panel);
panel.add(pianoLayout);
mainFrame.setSize(500,500);
mainFrame.setVisible(true);
}
Here is an example I found on a forum a long time ago. I know nothing about music so I don't understand how the logic works to create the sounds and keys.
But I did just change the code to implement the getPreferredSize()
method so that the scrollpane will work properly:
import java.awt.*;
import java.awt.event.*;
import javax.sound.midi.Instrument;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.*;
public class MidiPiano implements MouseListener {
final int OCTAVES = 4; // change as desired
private WhiteKey[] whites = new WhiteKey [7 * OCTAVES + 1];
private BlackKey[] blacks = new BlackKey [5 * OCTAVES];
MidiChannel channel;
public MidiPiano () {
try {
Synthesizer synth = MidiSystem.getSynthesizer ();
synth.open ();
synth.loadAllInstruments (synth.getDefaultSoundbank ());
Instrument [] insts = synth.getLoadedInstruments ();
MidiChannel channels[] = synth.getChannels ();
for (int i = 0; i < channels.length; i++) {
if (channels [i] != null) {
channel = channels [i];
break;
}
}
for (int i = 0; i < insts.length; i++) {
if (insts [i].toString ()
.startsWith ("Instrument MidiPiano")) {
channel.programChange (i);
break;
}
}
} catch (MidiUnavailableException ex) {
ex.printStackTrace ();
}
}
public void mousePressed (MouseEvent e) {
Key key = (Key) e.getSource ();
channel.noteOn (key.getNote (), 127);
}
public void mouseReleased (MouseEvent e) {
Key key = (Key) e.getSource ();
channel.noteOff (key.getNote ());
}
public void mouseClicked (MouseEvent e) { }
public void mouseEntered (MouseEvent e) { }
public void mouseExited (MouseEvent e) { }
private void createAndShowGUI () {
JPanel contentPane = new JPanel(null)
{
@Override
public Dimension getPreferredSize()
{
int count = getComponentCount();
Component last = getComponent(count - 1);
Rectangle bounds = last.getBounds();
int width = 10 + bounds.x + bounds.width;
int height = 10 + bounds.y + bounds.height;
return new Dimension(width, height);
}
@Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
for (int i = 0; i < blacks.length; i++) {
blacks [i] = new BlackKey (i);
contentPane.add (blacks [i]);
blacks [i].addMouseListener (this);
}
for (int i = 0; i < whites.length; i++) {
whites [i] = new WhiteKey (i);
contentPane.add (whites [i]);
whites [i].addMouseListener (this);
}
JFrame frame = new JFrame("Midi Piano");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
//frame.add( contentPane );
frame.add( new JScrollPane(contentPane) );
frame.pack();
frame.setLocationRelativeTo (null);
frame.setVisible(true);
}
public static void main (String[] args) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
new MidiPiano ().createAndShowGUI ();
}
});
}
}
interface Key {
// change WD to suit your screen
int WD = 16;
int HT = (WD * 9) / 2;
// change baseNote for starting octave
// multiples of 16 only
int baseNote = 48;
int getNote ();
}
class BlackKey extends JButton implements Key {
final int note;
public BlackKey (int pos) {
note = baseNote + 1 + 2 * pos + (pos + 3) / 5 + pos / 5;
int left = 10 + WD
+ ((WD * 3) / 2) * (pos + (pos / 5)
+ ((pos + 3) / 5));
setBackground (Color.BLACK);
setBounds (left, 10, WD, HT);
}
public int getNote () {
return note;
}
}
class WhiteKey extends JButton implements Key {
static int WWD = (WD * 3) / 2;
static int WHT = (HT * 3) / 2;
final int note;
public WhiteKey (int pos) {
note = baseNote + 2 * pos
- (pos + 4) / 7
- pos / 7;
int left = 10 + WWD * pos;
// I think metal looks better!
//setBackground (Color.WHITE);
setBounds (left, 10, WWD, WHT);
}
public int getNote () {
return note;
}
}
This is not a pure solution, because a custom layout manager really should be used to lay out the components and determine a preferred size.