JLabel keeps speeding up on each List Selection - java

list.getSelectionModel().addListSelectionListener(e -> {
timer = new Timer( DELAY, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0) {
StockItem p = list.getSelectedValue();
label.setText(p.toString2());
label.setLocation(label.getLocation().x+1, label.getLocation().y);
if(label.getLocation().x >= frame.getWidth()) {
label.setLocation(0-label.getWidth(),label.getLocation().y);
}
}
});
timer.start();
});
I created a list selection listener to check which current items are being selected inside of a JList, and whatever item is being selected will call a toString() method in another class and set this as the text for a JLabel. However, I just implemented moving text on my JLabel and every time an item in the JList is selected, the speed of the JLabel will increase. I would like it so the speed stays constant but i'm not sure how to do that.
The part of the code that controls the speed is
label.setLocation(label.getLocation().x+1, label.getLocation().y);
Any explanations would be very appreciated thank you

every time an item in the JList is selected, the speed of the JLabel will increase.
Because your code starts another Timer with each selection.
A Timer will keep running until you stop it. Since you never stop it you will have multiple Timers running at the same time.
You want to define your Timer outside of the ListSelectionListener.
Then the logic inside the listener becomes something like:
(if !timer.isRunning())
timer.start();
Or the question is do you even need the ListSelectionListener?
You could just start the Timer automatically when you class is created. Each time it fires the logic will get the selected value.
Of course with this approach you need to make sure the getSelectedValue() method doesn't return null (as no item will be selected when the GUI is first displayed).

Related

Java if two buttons have the same icons increase score and if not display "wrong match"

Creating a really basic Memory game using Java Swing. I created my GUI with a list of blank buttons where I set the icon property to none.
My code for some of the buttons is:
private void tbtnCard3ActionPerformed(java.awt.event.ActionEvent evt) {
tbtnCard3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card3Logo.png")));
if(tbtnCard5.isSelected()){
score++;
lblScore.setText(""+score);
}
}
private void tbtnCard4ActionPerformed(java.awt.event.ActionEvent evt) {
tbtnCard4.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card7EWaste.png")));
if(tbtnCard7.isSelected()){
score++;
lblScore.setText(""+score);
}
}
private void tbtnCard5ActionPerformed(java.awt.event.ActionEvent evt) {
tbtnCard5.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Card3Logo.png")));
if(tbtnCard3.isSelected()){
score++;
lblScore.setText(""+score);
}
}
I have about 20 toggle buttons and for example the code above works and the scores go up by 1 when a match is found. So for tbtnCard3, if tbtnCard5 is selected the score goes up by 1. Now my question is how would I make it so that if tbtnCard3 is selected but tbtnCard 5 is not selected, display "Wrong Match". Since im using if Selected I'm not too sure how to display "wrong match" when the case is false. It doesn't make sense to say else ifSelected as no parameters can be put either....
In my opinion, the OPs suggestion is not a good approach. You do not want the listener of one button to be "aware" of some other component unnecessarily. Suppose you have an 8-by-8 grid with toggle buttons. You don't want each toggle button listener to be aware of the other 63 toggle buttons.
I believe there is a much simpler (and cleaner) approach. What you want is for the toggle button listener to register and deregister the toggle when the state of the button changes. Let say, you add the toggle button to or remove from a list (most likely a custom class) where you can trigger some logic when the list size reaches two. Then, depending on the outcome of the comparison, it will count a match (and disable these two toggle buttons in the current state), or will display some message like "Try again" and then toggle the buttons to hide the image.
In pseudocode, this will look something like this:
public class ToggleListener implements ItemListener {
public void actionPerformed (ItemEvent event) {
JToggleButton button = (JToggleButton) event.getSource();
if (event.getStateChange()==ItemEvent.SELECTED) {
// TODO Add the button to your list..
} else {
// remove button
}
}
}
In your Swing application, you can create a single instance of the above listener and add it to every single toggle button. And, as you can see, this listener is only responsible to register and unregister the component associated with the triggered event.
The "List Listener" on the other hand, is responsible to trigger the comparison logic when the size of the list reaches two. So, if you click on the same toggle button over and over again, the only thing the button listener will do is add or remove the button from the list depending on the button's current state. However, once a second button is toggled to reveal its image, the list listener will trigger the comparison logic. I am not 100% sure, but I think you could use JavaFX ObservableList interface or one of its implementing classes to do this. If the ListChangeListener.Change class is not suitable to figure out the size of the list to trigger the logic, you will have to implement this on your own. Regardless, in pseudocode, you need to do something like this:
public void onListChange(Event event) {
if (list.size() == 2 && btn1.getIconName().equals(btn2.getIconName())) {
displayMatchMessage();
btn1.setEnabled(false);
btn2.setEnabled(false);
list.clear(); // you should remove matched items from list manually
} else {
displayMismatchMessage();
btn1.setSelected(false); // flip the card
btn2.setSelected(false); // flip the card
// list.clear(); // you should not need this because the setSelected should trigger the Item Listener which should remove item from list.
}
}
Doing something like this is a much cleaner implementation where the button listener have a single job to do and the "list listener" has another job to do. Neither one encroaches on the other's job.

how to identify a button in a group of loop generated buttons?

I have a group of loop generated buttons made with this code
this.panelCuerpo.setLayout(new GridLayout(4,5));
for(int i = 1; i<=20; i++){
final JToggleButton b = new JToggleButton(new ImageIcon("/images/available.png"));
panelCuerpo.add(b);
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/available1.png")));
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt){
if(b.isSelected()){
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/busy1.png")));
cantidadBoletas++;
}else{
b.setIcon(new javax.swing.ImageIcon(getClass().getResource("/Images/available1.png")));
cantidadBoletas--;
}
System.out.println(cantidadBoletas);
}
});
}
The problem here is that I can't use setText() to compare later cause there's no property to hide that text. How can I compare it later?
PS. Each button has a consecutive number, it's easy to assign that number. The real problem lies in where to put it.
You could:
Use the Action API, which lets you trigger the selected state of the associated button. This allows you to de-couple the button from the underlying "action" it should take. Take a look at How to Use ActionsHow to Use Actions for more details
Use the actionCommand property of the JButton. This allows you to have some kind of "identifier" associated with the button which is independent of the text
Use an array or List to maintain a reference to the buttons
You can maintain a List<JToggleButton> of JToggleButton and fetch element later by the index. Apart from that instead of adding ActionListener in loop you can implement ActionListener which can be used for all buttons and you just need to write b.addActionListener(this); in loop.
NOTE : better to start from i = 0 instead of 1

Pause game and add flashing text Java

I have this loop
while (true) {
game.update();
view.repaint();
Thread.sleep(DELAY);
}
In the game.update various components of the game have their position changed and those updates are reflected when the repaint() method is called on the view. The view extends JComponent and loops through the game objects and calls their print methods.
What I want to do is have a boolean called nextLevel in the game and if it's true Flash text on the screen for the player to notify them that they're going onto the next level. Maybe flash 4-5 times. Then continue the game.
Is this possible? I have been playing around with Thead.Sleep() but this only seems to pause the displaying and in the background the game is still going on.
Any ideas on how to do this?
Maybe you want to avoid threading by using a Timer object.
an example like that could be
int flashTimer = 0;
if(nextLevel) {
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
//flash something method here
flashTimer++;
}
});
timer.start();
}
and then check your flashTimer if it reaches the number you want then just stop the timer by timer.stop();
Just an idea which seems to me a bit simpler. the 1000 value is milliseconds which is passed and executes the code inside the actionPerformed method every 1 sec.
Hope it helped

How to change make tabbed panels change on their own every few seconds?

I am currently learning Swing and am trying to create a simple program that stores information about different sports teams.
I have created multiple tabbed panels which all hold various information about each team. I would like to be able to have a button that when press displays each tabbed panel say every 10 seconds or so - sort of a slide show effect.
I have read up on action listeners but have not spent a lot of time on them as of yet so i am having trouble implementing this. I would be very grateful if anyone could maybe help me or just give me a push in the right direction. I have posted a snippet of code that i have atempted but i am at a loss about what to actually put inside the loop to achieve this.
slides.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent actionEvent){
for(int i = 0; i<arrayList.size(); i++)
{
//code that changes the tabbed panels every few seconds.
}
}
});
I have created multiple tabbed panels which all hold various various information about each team.
Rather you should focus on creating a JPanel that can display team stats, and not so much JTabbedPanes. The JPanel can then be displayed in a JTabbedPane if desired.
I would use a CardLayout to swap JPanels, and then a Swing Timer to do the swapping. However if you use a single JPanel to display the stats, then you could even display one single JPanel and simply change the model (Team Stats information) that is displayed in it rather than swap JPanels.
As to what to put in your ActionListener, it will not be a for loop at all, but rather a Swing Timer, and you can read about it here: Swing Timer Tutorial.
e.g.,
slides.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent){
int timerDelay = 10 * 1000; // 10 seconds
new Timer(timerDelay, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent evt){
if (count < maxCount) {
// code to show the team data for the count index
count++;
} else {
((Timer) evt.getSource()).stop(); // stop timer
}
}
}).start();
}
});

Table model update + fireTableDataChanged() disrupts table selection

I have a JTable that tracks the amount of time a person has been waiting to be seated at a restaurant. My problem is that every second, when the timer 'ticks', the selection that is on a row is removed. In other words, if you click a row it becomes highlighted with a blue background and outline, but then when the timer ticks the blue goes away.
ActionListener actListner = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
aTable.updateTime();
}
};
Timer timer = new Timer(1000, actListner);
timer.start();
This is in the main class
public void updateTime()
{
data.updateTime();
fireTableDataChanged();
}
This is in the table model
public void updateTime()
{
Date newTime = new Date();
for (int i = 0; i < startTime.size(); i++)
{
this.setTimeWaiting(i, hoursMin.format(new Date(newTime.getTime() - startTime.get(i).getTime())));
}
}
This is in the data model.
fireTableDataChanged() tells the table that the data may have changed in any way, and essentially you use it when the entire data set has completely changed (e.g. you've replaced all of the previous contents with new information). The table will redraw itself from scratch and may also clear the selection.
Instead, use the more conservative fireTableCellUpdated(int,int) and specify each cell that may have been modified due to the time change (presumably, everything in the "wait time" column).
You could also use fireTableRowsUpdated(int,int) and specify the entire range of rows that have been updated in one call, but generally it's better to stick to the conservative side to minimize unnecessary redraws.

Categories

Resources