(A general Question)
I have an assignment in which I have to build a sudoku and I thought about the classes/logic to build it and thought I could use an advice.
I want to use a JFrame and build on it a JPanel with TextFields (the user is supposed to "solve" the sudoku).
I have a class called "DrawSudoku" which draws an empty board. I have to draw an empty board first, so the "user" can type numbers in it.
On that board I have to check some logic. So I have to access the textFields themselves.
So far that's all I've done. Now I am thinking about building another class with the "logic" behind the board.
But I've encountered a problem
How do I get the JTextFields that exists on the JPanel, from another class?
Can I have separate classes for the Drawing and Logic Behind it?
Thanks!
On that board I have to check some logic. So I have to access the textFields themselves.
Not necessarily
How do I get the JTextFields that exists on the JPanel, from another class?
How do you assess the state of any object from another object -- via an accessor or "getter" method.
Can I have separate classes for the Drawing and Logic Behind it?
Not only can you, you absolutely should.
If this were my project I would consider doing the following:
First and foremost, create a non-GUI Sudoku model class. This could include:
SudokuCellValue enum (name it what you want), an enum that can hold a value from 1 to 9 as well as possibly an EMPTY value (although you could use null to represent this)
SudokuCell objects, ones that have boolean editable, and holds a single value to the above enum.
SudokuGrid object, a 9 x 9 grid of SudokuCells.
A mechanism to hook listeners into the model so that they are notified of changes in state -- i.e., changes in the SudokuCellValue held by one or more SudokuCell objects. The View (the GUI) will be one of the major listeners to this model, and will change its display when the model's state changers.
I'd create a View class, meaning the GUI,
One that holds a reference to its model (see above)
and one that has attached listeners to its model -- I like to use PropertyChangeListeners for this
I'd hook it up with a grid of JTextFields,
These text fields would use a DocumentFilter to allow the user to either clear the field or enter only 1 through 9 single digit numeric text.
Would be enabled for input (or perhaps better -- focusable for input), based on the editable state of the corresponding model cell.
I'd create a Controller that would control some of the communication between the view and model.
With this type of set up, outside classes could listen for changes to the model and wouldn't have to have any access directly to the JTextFields of the view.
You don't need to have access to the text fields themselves if you include public methods in your DrawSudoku class that your logic class can then call. This would be very similar to writing getter and setter methods for private variables. For example, if you wanted your logic class to be able to write the number "6" into a certain square on the board, you could write a method in DrawSudoku like this:
public void setSquareText(String text, int row, int column) {
// change the appropriate text field here
textField.setText(text);
}
Then, call this method in your logic class, by making an instance of the drawing class:
DrawSudoku drawer = new DrawSudoku();
drawer.setSquareText("6", 1,1);
Alternatively, you could write a method in DrawSudoku that returns a given JTextField, like this:
public JTextField getTextField(int row, int column){
// find the appropriate text field, then return it
return textField;
}
Then, call this method in your logic class to get access to the JTextField, like this:
JTextField textField = drawer.getTextField(1,1);
textField.getText();
textField.setText("6");
I am working on a GUI java program for class where there are 10 numbered buttons in a grid layout. The user is trying to guess a 3 digit number where each digit is unique. When they click a number the corresponding number should be stored as one of the digits in the guess and then the button should be disabled. This is done in the actionPerformed method.
My problem is how to tell which button is disabled.
Currently I am trying to successfully read the value and disable the button for one digit and my code looks like this:
private class NumberListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent click){
Object source = click.getSource();
keyTry1 = getButtonNumber(source);
source.setEnabled(false); //error
}
However at the line I marked error NetBeans complains that source has no method setEnabled, presumably because in the method it is declared as type Object. However if I try to declare it as JButton I cannot use click.getsource();
I know I could go the brute force approach and have a long string of if/else statements or even another method which disables the button based on the number it represents, but I was wondering if there is a way to access source as a JButton, since it obviously is referencing a JButton.
source is an Object and does not have an setEnabled method, so it makes no sense to the compiler to allow you to make such a call. You need to first case the instance of source to it's appropriate class type.
Assuming you can guarantee that the source of the action is the button, you can use something like...
((JButton)click.getSource()).setEnabled(false);
If you can't guarantee that source is JButton, but might be component of some kind, you might even be able to use something like...
((Component)click.getSource()).setEnabled(false);
If you can't guarantee that, then you need to make appropriate checks (which you should do anyway)...
Object source = click.getSource();
if (source instanceof JButton) {
((JButton)source).setEnabled(false);
}
This concept is an example of Polymorphism, where one instance of an Object can act like one it's parents
I have a custom component based on the JSlider. It's essentially the same thing only it has 2 thumbs, which I named a DualSlider.
I need to change the maximum value of the slider once in a while, so every time I do, I call updateUI to reflect this.
public void updateUI() {
this.setUI(new DualSliderUI(this));
this.updateLabelUIs();
}
However, the maximum value of the DualSlider when I try to use it is still set at the original value no matter how many times I try to change it while using my program. I can confirm with a few println statements that a new DualSliderUI is being made with the slider that has the new max value, but for whatever reason the original DualSliderUI I initialized the slider with is the one that is in use.
What other things do I have to make sure I do when I update a property so I can avoid this?
1) I can't see reason for usage updateUI() this should be done once time, only when you built this JSlider, never do that repeately
2) you have look at BoundedRangeModel, maybe
Basically I want to display the current value pointed to by the slider handle when in motion (either via mouse or keyboard). I can easily get the current value by adding a new ChangeListener & overriding stateChanged method. But I cant seem to get the current location of the handle.
I can just bite the bullet and create a label at a constant place & update it continuously but I want to display the value just above (or below) the handle.
Not an good or very flexible solution but maybe you can implement your own SliderUI. E.g. using the already defined BasicUI you can access the field thumbRect which contains the values you need.
slider.setUI(new BasicSliderUI(slider) {
public void paintThumb(Graphics g) {
super.paintThumb(g);
g.setColor(Color.black);
g.drawString(Integer.toString(slider.getValue()), thumbRect.x, thumbRect.y + thumbRect.height);
}
});
If the Nimbus Look and Feel is an option, a live display of the value can be specified in the relevant UI default:
UIManager.getLookAndFeelDefaults().put("Slider.paintValue", true);
i think this not a specific problem to me; everybody might have encountered this issue before.
To properly illustrate it, here's a simple UI:
As you can see, those two spinners are controlling a single variable -- "A". The only difference is that they control it using different views.
Since these two spinners' displaying values are synchronized, cyclic event shows up.
If i change the top spinner, "A" will be changed and the bottom spinner's value will also be updated accordingly. However, updating the bottom spinner's call (such as setValue) will also trigger another event instructing the top spinner to update based on the bottom spinner's value. Thus creates a bad cycle which can eventually cause a StackOverFlow exception.
My previously solution is kinda cumbersome: i placed a guarding boolean to indicate whether the 2nd updating call should be performed.
Now i'd like to ask "how can i handle such situation elegantly? ( in general, not specific to spinners )"
thx
Update:
Since i've got 2 answers suggesting me to utilize the observer structure, i have to say something about it.
Like what i've said, it's great but far from being perfect. Not only because of its inherent complexity, but also Its inability to solve the problem.
Why? To see the reason, you must realize the tight coupling of the View and Model-Controller in Java Swing. Lets take my spinner UI for an example. Suppose the variable A is actually an Observer object. Then, after firing the first state change event from the top spinner, the Observer "A" will update its value and fire a PropertyChange event to notify the bottom spinner. Then comes the 2nd updating which updates the bottom spinner's View. However, changing bottom spinner's view inevitably triggers a redundant event that will try to set "A"'s value again. Afterwards, the deadly loop is fully constructed and the stack overflow will be thrown.
In theory, the Observer model tries to solve the direct cycle by introducing 2 independent feedback paths. The chained updating odds(in event-response codes) implicitly form a bridge connecting both paths, making a cycle again.
Going back to Model-View-Controller, think about what your Model is, and what your View is.
In your current implementation, you have two models (one for each Spinner control), and they're being synced through the View layer.
What you should be doing though is share the same backing model. For the spinner with a subtracted value, create a proxy to the original model. ie:
class ProxySpinnerModel implements SpinnerModel {
getValue() { return originalSpinner.getValue() - 10 }
setValue(v) { originalSpinner.setValue(v+10) }
}
spinnerA = new JSpinner()
spinnerB = new JSpinner( new ProxySpinnerModel( spinnerA.getModel() ) )
Now, you don't need to add listeners, since they're both working off the same model and the default implementation (the originalModel) already has change listeners which it fires to the view.
Problem Solved
I've got many different suggestions. Particularly,
i want to thank Marc W & Reverend Gonzo. I'm here to make a summary for these ideas; this can save your time navigating thru big chunk of texts.
This problem can be easily bypassed if you carefully decouple the View and Model-Controller.
The dead cycle is caused by dependent writes: write_1 -> write_2 -> write_1 ->.... Intuitively, breaking the dependency can solve the problem elegantly.
If we look into the problem in depth, we can find updating the corresponding views doesn't necessarily involves an external write call. Actually, a view only depends on the data it's representing. Known this, we can then re-write the logic as follow: write_1 -> read_2 & write_2 -> read_1.
To illustrate this idea, lets compare the 3 methods mentioned by different posters:
alt text http://www.freeimagehosting.net/uploads/2707f1b483.png
As you can see, only the proxied view can solve all the dependency thus it's the generic solution for this knid of problem.
In practice, it can be implemented as something like this (in your event-response codes):
setValue(newValue);
anotherSyncUI.parse(); // not anotherSyncUI.setValue() any more
anotherSyncUI.repaint();
No more loops. Solved.
It's a bit complicated, but you could make A actually be an object that's observable. Both spinners (or whatever needs to update itself based on A's value) would then observe A. Whenever A changes, the spinners (or again, whatever object) update themselves to reflect the new value of A. This decouples the spinners' logic from one another. In your example here, the spinners should not be coupled to one another because they really have nothing to do with each other. Instead, they should both simply be bound to A and take care of their own view updating individually.
Whenever the value in the first spinner is changed, you would simply update A's value to match it. Whenever the value in the second spinner is changed, you would of course add 10 to its value before assigning it to A.
Update
In response to the update to your original question, my answer is that the spinners do not listen to one another's change events. Have a separate event handling method for each spinner. A user clicking the up or down arrows in the spinner generates a different event than calling setValue on the spinner programmatically, correct? If the spinners are completely independent of one another, there will be no infinite loop.
E.g. for the second spinner, calculate A-10 and then compare it to the current value of the spinner. If it's the same, do nothing, ending the infinite loop. Similarly for the first spinner.
I think there are also ways to update the spinner's model in a way that doesn't fire an event, but I don't know them off the top of my head.
Use a single SpinnerModel for both JSpinners. See the following code:
Note that the call to setValue() is only made once each time a new value is defined by one of the JSpinners.
import java.awt.BorderLayout;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame jf = new JFrame();
SpinnerModel spinModel = new MySpinnerModel();
JSpinner jspin1 = new JSpinner(spinModel);
JSpinner jspin2 = new JSpinner(spinModel);
jf.setLayout(new BorderLayout());
jf.add(jspin1, BorderLayout.NORTH);
jf.add(jspin2, BorderLayout.SOUTH);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(3);
}
}
class MySpinnerModel extends AbstractSpinnerModel {
private int _value = 0;
private int _min = 0;
private int _max = 10;
#Override
public Object getNextValue() {
if (_value == _max) {
return null;
}
return _value + 1;
}
#Override
public Object getPreviousValue() {
if (_value == _min) {
return null;
}
return _value - 1;
}
#Override
public Object getValue() {
return _value;
}
#Override
public void setValue(Object value) {
System.out.println("setValue(" + value + ")");
if (value instanceof Integer) {
_value = (Integer) value;
fireStateChanged();
}
}
}
It seems you're really observing the wrong thing. From the example given I presume what you want to detect is the user's actions on the controls, not the changes in the values themselves. As you've outlined, changes in your model are reflected in the values of the spinners, and it is this which forms the infinite loop of events.
However, diving further into the UI implementation may not be the answer you want. In that case I'd say the best you can do is either your current guard solution, or to better extract the logic into your model (similar to what Marc and William have said). How that can be done will depend on the 'real world' model behind a particular implementation of the provided puzzle.
As a rule, your model should not be defined by your GUI. Ie, the SpinnerModel that backs each JSpinner should not be your value A. (That would be a horribly inelegant tightly coupled dependency on a particular view.)
Instead, your value A should either be a POJO or a property of an object. In which case, you can add PropertyChangeSupport to it. (And presumably have already done so in any case, as you want your spinners to update automatically if A is changed by other parts of your program).
I realise this is similar to Marc W's answer, and you were concerned that it's "complicated", but PropertyChangeSupport does almost all of it for you.
In fact, for trivially simple cases, you can just use a single class that wires a "setProperty" method through to a "firePropertyChange" call (as well as storing the value in a HashMap for any "getProperty" calls).
I don't really want to solve your problem but I find it interesting. I have already been confront to it and solved it each time a different way. But when I think about the 'why ?' and not about the 'how ?' am staying perplexed.
This problem only exists because I am using an automatism (MVC) which had to help me, and exactly in that way. The art how the components are used make this automatism a barrier to a beautiful code.
Why do set #setEvent() has to produce the same event as a GUI action?
Though, my opinion is also pretty close to Observer pattern but it is a bit lighter than that!!!
Have A as a variable with a setter
private Integer A;
setA(int A)
{
this.A = A;
refreshSpinners();
}
refreshSpinners()
{
setSpinnerA();
setSpinnerAMinus10();
}
setSpinnerA()
{
// show value of A
}
setSpinnerAMinus10()
{
// show value of A-10
}