How to go to last row in JTable when we press Up key at first row and also how to go to first row when we press Down key at last row? Like Enter key does when we press Enter key at last row it will go to the first row.
I already done this coding but it just show data to text field:
private void jtKeyReleased(java.awt.event.KeyEvent evt) {
if(evt.getKeyCode()==KeyEvent.VK_DOWN ||evt.getKeyCode()==KeyEvent.VK_UP){
int row=jt.getSelectedRow();
String TableClick=(jt.getModel().getValueAt(row,0).toString());
try{
String sql="select Product,Roo,TotalStock from pro where
Product='"+TableClick+ "'";
PreparedStatement pst = (PreparedStatement)
conn.prepareStatement(sql);
ResultSet res = pst.executeQuery();
if(res.next()){
String add1=res.getString("Product");
proo.setText(add1);
// String add2=res.getString("Id");
//idd.setText(add2);
String add3=res.getString("Roo");
rooo.setText(add3);
String add4=res.getString("TotalStock");
stkk.setText(add4);
abc=res.getString("TotalStock");
}
} catch(Exception e) {
} //catch
} // if
}
You need to create two custom Actions:
one Action to wrap from the first row to the bottom and
another Action to wrap from the bottom to the top.
The easiest way to do this is to take advantage of existing Actions defined in JTable. The UP keys move up one line at a time and the Down key moves down one line at a time. You can also use CTRL+HOME to go to the top line and CTRL_END to go to the last line.
So I would suggest to start with the UP Action and modify it to implement the CTRL+END Action. The easiest way to do this is to take advantage of the concept of Wrapping Actions. This class is a wrapper class for an existing Action and allows you to add custom code to enhance the Action.
import java.awt.event.*;
import javax.swing.*;
public class UpAction extends WrappedAction implements ActionListener
{
private JTable table;
private Action endAction;
/*
* Specify the component and KeyStroke for the Action we want to wrap
*/
public UpAction(JTable table, KeyStroke keyStroke)
{
super(table, keyStroke);
this.table = table;
endAction = table.getActionMap().get("selectLastRow");
}
/*
* Provide the custom behaviour of the Action
*/
public void actionPerformed(ActionEvent e)
{
if (table.getSelectedRow() == 0)
endAction.actionPerformed( e );
else
invokeOriginalAction( e );
}
private static void createAndShowGUI()
{
JTable table = new JTable(7, 5);
new UpAction(table, KeyStroke.getKeyStroke("UP"));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new JScrollPane(table) );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
You would need to create a similar Action for the DOWN functionality. Note the action string name for selecting the first row is: selectFirstRow. Check out Key Bindings for a list of all the Actions used by a given component.
Related
In my java code below it produces a frame with a jtextrea. This allows for simple text processing. All I want to do is add " Sam". Which is 5 spaces with sam at the end. Every time the user hits enter. You can see also the gif I added below which is exactly what I am looking for.
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
class text11 extends JFrame implements ActionListener {
// JFrame
static JFrame f;
// text area
static JTextArea jt;
// main class
public static void main(String[] args)
{
// create a new frame to store text field and button
f = new JFrame("textfield");
// create a label to display text
// create a object of the text class
text11 te = new text11();
// create a text area, specifying the rows and columns
jt = new JTextArea(" ", 20, 20);
JPanel p = new JPanel();
// add the text area and button to panel
p.add(jt);
f.add(p);
// set the size of frame
f.setSize(300, 300);
f.show();
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
}
String actionKey = "ADD_SAM";
InputMap inputMap = jt.getInputMap(JComponent.WHEN_FOCUSED);
KeyStroke enterPressed = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
inputMap.put(enterPressed, actionKey);
jt.getActionMap().put(actionKey, new TextAction(actionKey) {
#Override
public void actionPerformed(ActionEvent e) {
jt.append(" Sam\n");
}
});
To get input so you know when the use hits enter, you have to create your own KeyListener class. If you don't know how to use it, here is a handy link from the documentation you can use: https://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html.
But simply put, an KeyListener is an interface where you have to specify a few methods, but in your case I think the only one you need is keyPressed(KeyEvent e)(which is called pressed). If you're interested in the others, keyReleased(KeyEvent e) is when a key gets released, and keyType(KeyEvent e) is when it's pressed and released quickly. Then, use JFrames addKeyListener(KeyListener k) to add your custom action listener.
After you did that, you can use JTextArea's setText() and getText() method to append " sam" to the end (the 5 spaces get cut of by stack overflow, I know you want 5 spaces).
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
jt.setText(jt.getText() + " sam");
}
}
If you added the KeyListener correctly, you should be fine!
I want show customer details from a MySQL database in a JTable, but I don't see any result in my panel when I click the "show" button.
This is the method to add the table to the JScrollPane:
void addTable()
{
for(int i=0 ; i<myTableModel.getColumnCount();i++)
{
myTableModel.getColumnName(i);
}
showCustomers();
table.setModel(myTableModel);//mytablemodel is a object from MyTableModel Class
scrollPane.add(table); //was on another part of program but i edit it for helping to answers
panel_show.add(scrollPane);
}
And here is MyTableClass, implementing TableModel, with a new method, addCustomer. addCustomer will add a Customer to the CustomerList. I am trying ArrayList for first time to show Customer data but it doesn't work.
Also, columnName will make the table headers with the column names from the database.
public class MyTableModel implements TableModel
{
private ArrayList<Customer> customerList;
private String[] columnName =
{"id", "name", "family", "idc", "age", "sex", "balance", "tel", "haveFamily", "population"};
#Override
public int getRowCount()
{
//return customerList.size();
return 1;
}
#Override
public int getColumnCount()
{
return 10;
}
#Override
public String getColumnName(int columnIndex)
{
return columnName[columnIndex];
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == 0)
return Integer.class;
return String.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(columnIndex == 0)
return customerList.get(rowIndex).getId();
else if(columnIndex == 1)
return customerList.get(rowIndex).getName();
else if(columnIndex == 2)
return customerList.get(rowIndex).getFamily();
else if(columnIndex == 3)
return customerList.get(rowIndex).getIdc();
else if(columnIndex == 4)
return customerList.get(rowIndex).getDate();
else if(columnIndex == 5)
return customerList.get(rowIndex).getSex();
else if(columnIndex == 6)
return customerList.get(rowIndex).getBalance();
else if(columnIndex == 7)
return customerList.get(rowIndex).getTel();
else if(columnIndex == 8)
return customerList.get(rowIndex).isHaveFamily();
else if(columnIndex == 9)
return customerList.get(rowIndex).getPopulation();
else
return null;
}
public void addCustomer(Customer customer)
{
CustomerManager customerManager = new CustomerManager();
customerManager.addCustomer(customer);
customerList.add(customer);
}
The showCustomer method will be open a database connection and create a TableModel with customers, which is then used for the table on my panel.
public void showCustomers()
{
Connection conn = null;
try
{
//String user = "root";
// String pass = null;
//String url = "jdbc:mysql://localhost/estate";
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/estate", "root", null);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * from customer");
//int columnCount = rsmd.getColumnCount();
while (rs.next())
{
myTableModel.addCustomer(new Customer(1,"david","schmidt","0025674433","31","Male","200000","4545552132","true","2");
}//Original Form --> Customer({"id","name","family","idc","age",
// "sex","balance","tel","haveFamily","population"});
}
In MyTableModel I make an addCustomer method and fill the customer data using a ResultSet. Is it needed?
My question: How can I show my Customers using a JTable?
After some fiddling I now also have a problem with myTableModel.addCustomer(rs.getInt(), ...). The error I am getting is this:
Error in addCustomer,Duplicate entry '0' for KEY PRIMARY
However, I don't have a customer with an ID of 0.
When displaying a JTable you first add the table to a scroll pane and then you add the scroll pane to a panel.
When adding a component to a visible GUI you need to make sure the layout manager has been invoked so that the component has a size and location.
So the basic code would be:
table.setModel(...);
JScrollPane scrollPane = new JScrollPane( table );
panel.add( scrollPane );
panel.revalidate();
panel.repaint();
The other approach is the create an empty table and add the scroll pane to the GUI when you first create the GUI. Then all the code you need is:
table.setModel(...);
Edit:
You question is about displaying a table in a panel, when you click a button. So that is ALL the SSCCE should do. Where the data comes from is irrelevant, so posting code dealing with a database is completely unnecessary, because we can't execute the code. A custom TableModel is irrelevant to the real question.
Here is a simple SSCCE that uses my second suggestion of updating an existing table with a new model. Every time you click the button the number of columns changes. This simulates getting new data from somewhere.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class RefreshSSCCE extends JPanel
{
private JTable table = new JTable();
private int columns = 3;
public RefreshSSCCE()
{
setLayout( new BorderLayout() );
JButton refresh = new JButton( "Refresh Data" );
add(refresh, BorderLayout.NORTH);
refresh.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
refreshData();
}
});
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void refreshData()
{
DefaultTableModel model = new DefaultTableModel(5, columns++);
table.setModel( model );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Refresh SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new RefreshSSCCE() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
See how easy that code is to understand? The code is complete and in one class. It can easily be copied and pasted so others can test the code.
When you simplify the problem then solution is usually much easier. That is why you take the time to create a SSCCE. Even if the SSCCE doesn't work the way you want, we only have a couple of lines of code to look at to understand what you are attempting to do. There is no need to complicate the question with SQL code.
Once you get the simple code working you then modify the refreshData method to get real data. That is what I mean by hardcoding data. There is no need for a dynamic query of the database to demonstrate your problem of displaying a table in a panel.
I want to write a program that allows user to connect,view and add or delete values from database. I'm stuck with the swing part. When i select a combobox option nothing happens but i want to create a view like mysql workbench. It suppose to be like that; user picks a table name from combobox and can see column names from that table and textfields to add new values or existing values above column names.
My code is this so far:
public class DBC extends JFrame{
static String tablo;
static JTextField tf = new JTextField(20);
static int columnCount;
static JPanel tfPanel = new JPanel();
static JLabel depName = new JLabel("Name");
static JLabel depLocation = new JLabel("Location");
static Box box = new Box(BoxLayout.Y_AXIS);
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection connect = DriverManager.getConnection("jdbc:mysql://localhost:3306/project"
,"root","123456789");
final Statement statement = connect.createStatement();
JLabel tabloSec = new JLabel("Tablo Seçin:");
final JComboBox<String> tablolar = new JComboBox<String>();
DatabaseMetaData md = connect.getMetaData();
final ResultSet rs = md.getTables(null, null, "%", null);
while (rs.next()) {
tablolar.addItem(rs.getString(3));
}
tablolar.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
tablo = tablolar.getSelectedItem().toString();
try {
columnCount = rs.getMetaData().getColumnCount();
for(int i=0;i<=columnCount;i++ ){
box.add(tf);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
JButton ekle = new JButton("Ekle");
ekle.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
try {
switch(tablo){
case "department":
statement.executeUpdate("INSERT INTO department(Name,Location) VALUES('"+tf.getText()+"')");
case "employee":
statement.executeUpdate("INSERT INTO employee(Id,FirstName,LastName,Sex,Address,Email,Salary,BirthDate,JoinDate) VALUES('"+tf.getText()+"')");
case "engineer":
statement.executeUpdate("INSERT INTO engineer(EngineerType) VALUES('"+tf.getText()+"')");
case "manager":
statement.executeUpdate("INSERT INTO manager(Department) VALUES('"+tf.getText()+"')");
case "project":
statement.executeUpdate("INSERT INTO project(Name,Number,Value) VALUES('"+tf.getText()+"')");
case "secretary":
statement.executeUpdate("INSERT INTO secretary(TypingSpeed) VALUES('"+tf.getText()+"')");
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
JButton cik = new JButton("Çık");
cik.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
JPanel panel = new JPanel(new GridLayout(4,3));
panel.add(tabloSec);
panel.add(tablolar);
panel.add(box);
panel.revalidate();
panel.add(ekle);
panel.add(cik);
JFrame frame = new JFrame("Deneme");
frame.setSize(600,600);
frame.setLocationRelativeTo(null);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
Looks like you're adding the same text field when iterating over the metadata of the result set: box.add(tf);. This will add the same text field only once. You also need to validate() and repaint() the box container after adding new controls to it. Note that you also need to remove all controls from the box container when selecting new table. You may need to introduce scroll pane. In addition, SQL statement execution refers to the same text field. Unless of course there is only one column and one value that is always should be updated.
All in all, unless this is a very specific solution for a very specific set of tables, you may consider using friendlier controls for this, perhaps a list or a table. Maybe something similar to a properties table where first column specifies the name of the property, and the second column the value of that property. The value column is editable. You can repopulate the properties table once new SQL table is selected. Then on statement execution, just collect all the necessary values. As an alternative, you can also show the relevant view of a SQL table and let user tweak whatever values and then update SQL once done. Look at Table From Database by #camickr.
Also note that you should not execute SQL statements on Event Dispatch Thread, this may freeze your UI as long operations will block EDT. These operations should be handled on an auxiliary worker thread. See Event Dispatch Thread. It is common to use SwingWorker to handle such lengthy tasks.
I need your suggestions and guidence on following task.
I have a frame which has two JComboBoxes supposed they are named combo1 and combo2, a JTable and other components.
At initial stage when frame is visible with above component. The combo1 combobox is filled with some values but no value is selected at initial stage, the combo2 combobox is disabled and the table is empty.
I have added an actionListener on combo1 as well as combo2. There are two types of values in combo1 suppose those values are type1 and type2.
Condition 1:
When we selects value type1 from Combo1 the actionListener method is called of combo1 which invokes a method which combo2 remains disabled and adds some rows to table related to selected value type1 from combo1.
Condition 2:
when we selects value type2 from combo1 the actionListener method is called of combo1 which invokes a method who makes combo2 filled with some values related to type2 and gets enabled but no value is selected from combo2 and table also should remain empty until we selects any value from combo2.
table at every addition of value to combo2 the action listener method of combo2 is gets fired. In actionListener method of combo2 which gets combo2 selected value but here there is no selected value of combo2 which leads to a NullPointerException.
So what should I do that the action listner method of combo2 will not be get executed after addition of an values to combo2.
You could remove the action listener before you add the new elements, and add it back once you're done . Swing is single threaded so there is no need to worry about other threads needing to fire the listener.
Your listener could probably also check if something is selected and take appropriate action if not. Better than getting a NPE.
What i do instead of adding and removing action listeners i have a boolean variable in my action listeners that is true if it has to allow the action through or false if it has to block it.
I then set it to false when i do some changes that will fire off the action listener
JComboBox test = new JComboBox();
test.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(testActionListenerActive)
{
//runn your stuff here
}
}
});
//then when i want to update something where i want to ignore all action evetns:
testActionListenerActive = false;
//do stuff here like add
SwingUtilities.invokeLater(() -> testActionListenerActive = false);
//and now it is back enabled again
//The reason behind the invoke later is so that if any event was popped onto the awt queue
//it will not be processed and only events that where inserted after the enable
//event will get processed.
try this:
indicatorComboBox = new JComboBox() {
/**
* Do not fire if set by program.
*/
protected void fireActionEvent() {
// if the mouse made the selection -> the comboBox has focus
if(this.hasFocus())
super.fireActionEvent();
}
};
although its late, a better alternative would be to disabled the combobox to be modified prior to being modified. by doing so, you prevent firing events of the modified combobox, when for example, you use methods likes removeAllItems() or addItem()
String orderByOptions[] = {"smallest","highest","longest"};
JComboBox<String> jcomboBox_orderByOption1 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption2 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption3 = new JComboBox<String(orderByOptions);
jcomboBox_orderByOption1.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent itemEvent)
{
int eventID = itemEvent.getStateChange();
if (eventID == ItemEvent.SELECTED)
{
Object selectedItem = jcomboBox_orderByOption1.getSelectedItem();
jcomboBox_orderByOption2.setEnabled(false);
jcomboBox_orderByOption2.removeAllItems();
for (String item: string_orderByOptions)
{
if (!item.equals(selectedItem))
{
jcomboBox_orderByOption2.addItem(item);
}
}
jcomboBox_orderByOption2.setEnabled(true);
}
}
});
jcomboBox_orderByOption2.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent itemEvent)
{
int eventID = itemEvent.getStateChange();
if (eventID == ItemEvent.SELECTED)
{
Object selectedItem1 = jcomboBox_orderByOption1.getSelectedItem();
Object selectedItem2 = jcomboBox_orderByOption2.getSelectedItem();
jcomboBox_orderByOption3.setEnabled(false);
jcomboBox_orderByOption3.removeAllItems();
for (String item: string_orderByOptions)
{
if (!item.equals(selectedItem1) && !item.equals(selectedItem2))
{
jcomboBox_orderByOption3.addItem(item);
}
}
jcomboBox_orderByOption3.setEnabled(true);
}
}
});
The cleaner way is to use lambda expressions like this:
do(comboBox, () -> comboBox.setSelectedItem("Item Name"));
For the above to work, you need the following method defined somewhere:
public static void do(final JComboBox<String> component, final Runnable f) {
final ActionListener[] actionListeners = component.getActionListeners();
for (final ActionListener listener : actionListeners)
component.removeActionListener(listener);
try {
f.run();
} finally {
for (final ActionListener listener : actionListeners)
component.addActionListener(listener);
}
}
This works:
/** Implements a Combo Box with special setters to set selected item or
* index without firing action listener. */
public class MyComboBox extends JComboBox {
/** Constructs a ComboBox for the given array of items. */
public MyComboBox(String[] items) {
super(items);
}
/** Flag indicating that item was set by program. */
private boolean isSetByProgram;
/** Do not fire if set by program. */
protected void fireActionEvent() {
if (isSetByProgram)
return;
super.fireActionEvent();
}
/** Sets selected Object item without firing Action Event. */
public void setSelection(Object item) {
isSetByProgram = true;
setSelectedItem(item);
isSetByProgram = false;
}
/** Sets selected index without firing Action Event. */
public void setSelection(int index) {
isSetByProgram = true;
setSelectedIndex(index);
isSetByProgram = false;
}
}
Note: You can't just override setSelectedItem(...) or setSelectedIndex(...) because these are also used internally when items are actually selected by user keyboard or mouse actions, when you do not want to inhibit firing the listeners.
To determine whether or not to perform various methods in actionListener interface methods (actionPerformed() blocks of code) use setActionCommand() on source components (combo1 or combo2).
For your example, before adding elements to combo2, call setActionCommand("doNothing") and guard your comboBoxActionPerformed() method.
Here's a compilable example that uses this principle to have one combo set another combo's selected index while also displaying a String in a JTextField. By using setActionCommand() and guarding the comboActionPerformed() block of code, the JTextField will cycle through each word in the wordBank. If the comboActionPerformed() method was not guarded or if the actionCommand String was not changed, 2 actionEvents will trigger and the textField will skip words.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
/** #author PianoKiddo */
public class CoolCombos extends JPanel {
JComboBox<String> candyCombo;
JComboBox<String> flavorCombo;
JTextField field;
String[] wordBank;
int i = 0;
CoolCombos() {
super();
initComponents();
addComponentsToPanel();
}
private void initComponents() {
initCombos();
initTextField();
}
private void initCombos() {
ActionListener comboListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
comboActionPerformed(e);
}
};
String[] candyList = {"Sourpatch", "Skittles"};
String[] flavorList = {"Watermelon", "Original"};
candyCombo = new JComboBox<>(candyList);
candyCombo.addActionListener(comboListener);
flavorCombo = new JComboBox<>(flavorList);
flavorCombo.addActionListener(comboListener);
}
private void initTextField() {
wordBank = new String[]{"Which", "Do", "You", "Like", "Better?"};
field = new JTextField("xxxxx");
field.setEditable(false);
field.setText(wordBank[i]);
}
private void addComponentsToPanel() {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
this.add(candyCombo);
this.add(flavorCombo);
this.add(field);
}
public void comboActionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (!command.equals("doNothing")) {
JComboBox combo = (JComboBox) e.getSource();
if (combo.equals(candyCombo)) {
setOtherComboIndex(candyCombo, flavorCombo); }
else {
setOtherComboIndex(flavorCombo, candyCombo); }
displayText(); //replace here for toDo() code
}
}
private void setOtherComboIndex(JComboBox combo, JComboBox otherCombo) {
String command = otherCombo.getActionCommand();
otherCombo.setActionCommand("doNothing"); //comment this line to skip words.
otherCombo.setSelectedIndex(combo.getSelectedIndex());
otherCombo.setActionCommand(command);
}
private void displayText() {
i++;
String word;
if (i > 4) { i = 0; }
word = wordBank[i];
field.setText(word);
this.repaint();
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("CoolCombos");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new CoolCombos();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setMinimumSize(frame.getSize());
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I kind of went the stupid simple route with this issue for my program since I am new to programming.
I changed the action listeners to have a counter if statement:
if(stopActionlistenersFromFiringOnLoad != 0){//action performed ;}
Then at the end of the java program creation, I added 1 to the counter:
topActionlistenersFromFiringOnLoad += 1;
To avoid that addItem method fire events is better to use an DefaultComboBoxModel in the JComboBox to add data. Also, if you invoke a model.addElement(), an event is fired, so, you can add all the elements to the model and later use JComboBox.setModel(model). In this way, if you add elements to the model, events are not fired because you have not link the JComboBox with the model. Then, I show you an example.
private void rellenarArrendatarioComboBox(ArrayList<Arrendatario> arrendatarios) {
DefaultComboBoxModel model = new DefaultComboBoxModel();
model.addElement(new Arrendatario(" -- Seleccione un arrendatario --"));
for (Arrendatario arrendatario : arrendatarios) {
model.addElement(arrendatario);
}
ArrendatarioComboBox.setModel(model);
}
First, we create the model, add all elements to the model (events are not fired because you have not link the JComboBox with the model), we link the model with the JComboBox using ArrendatarioComboBox.setModel(model). After linking, events are fired.
I'm trying to display a list of items and, when the user clicks on an item, to clear the list and display another list.
If I run this and click on the first entry on the displayed list, the program dies with a long trail of runtime exceptions. If I remove the clear() line (commented below), it runs fine. Adding try/catch didn't reveal any information useful to me. Apologies for the long code, but I couldn't figure out how to shorten and still generate the errors.
What at I doing wrong?
import java.util.*;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class ListGui extends JPanel implements ListSelectionListener {
private static JList list;
private static DefaultListModel listModel = new DefaultListModel();
public ListGui() {
super(new BorderLayout());
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.addListSelectionListener(this);
JScrollPane listScrollPane = new JScrollPane(list);
add(listScrollPane, BorderLayout.CENTER);
}
public static void Populate(List<String> lines) {
listModel.clear();
for(String line : lines) {
listModel.addElement(line);
}
}
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
List<String> out = new ArrayList<String>();
out.add("three");
out.add("four");
Populate(out);
}
}
}
public class TestClear {
static JComponent newContentPane = new ListGui();
private static void createAndShowGUI() {
JFrame frame = new JFrame("toast");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
List<String> out = new ArrayList<String>();
createAndShowGUI();
out.add("one");
out.add("two");
ListGui.Populate(out);
}
}
I'm trying to display a list of items and, when the user clicks on an item, to clear the list and display another list.
That doesn't sound like the best design to me. The selection will change whenever you click on an item or when you use the arrow keys to move up or down the list. I'm sure for users that like to use the keyboard you don't want the list to change every time you use an arrow key.
The normal design would be to invoke an Action on the list on a "double click" or when the user users "Enter" from the keboard. This is easily implemente using the List Action concept.
However, if you really do want to update the list on every selection then I would use code like:
list.removeListSelectionListener( this );
populate(...);
list.addListSelectionListener(this);
The problem is that you are calling Populate() in valueChanged() which triggers valueChanged() and hence the stackoverflow.
The simplest solution is to have a flag to prevent reentry.
boolean busy = false;
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false && !busy) {
busy = true;
List<String> out = new ArrayList<String>();
out.add("three");
out.add("four");
Populate(out);
busy = false;
}
}
If your code could be accessed by multiple threads, you should be looking into ReentrantLock