Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making JButtons overlap

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.

like image 285
aharlow Avatar asked Nov 25 '10 19:11

aharlow


3 Answers

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

enter image description here

like image 167
aioobe Avatar answered Nov 06 '22 19:11

aioobe


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 (:)) :

alt text

like image 30
dacwe Avatar answered Nov 06 '22 19:11

dacwe


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.

like image 1
jackrabbit Avatar answered Nov 06 '22 18:11

jackrabbit