I have a question concerning the key bindings. I have the following Java code:
private void registerPressedReleasedKey(String keyChar, boolean key, boolean pressedKey) {
// 1. decide if the key is pressed or released
// 2. save key and its action name
// 3. decide, what to do, when the action name is being mentioned
// 4. change the boolean value in actionPerformed(ActionEvent ae)
String keyStatus;
if(pressedKey == true)
keyStatus = "pressed ";
else
keyStatus = "released ";
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(keyStatus + keyChar), keyStatus + keyChar);
getActionMap().put(keyStatus + keyChar, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
key = pressedKey;
}
});
}
Eclipse says to me that key = keyPressed; is wrong, because I only can use final variables. My question is if there is a possibility to access and change key inside the actionPerformed(ActionEvent ae) method.
Answering your question
It is impossible to modify external variables in an anonymous class the way you are trying to since these must be final.
If this was a field of your class, you could use access it directly (in Java > 7) or use an accessor (setter). Since it is not, the way to go would be to use a wrapper: final means you cannot assign a new value, but you can still call its methods and any accessor is basically a method.
Warning notice
I assume your code is incomplete, as in this example, you try to set the variable key, which is not used anywhere.
However, assigning a new value to a parameter is generally a bad practice.
Moreover, getActionMap() & AbstractAction suggest that a Swing component is being used, which means that actionPerformed() will get called by Swing thread, probably even after registerPressedReleaseKey() has finished. As a consequence, updating a parameter for this method makes no sense.
Related
I'm trying to create a small program, where you have to guess words while you are shown a matching anagram. My code is like:
JFrame frame = generateJFrame(BACKGROUND, FOREGROUND);
JLabel display = generateDisplay(BACKGROUND, FOREGROUND);
Pair now = woerter.shuffle();
display.setText(now.getAnagram());
JTextField input = generateInputBox(BACKGROUND, FOREGROUND);
ActionListener action = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
nextWord(now, woerter, display, input);
}
};
input.addActionListener(action);
[...]
private static void nextWord(Pair now, Woerter woerter, JLabel display, JTextField input) {
if (now.getRealWord().equalsIgnoreCase(input.getText())) {
now = woerter.shuffle();
display.setText(now.getAnagram());
input.setText("");
}
}
Now when I execute it, it works the first time (when you type in the correct word and press enter a new anagram is displayed and the inputfield clears), but afterwards it breaks and enter doesn't do anything anymore.
What am I doing wrong? Any help would be appreciated.
Pair now = woerter.shuffle();
You are defining "now" as a local variable.
now = woerter.shuffle();
But you also reference it in your nextWord(…) method, which indicates you have also defined it as a static variable.
Don't define the variable twice. Get rid of the local variable:
//Pair now = woerter.shuffle();
now = woerter.shuffle();
Also, defining methods and variables as static indicates a poor design. Your nextWor() method should not be static and therefore the "now" variable will not need to be static.
Read the section from the Swing tutorial on How to Use Text Fields. The TextDemo code will show you how to better structure your code so that the ActionListener of the text field can access the text field and the rest of the data in your class.
So download the working demo code and modify it to meet your requirements.
What's the best way to keep the value of a javafx Property within specific bounds?
(Or - is this bad practice, existing any reason to never filter values wrapped by javafx properties?)
Example1: avoid negative values in an IntegerProperty
Example2: keep the value of an IntegerProperty within the bounds of a List
First idea: - override IntegerPropertyBase.set(int). It's safe? Actually setValue(int) only calls set(int), but - if this implementation one day changes - the control over the values set goes lost.
Second idea: - override IntegerPropertyBase.invalidate(). But at this point the value already was set.
Will it fit better to javafx properties throw an IllegalArgumentException (or an ArrayIndexOutOfBoundsException, if the wrapped value is the index of an array), or better refuse the value out of bounds, setting back the last value in bounds?
Maybe like this:
class BoundedIntegerProperty extends IntegerPropertyBase {
(...)
int oldValue = defaultValueInBounds;
boolean settingOldValue = false;
public void invalidated() {
if(!settingOldValue){
if(outOfBounds(get())){
settingOldValue = true;
set(oldValue);
} else {
oldValue = get();
}
} else
settingOldValue = false;
}
}
Only throw an Exception in invalidated() for values out of bounds may keep the value of the property out of bounds.
Have I overlooked anything in javafx properties provided to filter values?
(If necessary, please help me improving the possibly bad english of this text...)
In both your examples, there seemed to be a logical default value (eg. if it's required to be positive, negative numbers turn into 0). Assuming you document that well (what the defaults are if the value is invalid), I think your first approach seems like it's on the right path.
I'd recommend starting with a concrete class like SimpleIntegerProperty as the class you're extending (unless there's some reason you chose IntegerPropertyBase instead.
I would then overwrite both the set(int) method and the setValue(Number) method, wrapping the parent in your logic:
/**
* Explanation that values under 0 are set to 0
*/
#Override
public void set(int value){
super.set(value > 0 ? value : 0);
}
/**
* Explanation that values under 0 are set to 0
*/
#Override
public void setValue(Number value){
super.setValue(value.intValue() > 0 ? value : 0);
}
There may be a case where there isn't logical default values (or you just want to reject invalid values). That case makes it a bit harder - you'd actually want to use a method signature of like this so the caller knows if the value changed:
public boolean set(int value)
In order to do that, you'll have to go back quite a few classes - all the way back to ReadOnlyIntegerProperty and implement the setting / invalidating structure yourself.
I would hesitate to use Exceptions to handle the invalid input. It is a legitimate use of exceptions, but my fear is that the Exception would be relied on for validation. Exceptions are very resource intensive, and should only be hit if there's something that needs to be fixed. So it's really about your intentions and how much you trust people using your class to do the right thing (and validate before sending to you).
I believe I understand what you're shooting for better now. You're looking to perform user input validation.
When you're doing your user validation, there's really two ways to approach it:
Validate immediately after any change takes place and provide
feedback
Validate when the focus leaves the input area
With both, you'll be using property listeners - it's just a matter of what property listener you're dealing with.
In the first case you'll listen directly to the property you're validating:
TextField field = new TextField();
field.textProperty().addListener(new ChangeListener<String>(){
#Override
public void changed(ObservableValue<? extends String> value,
String oldValue, String newValue) {
//Do your validation or revert the value
}});
In the second case, you'll listen to the focused property, and validate when focus is lost (you can maintain the last validated value in this listener to help revert the value if necessary):
TextField field = new TextField();
field.focusedProperty().addListener(new ChangeListener<Boolean>(){
String lastValidatedValue = "";
#Override
public void changed(ObservableValue<? extends Boolean> value,
Boolean oldValue, Boolean newValue) {
if(newValue == false && oldValue == true){
//Do your validation and set `lastValidatedValue` if valid
}
}});
Note:
I was assuming you just wanted to put in a fail safe for system code updating the UI. I'll leave my previous answer as I believe it provides useful information as well.
I'm trying to pick up java quickly and looking for a way to set the text of a number of labels in my java app.
What I have is a java app that starts\stops\checks status of windows services. I have a method, which is passed an array of these service names and each of these services has a corresponding label that contains it's status. For example, DummyService1 is contained in the array and there is a label called txt_DummyService1. My method (short version) does the following
public static void Checker(String Array[])
{
//check status of DummyService1
"txt_"+DummyService.Text = "started";
}
I realize that this isn't the way that you do this, but could anybody help me out with the best way to do this?
There's no way to generate a "variable" name from a String in this manner. Yes, you might use reflection, but that already rasies questions about the quality of the design.
Instead. Place each label into a Map keyed by it's name.
private Map<String, JLabel> labelLookup = new HashMap<>(25); // Instance variable.
In you constructor (or where ever you build your UI), add each label to the Map.
/* Other UI code */
labelLookup.put("DummyService1", txt_DummyService1);
Now, when you need to do you changes, simply look up the label by it's name
// You had better have a VERY good reason for making this static...
public void checker(String services[])
{
for (String service : services) {
JLabel label = labelLookup.get(service);
if (label != null) {
label.setText("Started");
}
}
}
For example...
Actually I was looking for something more like the following
public static void Checker()
{
try
{
Object Instance = getClass().getDeclaredField("txt_DummyService").get(this);
Method m = Instance.getClass().getMethod("setText",String.class);
m.invoke(Instance,"started");
}
catch(Exception e)
{
//exception handling
}
}
You cannot manipulate variable names at runtime since these are only available to the compiler. One solution to your problem is to keep a Map<String, JLabel> (assuming you are using JLabel and not some other component) to associate a name with each JLabel. I'm sure there are several other possible solutions depending on the exact design of your code.
I'm making a game, and i want the controls to be editable. well, i've got that part down, but they are being read and changed in a .txt file. that is the way i wanted it to work for now. the values are stored as the key value (ie. KeyEvent.VK_W is equal to 83, so the value for the line is 83). I also have it reading the values and saving them to a String array variable in my core class. In my key event class, the one that handles the pushing of the keys, i have it refering to the array to check if a command key was pushed. i'm continuously getting this error: case expressions must be constant expressions when i try it. here is the WRONG code:
switch(key){
case Integer.parseInt(commands[1]):
...
break;
}
and i get that error. the value of commands[1] is 83. it is the value for "W". here is my declaration of the variable:
for (int i = 0; i < commands.length; i++) {
commands[i] = io.readSpecificLine(FILES.controlsFileFinalDir,
i + 1);
}
and if i have it print out every value, it does work. i've tried making the array final but that didnt work. i've run across the solution before, about 2 years ago, but i cant find it again. does anyone have any ideas on how to fix this? thanks in advance!
As the compiler says, the case expressions must be constant expressions. You can't use an array element as a case expression. You can simply use an if/else if/else clause instead.
You can't use non-constant expressions in case statements. An alternative approach is to build a map from values to the actions. So instead of this (which doesn't actually make any sense to me):
switch (key) {
case Integer.parseInt(commands[1]):
// action 1
break;
// other cases...
default:
// default action
}
You can do something like this:
static Map<Integer, Runnable> keyMap = new HashMap<Integer, Runnable>();
static {
keyMap.put(83, new Runnable() {
#Override
public void run() {
// actions for code 83
}
});
. . .
}
(If it makes more sense, this could also be done on a per-instance basis instead of as a static map.) Then later:
Runnable action = keyMap.get(Integer.parseInt(commands[1]));
if (action != null) {
action.run();
} else {
// default action
}
If you need to pass variables to your actions, you can define your own interface instead of using Runnable for the actions.
I was going over the tutorials for GWT and was confused by this piece of code.
The code is at GWT tutorials
private void addStock() {
final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
// Stock code must be between 1 and 10 chars that are numbers, letters,
// or dots.
if (!symbol.matches("^[0-9A-Z\\.]{1,10}$")) {
Window.alert("'" + symbol + "' is not a valid symbol.");
newSymbolTextBox.selectAll();
return;
}
newSymbolTextBox.setText("");
// Don't add the stock if it's already in the table.
if (stocks.contains(symbol))
return;
// Add the stock to the table.
int row = stocksFlexTable.getRowCount();
stocks.add(symbol);
stocksFlexTable.setText(row, 0, symbol);
stocksFlexTable.setWidget(row, 2, new Label());
stocksFlexTable.getCellFormatter().addStyleName(row, 1,
"watchListNumericColumn");
stocksFlexTable.getCellFormatter().addStyleName(row, 2,
"watchListNumericColumn");
stocksFlexTable.getCellFormatter().addStyleName(row, 3,
"watchListRemoveColumn");
// Add a button to remove this stock from the table.
Button removeStockButton = new Button("x");
removeStockButton.addStyleDependentName("remove");
removeStockButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
int removedIndex = stocks.indexOf(symbol);
stocks.remove(removedIndex);
stocksFlexTable.removeRow(removedIndex + 1);
}
});
stocksFlexTable.setWidget(row, 3, removeStockButton);
// Get the stock price.
refreshWatchList();
}
The problem part is the anonymous inner class to add event handling to the removeStockButton. The class' onClick method accepts an event and then retrieves the index of row-to-be-deleted from the ArrayList stocks using the variable symbol.
How will symbol still be in scope when the user actually calls onClick(), i.e. clicks the remove button? If you doubt the correctness of the code, it's from Google engineers and so is correct (plus, it works, I've used it).
Is this some JavaScript trick or do I need Java referesher courses?
This is because and only because symbol was declared as final in the enclosing method.
Here's a link to a brief explanation of anonymous classes in Java : see here.
The language basically takes care of this in the background with a feature called a Closure. The closure binds "symbol" to the class. In order for this to work in Java, the variable must be final (which it is here).
Note the keyword "final" in the declaration of symbol.
(No & Yes, respectively to your final questions.)
Yes, you need to get a refresher on your Java skills. This type of programming is very common in Java. As said by the others: since symbol is final, you can access it from inner classes.