我用Java编写了一个GUI,用于使用2D图形的吉他和弦查找器应用程序。该程序在画布上打开.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。请帮忙。谢谢!
答案 0 :(得分:2)
第一件事就是使用getGraphics()
。你应该避免使用这种方法。
Java中的图形是无状态的。也就是说,用于渲染组件的Graphics
上下文在循环之间不保证是相同的。您不应该引用Graphics
上下文。
所有绘画都应该在组件paint
方法的上下文中完成,最好是JComponent#paintComponent
,因为paint
方法是acomplex方法,做了很多重要的工作你真的不想复制。
我会创建一种和弦的“模型”,每个实例都能够自己绘制它。然后我会创建一个能够画出音品和和弦的视图。
使用示例更新
这是一个概念证明示例。假设吉他弦从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;
}
}
}
图像和用户界面之间的映射有很多工作,你要弄清楚自己,我打开了PhotoShop并手动测量了所有点。如果你自己画出音板,就会变得更容易。
基本概念围绕“和弦”。和弦有一个名字,可以画自己。我创建了一个简单的抽象实现,我的Chord
接口占用了很多腿部工作,并且更容易创建新的和弦(因为你只需要为它提供每个音品的音品和字符串。那个和弦)
我还建议您阅读
答案 1 :(得分:0)
这样做的一种方法是每次调用paintComponent时从头开始绘制所有内容。无条件地绘制指板图像,移动代码以在paintComponent中绘制选定的线,并使其根据某个变量绘制线,并使actionListner将该变量设置为选定的线。