Runnable become very slow after loading multiple arraylist instances - java

I was trying to make a small simulation game with individual "Person" class. I used a JFrame to make a window and have real time simulations with Runnable. However, when I created more instances of Person, the Runnable becomes really slow. The more instances I have, the slower it becomes. Is there a better way of doing this? Or should I try to use SQL database rather than a text file to store data? It is just part of the code, I will add more if needed. Thank you very much.
public class Main extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
private JTextField textField;
private JTextArea textArea;
// create a list of people, each include person.java class
private List<Person> people = new ArrayList<Person>();
// set the number of miliseconds in a day
// 86400 = 24 hr x 60 min x 60 s
private int lengthOfDay = 100;
private int timeCounter = 0;
private int timeCounterPause = lengthOfDay / 24;
private int timeOfDay = 0;
private int hourOfDay = 0;
private int lengthOfYear = lengthOfDay * 365;
private int dayOfWeek = 1; // day starts monday
private String dayOfWeekS = "";
// boolean for stop Runnable
private volatile boolean stopRequested = false;
// runnable "run" class
Runnable simulation = new Runnable() {
public void run() {
try {
while (!stopRequested) {
for (int i = 0; i < people.size(); i++) {
// determine time of day
timeOfDay = timeOfDay + timeCounterPause;
if (timeOfDay > lengthOfDay) {
timeOfDay = timeOfDay % lengthOfDay;
// assign int to string dayOfWeekS
assignDay();
}
// display day of week if set visible (focused)
if (people.get(i).getVisible() && hourOfDay < 1) {
textArea.append(dayOfWeekS + "\n");
}
hourOfDay = timeOfDay / (lengthOfDay / 24);
people.get(i).setAge((float) (people.get(i).getAgeInMs() / (float)lengthOfYear));
// show time if focused
if (people.get(i).getVisible()) {
// textArea.append(hourOfDay +"o'clock\n");
}
// go to school
if (people.get(i).getFinishedSchool() == false && dayOfWeek != 6 && dayOfWeek != 7) {
people.get(i).goToSchool(timeOfDay);
} else if (people.get(i).getFinishedSchool() == true) {
people.get(i).findWork();
}
// go to work if has work, and not on weekends
if (!people.get(i).getProfession().equals("none") && dayOfWeek != 6
&& dayOfWeek != 7)
people.get(i).goToWork(timeOfDay);
// check alive
people.get(i).checkAlive();
// sleep at night
people.get(i).sleep(timeOfDay);
// interval between each time status are checked
Thread.sleep(timeCounterPause);
// adjust ageInMs
people.get(i).setAgeInMs(people.get(i).getAgeInMs() + timeCounterPause);
// if alive is false, then remove the person
// remove person as the last thing to run
if (!people.get(i).getAlive()) {
people.remove(i);
}
}
timeCounter++;
}
} catch (InterruptedException iex) {
}
}
};
// method to stop Runnable
public void requestStop() {
stopRequested = true;
}
public Main() {
super("Simulation");
textField = new JTextField(20);
textField.setText("Enter your commands here.");
textField.selectAll();
textField.setForeground(new Color(0, 204, 0));
textField.setBackground(Color.BLACK);
textField.addActionListener(this);
textArea = new JTextArea(40, 40);
textArea.setEditable(false);
textArea.setForeground(new Color(0, 204, 0));
textArea.setBackground(Color.BLACK);
JScrollPane scrollPane = new JScrollPane(textArea,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
GridBagLayout gridBag = new GridBagLayout();
Container contentPane = getContentPane();
contentPane.setLayout(gridBag);
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
gridBag.setConstraints(textField, c);
contentPane.add(textField);
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
gridBag.setConstraints(scrollPane, c);
contentPane.add(scrollPane);
// things to do when window opens
addWindowListener(new WindowAdapter() {
public void windowOpened(WindowEvent e) {
loadSimulation();
// load all data of each person
loadPeople();
// create thread when window is loaded
Thread thr = new Thread(simulation);
thr.start();
}
});
// things to do when window closes
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
savePeople();
saveSimulation();
}
});
}
public void actionPerformed(ActionEvent event) {
String prompt = textField.getText();
String[] words = prompt.split("\\s+");
// respond to commands
switch (words[0]) {
case "create":
if (words[1].equals("person")) {
createPerson();
} else {
textArea.append("Create what?\n");
}
break;
case "focus":
focus(words[1], words[2]);
break;
case "unfocus":
unfocus();
break;
case "topCash":
topCash();
break;
case "topAge":
topAge();
break;
case "topIQ":
topIQ();
break;
default:
textArea.append("I don't know what you are saying.");
break;
}
textField.selectAll();
}
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
Main window = new Main();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
window.getContentPane().setBackground(Color.BLACK);
}

You do a
Thread.sleep(timeCounterPause)
for every Person you check, so for every Person you add, that loop takes 4.166 (lengthOfDay / 24) ms longer.
If you want to make timeCounterPause the update interval for a complete status check, you should put it outside the
for (int i = 0; i < people.size(); i++) {
loop.

Related

Is there a way to display a loading JLabel when my JButton is busy?

I have uploaded a specific portion of my code and i hope that this will suffice to understand my problem. I'm using the p6 panel in a frame which has multiple buttons and one of the buttons display this panel.
This panel p6 has two buttons. One clicking check_api, the program requires some time to execute and to the user, it would seem as if the application has hung. Therefore I tried adding a method which would show a waiting message while the button is busy. But on doing that, my panels lose their user defined size.
Is there a way to display a wait message without compromising on my panel size?
public static void prepare_url(JPanel p6, JPanel content, int count) {
InnerOperations.removeexcept(6, content);
if (count != 1) {
p6.removeAll();
p6.revalidate();
System.out.println("Count is greater than 1");
}
JPanel p_back = new JPanel();
JTextField field = new JTextField(" Enter url(s) with delimiter ';'");
JButton check_list = new JButton(" CHECK URL LIST STATUS");
JButton check_api = new JButton(" CHECK PREDEFINED API LIST");
p_back.add(check_list);
p_back.add(field);
p_back.add(l_check);
p_back.add(check_api);
check_api.setBounds(200, 130, 400, 30);
l_check.setBounds(50, 300, 600, 20);
field.setBounds(200, 50, 400, 30);
check_list.setBounds(200, 90, 400, 30);
p6.add(p_back);
p_back.setBounds(0, 0, 800, 600);
check_list.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String url_pref
String list[] = url_pref.split(";");
int i = 0;
while (i < list.length) {
try {
// thread1.start();
prepareurl(p6, p_back, list, list.length);
// thread1.stop();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
i++;
}
}
});
check_api.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// thread2.start();
// p_back.removeAll();
//check_api.setText("Will load in a while");
prepare_api(p6, content, p_back);
// thread2.stop();
}
});
}
public static void userwait(JPanel ppanel)
{
JLabel waiting = new JLabel();
ppanel.add(waiting);
waiting.setText("Please Wait......");
waiting.setBounds(200, 200, 400, 30);
}
Executing long running task in the EDT is generally a bad idea - use SwingWorker instead ,see example Improve Application Performance With SwingWorker.
You could just display some text before starting the worker thread and disable the buttons to prevent the user to click again:
public class SwingworkerExample extends JFrame {
private JButton startBtn = new JButton("Start");
private JProgressBar progressBar = new JProgressBar(0, 100);
private JLabel progressLbl = new JLabel("");
private JLabel statusLbl = new JLabel("");
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SwingworkerExample swe = new SwingworkerExample("SwingWorker: progress");
swe.setVisible(true);
}
});
}
public SwingworkerExample(String title) {
super(title);
setupUI();
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
startWorkerThread();
}
});
}
private void startWorkerThread() {
// Disable JButton:
startBtn.setEnabled(false);
// Show info
statusLbl.setText("running...");
progressLbl.setText("...");
/*
* Create SwingWorker<Boolean, Integer> where
* Boolean : type for thread result, use "Void" if no return value
* Integer : type for published values
*/
SwingWorker<Boolean, Integer> swingWorker = new SwingWorker<Boolean, Integer>() {
#Override
protected Boolean doInBackground() throws Exception {
// WARN: do not update the GUI from within doInBackground(), use process() / done()!
// Do something ...
// prepareurl(p6, p_back, list, list.length);
//
for (int i = 0; i <= 100; i++) {
Thread.sleep((int) (Math.random() * 60));
// Use publish to send progress information to this.process() [optional]
publish(i);
}
return true;
}
#Override
protected void process(List<Integer> chunks) {
// handle published progress informations / update UI
StringBuilder sb = new StringBuilder();
chunks.forEach(val -> sb.append(" ").append(val));
progressLbl.setText(String.format("processing: %s%n", sb));
int progress = chunks.get(chunks.size() - 1);
progressBar.setValue(progress);
}
#Override
protected void done() {
// Swingworker finished - update UI / handle result
try {
Boolean result = get();
statusLbl.setText("DONE. Result= " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
statusLbl.setText("Error: " + e.getMessage());
}
// Enable JButton
startBtn.setEnabled(true);
}
};
swingWorker.execute();
} // end: startWorkerThread
private void setupUI() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(240, 160);
setLayout(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.fill = GridBagConstraints.BOTH;
gc.weightx = 1;
gc.weighty = 1;
gc.gridx = 0;
gc.weighty = 1;
gc.gridy = 0;
add(startBtn, gc);
gc.gridy = 1;
add(progressBar, gc);
gc.gridy = 2;
add(statusLbl, gc);
gc.gridy = 3;
add(progressLbl, gc);
}
}
A couple of options:
You could use an indeterminate JProgressBar. See the section from the Swing tutorial on How to Use Progress Bars for more information and working examples.
You could use a GlassPane to display the message on top of the panel. See Disabled Glass Pane for an example of this approach.

Multithread timer not working correctly

Again, please help me!
In the following code, I want to get started by pushing a timer thread at the push of a button and typing it in a Label. Each press of the button should start a new thread and mark it on each label. But unfortunately, the same timer is written for each label. Can you help us get it right? If you can tell me the mistake, what do I mean?
public class TimerThreads implements ActionListener{
JFrame jFrame = new JFrame();
JLabel[] labels;
int second = 0;
int minute = 0;
String s = "";
String m = "";
int l = 0;
public TimerThreads(){
JLabel one = new JLabel();
JLabel two = new JLabel();
JLabel three = new JLabel();
JLabel four = new JLabel();
labels = new JLabel[]{one, two, three, four};
jFrame.setLayout(new GridLayout(0, 2, 5, 5));
jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JButton start = new JButton("Start");
start.addActionListener(this);
JButton stop = new JButton("Stop");
stop.addActionListener(this);
jFrame.add(start);
jFrame.add(stop);
jFrame.setVisible(true);
jFrame.pack();
}
public static void main(String[] args) {
new TimerThreads();
}
#Override
public void actionPerformed(ActionEvent e) {
String select = e.getActionCommand();
switch(select){
case "Start":
jFrame.add(labels[l]);
jFrame.revalidate();
jFrame.repaint();
TimerThread t = new TimerThread(labels[l]);
t.start();
l++;
break;
case "Stop":
//
break;
}
}
class TimerThread extends Thread{
JLabel jLabel;
public TimerThread(JLabel jLabel) {
this.jLabel = jLabel;
}
#Override
public void run() {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
second++;
if(String.valueOf(second).length() == 1){
s = "0";
}
else{
s = "";
}
if(second == 60){
second = 0;
s = "0";
minute++;
}
if(String.valueOf(minute).length() == 1){
m = "0";
}
jLabel.setText(m + String.valueOf(minute) + ":" + s + String.valueOf(second));
}
},0, 1000);
}
}
}
The cause of your bug is here:
public class TimerThreads implements ActionListener {
JFrame jFrame = new JFrame();
JLabel[] labels;
// ***** these fields below
int second = 0;
int minute = 0;
String s = "";
String m = "";
// ***** these fields above
int l = 0;
The four fields are instance fields of the class and are shared by every TimerTask instance that you create, and so all will show the same exact time.
The solution is to make these fields local to the nested class:
public class TimerThreads implements ActionListener {
JFrame jFrame = new JFrame();
JLabel[] labels;
// int second = 0;
// int minute = 0;
// String s = "";
// String m = "";
int l = 0;
public TimerThreads() {
//.....
}
// ....
class TimerThread extends Thread {
JLabel jLabel;
public TimerThread(JLabel jLabel) {
this.jLabel = jLabel;
}
#Override
public void run() {
java.util.Timer timer = new java.util.Timer();
timer.scheduleAtFixedRate(new TimerTask() {
// ***** add these fields here
int second = 0;
int minute = 0;
String s = "";
String m = "";
Having said this, you've got risky code since you're using the wrong Timer, a java.util.Timer, when you should be using a Swing Timer or javax.swing.Timer. This is very important since the latter Timer works well with the Swing event model and prevents thread clashes. Please check out the Swing Timer Tutorial
Other problems: using a fixed size array, you risk index out of bounds exception if the user wants more than 4 threads running. Use an ArrayList instead.

Alert user when all cards are paired

I have this program about memory game that when user pairs all the cards there should be alert that. The user won. However I cannot find ways to identify if all cards are paired up. Can someone help me on this.
public class MemoryGame extends JFrame {
static String files[] = {
"japan.jpg",
"greece.jpg",
"canada.jpg",
"brazil.jpg",
"jamaica.jpg"};
static JButton buttons[];
ImageIcon closedIcon;
int numButtons;
ImageIcon icons[];
Timer myTimer;
int numClicks = 0;
int oddClickIndex = 0;
int currentIndex = 0;
public MemoryGame() {
// Set the title.
setTitle("Memory Game");
// Specify an action for the close button.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a BorderLayout manager.
setLayout(new GridLayout(2, files.length));
closedIcon = new ImageIcon("closed.JPG");
numButtons = files.length * 2;
buttons = new JButton[numButtons];
icons = new ImageIcon[numButtons];
for (int i = 0, j = 0; i < files.length; i++) {
icons[j] = new ImageIcon(getClass().getResource(files[i]));
buttons[j] = new JButton("");
buttons[j].addActionListener(new ImageButtonListener());
buttons[j].setIcon(closedIcon);
add(buttons[j++]);
icons[j] = icons[j - 1];
buttons[j] = new JButton("");
buttons[j].addActionListener(new ImageButtonListener());
buttons[j].setIcon(closedIcon);
add(buttons[j++]);
}
// randomize icons array
Random gen = new Random();
for (int i = 0; i < numButtons; i++) {
int rand = gen.nextInt(numButtons);
ImageIcon temp = icons[i];
icons[i] = icons[rand];
icons[rand] = temp;
}
setSize(300,200);
setVisible(true);
myTimer = new Timer(1000, new TimerListener());
// myTimer.start();
}
private class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
buttons[currentIndex].setIcon(closedIcon);
buttons[oddClickIndex].setIcon(closedIcon);
myTimer.stop();
}
}
private class ImageButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// we are waiting for timer to pop - no user clicks accepted
if (myTimer.isRunning())
return;
numClicks++;
System.out.println(numClicks);
// which button was clicked?
for (int i = 0; i < numButtons; i++) {
if (e.getSource() == buttons[i]) {
buttons[i].setIcon(icons[i]);
currentIndex = i;
}
}
// check for even click
if (numClicks % 2 == 0) {
// check whether same position is clicked twice!
if (currentIndex == oddClickIndex) {
numClicks--;
return;
}
// are two images matching?
if (icons[currentIndex] != icons[oddClickIndex]) {
// show images for 1 sec, before flipping back
myTimer.start();
}
} else {
// we just record index for odd clicks
oddClickIndex = currentIndex;
}
}
}
public static void main(String[] args) {
new MemoryGame();
}
}
Why not simply increment a counter variable when the icons match, and then checking the value of this variable?
Perhaps here:
if (icons[currentIndex] != icons[oddClickIndex]) {
// show images for 1 sec, before flipping back
myTimer.start();
} else {
pairCounter++;
if (pairCounter >= MAX_PAIRS) {
// NOTIFY USER
// end game
// reset pairCounter
}
}

How can i stop the program from skipping my check the second time around?

I am creating a program to take in sets of binary digits and convert them into hammingcodes (Effectively take in 8 digits, turn into 12, print out) but i am having trouble. Currently, i am using a JTextField for the user to enter their number, then they press a JButton to enter the data. I then do funky shit with that number to put it into a list and confirm that this is the last of the numbers they wish to enter. If they click a JButton called yes (New text in button, but same button) if goes on to do what i need. But if they click the other JButton called no, it goes back and repeats the same process. My problem is after clicking no once, the program stops allowing you to press no at the step to check if you want to add another list of numbers. IT appears to skip the check all together and assume they pressed yes as it does the rest of the working out thats done after all entry is finished.
My code is a tad messy due to messing with it for a few hours.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
public class MainProgram extends JFrame
{
public MainProgram()
{
}
public static void main(String[] args)
{
MainProgram mp = new MainProgram();
mp.run();
}
private void run()
{
java.util.List<Integer> streamSplit = new ArrayList<>();
java.util.List<Integer> tempEight = new ArrayList<>();
java.util.List<Integer> finalStream = new ArrayList<>();
yes.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
checkYes = true;
}
});
no.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
checkNo = true;
}
});
init();
yesChange("Enter");
boolean confirm = false;
int aCheck = 0;
while (aCheck == 0)
{
confirm = false;
while (!confirm)
{
setTopText("<html>Please enter your next 8 bits. Do not enter more than 8 bits.<br> Press Enter when done</html>");
yesChange("Enter");
confirm = checkYes();
}
confirm = false;
setTopText("Digits Successfully added.");
int stream = checkInput();
do
{
streamSplit.add(stream % 10);
stream /= 10;
} while (stream != 0);
setYesNo();
setTopText("<html>Are you finished entering streams?</html>");
yesChange("YES");
noChange("NO");
aCheck = 2;
checkYes();
checkNo();
while (aCheck == 2)
{
if ( checkNo())
{
aCheck = 0;
System.out.println("CrapNo");
}
else if (checkYes())
{
aCheck = 1;
System.out.println("CrapYes");
}
}
}
int arrayLength = streamSplit.size();
int bufferLength = 8 - arrayLength % 8;
int numberOfStreams = 0;
if (bufferLength != 8)
{
numberOfStreams = arrayLength / 8 + 1;
} else
{
numberOfStreams = arrayLength / 8;
}
int tempStreams = numberOfStreams;
System.out.println(numberOfStreams + "<Streams Buffer>" + bufferLength);
while (bufferLength > 0 && bufferLength != 8)
{
streamSplit.add(0);
bufferLength--;
}
while (tempStreams > 0)
{
for (int i = 0; i < 8; i++)
{
tempEight.add(streamSplit.get(i));
}
if ((tempEight.get(0) + tempEight.get(1) + tempEight.get(3) + tempEight.get(4) + tempEight.get(6)) % 2 == 0)
{
tempEight.add(0, 0);
} else
{
tempEight.add(0, 1);
}
if ((tempEight.get(1) + tempEight.get(3) + tempEight.get(5) + tempEight.get(6) + tempEight.get(7)) % 2 == 0)
{
tempEight.add(1, 0);
} else
{
tempEight.add(1, 1);
}
if ((tempEight.get(3) + tempEight.get(4) + tempEight.get(5) + tempEight.get(8) + tempEight.get(9)) % 2 == 0)
{
tempEight.add(3, 0);
} else
{
tempEight.add(3, 1);
}
if ((tempEight.get(7) + tempEight.get(8) + tempEight.get(9) + tempEight.get(10)) % 2 == 0)
{
tempEight.add(7, 0);
} else
{
tempEight.add(7, 1);
}
tempStreams--;
for (int i = 0; i < 12; i++)
{
finalStream.add(tempEight.get(0));
tempEight.remove(0);
}
}
Collections.reverse(streamSplit);
System.out.print("Your original bit-stream was: ");
for (int i = 0; i < numberOfStreams * 2; i++)
{
for (int j = 0; j < 4; j++)
{
System.out.print(streamSplit.get(j + (i * 4)));
}
System.out.print(" ");
}
System.out.println();
System.out.print("Your new HammingCode bit-stream is: ");
for (int i = 0; i < numberOfStreams * 3; i++)
{
for (int j = 0; j < 4; j++)
{
System.out.print(finalStream.get(j + (i * 4)));
}
System.out.print(" ");
}
System.out.println();
}
public Boolean checkYes = false;
public Boolean checkNo = false;
private JFrame frame = new JFrame("Absolute Layout Example");
private JPanel contentPane = new JPanel();
private JLabel topText = new JLabel("Welcome to my Hamming Code Generator", JLabel.CENTER);
private JTextField inputText = new JTextField();
private JButton yes = new JButton("YES");
private JButton no = new JButton("NO");
public void init()
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane.setOpaque(true);
contentPane.setBackground(Color.WHITE);
contentPane.setLayout(null);
topText.setLocation(0, 0);
topText.setSize(400, 50);
topText.setBorder(BorderFactory.createLineBorder(Color.black));
inputText.setLocation(0,50);
inputText.setSize(400,75);
inputText.setBorder(BorderFactory.createLineBorder(Color.black));
yes.setSize(80, 40);
yes.setLocation(60, 135);
no.setSize(80, 40);
no.setLocation(260, 135);
contentPane.add(topText);
contentPane.add(inputText);
contentPane.add(yes);
contentPane.add(no);
frame.setContentPane(contentPane);
frame.setSize(400, 225);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void setTopText(String s)
{
topText.setText(s);
}
public void setYesNo()
{
checkYes = false;
checkNo = false;
}
public Boolean checkYes() {return checkYes;}
public Boolean checkNo() {return checkNo;}
public int checkInput()
{
String temp1 = inputText.getText();
int temp = Integer.parseInt(temp1);
return temp;
}
public void yesChange(String s)
{
yes.setText(s);
}
public void noChange(String s)
{
no.setText(s);
}
}
I find it tough to answer this question not fully knowing what your code is doing, especially the part where you "... do funky #### with that number..."
But I do know that you have significant issues with your program structure, especially within your lengthy run() method where you have numerous nested while (...) loops and do-while loops, code constructs that might seem at home within a linear processing console program but which seems out of place within an event-driven Swing GUI.
What I suggest that you do is try to use some state-dependent coding. For instance, you could give your class the boolean variables, enteringData and dataValidYet, to represent two key states: whether the user is now entering data into the JTextField, and whether that data has yet been validated yet. And then within your JButton ActionListeners, use if and if/else blocks to decide what to do on button push depending on the state of these boolean fields, and likely other key fields of the class.
For a code "skeleton" example, one that doesn't yet do anything, but hopefully will show you the structure I'm talking about:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class StateMachine extends JPanel {
private static final String INITIAL_TITLE = "Please enter your next 8 bits. "
+ "Do not enter more than 8 bits.\n"
+ "Press Enter when done";
private static final String ARE_YOU_FINISHED = "Are you finished entering streams?";
private static final String YES = "Yes";
private static final String ENTER = "Enter";
private static final String NO = "No";
private static int GAP = 8;
private static final int COLUMNS = 30;
// this is a JTextArea built to look like a JLabel
private JTextArea topTextArea = new JTextArea(2, COLUMNS);
private JTextField dataEntryField = new JTextField(COLUMNS);
private JButton yesEnterButton = new JButton(ENTER);
private JButton noButton = new JButton(NO);
private boolean enteringData = true;
private boolean dataValidYet = false;
public StateMachine() {
yesEnterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yesEnterButtonActionPerfromed(e);
}
});
noButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
noButtonActionPerfromed(e);
}
});
topTextArea.setWrapStyleWord(true);
topTextArea.setLineWrap(true);
topTextArea.setFocusable(false);
topTextArea.setEditable(false);
topTextArea.setOpaque(false);
topTextArea.setText(INITIAL_TITLE);
JPanel innerButtonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
innerButtonPanel.add(yesEnterButton);
innerButtonPanel.add(noButton);
JPanel outerButtonPanel = new JPanel();
outerButtonPanel.add(innerButtonPanel);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(topTextArea, BorderLayout.PAGE_START);
add(dataEntryField, BorderLayout.CENTER);
add(outerButtonPanel, BorderLayout.PAGE_END);
}
protected void noButtonActionPerfromed(ActionEvent e) {
// TODO depending on state of enteringData and dataValidYet booleans
// change text in buttons, do things with JTextField data
// set state of enteringData and dataValidYet booleans
if (enteringData) {
// a no press is meaningless if entering data
return;
}
// .... more
}
private void yesEnterButtonActionPerfromed(ActionEvent e) {
// TODO depending on state of enteringData and dataValidYet booleans
// change text in buttons, do things with JTextField data
// set state of enteringData and dataValidYet booleans
if (enteringData) {
topTextArea.setText(ARE_YOU_FINISHED);
yesEnterButton.setText(YES);
yesEnterButton.setActionCommand(YES);
enteringData = false;
return;
}
// .... more
}
private static void createAndShowGui() {
StateMachine mainPanel = new StateMachine();
JFrame frame = new JFrame("State Machine");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also, as a "side" recommendation, one unrelated to your main problem, understand that null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Note that if this were my program, I would use more indirection including creating separate classes to separate out the GUI portion of the program from the logic portion.

How effects are added to the icon of JButton?

I'm trying to make a game like bejeweled or candycrush for homework. It's almost over. but I have a little problem. Now, when I clicked, buttons that have the same icon are popping up and the above pictures are coming instead of exploding. but it's going too fast. How can I slow this event? Or can I add effects?
public class ButtonActionListener implements ActionListener {
public JButton previousButton = null;
public int numP, numC;
public JButton[] buttons=butondeneme.getButton();
#Override
public void actionPerformed(ActionEvent e){
// TODO Auto-generated method stub
JButton currentButton = (JButton)e.getSource();
if (previousButton == null) {
previousButton = currentButton;
return;
}
int numP=Integer.parseInt(((JButton)previousButton).getActionCommand());
int numC=Integer.parseInt(((JButton)currentButton).getActionCommand());
//change picture of icons that clicked
if (numP==(numC+1) || numP==(numC-1) || numP==(numC+8) || numP==(numC-8) ){
Icon previousIcon = previousButton.getIcon();
Icon currentIcon = currentButton.getIcon();
currentButton.setIcon(previousIcon);
previousButton.setIcon(currentIcon);
previousButton = null;
}
else
previousButton=null;
Random r = new Random();
int a = r.nextInt(64);
int b = r.nextInt(64);
int c = r.nextInt(64);
//buttons that have same picture are explode.
for(int i=0; i<63; i++){
if(buttons[i].getIcon().toString()==buttons[i+1].getIcon().toString() &&
buttons[i+1].getIcon().toString()== buttons[i+2].getIcon().toString() ){
//System.out.println("slm");
if(i > 7){
buttons[i].setIcon(buttons[i-8].getIcon());
buttons[i+1].setIcon(buttons[i-7].getIcon());
buttons[i+2].setIcon(buttons[i-6].getIcon());
for(int j = i; j > 0; j=j-8){
if(j > 7){
buttons[j].setIcon(buttons[j-8].getIcon());
buttons[j+1].setIcon(buttons[j-7].getIcon());
buttons[j+2].setIcon(buttons[j-6].getIcon());
}
else{
buttons[j].setIcon(buttons[a].getIcon());
buttons[j+1].setIcon(buttons[b].getIcon());
buttons[j+2].setIcon(buttons[c].getIcon());
}
}
}
else{
buttons[i].setIcon(buttons[a].getIcon());
buttons[i+1].setIcon(buttons[b].getIcon());
buttons[i+2].setIcon(buttons[c].getIcon());
}
}
}
}
}
In this class, I created frame, buttons and random icon.
public class butondeneme extends JFrame{
private JPanel grid;
public String comand;
public static JButton[] buttons;
public String imgName;
public butondeneme(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 640, 640);
grid=new JPanel();
grid.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
grid.setLayout(new GridLayout(8,8,5,5));
buttons = new JButton[64];
//Creating random image for buttons
ActionListener buttonActionListener = new ButtonActionListener();
for (int i = 0; i<buttons.length; i++) {
Random r = new Random();
int a = r.nextInt(9)+1;
switch(a){
case 1 : buttons[i]=new JButton(new ImageIcon("img//Cakal.png"));
break;
case 2 : buttons[i]=new JButton(new ImageIcon("img//BugsBunny.png"));
break;
case 3 : buttons[i]=new JButton(new ImageIcon("img//Pig.png"));
break;
case 4 : buttons[i]=new JButton(new ImageIcon("img//Taz.png"));
break;
case 5 : buttons[i]=new JButton(new ImageIcon("img//Sam.png"));
break;
case 6 : buttons[i]=new JButton(new ImageIcon("img//DuffyDuck.png"));
break;
case 7 : buttons[i]=new JButton(new ImageIcon("img//Tweety.png"));
break;
case 8 : buttons[i]=new JButton(new ImageIcon("img//Slyvester.png"));
break;
case 9 : buttons[i]=new JButton(new ImageIcon("img//RoadRunner.png"));
break;
}
//Adding number to find easily
comand=Integer.toString(i);
//Get ImageIcon name
imgName=((ImageIcon)buttons[i].getIcon()).toString();
buttons[i].addActionListener(buttonActionListener);
buttons[i].setActionCommand(comand);
grid.add(buttons[i]);
}
add(grid);
}
static JButton[] getButton(){
return buttons;
}
public static void main(String[] args){
butondeneme erdem=new butondeneme();
erdem.setVisible(true);
}
}
When you want to add a delay to such an "animation", then you have to use Thread.sleep(ms).
But this code is contained in the actionPerformed method, so you can't use Thread.sleep(ms) directly, because this would block the Event-Dispatch-Thread (EDT) and the GUI would become inresponsive.
So you have to perform this "animation" with an own Thread.
But you may not call Button#setIcon(...) on a different thread than the EDT.
So the actual solution is a bit tricky here - you have to "ping-pong" the responsibility between the two threads (alternatively, you could use a javax.swing.Timer, but I think this might require even more restructuring of your original code... Although I did not entirely understand what you are doing there (that is, which icons you are changing there for which reason)).
However, the general structure of such an approach could roughly look like this:
#Override
public void actionPerformed(ActionEvent e){
...
//buttons that have same picture are explode.
startAnimation();
}
private void startAnimation()
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
runAnimation();
}
});
t.start();
}
private int a;
private int b;
private int c;
private void runAnimation()
{
Random r = new Random();
a = r.nextInt(64);
b = r.nextInt(64);
c = r.nextInt(64);
for(int i=0; i<63; i++)
{
final int iFinal = i;
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
doAnimationStep(iFinal);
}
});
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
return;
}
}
}
private void doAnimationStep(int i)
{
String s0 = buttons[i].getIcon().toString();
String s1 = buttons[i+1].getIcon().toString();
String s2 = buttons[i+2].getIcon().toString();
if(s0.equals(s1) && s1.equals(s2))
{
//System.out.println("slm");
if(i > 7){
buttons[i].setIcon(buttons[i-8].getIcon());
buttons[i+1].setIcon(buttons[i-7].getIcon());
buttons[i+2].setIcon(buttons[i-6].getIcon());
for(int j = i; j > 0; j=j-8){
if(j > 7){
buttons[j].setIcon(buttons[j-8].getIcon());
buttons[j+1].setIcon(buttons[j-7].getIcon());
buttons[j+2].setIcon(buttons[j-6].getIcon());
}
else{
buttons[j].setIcon(buttons[a].getIcon());
buttons[j+1].setIcon(buttons[b].getIcon());
buttons[j+2].setIcon(buttons[c].getIcon());
}
}
}
else{
buttons[i].setIcon(buttons[a].getIcon());
buttons[i+1].setIcon(buttons[b].getIcon());
buttons[i+2].setIcon(buttons[c].getIcon());
}
}
}
BTW: You should not compare String objects with if (string0==string1) ... but always with if (string0.equals(string1)) ...

Categories