问题
I'm creating a virtual piano type program in Java Swing. My area for the piano keys right now is a JPanel with a horizontal BoxLayout containing white JButtons as white keys. I want to add black keys as well, and have them overlap the white keys.
There are two different approaches I've tried. One is using the OverlayLayout. Unfortunately there isn't much documentation online for the OverlayLayout manager, and it's not available in the NetBeans GUI builder. I don't have a clue how to make it work. The second thing I've tried is using JLayeredPanes. I can't seem to figure that one out either, even after messing with it in Netbeans.
So I think my question is pretty simple. What is the best approach, if there is one, to add JButtons on top of other JButtons? Or maybe there is an alternative to using JButtons for piano keys?
EDIT
I've combined aioobe's and dacwe's code to get the result I wanted. I basically used dacwe's z-ordering with aioobe's basic dimensions (scaled up a bit) and the mod 7 part too. I also added some variables to make things more clear. This is what I have now.
import javax.swing.*;
import java.awt.Color;
public class Test2 {
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
JLayeredPane panel = new JLayeredPane();
frame.add(panel);
int maxKeys = 8;
int width = 60;
int height = 240;
for (int i = 0; i < maxKeys; i++) {
JButton b = new JButton();
b.setBackground(Color.WHITE);
b.setLocation(i * width, 0);
b.setSize(width, height);
panel.add(b, 0, -1);
}
int width2 = 48;
int height2 = 140;
for (int i = 0; i < maxKeys; i++) {
int j = i % 7;
if (j == 2 || j == 6)
continue;
JButton b = new JButton();
b.setBackground(Color.BLACK);
b.setLocation(i*(width) + (width2*3/4), 0);
b.setSize(width2, height2);
panel.add(b, 1, -1);
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,280);
frame.setVisible(true);
}
}
Thanks guys! Now I need to attach the listener and text to these buttons somehow.
回答1:
I would write a custom PianoLayoutManager
and position the black keys with a higher z-order than the white buttons. Create your own "constraint" class that allows you to add components like this:
add(new WhiteKey(), new PianoLayoutConstraint(WHITE, 1);
add(new WhiteKey(), new PianoLayoutConstraint(WHITE, 2);
...
add(new WhiteKey(), new PianoLayoutConstraint(WHITE, n);
add(new BlackKey(), new PianoLayoutConstraint(BLACK, 1);
add(new BlackKey(), new PianoLayoutConstraint(BLACK, 2);
...
add(new BlackKey(), new PianoLayoutConstraint(BLACK, m);
From Using Swing Components tutorial trail
Note: The z-order determines the order that components are painted. The component with the highest z-order paints first and the component with the lowest z-order paints last. Where components overlap, the component with the lower z-order paints over the component with the higher z-order.
Here is an ugly hack that uses null-layout to get you started.
import java.awt.Color;
import javax.swing.*;
class PianoComponent extends JPanel {
PianoComponent() {
setLayout(null);
for (int i = 0; i < 20; i++) {
JButton key = new JButton();
key.setBackground(Color.WHITE);
key.setLocation(i * 20, 0);
key.setSize(20, 120);
add(key);
setComponentZOrder(key, i);
}
for (int i = 0; i < 20; i++) {
int j = i % 7;
if (j == 2 || j == 6)
continue;
JButton key = new JButton();
key.setBackground(Color.BLACK);
key.setLocation(i * 20 + 12, 0);
key.setSize(16, 80);
add(key);
setComponentZOrder(key, 0);
}
}
}
public class Test {
public static void main(String[] args) {
JFrame jf = new JFrame("Piano!");
jf.setSize(400, 200);
jf.add(new PianoComponent());
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
}
回答2:
This is an example of layeredpane (it works as you expect):
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
JLayeredPane panel = new JLayeredPane();
frame.add(panel);
for (int i = 0; i < 8; i++) {
JButton b = new JButton();
b.setBackground(Color.WHITE);
b.setLocation(i * 20, 0);
b.setSize(20, 100);
panel.add(b, 0, -1);
}
for (int i = 0; i < 4; i++) {
JButton b = new JButton();
b.setBackground(Color.BLACK);
b.setLocation(10 + i * 40, 0);
b.setSize(20, 70);
panel.add(b, 1, -1);
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setVisible(true);
}
However, you need to lay out the piano keys in the correct order (:)) :
回答3:
Apart from writing your own layout manager, you could just use a null
LayoutManager (that is, no automatic layout management) and position the keys manually. Then use setComponentZOrder
on the container to make sure the black keys appear above the white ones.
回答4:
GridBag layout does allow widget overlapping; You could use overlapping spans to construct the layout You want. But yes, writing own layout manager wold be nicer.
回答5:
I've done a couple games in Swing, and I find it's easier just not to use a layout manager or make your own. For example I have a card game and a custom layout manager that looks at where the card has been played and positions it based on that and the current panel size.
回答6:
Check out Darryl's solution (the last reply) in this posting. It gets tricky and uses a "Button" as the black button so it paints on top of the white "JButton". This probably won't work in new versions of the JDK that allow AWT and Swing components to be mixed.
However, the neat part is that the keys actually produce a sound when clicked.
来源:https://stackoverflow.com/questions/4280281/making-jbuttons-overlap