0

我用 Java 为使用 2D 图形的吉他和弦查找器应用程序编写了一个 GUI。程序在画布上打开一个 .jpg 图像。然后它将每个单独的音符(音品之间的空间)绘制为带有音符名称的椭圆。该程序允许用户通过更改和弦各个音符的颜色从工具栏中选择和弦,这些和弦显示在指板上。然而,每当用户选择一个新的和弦时,之前的和弦都不会被删除。我怎样才能解决这个问题?这是我的一些代码(该程序超过 1000 行代码)。

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

这是该计划的要点。其他一切基本上是每个单独音符的位置或显示和弦的类似方法。我被卡住了,不知道如何修复这个程序。这也是我编写的第一个 GUI。请帮忙。谢谢!

4

2 回答 2

2

我首先想到的是使用getGraphics()。你应该避免使用这种方法。

Java 中的图形是无状态的。也就是说,Graphics用于渲染组件的上下文不能保证在循环之间是相同的。您不应该保留对Graphics上下文的引用。

所有的绘画都应该在组件paint方法的上下文中完成,最好是JComponent#paintComponent,因为该paint方法是一个复杂的方法,做很多你真的不想重复的重要工作。

我会创建某种和弦“模型”,每个实例都可以自己绘制。然后,我将创建一个能够绘制音品和和弦的视图。

更新示例

这是一个概念证明示例。假设吉他弦从 5(厚度)到 0(最小)开始。

我不是音乐家,我没有节拍或节奏,所以我可能犯了一些根本性的错误。

在此处输入图像描述

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

    }
}

很多工作都涉及到图像和 UI 之间的映射,这你要自己弄清楚,我打开 PhotoShop 并手动测量所有点。如果你自己画指板,它会变得更容易。

基本概念围绕“和弦”展开。一个和弦有一个名字,可以自己画。我已经为我的Chord界面创建了一个简单的抽象实现,它占用了大量的腿部工作并且更容易创建新的和弦(因为你只需要为它提供构成该和弦的每个音品的音品和弦)

我还建议您通读

于 2012-11-30T22:16:08.243 回答
0

一种方法是在每次调用paintComponent 时从头开始绘制所有内容。无条件绘制指板图像,在paintComponent中移动绘制选中线的代码,使其根据某个变量绘制线,并让actionListner将该变量设置为选中线。

于 2012-11-30T20:42:52.783 回答