- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我用 Java 为使用 2D 图形的吉他和弦查找应用程序编写了一个 GUI。该程序在 Canvas 上打开一个 .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。请帮忙。谢谢!
最佳答案
我首先想到的是使用 getGraphics()
。您应该避免使用此方法。
Java 中的图形是无状态的。也就是说,用于渲染组件的Graphics
上下文不能保证在周期之间是相同的。您不应保留对 Graphics
上下文的引用。
所有绘制都应在组件 paint
方法的上下文中完成,最好是 JComponent#paintComponent
,因为 paint
方法是一个复杂的方法,做了很多你真的不想重复的重要工作。
我会创建某种和弦“模型”,其中每个实例都能够自行绘制。然后,我将创建一个能够绘制品格和和弦的 View 。
用示例更新
这是一个概念验证示例。假设吉他弦从 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
界面创建了一个简单的抽象实现,该实现占用了大部分的工作时间,并且可以更轻松地创建新和弦(因为您只需要为其提供每个和弦的品格和琴弦即可)组成该和弦的品格)
我还建议您仔细阅读
关于java - 如何将 GUI 的状态恢复到原来的外观?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13652594/
如何使用 Blazor 在 Linux 平台下运行 Desktop 程序 本文将讲解如何使用 Blazor 运行跨平台应用,应用到的技术有以下几点 Blazor
低并发的友友们好,我是闪客。 Lambda 表达式非常方便,在项目中一般在 stream 编程中用的比较多。 List<Student> studen
我是一名优秀的程序员,十分优秀!