How to do a JList MVC style? - java

I've been trying to implement a simple JList using MVC. Basicaly, show the JList and under it add a button to delete an item. I want to use AbstractListModel for the model because later on I want more than just a simple ArrayList as data.
I'm having trouble using the JList in a proper MVC way. For example in the View I create the list. But this list need the model (addModel(method), and is added in the View.
It seems weird because I thought in MVC the View had no knowledge of the model.
I also don't really know what I should put in the controller.
Anyway if someone could give me guidelines to implement this it would be nice.
Here's the code I started:
public class SimpleJlist extends JFrame
{
public static void main(String[] args)
{
Controller controller = new Controller();
View view = new View(controller);
Model model = new Model();
SimpleJlist jl = new SimpleJlist();
jl.setLayout(new FlowLayout());
jl.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jl.add(view);
jl.setVisible(true);
jl.pack();
}
}
public class View extends JPanel implements ListDataListener, ActionListener
{
Controller controller;
JButton button;
JList list;
public View(Controller controller)
{
this.controller = controller;
button = new JButton("Delete");
/* Creation of the Jlist, but need the model. */
}
/* For the button */
public void actionPerformed(ActionEvent event) { }
/* For the list */
public void contentsChanged(ListDataEvent event) { }
public void intervalAdded(ListDataEvent event) { }
public void intervalRemoved(ListDataEvent event) { }
}
public class Model extends AbstractListModel
{
private ArrayList<String> names;
public Model()
{
names = new ArrayList<String>();
/* add names... */
}
public void deleteElement(int index) { names.remove(index); }
public String getElementAt(int index) { return names.get(index); }
public int getSize() { return names.size(); }
}
The code is far from complete obviously, but this is about where I got to before wondering what to do next...
The controller is not there, because I'm simply not sure what to put in it.

I've been trying to implement a simple JList using MVC.
Swing components are already designed in an MVC like style. You just need to use the components. The LIstModel is the model and the JList is a combined view-controller. You don't create additional classes called Model-View-Controller.
Basicaly, show the JList and under it add a button to delete an item.
Read the section from the Swing tutorial on How to Use Lists for an example of how to add/remove items from the DefaultListModel
I want to use AbstractListModel for the model because later on I want more than just a simple ArrayList as data.
That's fine, all you are doing is replacing the model. You don't need to make any changes to the JList when you do this, assuming that your model invokes the proper fireXXX() methods when the data is changed.
You should also check out the section from the Swing tutorial on How to Use Models which shows how you might use the MVC approach for your own custom component.

Related

JComboBox to display multiple lines of text

I'm currently writing a small tool for sending sql queries to a database and recieving the according data.
Now to my problem:
I want to allow the user to enter a new search query or select from a "latest" list, where the last few queries are saved.
For that, I planned on using an editable JComboBox, but I'm having trouble diplaying multiple lines of text in the box itself.
The reason I want to do that, is because sql queries can get quite long and since I want make the box editable and at the same time keep the frame clean.
I've found ways to display multiple lines in the dropdown menu, but nothing for the box itself.
Thank you in advance and please forgive me if I overlooked something simple ;)
Greetings
Zeus
Extended editing functionality is supplied by the ComboBoxEditor, this allows you to define the actual component which is used as the combobox's editor
Based on your requirements, you're going to need (at the very least) a JTextArea, to provide (optionally) word wrapping and multiple lines
A rough and ready example might look something like this...
public class TextAreaComboBoxEditor implements ComboBoxEditor {
private JTextArea ta = new JTextArea(4, 20);
private JScrollPane sp = new JScrollPane(ta);
public TextAreaComboBoxEditor() {
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
}
#Override
public Component getEditorComponent() {
return sp;
}
#Override
public void setItem(Object anObject) {
if (anObject instanceof String) {
ta.setText((String) anObject);
} else {
ta.setText(null);
}
}
#Override
public Object getItem() {
return ta.getText();
}
#Override
public void selectAll() {
ta.selectAll();
}
#Override
public void addActionListener(ActionListener l) {
}
#Override
public void removeActionListener(ActionListener l) {
}
}
This doesn't support ActionListener, as JTextArea uses the Enter key for it's own purposes. If you wanted to, you could use the key bindings API to add your own Action that can trigger the ActionListeners, for that, you'd need to supply a List or other means for managing them so you can call them back

How MVC work with java swing GUI

Lets say I have a swing GUI which has textfeild and button. When I click button I want to save that value in text in db and return joptionpane "success" message.
The way I used to do this is
Model : JDBC class
View : GUI : In that button's 'action performed' action I call save method with parameter.
Controller con = new Controller();
con.save(text1.getText());
Controller : Write a save method.
JDBC db = new
public void save(jTextfeild text){
text= text1.getText();
boolean b= db.putData("insert into .. values(text)");
if(b){
JOptionPane("Success");
}
}
This is how I started. But later I understood this is not how this should be and this is utterly unsafe and stupid.
I really want to learn how to do this in MVC properly. Please be kind enough to explain this to with a small example. Thank you for your time.
This is a difficult subject to grasp in something like Swing, which already uses a form of MVC, albeit more like VC-M, where the model is separated from the view and controller, but where the view and controller are combined.
Think about a JButton, you don't supply a controller to manage how it's triggered when a user presses a key or clicks on it with the mouse, this is done internally and you are notified about the actions when the occur.
With this in mind, you need to allow the view to be semi self managed. For instance, based on your requirements, the view would have a button and text field.
The view itself would manage the interactions between the user and the button itself (maintain a internal ActionListener for example), but would then provide notifications to the controller about any state changes that the controller might be interested in.
In a more pure sense of a MVC, the view and model won't know anything about each other and the controller would manage them. This is a little contradictive to how Swing works, as Swing allows you to pass the model directly to the view, see just about any Swing component.
This doesn't mean that you can't make things work, but you need to know where the concept can falter or needs to be "massaged" to work better.
Normally, when I approach these type of things, I take step back and look at much wider picture, for example.
You have a view which can accept text and produce text or changes to it
You have a model which can load and modify text, but provides little other events
You have a controller which wants to get text from the model and supply it to the view and monitor for changes to the text by the view and update them within the model
Now, MVC works REALLY well with the concept of "code to interfaces (not implementation)", to that extent, I tend to start with the contracts...
View contract...
public interface TextView {
public void setText(String text);
public String getText();
public void addTextViewObserver(TextViewObserver observer);
public void removeTextViewObserver(TextViewObserver observer);
}
public interface TextViewObserver {
public void textWasChanged(TextView view);
}
Now, one of the requirements of the view is to generate events when the text has changed in some meaningful way, to this end, I've used a simple observer pattern to implement. Now you could argue that the controller is the observer, but to my mind, the controller may have functionality that I don't want to expose to the view (like the model for instance)
Model contract...
Next comes the model...
public interface TextModel {
public String getText();
public void setText(String text);
}
pretty simple really. Now, you might consider adding some kind of Exception to these methods to allow the model the ability to fail for some reason, but the Exception should be as generic as you can make it (or even a custom Exception), so that you can replace the implementation should you need to
Controller contract...
And finally, the controller...
public interface TextViewController {
public TextView getTextView();
public TextModel getTextModel();
}
again, pretty simple. You might have a more complex requirement for your controller, but for this example, this is about all we really need.
Implementations...
View...
public class TextViewPane extends JPanel implements TextView {
private JTextField textField;
private JButton updateButton;
private List<TextViewObserver> observers;
public TextViewPane() {
observers = new ArrayList<>(25);
textField = new JTextField(25);
updateButton = new JButton("Update");
updateButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireTextWasChanged();
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(textField, gbc);
add(updateButton, gbc);
}
#Override
public void setText(String text) {
textField.setText(text);
}
#Override
public String getText() {
return textField.getText();
}
#Override
public void addTextViewObserver(TextViewObserver observer) {
observers.add(observer);
}
#Override
public void removeTextViewObserver(TextViewObserver observer) {
observers.remove(observer);
}
protected void fireTextWasChanged() {
for (TextViewObserver observer : observers) {
observer.textWasChanged(this);
}
}
}
Model...
public class SimpleTextModel implements TextModel {
private String text = "This is some text";
#Override
public String getText() {
return text;
}
#Override
public void setText(String text) {
this.text = text;
}
}
Controller...
public class SimpleTextController implements TextViewController, TextViewObserver {
private TextView view;
private TextModel model;
public SimpleTextController(TextView view, TextModel model) {
this.view = Objects.requireNonNull(view, "TextView can not null");
this.model = Objects.requireNonNull(model, "TextModel can not be null");
view.addTextViewObserver(this);
}
#Override
public TextView getTextView() {
return view;
}
#Override
public TextModel getTextModel() {
return model;
}
#Override
public void textWasChanged(TextView view) {
getTextModel().setText(view.getText());
}
}
Putting it together...
TextViewPane view = new TextViewPane();
TextModel model = new SimpleTextModel();
TextViewController controller = new SimpleTextController(view, model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(view);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Now, all this is just an example of one possible solution. You could have a controller implementation which has a particular implementation of the model or view or both, for example.
The point is, you just shouldn't care. The controller doesn't care how the view is implemented, it only cares that it will generate textWasChanged events. The model doesn't care about the view at all (and visa-versa) and the controller doesn't care about model, only that it will get and set some text.
For a more complex example, you can have a look at Java and GUI - Where do ActionListeners belong according to MVC pattern?
After thoughts
This is just ONE possible way to approach the problem. For example, you could limit the view to a single observer.
You should always be thinking "can I change any one part of the MVC and will it still work?" This makes you think about the possible issues that changing any one part of the implementation might have on the surrounding contracts. You should get to the point that it simply doesn't matter how each layer is implemented
A view may act as a controller for another sub-view (or act as a container for another controller of a sub-view). This can scare people sometimes, but it's possible for a view to act as parent container for one or more sub controllers/views, this allows you to develop complex UIs
Don't expose implementation details in your contracts, for example, the model shouldn't throw a SQLException, as another implementation might not be based on a SQL based solution. Don't expose UI elements, this means that ALL implementations would then need to implement those elements. What happens if I want a implementation of the view that presents a JComboBox to the user instead of JTextField? This is also the reason I don't use a ActionListener in the view contract, because I have no idea how a textWasChanged event might actually be generated by an implementation of the view

Splitting a View into several files in Swing MVC

My project follows the MVC pattern. To make it quick, I will only post the code relevant to my problem (which doesn't involve the Model). Explanations below.
Controller.java :
public class Controller {
public View myView;
public Controller() {
myView = new myJFrame(this);
}
public void displayViews() {
myView.display();
}
public void closeViews() {
myView.close();
}
}
View.java :
public abstract class View {
private Controller controller;
public View(Controller controller) {
super();
this.controller = controller;
}
public final Controller getController() {
return controller;
}
public abstract void display();
public abstract void close();
}
myJFrame.java :
public class myJFrame extends View {
private JFrame frame;
private JMenuBar myMenuBar;
public myJFrame(Controller controller) {
super(controller);
buildFrame();
}
private void buildFrame() {
frame = new JFrame();
menuBar = new JMenuBar();
...
}
#Override
public void close() {
frame.dispose();
}
#Override
public void display() {
frame.setVisible(true);
}
}
This code works perfectly. However, the buildFrame() method will soon become huge if I keep adding components, so I would like to split it and create a new class in a new file for every Swing component.
The problem is that I want my components to retain their access to the Controller. myFrame extends View, therefore the getController() method can be called anytime. But it is no longer the case when I create a separate file. Extending View doesn't seem to be an option (besides, myMenuBar already extends JMenuBar, for example).
What would you suggest?
The problem is that I want my components to retain their access to the Controller.
Fine. Create a class for the JPanels that make up your GUI, and pass an instance of View to each of them through their respective constructors.
You shouldn't extend Swing components unless you're overriding a component method. You should use Swing components.
Composition over inheritance
You shouldn't be extending View either. Passing an instance of the view is sufficient.
Read this excellent article, Sudoku Solver Swing GUI, for a better idea of how to use the MVC pattern when constructing a Swing GUI.

Refresh JPanel content on tab switch

I'm writing a simple UI just to get the hang of things. I have a tabbed window with two tabs, one has a button that counts up an integer, the other has a text field showing the content of said integer. Or at least that's the plan.
Everything works just fine if I stuff everything into one class. I can access tab 1 from my actionlistener and change the text field in tab 1 from the button press in tab 2. But I don't want my entire program to be in one class, obviously.
And here I have no idea what to do: I need to tell the textfield in the Class Tab1 to change on the button press in the Class Tab2. What's the right thing to do here? My first thought was to hand over an instance of Tab1 in the creation of Tab2, so I could do tab1.changeText(). But that would get messy quickly once I'd get more tabs that interact with each other. So, instead, I want to update the content of the first tab every time it is opened, but I don't know how to do that. And I don't know if that's the right thing to do, either. So, help!
Here's some code. "content" is an instance of Content, a class handling all the logic like adding to the counter.
Main GUI Class:
public class GUI extends JFrame {
//Stuff..
JTabbedPane tabs = new JTabbedPane();
tabs.addTab("One", new Tab1(content));
tabs.addTab("Two", new Tab2(content));
//Stuff..
Tab 1:
public class Tab1 extends JPanel {
public Tab1(Content content) {
JPanel tab1 = new JPanel();
//Stuff..
JTextField tfCount = new JTextField(content.getCounter(), 10);
tab1.add(tfCount);
this.add(tab1);
//Stuff..
Tab 2:
public class Tab2 extends JPanel {
public Tab2(Content content) {
JPanel tab2 = new JPanel();
//Stuff..
JButton btnCount2 = new JButton("Count");
btnCount2.addActionListener(new TestListener(this.content));
tab2.add(btnCount2);
this.add(tab2);
}
private class TestListener implements ActionListener {
Content content;
public TestListener(Content content) {
this.content = content;
}
#Override
public void actionPerformed(ActionEvent e) {
this.content.addToCounter(1);
}
}
Now, if all of that would be in one class (plus subclasses), I could just access tfCount from Tab2 and do tfCount.setText(content.getCounter());. Now tfCount is in a different class, though, and I cannot access it, unless I hand over an instance of Tab1 to Tab2 (like tabs.addTab("Two", new Tab2(content, Tab1);). Couldn't I instead get Tab1 to repaint itself whenever it is opened, like having a method that executes tfCount.setText(content.getCounter()) in Tab1 whenever it is opened, or something along those lines? If so, how do I do that?
With you controls separated in this manner you have a view choices...
You Could...
Share an instance of each "tab" with each of the other tabs, allowing them to either access the others controls or attach listeners across each other. This is very tightly coupled and messy.
The other problem is, does the button really care about the text field or visa versa...
You Could...
Create a simple model that contains the current int value and provides a means to change that value.
The model would have the capacity to fire a ChangeEvent (for example) when the value is changed, which interested parties could listen for and update themselves accordingly.
This decouples the code, reducing the complexity and greatly increasing the flexibility and reuse of various elements of your code.
This is commonly known as an observer pattern and is widely used in Swing.
A possible (listener) example...
For me, I always start with an interface, this describes the absolute minimum requirements that must be meet in order to achieve the required goal. Each tab will want to know the current value, be able to set the next value and listener for changes to the model...
public interface NumberModel {
public int getValue();
public void setValue(int value);
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
An abstract implementation deals with the more "common" implementation details, things that a concrete implementation won't want to have to implement, as it's common enough to all implementations. In this case, that would the listener management...
public abstract class AbstractNumberModel implements NumberModel {
private List<ChangeListener> listeners;
public AbstractNumberModel() {
listeners = new ArrayList<>(25);
}
#Override
public void addChangeListener(ChangeListener listener) {
listeners.add(listener);
}
#Override
public void removeChangeListener(ChangeListener listener) {
listeners.remove(listener);
}
protected ChangeListener[] getChangeListeners() {
// FIFO...
List<ChangeListener> copy = new ArrayList<>(listeners);
Collections.reverse(copy);
return copy.toArray(copy.toArray(new ChangeListener[listeners.size()]));
}
protected void fireStateChanged() {
ChangeListener[] listeners = getChangeListeners();
if (listeners != null && listeners.length > 0) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : listeners) {
listener.stateChanged(evt);
}
}
}
}
And finally, a concrete implementation, which deals with the implementation specific details...
public class DefaultNumberModel extends AbstractNumberModel {
private int value;
public DefaultNumberModel() {
}
public DefaultNumberModel(int value) {
setValue(value);
}
#Override
public int getValue() {
return value;
}
#Override
public void setValue(int num) {
if (num != value) {
value = num;
fireStateChanged();
}
}
}
We could be a slightly more flexible model by doing something like public interface NumberModel<N extends Number> which would allow you define models that could hold Integer, Double, Float and Long for example, but I'll leave that to you.
Each of you tab views will need a setModel(NumberModel) method, so you can pass the model it. In these methods, you will attach a listener to the model and get the current value so that the model and view are in sync.

Singleton Action versus Multi instance Action

I have a dude about how to implement Actions in Swing.
My idea is create a Class for each action of my application extending AbstractAction so I can use in many components that must have the same behavior. So I finaly have something as:
public class ActionExample extends AbstractAction {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
Well, now when I want to use it I have three options in my mind:
public void makeUI1() {
JButton btn = new JButton(new ActionExample("Do it"));
JMenuItem mi = new JMenuItem(new ActionExample("Do it"));
}
public void makeUI2() {
Action a = new ActionExample("Do it");
JButton btn = new JButton(a);
JMenuItem mi = new JMenuItem(a);
}
Or use it as a singleton (also changing ActionExample):
public void makeUI2() {
JButton btn = new JButton(ActionExample.getInstance());
JMenuItem mi = new JMenuItem(ActionExample.getInstance());
}
public class ActionExample extends AbstractAction {
private static final ActionExample INSTANCE = new ActionExample("Do it");
public static Action getInstance() {
return INSTANCE;
}
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("Do something");
}
}
My first opinion was make it through singleton instance but I see in oracle tutorial that it make a new instance before setting it into components and in the I also see that many code create new instance for each component so I don't know what it's better and why.
Is preferred one method to be used over the other?
The multi instance action allows you to save data in the moment of the action for further use.
Imagine you want to add undo/redo functionality. You need to save what actions have been done for every action.
Singleton does not provide any advantage in this case.
I think the best thing to do would be to use the MVC pattern. Your AbstractAction class is a controller. It's responsible for extracting the information necessary for the model (ie: business logic) to use. The model/business logic is the part you reuse, but the controller may differ greatly even if it uses the same business logic.
For example, you may have a JComponent that you need to add a KeyListener to. Suddenly, your pre-made AbstractAction has become worthless because it can't be used in this situation. But, as long as you reuse all the business logic code in your KeyListener that you used in your AbstractAction, you're doing things right.

Categories