简体   繁体   中英

How can I restore the state of my GUI to its original appearance?

I wrote a GUI in Java for a guitar chord finder application using 2D graphics. The program opens an .jpg image on the canvas. It then draws each individual note (space between the frets) as an ellipse with the name of the note. The program allows the user to select chords from a toolbar where they are displayed on the the fretboard by changing the color of the chord's individual notes. However, whenever the user selects a new chord, the previous chord is not deleted. How can I fix this? Here is some of my code (The program is over 1000 lines of code).

    public class Fretboard extends JFrame  implements ActionListener{
        public static void main(String[] args) {
            JFrame frame = new Fretboard();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        }
        // Variables to be used throughout the program
        ImagePanel imageSrc;                                    
        // Declare fonts to be used
        Font font1 = new Font("SansSerif", Font.BOLD, 18);          // Font to be used for notes with # or b
        Font chordFont = new Font("SansSerif", Font.BOLD, 50);      // Font for the name of the chord displayed
        Font font = new Font("SansSerif", Font.BOLD, 20);           // Font to be used for whole note

        int h = 40, w = 26, x = 695, y = 254;

        // Declare the note variables
        // First string
        Ellipse2D E1 = new Ellipse2D.Double(x, y-110, w, h);        // E note, open 1st string
        Ellipse2D F1 = new Ellipse2D.Double(x, y, w, h);            // F note, 1st string, 1st fret
        Ellipse2D fSharp1 = new Ellipse2D.Double(x, y+125, w, h);   // F#/Gb note, 1st string, 2nd fret
        Ellipse2D G1 = new Ellipse2D.Double(x+2, y+240, w, h);      // G note, 1st string, 3rd fret

        /**
         * Create the menu bar and set title
         */

        public Fretboard() {
            // Change the title of the window
            setTitle("Fretboard Chord Finder");
            // Create a menu bar where user will be given choice of chords
            JMenuBar mb = new JMenuBar();
            setJMenuBar(mb);

            JMenu menu = new JMenu("Chords");
            // Add names of chords to the menu
            JMenuItem mi = new JMenuItem("A Major");
            mi.addActionListener(this);
            menu.add(mi);
            mi = new JMenuItem("A Minor");
            mi.addActionListener(this);
            menu.add(mi);

            Container cp = this.getContentPane();
            cp.setLayout(new FlowLayout());
            imageSrc = new ImagePanel();
            cp.add(imageSrc);
        }


        /**
         * Obtain the user's chord selection from the chord menu
         */
        public void actionPerformed(ActionEvent e) {
            String command = e.getActionCommand();
            if("A Major".equals(command))
                paintAMajor();
            if("A Minor".equals(command))
                paintAMinor();
            }

    /**
     * Displays the notes for the A Major chord when the user selects
     * "A Major" from the toolbar.
     */
    public void paintAMajor() {
        // Declare local variables
        Graphics g = getGraphics();
        Graphics2D g2 = (Graphics2D) g;

        // Display the name of the chord
        g2.drawString("A Major Chord", 40, 150);
        g2.drawString("Notes: A, C#, E", 40, 180);

        // Display notes for the A Major chord
        // Draw the E note on the open 1st string
        // Change color to blue
        g2.setColor(Color.red);
        g2.draw(E1);
        g2.fill(E1);
        g2.setColor(Color.white);
        g2.setFont(font);
        g2.drawString("E", x+7, y-82);
        // Change color back to red
        g2.setColor(Color.red);
    }

class ImagePanel extends JPanel {
    BufferedImage image = null; 

    public ImagePanel() {
        File fretBoardFile = new File("/Users/macbook/documents/workspace/Fretboard App/Gibson_Fretboard.jpg");     // The location of the fretboard image
        // Open the image
        try {
            image = ImageIO.read(fretBoardFile);
        } 
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        setPreferredSize(new Dimension(1280, 960));
    }

    /**
     * 
     * @param bi
     */
    public ImagePanel(BufferedImage bi) {
        image = bi; 
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Draw the image of the fretboard on the canvas.
        // Check to see if the image is available
        if(image != null) {
            g2.drawImage(image, 25, 0, null);
        }
        else
            g2.drawRect(0, 0, getWidth()-1, getHeight()-1);

        // Draw notes
        // Draw the E note on the open 1st string
        // Change color to blue
        g2.setColor(Color.blue);
        g2.draw(E1);
        g2.fill(E1);
        g2.setColor(Color.white);
        g2.setFont(font);
        g2.drawString("E", x+7, y-82);
        // Change color back to blue
        g2.setColor(Color.blue);
}

This is the gist of the program. Everything else is basically the placement of each individual notes or similar methods to display the chords. I am stuck and have no idea how to fix this program. This is the first GUI I have ever programmed as well. Please help. Thanks!

The first thing that jumps out at me is the use getGraphics() . You should avoid using this method.

Graphics in Java are stateless. That is, the Graphics context used to render your component is not guaranteed to be the same between cycles. You shouldn't keep a reference to the Graphics context.

All painting should be done from within the context of the components paint methods, preferably, JComponent#paintComponent , as the paint method is acomplex method, doing a lot of important work you really don't want to have to duplicate.

I would create a chord "model" of some kind, where each instance was capable of painting it self. I would then create a view that was capable of painting the fret and the chords.

Update with example

This is a proof of concept example. It is assumed that guitar strings start at 5 (thickets) to 0 (smallest).

I'm not a musician, I have no beat or rhythm, so I may have made some fundamental mistakes.

在此输入图像描述

public class TestFretBoard {

    public static void main(String[] args) {
        new TestFretBoard();
    }

    public TestFretBoard() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new ChordsPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ChordsPane extends JPanel {

        public ChordsPane() {
            setLayout(new BorderLayout());
            FretPane fretPane = new FretPane();
            fretPane.setChord(new AChord());
            add(fretPane);
        }
    }

    public static interface FretBoard {

        public Rectangle getFretBounds(int index);

        public GuitarString getGuitarString(int index);

        public GuitarString[] getGuitarStrings(int... index);
    }

    public static class FretPane extends JPanel implements FretBoard {

        private static final Point BOARD_OFFSET = new Point(9, 9);
        private static final int BOARD_WIDTH = 84;
        private static final Rectangle[] FRET_BOUNDS = {
            new Rectangle(BOARD_OFFSET.x, 20, BOARD_WIDTH, 68 - 20),
            new Rectangle(BOARD_OFFSET.x, 71, BOARD_WIDTH, 113 - 71),
            new Rectangle(BOARD_OFFSET.x, 116, BOARD_WIDTH, 153 - 116),
            new Rectangle(BOARD_OFFSET.x, 156, BOARD_WIDTH, 189 - 156),
            new Rectangle(BOARD_OFFSET.x, 192, BOARD_WIDTH, 222 - 192),
            new Rectangle(BOARD_OFFSET.x, 225, BOARD_WIDTH, 254 - 225),
            new Rectangle(BOARD_OFFSET.x, 257, BOARD_WIDTH, 289 - 257),
            new Rectangle(BOARD_OFFSET.x, 287, BOARD_WIDTH, 312 - 287),
            new Rectangle(BOARD_OFFSET.x, 315, BOARD_WIDTH, 338 - 315),
            new Rectangle(BOARD_OFFSET.x, 341, BOARD_WIDTH, 364 - 341),
            new Rectangle(BOARD_OFFSET.x, 367, BOARD_WIDTH, 389 - 367),
            new Rectangle(BOARD_OFFSET.x, 392, BOARD_WIDTH, 412 - 392),};
        private static final GuitarString[] GUITAR_STRINGS = {
            new GuitarString(85 - BOARD_OFFSET.x, 1),
            new GuitarString(72 - BOARD_OFFSET.x, 1),
            new GuitarString(58 - BOARD_OFFSET.x, 1),
            new GuitarString(43 - BOARD_OFFSET.x, 2),
            new GuitarString(29 - BOARD_OFFSET.x, 2),
            new GuitarString(15 - BOARD_OFFSET.x, 2),};
        private BufferedImage background;
        private Chord chord;

        public FretPane() {
            try {
                background = ImageIO.read(new File("fretboard02.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
        }

        public Chord getChord() {
            return chord;
        }

        public void setChord(Chord chord) {
            this.chord = chord;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            if (background != null) {
                int x = (getWidth() - background.getWidth()) / 2;
                int y = (getHeight() - background.getHeight()) / 2;
                g2d.drawImage(background, x, y, this);
                Chord chord = getChord();
                if (chord != null) {
                    g2d.translate(x, y);
                    chord.paint(this, g2d);
                    g2d.translate(-x, -y);
                }
            }
            g2d.dispose();
        }

        @Override
        public Rectangle getFretBounds(int index) {
            Rectangle bounds = null;
            if (index >= 0 && index < FRET_BOUNDS.length) {
                bounds = FRET_BOUNDS[index];
            }
            return bounds;
        }

        @Override
        public GuitarString getGuitarString(int index) {
            GuitarString gs = null;
            if (index >= 0 && index < GUITAR_STRINGS.length) {
                gs = GUITAR_STRINGS[index];
            }
            return gs;
        }

        @Override
        public GuitarString[] getGuitarStrings(int... indices) {
            List<GuitarString> strings = new ArrayList<GuitarString>(indices.length);
            for (int index : indices) {
                strings.add(getGuitarString(index));
            }
            return strings.toArray(new GuitarString[strings.size()]);
        }
    }

    public static class GuitarString {

        private int x;
        private int width;

        public GuitarString(int x, int width) {
            this.x = x;
            this.width = width;
        }

        public int getX() {
            return x;
        }

        public int getWidth() {
            return width;
        }
    }

    public interface Chord {

        public String getName();

        public void paint(FretBoard board, Graphics2D g2d);
    }

    public abstract class AbstractChord implements Chord {

        public abstract int[] getFrets();

        public abstract GuitarString[] getGuitarStrings(FretBoard board, int fret);

        @Override
        public void paint(FretBoard board, Graphics2D g2d) {
            for (int fret : getFrets()) {
                Rectangle fretBounds = board.getFretBounds(fret);
                // Guitar Strings start at 5 (thickest) to 0 (smallest)
                GuitarString[] guitarStrings = getGuitarStrings(board, fret);

                int y = fretBounds.y + (fretBounds.height / 2);

                g2d.setColor(Color.RED);
                Ellipse2D dot = new Ellipse2D.Float(0, 0, 10, 10);

                for (GuitarString gs : guitarStrings) {
                    int x = ((gs.x + fretBounds.x) + (gs.width / 2)) - 5;
                    g2d.fill(getDot(dot, x, y - 5));
                }
            }
        }

        public Shape getDot(Ellipse2D dot, int x, int y) {

            PathIterator pathIterator = dot.getPathIterator(AffineTransform.getTranslateInstance(x, y));
            Path2D path = new Path2D.Float();
            path.append(pathIterator, true);

            return path;

        }
    }

    public class AChord extends AbstractChord {

        private int index;

        @Override
        public String getName() {
            return "A";
        }

        @Override
        public int[] getFrets() {
            return new int[]{1};
        }

        @Override
        public GuitarString[] getGuitarStrings(FretBoard board, int fret) {
            GuitarString[] strings = new GuitarString[0];
            switch (fret) {
                case 1:
                    strings = board.getGuitarStrings(3, 2, 1);
                    break;
            }
            return strings;
        }

    }
}

A lot of work goes into mapping between the image and UI, this you're going to have figure out yourself, I cracked open PhotoShop and measured out all the points manually. If you draw the fret board your self, it becomes easier.

The basic concept revolves around "Chords". A chord has a name and can paint itself. I've create a simple abstract implementation of my Chord interface that takes up much of the leg work and makes it easier creating new chords (as you simply only need to provide it with the frets and strings for each fret that makes up that chord)

I'd also suggest you have a read through

One way to do this is to draw everything from beginning each time paintComponent is called. Draw fretboard image unconditionally, move code for drawing selected cord in paintComponent, and make it draw cord according to some variable, and make actionListner set that variable to selected cord.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM