I have most of my java application done already, so I am now logging all user activity. One thing I want to keep track of is whenever one of my checkboxes is checked and unchecked. I am able to read at the end if the object is checked or unchecked, however I want to know each time the checkbox is used in real time. I want to know how many times the user checks and unchecks the box. Right now I am using something similar to this syntax, but it doesn't print a statement each time the checkbox is checked and unchecked.
public CheckboxAction(String text) {
super(text);
}
#Override
public void actionPerformed(ActionEvent event) {
JCheckBox cbLog = (JCheckBox) event.getSource();
if (cbLog.isSelected()) {
System.out.println("Logging is enabled");
} else {
System.out.println("Logging is disabled");
}
}
An ItemListener seems appropriate here
yourJCheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent arg0) {
if (yourJCheckBox.isSelected()) {
// Code to execute when it's selected
}
else {
// Code to execute when not selected
}
}
});
First and foremost, do not override actionPerformed. If you need to - at least call super.actionPerformed before performing your action. Best way is to use addActionListener or in this case as #BoDidely mentioned use an addItemListener.
Related
I am using this code to create a JCheckBox
private final JCheckBox cbDisplayMessage = new JCheckBox("Display");
cbDisplayMessage.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if(e.getItemSelectable() == cbDisplayMessage) {
if(cbDisplayMessage.isSelected()) {
cbDisplayMessage.setSelected(false);
} else {
cbDisplayMessage.setSelected(true);
}
}
}
});
When I run this it causes an StackOverflow error on setSelected(true). Can't figure out what I am doing wrong. Any ideas appreciated....
You can try with ActionListener instead of ItemListener as shown below without causing StackOverflow error.
cbDisplayMessage.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (cbDisplayMessage.isSelected()) {
cbDisplayMessage.setSelected(false);
} else {
cbDisplayMessage.setSelected(true);
}
}
});
There is no need to check the source of the event again because you are sure that you have added this listener on the same object. This is required only if same listener is added for more components.
-- EDIT--
Now Your requirement is clear to me. If you want to toggle the state of the check box then there is no need to do it using listener because that's the default behavior of the check box.
Your listener is called every time the state changes, but you trigger a new state change from within that listener, so each state change results in that listener being called over and over again until your stack is full. Your setup has to be a bit more complicated to do something like that - if you want to change the state of the component you're listening to, you'll want to remove its listener(s), fire your programmatic state change, then re-add them.
Clicking the button should toggle between setting the textfield as editable or not. When the GUI opens the textfield should be editable.
public void actionPerformed( ActionEvent evt)
{
if(inputField.setEditable() == (true))
{
inputField.setEditable(false);
}
else
{
inputField.setEditable(true);
resultMessage.setText("");
resultMessage.setText("Edit Button Pressed");
}
What am i doing wrong here?
I know that the else statement is right just the start of the IF is wrong, i'm not sure where i'm going wrong.
Additional question:
public void actionPerformed( ActionEvent evt)
{
if(inputField.isEditable() == (true))
{
inputField.setEditable(false);
}
else
{
inputField.setEditable(true);
inputField.setText("");
resultMessage.setText("Edit Button Pressed");
}
if(inputField.getBackground() == Color.RED)
{
inputField.setBackground(Color.WHITE);
}
else
{
inputField.setBackground(Color.RED);
resultMessage.setText("Colour Button Pressed");
}
}
I now currently have 2 IFs but obviously the first IF will stop the second IF from working how do i get around this so that when i press one button that will do the setEditable section and when i press the other button it will do my change colour?
You're checking if setEditable() is true which really makes no sense; after all, it returns void and requires a boolean parameter. The bigger problem is that you shouldn't be checking the editable status via a "setter" field but rather a "getter" field, here isEditable()
if (inputField.isEditable()) {
//....
} else {
//...
}
As MadProgrammer points out, this information is all readily available in the Java API, something you'll want to get very familiar with.
Edit, regarding your new question:
I now currently have 2 IFs but obviously the first IF will stop the second IF from working how do i get around this so that when i press one button that will do the setEditable section and when i press the other button it will do my change colour?
No, the first if/else will have no effect on the second if/else block. Your problem lies elsewhere. Consider asking this with additional information as a new question on stackoverflow if still suck.
Also note that this is unnecessarily wordy:
if(inputField.isEditable() == (true))
and is better written as I have it above:
if (inputField.isEditable())
I have implemented ListSelectionListener as you can see below, so that after a specific line in the first table is being chosen, the second table gets updated accordingly.
class SelectionListener implements ListSelectionListener {
public SelectionListener(){}
#Override
public void valueChanged(ListSelectionEvent e)
{
if (e.getSource() == myTrumpsAndMessages.jTable1.getSelectionModel()
&& myTrumpsAndMessages.jTable1.getRowSelectionAllowed()
&& e.getValueIsAdjusting())
{
int selected = myTrumpsAndMessages.jTable1.getSelectedRow();
clearjTable(jTable4);
showSubscribers(selected);
}
}
}
Is there a way to invoke the listener not only when the mouse is choosing, but also when the choice is being made from the keyboard?
The reason for the unusual experience - no notification on selection via keyboard - is a subtle different setting of valueIsAdjusting for keyboard vs. mouse-triggered selection events:
keyboard triggered selection (even with modifiers) only fires once (with adjusting == false)
mouse triggered selection always fires twice (first with true, second with false)
That fact combined with the unusual logic (which #Robin spotted, +1 to him :-)
if (e.getSource() == myTrumpsAndMessages.jTable1.getSelectionModel()
&& myTrumpsAndMessages.jTable1.getRowSelectionAllowed()
// typo/misunderstanding or feature? doing stuff only when adjusting
&& e.getValueIsAdjusting())
(reacting only if the selection is adjusting) leads to not seeing keyboard triggered changes.
Is there a way to invoke the listener not only when the mouse is choosing, but also when the choice is being made from the keyboard?
The listener will be triggered, independent of the source of the selection change. So yes, this is perfectly possible and even the default behavior. So nothing special must be done to get this working.
Looking at the code of your listener, I would suggest to rewrite it to
class SelectionListener implements ListSelectionListener {
public SelectionListener(){}
#Override
public void valueChanged(ListSelectionEvent e){
if ( e.getValueIsAdjusting() ){
return;
}
if (e.getSource() == myTrumpsAndMessages.jTable1.getSelectionModel() &&
myTrumpsAndMessages.jTable1.getRowSelectionAllowed() ) {
int selected = myTrumpsAndMessages.jTable1.getSelectedRow();
clearjTable(jTable4);
showSubscribers(selected);
}
}
}
Note the quick break from the method when getValueIsAdjusting() returns true as this is the behavior you want in most cases.
I've just tried a ListSelectionListener and the valueChanged() event is actually being triggered on keyboard selection change as well. See my example below:
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
System.out.println(list.getSelectedValue());
}
});
I am making a text based game, but I am having a rather large problem. This problem is that when I assign a new ActionListener to a button that already has an ActionListener assigned to it, it does both of the actions. Here's my code:
while(shouldLoop) {
if(Player.loc == 1) {
left.setText("Do nothing");
left.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nar1.setText("You are still in a dungeon."); //Here's my first assignment
}
});
right.setText("Pick the lock");
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Player.loc = 2;
}
});
} if(Player.loc == 2) {
nar1.setText("You are now outside");
nar2.setText("the door. What now?");
forward.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nar1.setText("You hear a guard.");
nar2.setText("What do you do now?");
Player.loc = 3;
}
});
left.addActionListener(new ActionListener() { //Here's another
#Override //assignment to
public void actionPerformed(ActionEvent e) {//the same button
nar1.setText("You hear a guard."); //so when I press
nar2.setText("What do you do now?"); //it here, it
Player.loc = 3; //performs the
} //original assignment
});
right.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nar1.setText("You hear a guard.");
nar2.setText("What do you do now?");
Player.loc = 3;
}
});
right.setText(rgt);
forward.setText(fwd);
back.setText(bck);
left.setText(lft);
forward.setVisible(true);
} if(Player.loc == 3) {
forward.setVisible(false);
right.setText("Sneak around him!");
left.setText("Fight him!");
}
Thanks for helping,
billofbong
Why don't you just hoist the addActionListener code out of the while loop?
Anonymous inner class listeners look nice in quick and dirty example code, but, in practice, they are a horrible idea. You are learning one of the reasons. Other reasons are that they can't readily be subclassed or modified via Dependency Injection types of things, can't readily be shared (say a button, a toolbar icon, and a menu do the same thing), can't readily be enabled / disabled, (say, "Save As" should be disabled cause nothing is open) etc...
For this use case, break them out into some series of actual Listeners, perhaps organized in a class, array, or some Enums, so they can get swapped in and out.
You are mixing data with code in a bad way by hard-coding the logic of your program in the program itself, and this is the main source of your current and future problems. This won't work, at least not without a lot of kludges.
I suggest you try to separate your data out of your code and make your program more MVC-ish, which stands Model-View-Controller (or one of its many variants). For instance the logic of the program is held by the Model and this includes the non-visualized map of the land being explored, the position of the player in this map, his companions, and your inventory of items that you've collected. So you will likely have several non-GUI classes such as Player, Map, Item (this may be an interface), Room, etc... The Map itself, the possible items, there locations, will be specified in a data file, perhaps a text file for a simple program or a database for a more complex one, and you will need classes to read and parse this file(s).
The View of your program is your GUI, and it should be as dumb as possible. Here is where you'll have your buttons, your text display, and if fancy, a graphical display of your game. No program logic goes in here. None.
The Control will be the Action Listeners that you add to your JButtons and any other code that has to deal with handling user input. But this code could be relatively simple, and it's main task is to pass user interactions to the model. So for instance if the left button is pressed, it may be simply something like
model.leftButtonPress();
Then the model will decide what to do with a left button press, if anything. The model will know where you are in the game for instance, if there's a door to your left or if it's a wall, and based on the state of the model your game will perform the requisite action.
I have a view that has several "flag" buttons (JToggleButton). When the user clicks a flag button, I want to update an array that keeps track of which buttons are flagged. This is pretty simple, and here is what I have done:
...
bflag[i].addItemListener(this); //View listens to state change
...
void itemStateChange(ItemEvent ie) {
try {
jtb = (JToggleButton)ie.getSource();
//Update mark array
}
catch (ClassCastException cce) {}
}
This works just fine, but it is ugly with the try/catch. I would much prefer to do something like this:
void itemStateChange(ItemEvent ie) {
handleStateChange(ie.getSource());
}
void handleStateChange(JToggleButton jtb) {
//handle state change
}
void handleStateChange(Object o) {}
This is much nicer, but the only problem is that the Object method will fire even if the source is a JToggleButton (because a JToggleButton is an Object after all).
I have also considered using double dispatch, but this would require overriding the JToggleButton method that fires itemStateChange(), and I don't know what that is (and that seems unsafe).
Any suggestions on how to get this to work without a try/catch, conditional check, etc.?
You will have to check for the Type explicitly I think.
if(ie.getSource() instanceof JToggleButton){
//toggle button logic
}else{
//something else
}
Alternatively you can use anonymous inner classes to implement your listeners and avoid having to check the source altogether.
Something like this:
toggleBtn.addItemListener(new ItemListener(){
public void itemStateChange(ItemEvent ie){
//handle toggle button event
}
});