I'm trying to create a custom JSpinner that represents a sequence of numbers of power 2. Like, 1-2-4-8-16 and so on. I have to do that with extending AbstractSpinnerModel and changing its implemented methods (getNextValue etc.). The problem is, the arrow keys on my custom JSpinner don't work. Nothing changes when I click one of them. I need to show previous and next values of current value. ( Previous value = 4 Current Value = 8 Next Value = 16 ).
Here is my code :
import java.awt.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Hw5SpinnerModel extends JFrame {
public static void main(String[] args) {
Hw5SpinnerModel frame = new Hw5SpinnerModel();
}
public Hw5SpinnerModel() {
setTitle("Hw5SpinnerModel");
setSize(350, 300);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JSpinner jspn = new JSpinner(new CustomSpinnerModel());
final JLabel jlbl = new JLabel("");
add(jspn, BorderLayout.NORTH);
add(jlbl, BorderLayout.CENTER);
jspn.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
jlbl.setText(jspn.getPreviousValue() + jspn.getValue() + jspn.getNextValue());
}
});
//jlbl.setText(jspn.getNextValue() + "");
setVisible(true);
}
public class CustomSpinnerModel extends AbstractSpinnerModel {
Integer i = 1;
#Override
public Object getValue() {
return i;
}
#Override
public void setValue(Object value) {
i = (Integer) value;
}
#Override
public Object getNextValue() {
return 2 * i;
}
#Override
public Object getPreviousValue() {
return i / 2;
}
}
}
Any help will be appreciated.
jSpinner1.setModel(new SpinnerNumberModel(1, null, null, 1) {
#Override
public Object getNextValue() {
Object nextValue = super.getValue();
int x = Integer.valueOf(nextValue.toString())*2;
//Object o = x;
return x;
}
});
It works 100%
I had this problem this week, and here's the answer to your question: The JSpinner operates through the use of ChangeListeners, and those ChangeListeners need to be activated when the model's value changes.
You may have had difficulty finding this (as I did), because you thought that getNextValue and getPreviousValue, in your custom class, were returning bad values. They aren't; but the JSpinner just doesn't use them directly. It feeds them straight back to the SpinnerModel through setValue.
(Except if getNextValue or getPreviousValue return null, which is a special case. Then the JSpinner does nothing.)
So, in code, your setValue function needs a fireStateChanged call after the change to i:
#Override
public void setValue(Object value) {
i = (Integer) value;
fireStateChanged ();
}
You might think that the JSpinner shouldn't operate this way, and should take the value from getNextValue and getPreviousValue to display immediately, instead of this indirect process. But the JSpinner has to call setValue in any case, and setValue has to notify the ChangeListeners in any case (because a single model can be passed around to many places in your program).
For instance, if you had two JSpinners in different places, with the same SpinnerModel, then changes to the model need to be reflected on both JSpinners, but only one JSpinner gets to call getNextValue or getPreviousValue at one time.
Related
I got two ListBoxes, assume customerListBox and timeListBox, and Label resultLabel. When timeListBox's value is changed, it fires its ValueChangeHandler (let's call it recalculateValueHandler) which recalculates result and put it into resultLabel. I need this to work vice versa - when customerListBox's value is changed I need to recalculate new result for same time value but different customer.
So I'd need something like customerListBox.onValueChange(fire(recalculateValueHandler)), hope you do understand. Is there anything that could work for me this way? I try to avoid duplicating pretty much same code into both handlers.
You just need three things
declare all elements at top level so they are accessible across all methods
create a method that recalculates the value, let's call it recalculateValue
add ChangeHandlers for both ListBoxes (ListBox don't have ValueChangeHandler) that will call recalculateValue method.
Working example:
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
public class Test implements EntryPoint {
// elements declared at top level are accessible across all methods
private ListBox customerListBox;
private ListBox timeListBox;
private Label resultLabel;
#Override
public void onModuleLoad() {
// add some data
customerListBox = new ListBox();
for(int i = 0; i < 10; i++)
customerListBox.addItem("Customer " + (i + 1));
// add some data
timeListBox = new ListBox();
for(int i = 0; i < 10; i++)
timeListBox.addItem("Time " + (i + 1));
resultLabel = new Label();
// recalculateValue when customerListBox changes
customerListBox.addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
recalculateValue();
}
});
// recalculateValue when timeListBox changes
timeListBox.addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
recalculateValue();
}
});
// initial result (optional)
recalculateValue();
// show elements
RootPanel.get().clear();
RootPanel.get().add(customerListBox);
RootPanel.get().add(timeListBox);
RootPanel.get().add(resultLabel);
}
private void recalculateValue() {
// use values from ListBoxes
resultLabel.setText(customerListBox.getSelectedValue() + " / " + timeListBox.getSelectedValue());
}
}
Notice that both handlers ares identical, so you can create only one handler and use it for both ListBoxes like that:
ChangeHandler handler = new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
recalculateValue();
}
};
// recalculateValue when customerListBox changes
customerListBox.addChangeHandler(handler);
// recalculateValue when timeListBox changes
timeListBox.addChangeHandler(handler);
I'm trying to filter Rows in a JTable which contains Columns with numbers.
The filtering is working so far, but it filters over the numbers including the thousands-separators. For example, if there is a row with the number 25689 in one row and I try to filter for this row, i have to use "25.689". So it seems there is a formatting that is performed before the filtering.
I've tried to set an own default renderer and the numbers are shown without the separators but the filtering is the same.
Edit
I've added a full example re-creating my problem:
public class GroupingTest {
JFrame frame= null;
Container pane= null;
JTextField tf=null;
JXTable table=null;
public void searchTable() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
final String searchEx = "(?i)"
+ Pattern.quote(tf.getText());
final RowFilter<TableModel, Object> filter;
filter = RowFilter.regexFilter(searchEx);
table.setRowFilter(filter);
//packAll in edt
Utility.packTableView(table);
} catch (final Exception e) {
return;
}
}
});
}
public void createTable() {
frame = new JFrame();
pane=frame.getContentPane();
tf = new JTextField();
tf.setPreferredSize(new Dimension(200,25));
tf.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void insertUpdate(final DocumentEvent e) {
searchTable();
}
#Override
public void changedUpdate(final DocumentEvent e) {
searchTable();
}
});
String[] columnHeaders = {"long","strings"};
DefaultTableModel $model = new DefaultTableModel(columnHeaders, 0) {
#Override
public Class<?> getColumnClass(final int $col) {
if($col == 0) {
return Long.class;
} else if($col == 1){
return String.class;
} else {
return Object.class;
}
}
};
table = new JXTable($model);
table.setDefaultRenderer(Long.class, new DefaultTableCellRenderer() {
#Override
public java.awt.Component getTableCellRendererComponent(final JTable $table,
final Object $value, final boolean $isSelected, final boolean $hasFocus, final int $row,
final int $column) {
super.getTableCellRendererComponent($table, $value, $isSelected, $hasFocus, $row, $column);
if ($value instanceof Long) {
this.setHorizontalAlignment(SwingConstants.RIGHT);
}
return this;
}
});
Object[] line1 = {new Long(23345),"asdf"};
$model.addRow(line1);
Object[] line2 = {new Long(3),"dfw"};
$model.addRow(line2);
pane.add(tf,BorderLayout.NORTH);
pane.add(new JScrollPane(table),BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(300,200));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
GroupingTest gt = new GroupingTest();
gt.createTable();
}
}
The Filtering is working so far, but it filters over the numbers including the thousands-separators.
When the value's format interferes with the expected functioning of sorters and filters then it's time to check if getColumnClass(int columnIndex) in the table model is retrieving the appropriate class (in this case Double).
By default AbstractTableModel implementation of such method returns Object.class which is rendered using the toString() method (that's why you see the thousands-separator) and probably filtered according to the string representation as well. Subclasses of AbstractTableModel (such as DefaultTableModel) inherit this implementation and thus this method should be overriden. For example let's say your table model is DefaultTableModel and the first column is a Double:
DefaultTableModel model = new DefaultTableModel() {
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Double.class
: super.getColumnClass(columnIndex);
}
};
See Sorting and Filtering section of How to Use Tables tutorial for further details.
Update
Given your new MVCE it is clear now what are you trying to achieve. I'd start saying that I've mistakenly assumed your table model holds Double instead of Long which makes no difference about overriding getColumnClass() method (it should be done anyways) but it will make a slight difference in the final solution.
Now, to state the requirements clear, you need to filter that column either:
Users input a number (Long) including grouping character.
Users input a number without grouping character.
The string representation of the value contains the substring typed by the users.
To achieve this goal I'd use a custom RowFilter instead of using a regex filter like you do in your example. This is to have control about the string typed by the user and check the three conditions listed above. I've managed to modify your searchTable() to satisfy the requirements. Note: I've included the queried String as an argument in this method to keep tf text field out of the implementation. Please see the code below:
private void searchTable(final String query) {
RowFilter<TableModel, Integer> filter = null;
if (query.length() > 0) {
filter = new RowFilter<TableModel, Integer>() {
#Override
public boolean include(RowFilter.Entry<? extends TableModel, ? extends Integer> entry) {
for (int i = 0; i < entry.getValueCount(); i++) {
String stringValue = entry.getStringValue(i);
Object entryValue = entry.getValue(i);
String numberString = entryValue instanceof Long
? String.valueOf(entryValue)
: "";
if (stringValue.contains(query) || numberString.contains(query)) {
return true;
}
}
return false;
}
};
}
table.setRowFilter(filter);
}
The flow will be more or less as follows:
If the query length is 0 just let the filter be null. This means the table won't be filtered and all rentries will be included.
If not (1) then prepare a new filter which iterates over the whole row asking if the String representation of the entry or the String value of the entry contains the queried String. While those might look the same thing they are not because Entry#getStringValue(int index) might (and actually does) retrieve a different value than String#valueOf(entry#getValue(int index)). In this case the first one retrieves the Long including grouping separators (or formatted if you prefer) while the second one retrieves the Long with no formatting at all (it means, no grouping separators).
Apply the filter to the table in either case.
I hope the idea is clear enough. If you want to filter a Double then it has to be tweaked a little bit because String.valueOf(double) includes the decimal (not grouping) separator and you might want to remove it before checking if it contains the queried String.
I am using a Vaadin text field and I want to restrict it to support numbers only in it. I tried to override setValue() and return without calling super. setValue() if text is not a number. But it doesn't seems to be working. How can I correct this?
I am using Vaadin 7. And I think it doesn't support NumberField as well.
If I understand you question correct, you want to have a field that ignores all inputs that are not a number and not only mark the field as invalid. Vaadins architecture is designed that every field in the browser has its representation on the server. In my opinion the cleanest way to achieve this would be to have a browser field, that permits input of letters and other wrong characters. I couldn't find such a field in Vaadin 7. There seems to be an add-on for vaadin 6 called Number Field for that, but I didn't test it.
You have multiple options:
Port this add-on to vaadin 7 or ask the author to do it
Write your own field. Maybe extending VTextField and TextFieldConnector
Do everything on the server side and accept the delays and the traffic (IMHO ugly)
Since I think option 3 is not the way to go, I probably shouldn't show this code, but it's the quickest way to implement this.
public class IntegerField extends TextField implements TextChangeListener {
String lastValue;
public IntegerField() {
setImmediate(true);
setTextChangeEventMode(TextChangeEventMode.EAGER);
addTextChangeListener(this);
}
#Override
public void textChange(TextChangeEvent event) {
String text = event.getText();
try {
new Integer(text);
lastValue = text;
} catch (NumberFormatException e) {
setValue(lastValue);
}
}
}
Vaadin 7 allows to extend their built in widgets (if you want to have more knowledge on this I really recommend this post) here is a solution which uses that mechanism.
It is composed of two classes:
Connector and the Extension
The Extension
package com.infosystem.widgets.vaadin;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.AbstractExtension;
import com.vaadin.ui.TextField;
public class NumberField extends AbstractExtension {
public static void extend(TextField field) {
new NumberField().extend((AbstractClientConnector) field);
}
}
Connector:
package com.infosystem.widgets.vaadin.client.numberField;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.infosystem.widgets.vaadin.NumberField;
import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.client.ui.VTextField;
import com.vaadin.shared.ui.Connect;
#Connect(NumberField.class)
public class NumberFieldConnector extends AbstractExtensionConnector {
private static final long serialVersionUID = -737765038361894693L;
private VTextField textField;
private KeyPressHandler keyPressHandler = new KeyPressHandler() {
#Override
public void onKeyPress(KeyPressEvent event) {
if (textField.isReadOnly() || !textField.isEnabled()) {
return;
}
int keyCode = event.getNativeEvent().getKeyCode();
switch (keyCode) {
case KeyCodes.KEY_LEFT:
case KeyCodes.KEY_RIGHT:
case KeyCodes.KEY_BACKSPACE:
case KeyCodes.KEY_DELETE:
case KeyCodes.KEY_TAB:
case KeyCodes.KEY_UP:
case KeyCodes.KEY_DOWN:
case KeyCodes.KEY_SHIFT:
return;
}
if (!isValueValid(event)) {
textField.cancelKey();
}
}
};
#Override
protected void extend(ServerConnector target) {
textField = (VTextField) ((ComponentConnector) target).getWidget();
textField.addKeyPressHandler(keyPressHandler);
}
private boolean isValueValid(KeyPressEvent event) {
String newText = getFieldValueAsItWouldBeAfterKeyPress(event.getCharCode());
try {
parseValue(newText);
return true;
} catch (Exception e) {
return false;
}
}
protected long parseValue(String value) {
return Long.valueOf(value);
}
private String getFieldValueAsItWouldBeAfterKeyPress(char charCode) {
int index = textField.getCursorPos();
String previousText = textField.getText();
StringBuffer buffer = new StringBuffer();
buffer.append(previousText.substring(0, index));
buffer.append(charCode);
if (textField.getSelectionLength() > 0) {
buffer.append(previousText.substring(index + textField.getSelectionLength(),
previousText.length()));
} else {
buffer.append(previousText.substring(index, previousText.length()));
}
return buffer.toString();
}
}
To use the code above you need to add it to your current widget set.
Afterwards the use of this is as follows:
TextField field = new TextField();
NumberField.extend(field);
In Vaadin 7, you can use a TextField and set a validator to allow only numbers:
TextField textField;
textField.addValidator(new RegexpValidator("[-]?[0-9]*\\.?,?[0-9]+"), "This is not a number!");
Change the regex to fit your needs.
Remember that still is handling Strings and therefore you still need to convert the returning value of the TextField:
Long.parseLong(textField.getValue())
With Vaadin 8, you can use Binder:
Binder<YouBean> binder = new Binder<>();
binder.forField(textField)
.withConverter(new StringToIntegerConverter("Must be Integer"))
.bind(YouBean::getter, YouBean::setter);
binder.setBean(bean); //optional
A TextField is a component that always has a value of type String. When binding a property of another type to a text field, the value is automatically converted if the conversion between the two types is supported.
public class MyBean {
private int value;
public int getValue() {
return value;
}
public void setValue(int integer) {
value = integer;
}
}
The property named "value" from a BeanItem constructed from MyBean will be of type Integer. Binding the property to a TextField will automatically make validation fail for texts that can not be converted to an Integer.
final MyBean myBean = new MyBean();
BeanItem<MyBean> beanItem = new BeanItem<MyBean>(myBean);
final Property<Integer> integerProperty = (Property<Integer>) beanItem
.getItemProperty("value");
final TextField textField = new TextField("Text field", integerProperty);
Button submitButton = new Button("Submit value", new ClickListener() {
public void buttonClick(ClickEvent event) {
String uiValue = textField.getValue();
Integer propertyValue = integerProperty.getValue();
int dataModelValue = myBean.getValue();
Notification.show("UI value (String): " + uiValue
+ "\nProperty value (Integer): " + propertyValue
+ "\nData model value (int): " + dataModelValue);
}
});
addComponent(new Label("Text field type: " + textField.getType()));
addComponent(new Label("Text field type: " + integerProperty.getType()));
addComponent(textField);
addComponent(submitButton);
With this example, entering a number and pressing the button causes the value of the TextField to be a String, the property value will be an Integer representing the same value and the value in the bean will be the same int. If e.g. a letter is entered to the field and the button is pressed, the validation will fail. This causes a notice to be displayed for the field. The field value is still updated, but the property value and the bean value are kept at their previous values.
This is an update (2017 with vaadin 8) for #raffael answer:
public class DoubleField extends TextField implements ValueChangeListener<String> {
public String lastValue;
public DoubleField() {
setValueChangeMode(ValueChangeMode.EAGER);
addValueChangeListener(this);
lastValue="";
}
#Override
public void valueChange(ValueChangeEvent<String> event) {
String text = (String) event.getValue();
try {
new Double(text);
lastValue = text;
} catch (NumberFormatException e) {
setValue(lastValue);
}
}
NumberField is available for Vaadin 7 and 8 by now.
I have a JTextField and a JList in my program. The JList contains the user's contacts. I'd like to filter the JList based on the text on the JTextField. For example, if I type in "Mike" it will only show contacts including "Mike". When the user clears the JTextField it would reset the filter.
I know I could do this manually by having two arrays. One for the original contacts and one for the filtered ones. When the user changes the value of the JTextField I would go trought the original list, update the temporary list and update the JList. I just wonder if there is some built in feature to avoid manual labour.
The best way to do things like that is to have a ListModel implementation that filters its contents.
I don't know of any default filtering ListModel implementations, but it should not be too hard to do.
Here's a quick and dirty solution just to give you an idea. You might want to add more bells and whistles to it.
package test;
import java.util.ArrayList;
import javax.swing.AbstractListModel;
import javax.swing.ListModel;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
public class FilteredListModel extends AbstractListModel {
public static interface Filter {
boolean accept(Object element);
}
private final ListModel _source;
private Filter _filter;
private final ArrayList<Integer> _indices = new ArrayList<Integer>();
public FilteredListModel(ListModel source) {
if (source == null)
throw new IllegalArgumentException("Source is null");
_source = source;
_source.addListDataListener(new ListDataListener() {
public void intervalRemoved(ListDataEvent e) {
doFilter();
}
public void intervalAdded(ListDataEvent e) {
doFilter();
}
public void contentsChanged(ListDataEvent e) {
doFilter();
}
});
}
public void setFilter(Filter f) {
_filter = f;
doFilter();
}
private void doFilter() {
_indices.clear();
Filter f = _filter;
if (f != null) {
int count = _source.getSize();
for (int i = 0; i < count; i++) {
Object element = _source.getElementAt(i);
if (f.accept(element)) {
_indices.add(i);
}
}
fireContentsChanged(this, 0, getSize() - 1);
}
}
public int getSize() {
return (_filter != null) ? _indices.size() : _source.getSize();
}
public Object getElementAt(int index) {
return (_filter != null) ? _source.getElementAt(_indices.get(index)) : _source.getElementAt(index);
}
}
In order to use it you need to set it to your JList and then call setFilter() as you need.
Here's an example:
ListModel source = new DefaultListModel(); // use a model of your choice here;
FilteredListModel filteredListModel = new FilteredListModel(source);
JList list = new JList(filteredListModel);
filteredListModel.setFilter(new FilteredListModel.Filter() {
public boolean accept(Object element) {
return false; // put your filtering logic here.
}
});
Once method setFilter() is invoked your JList on the screen is expected to change its contents accordingly.
Alternatively, you may want to implement an observer/observable pattern for your Filter, so you can re-filter the list without calling method setFilter(). You can experiment with that later. For the first iteration it's good enough as long as you call method setFilter every time user types something in your JTextField.
A simpler solution might be to use JTable, which does have a built-in ability to filter and sort (RowSorter). A single-column table is not too different from a list.
If you're okay with external libs, I would recommend Jide's QuickListFilterField/QuickTreeFilterField. With few lines of code, you could get a visually filterable JList/JTree, case sensitive/insensitive search, wildcard/regex matching etc ... Amazingly easy to use !
hey i have another problem. I created JList in my main window and now i want to add something to it. I do it this way...
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt)
{
Dodaj_Przedmiot dodaj_przedmiot = new Dodaj_Przedmiot(null, true);
dodaj_przedmiot.setVisible(true);
SterowanieBazy instance = SterowanieBazy.getInstance();
Zmienne_pomocnicze zp = new Zmienne_pomocnicze();
String przedmiot = zp.getPrzechowaj();
instance.dodajPrzedmiot(przedmiot);
String przedm[] = instance.zwrocPrzedmioty();
jList1.setListData(przedm);
}
what i want to write in that list is what i collect from my jDialogForm: dodaj_przedmiot
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
{
String sciezka = jTextField1.getText();
if (sciezka.length() > 0)
{
Zmienne_pomocnicze zp = new Zmienne_pomocnicze();
zp.setPrzechowaj(sciezka);
}
this.setVisible(false);
}
and i collect try to copy that date using this class
public class Zmienne_pomocnicze {
public String n;
public int a;
public void setPrzechowaj (String neew)
{
n = neew;
}
public String getPrzechowaj ()
{
return n;
}
}
i would be grateful for any ideas how to make it work.
This is somewhat difficult to follow, but from what I gather, you are using your Zmienne_pomocnicze class in two places, and both of them seem to do nothing.
First, in jButton2ActionPerformed you instantiate a new Zmienne_pomocnicze and try to get the data from it using the getPrzechowaj method. This will return n, but as you have just instantiated the instance, n is null. As I cant infer from the method names of the following code, I cant figure out what you want to do with that data, but this action is most certainly not what you want to do.
In the second case, jButton1ActionPerformed takes the value from the text field and then test for validity (legnth is greater than 0). If the validation passes, you then create a new Zmienne_pomocnicze, call setPrezechowaj with the text field value and then let the new object fall out of scope. Again, this is certainly not the desired effect.
It would be interesting to see what the flow of your program is supposed to be, ie what button triggers which jButton[12]ActionPerformed methods and how you expect them to interact.
Here's a simple example of adding entries to a JList.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.*;
public class JListTest {
private static final Random random = new Random();
public static final void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
final JFrame frame = new JFrame("Test");
final DefaultListModel dlm = new DefaultListModel();
final JList list = new JList(dlm);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new JScrollPane(list));
frame.add(new JButton("Add") {
{
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dlm.addElement("A" + (random.nextInt(9000) + 1000));
}
});
}
}, BorderLayout.SOUTH);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
}
I always recommend reading the API for basic information.
If you read the JList API you will find a link to the Swing tutorial on "How to Use Lists". The example there shows how to dynamically add and remove entries from the ListModel.
Tutorials are a good place to start because you find working examples as well as explanations as to how the code works. Then, if required you can ask a specific question about a specific piece of code.
Not only that you now have a reference that might come in handy for other problems.