I have a problem with my Java application. Sometimes my application crashes for unknown reasons.
I have replicated the problem and the indicted method seems to be
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y)
It's very strange, however, that is not generated any JVM crash file (hs_pid.log...) neither errors are reported in console window although I use the following command line options
-verbose:gc -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/TEST -XX:ErrorFile=/root/TEST/vmlogs%p.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/TEST -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/root/TEST/gc.log
Below you can find a small test case that reproduce the problem by emphasizing it
Thread.sleep (1)
Launch the program and after 5-6 hours (more o less) the problem is reproduced.
I run the application on Linux PC with Java 1.7.0_80 (the latest version of Java 7 family) and I reproduced the same problem also with the latest of Java 6.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TestDraw {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 480;
private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:?$";
private static Random rnd = new Random();
private static final Font m_font = new Font("monospaced", Font.PLAIN, 22);
private static final int NUM_ROWS = 17;
private static final int CELL_WIDTH = 12;
private static final int ROW_HEIGTH = 25;
private static final int ROW_WIDTH = WINDOW_WIDTH;
private static String randomString(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args) {
JLabel textLabel = new JLabel() {
#Override
protected void paintComponent(Graphics g) {
for (int i = 0; i <= NUM_ROWS; i++) {
int y_text = (i + 1) * ROW_HEIGTH - 7;
String text = randomString(64);
drawString(text, y_text, g, Color.black);
}
}
private void drawString(String text, int y, Graphics g, Color color) {
for (int c = 0; c < text.length(); c++) {
String s = text.substring(c, c + 1);
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y);
}
}
};
JFrame myFrame = new JFrame("TEST COMPONENT");
myFrame.setLayout(new BorderLayout());
myFrame.setResizable(false);
myFrame.add(textLabel, BorderLayout.CENTER);
myFrame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
myFrame.setVisible(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textLabel.setFont(m_font);
while (true) {
textLabel.repaint();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I changed test case with #Hovercraft Full Of Eels suggestions.
This is the modify:
textLabel.setFont(m_font);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
textLabel.repaint();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
I have update my test case adding the suggestion of #apangin and I call this method inside the main.
private static void attachShutDownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
System.out.println("Inside Shutdown Hook");
}
});
System.out.println("Shut Down Hook Attached.");
}
The result are that application crash always but the
System.out.println("Inside Shutdown Hook");
isn't called.
Instead if I kill Java process manually the previous print is called.
I have modify my test case according #Hovercraft Full Of Eels suggestion. Now I am not using while true and thread.sleep but swing timer to queuing the Swing code on the event thread. The test case crash always.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class TestDraw {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 480;
private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-:?$";
private static Random rnd = new Random();
private static final Font m_font = new Font("monospaced", Font.PLAIN, 22);
private static final int NUM_ROWS = 17;
private static final int CELL_WIDTH = 12;
private static final int ROW_HEIGTH = 25;
private static final int ROW_WIDTH = WINDOW_WIDTH;
private static Timer updateTimer = null;
private static JLabel textLabel = null;
private static String randomString(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
sb.append(AB.charAt(rnd.nextInt(AB.length())));
}
return sb.toString();
}
public static void main(String[] args) {
textLabel = new JLabel() {
#Override
protected void paintComponent(Graphics g) {
for (int i = 0; i <= NUM_ROWS; i++) {
int y_text = (i + 1) * ROW_HEIGTH - 7;
String text = randomString(64);
drawString(text, y_text, g, Color.black);
}
}
private void drawString(String text, int y, Graphics g, Color color) {
for (int c = 0; c < text.length(); c++) {
String s = text.substring(c, c + 1);
g.drawString(s, CELL_WIDTH + c * CELL_WIDTH, y);
}
}
};
updateTimer = new Timer(1, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textLabel.repaint();
}
});
JFrame myFrame = new JFrame("TEST COMPONENT");
myFrame.setLayout(new BorderLayout());
myFrame.setResizable(false);
myFrame.add(textLabel, BorderLayout.CENTER);
myFrame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
myFrame.setVisible(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textLabel.setFont(m_font);
updateTimer.start();
}
}
Related
I am working on my first sorting visualizer using java and swing. If i hard code selection sort, the visualizer works properly. Its when i add the actionPerformed() method to the "sort" JButton things start going wrong. The program now waits until the sort is done before it repaints. Is there a way around this?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SortingAlgorithmVisualizer implements ActionListener {
private static final int WINDOW_WIDTH = 1200;
private static final int WINDOW_HEIGHT = 600;
private static final String[] ALGORITHM_NAMES = { "Selection Sort", "Insertion Sort", "Quick Sort", "Merge Sort" };
// JFrame to create GUI window
private JFrame window;
private Sort array;
private JButton sortButton;
private JComboBox<String> algorithmMenu;
// Constructor
public SortingAlgorithmVisualizer() {
// Construct the JFrame and add components
addComponents();
// Set Basic JFrame Settings
frameSetup();
// Unsort the array
array.unsort();
}
// Construct the JFrame and add components
public void addComponents() {
window = new JFrame("Sorting Algorith Visualiser");
array = new Sort();
sortButton = new JButton("Sort");
sortButton.addActionListener(this);
algorithmMenu = new JComboBox<>(ALGORITHM_NAMES);
// algorithmMenu.addActionListener(this);
window.add(algorithmMenu, BorderLayout.PAGE_START);
window.add(array, BorderLayout.CENTER);
window.add(sortButton, BorderLayout.SOUTH);
}
// Set Basic JFrame Settings
public void frameSetup() {
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == sortButton) {
array.selectionSort();
}
}
public static void main(String[] args) {
SortingAlgorithmVisualizer vis = new SortingAlgorithmVisualizer();
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Sort extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ARRAY_SIZE = 25;
private static final int WINDOW_WIDTH = 1200;
private static final int WINDOW_HEIGHT = 600;
private static int barWidth = WINDOW_WIDTH / ARRAY_SIZE;
private int[] elementColor;
// Array to sort
private int[] array;
// Contructor
public Sort() {
// DO SOMETHING WITH THIS!!!!
array = new int[ARRAY_SIZE];
elementColor = new int[ARRAY_SIZE];
// Fill Array
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i + 1;
elementColor[i] = 0;
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
for (int i = 0; i < ARRAY_SIZE; i++) {
int height = (array[i] * 20 + 100);
int x = i + (barWidth - 1) * i;
int y = WINDOW_HEIGHT - height;
g.fillRect(x, y, barWidth, height);
}
}
public void unsort() {
int shuffles = 100;
for (int i = 0; i < shuffles; i++) {
int rand1 = (int) (Math.random() * ARRAY_SIZE);
int rand2 = (int) (Math.random() * ARRAY_SIZE);
int temp = array[rand1];
array[rand1] = array[rand2];
array[rand2] = temp;
}
}
public void selectionSort() {
int n = ARRAY_SIZE;
// Loop thought unsorted array
for (int i = 0; i < n - 1; i++) {
// Set Current Min
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (array[j] < array[minIndex])
minIndex = j;
}
// Swap the min element with the first element
int temp = array[minIndex];
array[minIndex] = array[i];
array[i] = temp;
// Repaints the Array after every swap
repaint();
wait(1000);
}
}
public void insertionSort() {
}
public void quickSort() {
}
public void mergeSort() {
}
// Wait fuction for visualisation delay
public static void wait(int ms) {
try {
Thread.sleep(ms);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
This is one code file (i think this is where the error is but i might be mistaken) the run time error mentions "years: " which is located in this file. here is the full error. Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "Years:"
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
A frame that shows the growth of an investment with variable interest,
using a bar chart.
*/
public class InvestmentFrame4 extends JFrame
{
private static final int FRAME_WIDTH = 400;
private static final int FRAME_HEIGHT = 400;
private static final int CHART_WIDTH = 300;
private static final int CHART_HEIGHT = 300;
private static final double DEFAULT_RATE = 5;
private static final double INITIAL_BALANCE = 1000;
private JLabel rateLabelInitialInvestment;
private JLabel rateLabelInterestRate;
private JLabel rateLabelYears;
private JTextField rateFieldInitialInvestment;
private JTextField rateFieldInterestRate;
private JTextField rateFieldYears;
private JButton button;
private ChartComponent chart;
private double balance;
public InvestmentFrame4()
{
// balance = Double.parseDouble(rateLabelInitialInvestment.getText());
chart = new ChartComponent(3 * INITIAL_BALANCE);
chart.setPreferredSize(new Dimension(CHART_WIDTH, CHART_HEIGHT));
chart.append(INITIAL_BALANCE);
createInterestRateTextField();
createInitialInvestmentTextField();
createYearsTextField();
createButton();
createPanel();
setSize(FRAME_WIDTH, FRAME_HEIGHT);
}
private void createInterestRateTextField()
{
rateLabelInterestRate = new JLabel("Interest Rate: ");
final int FIELD_WIDTH = 25;
rateFieldInterestRate = new JTextField(FIELD_WIDTH);
rateFieldInterestRate.setText("" + DEFAULT_RATE);
}
private void createInitialInvestmentTextField()
{
rateLabelInitialInvestment = new JLabel("Initial Investment: ");
final int FIELD_WIDTH = 20;
rateFieldInitialInvestment = new JTextField(FIELD_WIDTH);
rateFieldInitialInvestment.setText("" + DEFAULT_RATE);
}
private void createYearsTextField()
{
rateLabelYears = new JLabel("Years: ");
final int FIELD_WIDTH = 20;
rateFieldYears = new JTextField(FIELD_WIDTH);
rateFieldYears.setText("" + DEFAULT_RATE);
}
class AddInterestListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
for(int i = 1; i < Integer.parseInt(rateLabelYears.getText().trim()); i++)
{
balance = Double.parseDouble(rateLabelInitialInvestment.getText()) * Math.pow(1.0 + Double.parseDouble(rateLabelInterestRate.getText()), Integer.parseInt(rateFieldYears.getText()));
}
}
}
private void createButton()
{
button = new JButton("Compound Interest");
ActionListener listener = new AddInterestListener();
button.addActionListener(listener);
}
private void createPanel()
{
JPanel panel = new JPanel();
panel.add(rateLabelInitialInvestment);
panel.add(rateFieldInitialInvestment);
panel.add(rateLabelInterestRate);
panel.add(rateFieldInterestRate);
panel.add(rateLabelYears);
panel.add(rateFieldYears);
panel.add(button);
panel.add(chart);
add(panel);
}
}
Here is another file
public class ChartComponent extends JComponent
{
private ArrayList<Double> values;
private double maxValue;
public ChartComponent(double max)
{
values = new ArrayList<Double>();
maxValue = max;
}
public void append(double value)
{
values.add(value);
repaint();
}
public void paintComponent(Graphics g)
{
final int GAP = 5;
final int BAR_HEIGHT = 10;
int y = GAP;
for (double value : values)
{
int barWidth = (int) (getWidth() * value / maxValue);
g.fillRect(0, y, barWidth, BAR_HEIGHT);
y = y + BAR_HEIGHT + GAP;
}
}
}
and finally the main
import javax.swing.JFrame;
/**
This program displays the growth of an investment, showing
a bar chart.
*/
public class InvestmentViewer4
{
public static void main(String[] args)
{
JFrame frame = new InvestmentFrame4();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
if someone could please explain i am getting the run time error it would be greatly appreciated!!!!
the problem occurs here:
for(int i = 1; i < Integer.parseInt(rateLabelYears.getText().trim()); i++)
rateLabelYears is a JLabel, that will always have the text of "years" unless you change it otherwise. You've mistaken it with the rateFieldYears which is an identifier to the JTextField that takes input for the years.
you'll need this:
for(int i = 1; i < Integer.parseInt(rateFieldYears.getText().trim()); i++){
...
...
...
}
I've created an Applet that creates a row of buttons, up to 15 buttons, when you push the "add to queue" button. I now want to decrement that row using a for loop. I want it to decrement from left to right. I can only get it to decrement from right to left. I know it has to do with the code in my "Remove" method, but I can't seem to figure out how to fix it. As a newbie I would appreciate any help you can provide.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class Main extends javax.swing.JApplet {
private final int width = 60;
private final int height = 24;
private final int maxItems = 15;
private int x = 40 + width;
private int y = 260;
private int count = 1;
private JButton jAdd;
private JButton jRemove;
Vector<JButton> stack = new Vector<JButton>();
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
Main inst = new Main();
frame.getContentPane().add(inst);
((JComponent) frame.getContentPane()).setPreferredSize(inst
.getSize());
frame.pack();
frame.setVisible(true);
}
});
}
public Main() {
super();
initGUI();
}
private void initGUI() {
try {
this.setSize(719, 333);
getContentPane().setLayout(null);
{
jAdd = new JButton();
getContentPane().add(jAdd);
jAdd.setText("Add to Queue");
jAdd.setBounds(43, 300, 150, 24);
jAdd.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jAddActionPerformed(evt);
}
});
}
{
jRemove = new JButton();
getContentPane().add(jRemove);
jRemove.setText("Remove from queue");
jRemove.setBounds(950, 300, 150, 24);
jRemove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jRemoveActionPerformed(evt);
}
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void jAddActionPerformed(ActionEvent evt) {
if (count > maxItems) {
JOptionPane.showMessageDialog(null, "The queue is full");
return;
}
JButton b = new JButton();
stack.add(0, b);
getContentPane().add(b);
int textCount = count;
b.setText("" +textCount++);
b.setBounds(x, y, width, height);
x = x + width;
count++;
}
private void jRemoveActionPerformed(ActionEvent evt) {
if (stack.isEmpty()) {
JOptionPane.showMessageDialog(null, "The queue is empty");
return;
}
JButton b = stack.remove(0);
this.remove(b);
for(int originalX = 880; originalX < 880; originalX--){
x = 880 - width;
}
repaint();
count--;
}
}
The issue is this:
stack.add(0, b);
You are always adding the new one to the start of the Vector (index 0). Remove that and you will see the behavior you want.
stack.add(b);
I'm making a piano application in Java. This is one of the functions,
public void playOnce(int time) {
play();
doClick(time);
stop();
}
public void play() {
channel[0].noteOn(note, 60);
}
public void stop() {
channel[0].noteOff(note);
}
I'll provide a minimal working example if necessary, but I wanted to make sure it's not an obvious issue. The problem is that playOnce is called in a while loop. playOnce is in a Key class, and each Key has a different note. In each iteration of the while loop, playOnce is called on a different key. Once all the keys have been played, it stops.
The doClick method correctly pressed the key, but it's not released until all the keys have been played. In fact, while the keys are being played, you can't do anything, even press the pause button. For this problem, I guess I could put the entire loop in a different thread, but I don't think that type of solution will allow the key to be released.
EDIT: Yea, I figured out I need a new thread to get other actions to work, but I still need a fix for doClick(). This might be more complicated than I thought so here's a working example,
Main.java
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
public class Main implements ActionListener {
final int WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT, BLACK_KEY_WIDTH,
BLACK_KEY_HEIGHT;
final int WIDTH;
final JFileChooser fc;
{
WHITE_KEY_WIDTH = Key.WHITE_KEY_WIDTH;
BLACK_KEY_WIDTH = Key.BLACK_KEY_WIDTH;
WHITE_KEY_HEIGHT = Key.WHITE_KEY_HEIGHT;
BLACK_KEY_HEIGHT = Key.BLACK_KEY_HEIGHT;
WIDTH = 3 * (WHITE_KEY_WIDTH * 7) + WHITE_KEY_WIDTH;
fc = new JFileChooser();
}
public static Key keys[] = new Key[48];
private static int index = 0;
private String prevText = "";
JTextArea shabadEditor = null;
JSpinner tempoControl;
JFrame frame;
File curFile;
public static void main(String[] args) {
new Main();
}
public Main() {
frame = new JFrame();
JPanel mainPanel = new JPanel();
JPanel controlPanel = new JPanel();
JLayeredPane pianoPanel = new JLayeredPane();
mainPanel.setLayout(new GridBagLayout());
JButton playButton = new JButton("Play");
JButton pauseButton = new JButton("Pause");
playButton.addActionListener(this);
playButton.setActionCommand("play");
pauseButton.addActionListener(this);
pauseButton.setActionCommand("pause");
SpinnerNumberModel model = new SpinnerNumberModel(1, 0, 2, .1);
tempoControl = new JSpinner(model);
JSpinner.NumberEditor editor = (JSpinner.NumberEditor) tempoControl
.getEditor();
DecimalFormat format = editor.getFormat();
format.setMinimumFractionDigits(1);
Dimension d = tempoControl.getPreferredSize();
d.width = 40;
tempoControl.setPreferredSize(d);
GridBagConstraints c = new GridBagConstraints();
// Construct each top level component
controlPanel.add(playButton);
controlPanel.add(pauseButton);
controlPanel.add(tempoControl);
shabadEditor = new JTextArea(20, 78);
constructKeyboard(pianoPanel);
// Add the piano panel and shabad editor to the window
c.gridx = 0;
c.gridy = 0;
c.weightx = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
mainPanel.add(controlPanel, c);
c.gridx = 0;
c.gridy = 1;
c.weightx = 1.0;
// c.weighty = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
pianoPanel
.setPreferredSize(new Dimension(WIDTH - 18, WHITE_KEY_HEIGHT));
mainPanel.add(pianoPanel, c);
c.gridx = 0;
c.gridy = 2;
c.weightx = 1.0;
c.weighty = 1.0;
c.anchor = GridBagConstraints.NORTHWEST;
mainPanel.add(shabadEditor, c);
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(WIDTH, WHITE_KEY_HEIGHT * 3 + 30);
frame.setLocation(250, 60);
frame.setVisible(true);
}
void constructKeyboard(Container panel) {
int i = 0;
int j = 0;
for (int k = 0; k < 3; k++) {
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addWhiteKey(panel, i++);
j++;
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
addWhiteKey(panel, i++);
addBlackKey(panel, j++);
j++;
addWhiteKey(panel, i++);
}
}
void addWhiteKey(Container panel, int i) {
WhiteKey b = new WhiteKey();
b.setLocation(i++ * WHITE_KEY_WIDTH, 0);
panel.add(b, 0, -1);
keys[index++] = b;
}
void addBlackKey(Container panel, int factor) {
BlackKey b = new BlackKey();
b.setLocation(WHITE_KEY_WIDTH - BLACK_KEY_WIDTH / 2 + factor
* WHITE_KEY_WIDTH, 0);
panel.add(b, 1, -1);
keys[index++] = b;
}
#Override
public void actionPerformed(ActionEvent arg0) {
String action = arg0.getActionCommand();
if (action.equals("play")) {
System.out.println("working");
for (int i = 0; i < 10; i++) {
keys[i].playOnce(500);
}
}
}
}
Key.java
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.JButton;
public class Key extends JButton implements MouseListener {
private static final long serialVersionUID = 1L;
public static final int WHITE_KEY_HEIGHT = 200;
public static final int WHITE_KEY_WIDTH = 40;
public static final int BLACK_KEY_WIDTH = 20;
public static final int BLACK_KEY_HEIGHT = 120;
private static int noteCount = 40;
public int note;
private static Synthesizer synth = null;
static {
try {
synth = MidiSystem.getSynthesizer();
synth.open();
} catch (MidiUnavailableException e) {
e.printStackTrace();
}
}
MidiChannel channel[];
public Key() {
note = noteCount++;
// Instrument[] instruments = synth.getAvailableInstruments();
// for (Instrument instrument : instruments) {
// System.out.println(instrument.getName());
// System.out.println(instrument.getPatch().getBank());
// System.out.println(instrument.getPatch().getProgram());
// }
channel = synth.getChannels();
channel[0].programChange(20);
addMouseListener(this);
}
public void playOnce(int time) {
play();
doClick(time);
stop();
}
public void play() {
channel[0].noteOn(note, 60);
}
public void stop() {
channel[0].noteOff(note);
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(this.note);
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
play();
}
#Override
public void mouseReleased(MouseEvent e) {
stop();
}
}
BlackKey.java
import java.awt.Color;
public class BlackKey extends Key {
private static final long serialVersionUID = 1L;
public BlackKey() {
super();
setBackground(Color.BLACK);
setSize(BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT);
}
}
WhiteKey.java
import java.awt.Color;
public class WhiteKey extends Key {
private static final long serialVersionUID = 1L;
public WhiteKey() {
super();
setBackground(Color.WHITE);
setSize(WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT);
}
}
EDIT: After doing a bit of work with threading, this is what I have
By putting the for loop in another thread, the keys are released at the right time:
#Override
public void actionPerformed(ActionEvent arg0) {
String action = arg0.getActionCommand();
if (action.equals("play")) {
System.out.println("working");
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
keys[i].playOnce(100);
}
}
}).start();
}
}
}
The issue now is that the keyboard glitches. The keyboard is created using a layered pane, and for some reason when the keys are released the layers that are supposed to be on the bottom come to the top. When I hover my mouse over them, the glitch goes away. Any ideas?
EDIT2: I fixed the glitches. I simply had to add
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
after doClick();
The problem with your approach is that you are blocking the event thread. This thread is responsible for user input, painting and window updates. My guess is now, that doClick's timeout gets checked inside the event thread (seems logical), so it won't get released until your actionPerformed method exits (and so the event thread can continue its event processing).
A solution to this problem would be (as you already mentioned) moving your for loop to another thread and call doClick using SwingUtilities.invokeLater.
I have a simple puzzle game. There is an image consisting of 16 tiles (randomly placed). Images are stored in an array and when game is launched they're added to main JPanel.
Game works in this way : Each image has atributes 'place' and 'number'. 'Place' is the current place on grid (either correct or not) and 'number' is the desired place for the image. When a user clicks image their 'place' and 'number' attributes are checked. If they match nothing happens. If not game checks if any image is currently in memory. If there is none, then this image's 'place' and 'number' are stored. If there is some image in memory, then the currently clicked image's 'plac'e is checked with stored image's 'number'. When they match - their places are exchanged. This part works properly. But now, I'm calling addComponent method on my JPanel with updated images and simply nothing happens. Shouldn't the new images be added to JPanel replacing the old ones ?
package Bonus;
import javax.swing.*;
import java.util.Random;
import java.awt.event.*;
import java.awt.*;
class Puzzle extends JPanel implements ActionListener {
private int selected_nr=-1;
private int selected_pl=-1;
private boolean memory=false;
private static Img[] images;
public Puzzle(){
JFrame f = new JFrame("Smile");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.setSize(252,252);
f.setVisible(true);
setLayout(new GridLayout(4, 4));
images = new Img[16];
int[] buttons = new int[16];
for(int i=0; i<16; i++){
buttons[i] = i;
}
int rand;
int temp;
Random random;
random = new Random(System.currentTimeMillis());
for (int i = 0; i < buttons.length; i++) {
rand = (random.nextInt() & 0x7FFFFFFF) % buttons.length;
temp = buttons[i];
buttons[i] = buttons[rand];
buttons[rand] = temp;
}
for (int i = 0; i < 16; i++) {
images[i] = new Img(i, buttons[i]);
}
addComponents(images);
}
public void addComponents(Img[] im){
this.removeAll();
for(int i=0; i<16; i++){
im[i].addActionListener(this);
im[i].setPreferredSize(new Dimension(53,53));
add(im[i]);
}
this.validate();
}
public void actionPerformed(ActionEvent e) {
Img b = (Img)(e.getSource());
int num = b.getNumber();
int pl = b.getPlace();
if(!(b.rightPlace())){
if(memory){
if(pl == selected_nr){
images[pl].setPlace(selected_pl);
images[selected_pl].setPlace(selected_nr);
selected_nr = -1;
selected_pl = -1;
memory = false;
addComponents(images);
}
else{
System.out.println("Try other image");
}
}
else{
memory = true;
selected_nr = num;
selected_pl = pl;
}
}
else{
System.out.println("OK !");
}
}
public static void main(String args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Puzzle();
}
});
}
}
class Img extends JButton {
int number;
int place;
ImageIcon img;
public Img(int p, int n){
number = n;
place = p;
img = new ImageIcon("u"+number+".jpg", BorderLayout.CENTER);
setIcon(img);
}
public boolean rightPlace(){
boolean correct=false;
if(number == place){
correct = true;
}
return correct;
}
public void setPlace(int i){
place = i;
}
public int getNumber(){
return number;
}
public int getPlace(){
return place;
}
}
EDIT: Changed the code to use the answers, but still no luck. addComponents() gets updated images[] but doesn't revalidate them.
Rather than relying on precut image files, here's an example of slicing an existing image and shuffling the resulting pieces. It combines the helpful (+1) suggestions of both #Frederick and #akf.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ImageLabelPanel extends JPanel implements ActionListener {
private static final int N = 4;
private final List<JLabel> list = new ArrayList<JLabel>();
private final Timer timer = new Timer(1000, this);
ImageLabelPanel() {
this.setLayout(new GridLayout(N, N));
BufferedImage bi = null;
try {
bi = ImageIO.read(new File("image.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
int w = bi.getWidth() / N;
int h = bi.getHeight() / N;
BufferedImage b = bi.getSubimage(c * w, r * h, w, h);
list.add(new JLabel(new ImageIcon(b)));
}
}
createPane();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
timer.start();
}
private void createPane() {
this.removeAll();
for (JLabel label : list) add(label);
this.validate();
}
#Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(list);
createPane();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ImageLabelPanel();
}
});
}
}
You are adding all of your components again to your JPanel without actually removing any of them. In your addComponents() method, I would first call removeAll(). You might want to rename that method to highlight the side-effects, as it no longer would only be adding components. Perhaps, resetComponents() would be better.
After changing the components, you need to 'refresh' the Swing component by calling invalidate() or revalidate().