Using an action to update an instance variable in another class - java

So I'm trying to use Key Bindings, and the action map's put() method takes an action and a string parameter.
/* all declartion is above
* the class extends JPanel so the keyword "this" can be used
* xlist is an ArrayList of Integers
* keyActionRight ka = new keyActionRight(x); is declared above, where x is a global int
* this is part of the keyBindingsTest class */
xlist.add(x);
im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right");
am = this.getActionMap();
am.put("right", ka);
System.out.println(ka.getNextX(xlist)); //Any way for this to be called just like if I printed in the actionPerformed of the action class?
This is the keyActionRight class. It is an action as you get an action when you extend AbstractAction:
public class keyActionRight extends
AbstractAction
{
private int x;
private ArrayList<Integer> xlist;
public keyActionRight(int x)
{
this.x = x;
xlist = new ArrayList<Integer>();
xlist.add(x);
}
public int getNextX(ArrayList<Integer> x)
{
x = xlist;
return x.get(0);
}
public void actionPerformed(ActionEvent e)
{
if(x != 440)
{
x++; //this incrementing works fine
xlist.add(0, x); //this updates xlist fine
}
}
}
The goal is essentially just to update the instance variable x in the keyBindingsTest class whenever I press or hold the right arrow key. The x in the Action class is updating just fine when I do this (I printed it out and it works). It's been pointed out why it's not updating - it is only being called once in the print statement. I want to know if there is a way to make this work with a separate class for the action or if I need to take a different approach.
I could try making the Action in the keyBindingsTest class, but that was giving me strange errors last time I tried that. Any help would be appreciated.
Thanks.

You have faulty assumptions:
xlist.add(x);
im = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "right");
am = this.getActionMap();
am.put("right", ka);
// **** the comment below is incorrect ****
//only prints out zero - should print out ascending values of x as I hold down the right arrow key
System.out.println(ka.getNextX(xlist));
The assumption you're making is that the println gets called when the Key Bindings action is called, but that is simply not so. The println is called once and only once when the key binding is created. The only code that gets called repeatedly is that which is within the Action's actionPerformed method, the code that is called in response to an event.
If you want code called multiple times and in response to an event, it must be placed within an event listener, not the listener's creation code.

Related

Java Methods when working with Swing - It is possible to call event method in another event method in same class?

so I am new to java and using right now swing. I have an method (first method) that does some code when I type in specific jField and release key.
I also have jCheckBox, so when I tick or untick checkbox it does some action, this is second method.
So I want when I tick or untick checkbox make it call my first method and first method must do it's code. But it seems I have problem and I can't call this method fro some reason.
Part of first method:
private void bruttoTextFieldKeyReleased(java.awt.event.KeyEvent evt) {
//code
}
Second method trying to call first method:
private void pensionCheckBoxStateChanged(javax.swing.event.ChangeEventevt) {
bruttoTextFieldKeyReleased();
}
Those methods were created with this menu
And this is hint, but I am not sure what I need to do, it required some KeyEvent? I just want launch one method from another, not putting any value and not returning.
Error Hint
To expand a bit on Luvy's comment, and convert it into an answer using your code.
Right now, the first method takes a KeyEvent, and looks like this:
private void bruttoTextFieldKeyReleased(java.awt.event.KeyEvent evt) {
double salaryBrutto = Double.parseDouble(bruttoTextField.getText());
double taxPensRound;
if (pensionCheckBox.isSelected()) {
double taxPens = salaryBrutto * TAX_PENS;
//more code
Going by the screenshots, it looks like you most likely auto-created it from a GUI builder, and are not using the KeyEvent evt parameter. You aren't able to call this method without passing in a KeyEvent, which is why pensionCheckBoxStateChanged cannot call it.
Luvy's suggestion would be to create a new method out of the bruttoTextFieldKeyReleased button, so afterwords you would have two methods:
private void bruttoTextFieldKeyReleased(java.awt.event.KeyEvent evt) {
calculatePensionInformation();
//maybe do something with evt later on
}
and
private void calculatePensionInformation() {
double salaryBrutto = Double.parseDouble(bruttoTextField.getText());
double taxPensRound;
if (pensionCheckBox.isSelected()) {
double taxPens = salaryBrutto * TAX_PENS;
//more code
}
At this point, you could change your existing second method to be:
private void pensionCheckBoxStateChanged(javax.swing.event.ChangeEventevt) {
calculatePensionInformation();
}
And it would work as expected, since calculatePensionInformation requires no parameters, and you are passing in no parameters.

Find the index of a Java JButton Array

I am writing a Java program in which I have an array of buttons (not a calculator!) and I'm looking for an efficient way to know which JButton was clicked. From what I know of Java so far, the only way to do it is to have them all in the same ActionListener and then loop through looking for a match.
Another solution I just thought of would be to extend JButton to include a unique ID number variable in the constructor. It seems that should work when the event object is cast to JButton after an instanceof check. Kind of like using VB's Tag property which is assigned to the index number.
Is there a better/more elegant way?
Is there a better/more elegant way?
yes to use (for almost JComponents) the put/getClientProperty, there you can to set endless number of properties, can be multiplied in contrast with setName / setActionCommand / etc
getClientProperty can be used as unique identificator for Swing Action or EventHandler (rather than to use ActionListener)
Links to the Javadocs: putClientProperty(), getClientProperty()
Here is an example from programm I writing last few months.
I have an enum called Position
public enum Position {
BB, SB, BU, CO, MP3, MP2, MP1, EP3, EP2, EP1;
}
and have some JToggleButtons each of them holding ist own Position.
public class PositionButton extends JToggleButton {
private final Position p;
public PositionButton(Position p) {
this.p = p;
setText(p.toString());
setActionCommand(p.toString());
}
public Position getPosition() {
return p;
}
}
This allows you to create Buttons in a Loop and get value direct from button without comparing:
ActionListener positionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
PositionButton b = (PositionButton )e.getSource();
Position p = b.getPosition();
//do something with Position
}
}
for (Position p : Position.values()) {
PositionButton b = new PositionButton (p);
b.addActionListener(positionListener);
}
The way I've done it before was using actionPerformed for different buttons. I like it more compared to some other ways I've seen.
public void actionPerformed(ActionEvent clicking)
{
if (clicking.getSource() == button[0])
// Do this
if (clicking.getSource() == button[1])
// Do something different
}
Since you built an array, you can throw the ID right where that 0 is and that's your unique ID.
Add a separate action listener for each button.

problems with returning a value java

I have this method that contains a MouseEvent. How do I return the idu variable?
it is like a method in a method or how to call it and I can't figure out how top return the idu variable.
public int getId() {
int idu;
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
JTable target = (JTable)e.getSource();
int row = target.getSelectedRow();
Object record = data[row][0];
idu = (Integer) record;
}
}
});
return idu;
}
In nearly all cases the 'listener' pattern involves registering listeners with methods that do not have return values. In general a return value from a listener is meaningless because it's being returned to caller that has no context about what to do with it.
It seems to me you have misunderstood a few things in the code you have posted:
1. it makes little sense to register a listener in a 'getter' method. The listener needs to be registered once, generally in the class's constructor
2. registering a listener doesn't do anything on its own: it just tells the handler to call your method when an event occurs (in this case clicking a mouse).
3. unless you are reusing the listener in several places (which you are not in this code fragement because it's an anonymous class) then you don't need to get the event source - you should already have it as a member field in the class.
So the answer in your case is that your JTable should be a member field of your class. Then the getId method need only return the selected row of the table. There is no need to register a mouse listener at all as the selected row is available in JTable without any additional work.
Think about the following question:
When do expect to have the idu value ready for use - when the method getId() finished running, or when the user clicked the mouse button over the table?
What your code actually do is to register an event listener, kind of like setting an alarm or scheduling a task for later, and then going to sleep, or back to do whatever other task you need to do now.
The value of idu doesn't neccessarily exist when you exit the getId() method, because the code in the mouseClicked() method didn't neccessarily executed yet. It will only execute when the event actually happen.

LWJGL Keyboard loop

I've created a static Input class, that basicly have a method that I can call, which is this:
public static boolean GetKeyDown(int keyCode) {
while(Keyboard.next()) {
Keyboard.enableRepeatEvents(false);
if (Keyboard.getEventKeyState()) {
if (Keyboard.getEventKey() == keyCode) {
return true;
} else {
return false;
}
}
}
return false;
}
And in my game update loop, I've wanted to use this, instead of having to make a single while-loop:
if(Input.GetKeyDown(KeyCode.S)) {
//Something happens
}
if(Input.GetKeyDown(KeyCode.R)) {
//Something happens
}
//etc..
But it seems that only the first one loaded, will work. In this case 'S'. Is there a way for me to do be able to use the others too?
That is because in your GetKeyDown() method, you call Keyboard.next(), when you call that method it removes the Event of the current key from Keyboard, the only gets refilled with Events, when you call Display.update();
NOTE: This method does not query the operating system for new events. To do that, Display.processMessages() (or Display.update()) must be called first.
Source: LWJGL Docs
You Could
Instead you can use the Keyboard.isKeyDown(int key) method, to achieve what you're trying to do.
Though it returns true/false depending on the following.
Returns: true if the key is down according to the last poll()
But that still doesn't quite fix the problem because it relies on the poll() method.
Fixing The Problem
You can fix the problem by creating some custom methods to use with the Keyboard class, as you already did, though as said the Keyboard Events only gets updated when you call the Display.update(); method.
You already got the right idea about which function to create, though you need to split them into, two different methods. You need a secondary method which you call once each time you want to update your keyboard.
public class MyKeyboard {
/*
* Remember that the index where we store the value,
* is the index of the key. Thereby one key might have
* an index of 400, then your array need to have at least
* the same size, to be able to store it.
*/
public static boolean[] keys = new boolean[100]; // 100 is the amount of keys to remember!
public static void update() {
while(Keyboard.next()) {
if (Keyboard.getEventKey() < keys.length) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
}
public static boolean isKeyDown(int key) {
if ((key > 0) && (key < keys.length)) {
return keys[key];
}
return false;
}
}
Remember to only call the MyKeyboard.update() method once per Display.update() I also renamed your GetKeyDown() method to isKeyDown(), because I think that sounds and describes it better, but you can rename it again in your project if you want to.
The above code was made within this answer, without the use of an IDE, etc. So if there's anything wrong with it I apologize, but just comment and I will fix it.
One problem that arises with this method is the lack of rechecking. Since Keyboard.next() only checks the inputs that have occurred in the current frame. A button which was once pressed will remain "pressed" until it is pressed again. I ran into this problem while trying to implement this solution. The answer to this new problem is here:
public static void update() {
for(int i = 0; i < keys.length; i++) {
keys[i] = false;
}
while(Keyboard.next()) {
keys[Keyboard.getEventKey()] = Keyboard.getEventKeyState();
}
}
You must clear the keypresses of the previous frame by setting everything to false.

MVC pattern in java, more than one update method in observer

I'm writing a chess game with gui in mvc design.
Step 1: a main menu pops and you choose a game mod.
Step 2: once you chose, the main menu closes and a new window opens with the board and pieces, then you play with the mouse.
for step 1; I use actionEvent and check the string of the button you clicked on.
for example, you have the button Standard Game, and then model sets up the dataBoard and notifies observer(=view).
for step 2; I use mouseEvent and check relative coordinates x/y, the model does what it does and decides if you can move the piece.
I want to have two update methods in view, one for step 1, the other for step 2.
currently it always goes to the first update.
// this is in the model, initializing once you chose a game mod,
// this is for the first step.
public void initializeGame(String givenString){
abstractGame = factoryGame.createGame(givenString);
abstractGame.startPlaying(boardTest);
setChanged();
notifyObservers(5);
}
// this is in the model, doing stuff, this is for the second step.
public boolean checkGivenCoordinates(int sourceRow, int sourceColumn, int destinationRow, int destinationColumn) throws IncorrectCoordinatesException, IncorrectColorException, InvalidMoveException
{
if(abstractGame.checkCorrectCoordinates(sourceRow, sourceColumn, destinationRow, destinationColumn) == true)
{
abstractGame.checkMove(sourceRow, sourceColumn, destinationRow, destinationColumn);
int [] changeView = {sourceRow, sourceColumn, destinationRow, destinationColumn};
System.out.println("Model : in the move ");
setChanged();
notifyObservers(changeView);
return true;
}
else
throw new IncorrectCoordinatesException();
}
// Called from the Model
public void update(Observable obs, Object obj) { // this is the one it always goes to now.
//who called us and what did they send?
System.out.println ("First update View : Observable is " + obs.getClass() + ", object passed is " + obj.getClass());
} //update()
// Called from the Model
/* this is for step 2, but is not invoked.
The array I send holds 4 ints, source row/column and destination row/column.
this is what I do in model just prior to notifying,
supposed to go to step 2's update method,
but as stated, it doesnt.
*/
public void update(Observable obs, int[] obj) {
//who called us and what did they send?
System.out.println ("Second update View : Observable is " + obs.getClass() + ", object passed is " + obj.getClass());
graphBoardView.setMove(obj[0], obj[1], obj[2], obj[3]);
} //update()
so,
1) can I have more than one update in a single class?
2) if so, how can I direct notifying to the correct update method?
3) if q1 isn't possible, how can I bypass this?
4) could it be that Object obj causes it to always go to the first one?
thanks in advance,
ariel.
You can indeed have more than one method of the same name, but with different parameters, in the same class.
You can see what you're seeing when the compiler cant determine which method to use if there is ambiguity - remember that that an array is an Object too.
With a simple test case it should work:
new Test().update(new Observable(), new int[]{1, 2});
new Test().update(new Observable(), new Object());
The correct methods get invoked.
How are you calling your methods?

Categories

Resources