I've been reading a lot on Swing but I've hit a dead end and I know you can help me.
I've read lots of questions like Updating an JList but still I'm clueless as how to proceed.
My problem is the same as the guy who asked the question I mentioned. I'm making a server and users will access it.
This are my classes.
Server
private string name;
private string dateOfAccess;
#Override
public String toString() {
// TODO Auto-generated method stub
return nombreAmigo;
}
Main
private DefaultListModel listModel = new DefaultListModel();
private JList list=new JList(listModel);
and my ClientHandler
public static List<Conexion> clientes=new ArrayList<Conexion>();
So, I'm going to be filling the clientes list from different threads as they connect to my server and I need to show them in my Jlist. Any suggestions on how to update it? I'm really stuck here
Thanks!
Personally, I would have some kind of "client manager" that is responsible for collating all the clients into a centralised repository. This would be a singleton within your server. It could be queried at any time for a list of currently active users (and other management functions), but there should only ever be one active.
The manager would then fire notification events to interested parties (using an observe pattern).
One of these parties would be your UI. When a "connect" or "disconnect" event is raised, you will need to ensure that this is synced back the Event Dispatching Thread BEFORE you try and update the list model, for example...
public void userConnected(UserManagerEvent evt) { // You would need to define all this yourself...
final Conexion user = evt.getConnection(); // You would need to define this event yourself...
EventQueue.invokeLater(new Runnable() {
public void run() {
listModel.addElement(user);
}
});
}
The actually implementation will come down to what it is you want to achieve and the way you want to achieve it, but this is the basic concept...
Updated with conceptual example
This is a basic, conceptual, example. An event is raised by the button, which simulates (for example) a connection. This event is then sent to the list, via an listener interface, where the model is updated
Events are generated from some other source and the UI is updated when they occur, classic observer pattern
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.ls.LSInput;
public class UpdateListOnEvent {
public static void main(String[] args) {
new UpdateListOnEvent();
}
public UpdateListOnEvent() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ConnectionEvent {
private Date date;
public ConnectionEvent(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
public interface ConnectionListener {
public void connectionEstablished(ConnectionEvent evt);
}
public class TestPane extends JPanel implements ConnectionListener {
private JList list;
private DefaultListModel<String> model;
public TestPane() {
setLayout(new BorderLayout());
model = new DefaultListModel<>();
list = new JList(model);
add(new JScrollPane(list));
EventPane eventPane = new EventPane();
eventPane.addConnectionListener(this);
add(eventPane, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
#Override
public void connectionEstablished(ConnectionEvent evt) {
model.addElement(DateFormat.getDateTimeInstance().format(evt.getDate()));
}
}
public class EventPane extends JPanel {
private List<ConnectionListener> listeners;
private JButton update;
public EventPane() {
listeners = new ArrayList<>(5);
setLayout(new GridBagLayout());
update = new JButton("Update");
update.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// connectionEstablished(new Date());
fireConnectionEstablished(new Date());
}
});
add(update);
}
public void addConnectionListener(ConnectionListener listener) {
listeners.add(listener);
}
public void removeConnectionListener(ConnectionListener listener) {
listeners.remove(listener);
}
protected ConnectionListener[] getConnectionListeners() {
return listeners.toArray(new ConnectionListener[listeners.size()]);
}
protected void fireConnectionEstablished(Date date) {
ConnectionListener[] listeners = getConnectionListeners();
if (listeners != null && listeners.length > 0) {
ConnectionEvent evt = new ConnectionEvent(date);
for (ConnectionListener listener : listeners) {
listener.connectionEstablished(evt);
}
}
}
}
}
Related
Found a simple JComboBox sample on java2 and expanded it to include both a DocumentListener and KeyListener
hoping to capture keystrokes done within the JComboBox.
Eventually those keystrokes will be captured to display the data which matches the keys entered.
For example, as the user types APP, all records beginning with A is return then AP is return and then all data beginning with APP. Basically doing a Filter on "APP*".
But for now, I am unable to get either a KeyListener or DocumentListener working.
Actually, it works sporadically and can't narrow down why. It seems to only work for the Enter key but would like for it to capture for all keystrokes.
Here is the code.
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.ComboBoxEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.metal.MetalFileChooserUI;
public class JComboBoxFilter extends JPanel implements ItemListener {
public JComboBoxFilter () {
JComboBox jc = new JComboBox();
jc.addItem("A");
jc.addItem("AA");
jc.addItem("AAA");
jc.addItem("C");
jc.addItem("CC");
jc.addItem("CCC");
jc.addItem("B");
jc.addItem("BB");
jc.addItem("BBB");
jc.addItemListener(this);
add(jc);
ComboBoxEditor editor = jc.getEditor();
JTextField textField = (JTextField)editor.getEditorComponent();
textField.addKeyListener(new KeyListener()
{
#Override
public void keyPressed(KeyEvent arg0) {
runThisKeyListener();
}
#Override
public void keyReleased(KeyEvent arg0) {
runThisKeyListener();
}
#Override
public void keyTyped(KeyEvent arg0) {
runThisKeyListener();
}
private void runThisKeyListener()
{
System.out.println("Inside runThisKeyListener() : " + textField.getText());
}
});
DocumentListener textFieldDL = new DocumentListener()
{
#Override
public void insertUpdate(DocumentEvent e)
{
runThis();
}
#Override
public void removeUpdate(DocumentEvent e)
{
runThis();
}
#Override
public void changedUpdate(DocumentEvent e)
{
runThis();
}
private void runThis()
{
System.out.println("Inside runThis() : " + textField.getText());
}
};
textField.getDocument().addDocumentListener(textFieldDL);
}
public void itemStateChanged(ItemEvent ie) {
String s = (String)ie.getItem();
System.out.println(s);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new JComboBoxFilter ());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
}
}
Is there anything wrong with the code or is there a Java rule or restriction not allowing this type of functionality?
I want to get rid of the repeating code from an application. I tried to do it in several ways, but when I did it, the application did not work as I expected. Only putting the same code in a function had an effect.
In a nutshell, in the application I can delete records in 2 ways, either by pressing a button or by pointing to a record and deleting it with the right mouse button. Button deletion works, but I do not know how to make the mouse deletion have the same effect.
Button that deletes a record in a table.
deleteButton.addActionListener(event -> {
String name;
name = Name.getText();
try {
removeSelectedRow(table1);
pst = con.prepareStatement("delete from recipe where recipe_name = ?");
pst.setString(1, name);
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Record deleted");
Name.setText("");
Time.setText("");
Difficulty.setSelectedItem("");
Name.requestFocus();
} catch (SQLException e) {
e.printStackTrace();
}
});
here is a function that is supposed to delete with the right mouse button, as you can see it works but the code is almost identical to the previous example.
public void setDeleteButton(ActionEvent event) {
JMenuItem menu = (JMenuItem) event.getSource();
if (menu == menuItemRemove) {
removeSelectedRow(table1);
}
String name;
name = Name.getText();
try {
removeSelectedRow(table1);
pst = con.prepareStatement("delete from recipe where recipe_name = ?");
pst.setString(1, name);
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Record deleted");
Name.setText("");
Time.setText("");
Difficulty.setSelectedItem("");
Name.requestFocus();
} catch (SQLException e) {
e.printStackTrace();
}
}
function that points to a specific record
public void removeSelectedRow(JTable table) {
DefaultTableModel model = (DefaultTableModel) table1.getModel();
if (table.getSelectedRow() != -1) {
model.removeRow(table.getSelectedRow());
}
}
Okay, so this is going to require a slight shift in mindset. To make this truely flexible, you're going to need to support concepts such as "dependency inject" and "delegation".
The reason for this is, you "operation" needs a lot of information, but, we should be driving towards having a low level of cohesion or coupling between our classes. Your "operation", for example, shouldn't care "how" the row is deleted, only that when requested, it should be done.
So, lets start with some basic delegation...
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
Now, obviously, I'm overly simplifying this for more my needs, but here I've provided a "basic" level delegate and more focused delegate. Why? Because what happens if you want to provide a "insert" action? Why should it have "delete" functionality? Instead, we deliberately isolate the functionality we want to expose.
Next, we need to design our action...
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
Okay, nothing really special, which is kind of the point. It monitors the selection state so we can enable/disable the action and when triggered, we call our delegate to do the actual work. This decouples the action from the implementation, as the action doesn't need to know what type of TableModel is in use or what type of data source it might be using, it just wants to tell the delegate that it should carry out some kind of operation.
Also note, we've set up a keyboard short cut, which can be used by the JMenuItem and mnemonic support (hold down the Alt or Option key)
Okay, but that's really doing a lot for us, or is it...
Let's have a look at what a delete action might look like...
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
DefaultTableModel model = (DefaultTableModel) table.getModel();
int visibleRowIndex = table.getSelectedRow();
if (visibleRowIndex == -1) {
return;
}
int modelIndex = table.convertRowIndexToModel(visibleRowIndex);
// I'm guessing here, but if you're deleting a row, you should
// use the row data
String recordId = (String) model.getValueAt(modelIndex, 0);
try (PreparedStatement pst = getConnection().prepareStatement("delete from recipe where recipe_name = ?")) {
pst.setString(1, recordId);
// You could check the number of rows effected by this change
pst.executeUpdate();
JOptionPane.showMessageDialog(TestPane.this, "Record deleted", "Success", JOptionPane.INFORMATION_MESSAGE);
model.removeRow(modelIndex);
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(TestPane.this, "Failed to delete row from database", "Error", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
Now, this is just an example, but the basic idea is, we've provide implementation for both the MutableTableSupportable and TableRowDeletable interfaces (but the DeleteRowAction doesn't care about the "how") and we've implemented the removeSelectedRow functionality to delete the row from the TableModel and database.
Again, DeleteRowAction doesn't care how this is implemented, it's just delegating that responsibility, so you could have multiple DeleteRowActions which work with different TableModels and data sources all at the same time 😱
Delegation 💪
Okay, but how would all that work together? Well, actually, really easily in fact
Runnable example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.sql.*;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
nb: This example removes the database support, as I don't have one and instead displays a message
Okay, lets take a quick look at some of the interesting things here...
Firstly...
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
We inject the menu bar into the panel. This is done so that the panel can configure the menu bar as it needs. We could use a type of factory or another delegate here, but I'll leave that up to you to figure out.
Next...
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
We build the JMenu and add our delete row action and create a JButton, using the same Action ... for five lines of code, we've actually done a lot. We've been able to set up the text displayed by each component, the tooltip text, the accelerator key and mnemonic ... try doing that manually, and then need to change something down the track 🙄 (want to support localisation - need to make changes in one location)
But wait, we can do more!! 😱
If we add...
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
to the end of the constructor, we can can provide a key binding to the user, so that when the JTable has keyboard focus and the hit they Delete/Backspace key, it will trigger the action as well!!!
Now we have four ways to trigger the action:
Press the button
Open and trigger the menu items
Use the menus keyboard "accelerator" key binding
Hit the Delete key
Actions 💪
Key bindings runnable example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JMenuBar menuBar = new JMenuBar();
JFrame frame = new JFrame();
frame.setJMenuBar(menuBar);
frame.add(new TestPane(menuBar));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
public TestPane(JMenuBar menuBar) {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(new Object[][]{new Object[]{"Test"}}, new Object[]{"Test"});
table = new JTable(model);
add(new JScrollPane(table));
DeleteRowAction deleteRowAction = new DeleteRowAction(new TableRowDeletable() {
#Override
public int getSelectedRowCount() {
return table.getSelectedRowCount();
}
#Override
public void removeSelectedRow() {
JOptionPane.showMessageDialog(TestPane.this, "Delete the row please", "Debug", JOptionPane.INFORMATION_MESSAGE);
}
#Override
public void addListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().addListSelectionListener(listener);
}
#Override
public void removeListSelectionListener(ListSelectionListener listener) {
table.getSelectionModel().removeListSelectionListener(listener);
}
});
JMenu actionsMenu = new JMenu("Actions");
actionsMenu.add(deleteRowAction);
menuBar.add(actionsMenu);
JButton deleteButton = new JButton(deleteRowAction);
add(deleteButton, BorderLayout.SOUTH);
InputMap inputMap = table.getInputMap(WHEN_FOCUSED);
ActionMap actionMap = table.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), "deleteRow");
actionMap.put("deleteRow", deleteRowAction);
}
}
public interface MutableTableSupportable {
public void addListSelectionListener(ListSelectionListener listener);
public void removeListSelectionListener(ListSelectionListener listener);
}
public interface TableRowDeletable extends MutableTableSupportable {
public int getSelectedRowCount();
public void removeSelectedRow();
}
public class DeleteRowAction extends AbstractAction {
private TableRowDeletable delgate;
public DeleteRowAction(TableRowDeletable delgate) {
putValue(SHORT_DESCRIPTION, "Delete the currently selected row");
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
putValue(NAME, "Delete Row");
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.CTRL_DOWN_MASK));
this.delgate = delgate;
delgate.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
selectionDidChange();
}
});
selectionDidChange();
}
protected void selectionDidChange() {
setEnabled(delgate.getSelectedRowCount() > 0);
}
#Override
public void actionPerformed(ActionEvent e) {
delgate.removeSelectedRow();
}
}
}
But that's not all! We could also add a button to a JToolBar, because, why not!?
I am writing a java program to get realtime stock data. I used the URL class to get the information from yahoo finance. I am able to get all the information I need using a class for the stock. However, I want the GUI to update the value of the stock price every second or half second by calling a method that get the price of the stock, and update the value in a JLabel every second. So essentially, how do I update a value in Swing GUI by calling a method every second? Thank you.
There are a number of ways you might achieve this. A Swing Timer is a good choice to generate calls backs (into the Event Dispatching Thread) after a specified delay.
But what you probably want to is schedule a callback to perform the request and once it's completed, update the UI and schedule another callback. The point been, the actual request might take longer to call/process than the amount of time you've allocated between calls.
The following makes use of SwingWorker to perform the actual "work", using a random delay of between 1-5 seconds, once completed, it notifies a "observer" of some new state (in this case, just some nice messages).
Once the worker completes, a new worker is scheduled, using a ScheduledExecutorService (although you could do it manually with a Swing Timer)
See Concurrency in Swing
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
private JLabel label;
public TestPane() {
setBorder(new EmptyBorder(16, 16, 16, 16));
label = new JLabel("Nothing happening here, just waiting for stuff");
setLayout(new GridBagLayout());
add(label);
startNextWorker();
}
protected void startNextWorker() {
ExecutorWorker worker = new ExecutorWorker(new ExecutorWorker.Observer() {
#Override
public void workerDidUpdate(String message) {
label.setText(message);
}
});
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
worker.removePropertyChangeListener(this);
startNextWorker();
}
}
});
service.schedule(worker, 1500, TimeUnit.MILLISECONDS);
}
}
public class ExecutorWorker extends SwingWorker<Void, String> {
public interface Observer {
public void workerDidUpdate(String message);
}
private Random rnd = new Random();
private Observer observer;
public ExecutorWorker(Observer observer) {
this.observer = observer;
}
#Override
protected Void doInBackground() throws Exception {
publish("Starting some new work, back in a seocnd");
Thread.sleep(1000 + rnd.nextInt(5000));
publish("All the work is now done");
return null;
}
#Override
protected void process(List<String> chunks) {
for (String messages : chunks) {
observer.workerDidUpdate(messages);
}
}
}
}
I have this java swing program, and im trying to figure out how can i create a button that upon clicking it will clear the text areas & change the icon of the person to put their hand down.
The buttons are dynamically generated using a for loop
And this
// To create buttons
for(int i=0 ; i < list.length; i++){
Participant pa = list[i];
JButton b = new JButton(pa.getNameButton(),participant);
b.addActionListener(e ->
{
String s = pa.toString() + questionPane.getText();
final ImageIcon raise = resizeIcon(new ImageIcon("src/raise.png"),30,30);
b.setIcon(raise);
JOptionPane.showMessageDialog(null,s,"Welcome to Chat Room",JOptionPane.INFORMATION_MESSAGE,pa.getImage());
});
p.add(b);
}
// Clear button logic
clearButton.addActionListener(e ->{
questionPane.setText("");
hostPane.setText("");
});
Okay, this is going to be a bit of fun.
The following example decouples much of the concept and makes use of a basic "observer pattern" to notify interested parties that the state has changed (ie, the chat's been cleared).
This is a basic concept where by you decouple the "what" from the "how", ie, "what" it is you want done (update the model) from the "how" it gets done (ie, button push). This makes it easier to adapt to more complex systems.
The example contains a ChatService, which has a single listener, which, for this example, simple tells interested parties that the chat has been cleared.
A more complex solution might have the ChatService generating events for when a user "raises" their hand, which allows the interested parties to deal with it in what ever way is relevant to them.
The example makes use of the Action API to decouple the work performed by each action from the UI itself. This helps create a single unit of work which is easier to deal with when you have a dynamic data set.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
ChatService chatService = new ChatService();
JPanel panel = new JPanel();
String[] names = new String[] {"Bryan", "Alan", "George", "Henry"};
List<PeopleAction> actions = new ArrayList<>(names.length);
for (String name : names) {
PeopleAction action = new PeopleAction(chatService, name, false);
actions.add(action);
}
Random rnd = new Random();
actions.get(rnd.nextInt(names.length)).setRaised(true);
for (Action action : actions) {
JButton btn = new JButton(action);
panel.add(btn);
}
setLayout(new GridLayout(2, 1));
add(panel);
JPanel hostPane = new JPanel();
JButton clearButton = new JButton(new ClearAction(chatService));
hostPane.add(clearButton);
add(hostPane);
}
}
public class ChatService {
private List<ChatListener> listeners = new ArrayList<>(25);
public void addChatListeners(ChatListener listener) {
listeners.add(listener);
}
public void removeChatListener(ChatListener listener) {
listeners.remove(listener);
}
protected void fireChatCleared() {
if (listeners.isEmpty()) {
return;
}
for (ChatListener listener : listeners) {
listener.chatCleared();
}
}
public void clear() {
// Do what's required
fireChatCleared();
}
}
public interface ChatListener {
public void chatCleared();
}
public class PeopleAction extends AbstractAction implements ChatListener {
private String name;
private boolean raised;
public PeopleAction(ChatService chatService, String name, boolean raised) {
// You can use either LARGE_ICON_KEY or SMALL_ICON to set the icon
this.name = name;
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
chatService.addChatListeners(this);
}
public void setRaised(boolean raised) {
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
}
public boolean isRaised() {
return raised;
}
#Override
public void actionPerformed(ActionEvent evt) {
// Do what ever needs to be done
setRaised(!isRaised());
}
#Override
public void chatCleared() {
setRaised(false);
}
}
public class ClearAction extends AbstractAction {
private ChatService chatService;
public ClearAction(ChatService chatService) {
this.chatService = chatService;
putValue(NAME, "Clear");
}
#Override
public void actionPerformed(ActionEvent evt) {
chatService.clear();
}
}
}
I have a swing GUI named SpyBiteDemo, it will call another class(Parser) and do some calculations and show some data in a jtable inside this GUI(SpyBiteDemo). I have a jbutton1 and I want to when click on it to show my timer to begin like 1,2,3,4,5,....seconds
what happens is my timer is running correctly however it does not show value unless it is done with all the program that is filling the jtable which means the action it is detecting is I perform the action after jtable appears.
I am a complete newbie on Java for event listeners and I have searched timer, timer task, schedule, everything and could not understand what's wrong.I also tried while(true) and did not fix it.I also tried duration of 1000,0,everything no affects.
I tried to use action command,sleeping the thread, it did not help.here is what I did:
public class SpyBiteDemo extends javax.swing.JFrame {
/**
* Creates new form SpyBiteDemo
*/
private long startTime;
Timer timer = new Timer(0, new TimerListener());
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent aEvt) {
long time = (System.currentTimeMillis() - startTime) / 1000;
label3.setText(time + " seconds");
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
jButton1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
startTime = evt.getWhen();
String SeedUrl = jTextField1.getText();
Parser P = new Parser(this);
jTable2.setVisible(true);
}
}
it will start showing label3 value only after it is filling the jtable on my jframe.I want the timer to start from when I am clicking the button.
with trashgod's links I had come up with this example which is comepletely runnable on your machine, this works perfect except that when I the program finishes, it does not stop the timer since I don't know where to do it, I know I should do it in addPropertyChangeListener, however I do not have timer value.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javaapplication7;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
/**
* #see http://stackoverflow.com/a/25526869/230513
*/
public class DisplayLog {
private static final String NAME = "C:\\wamp\\bin\\mysql\\mysql5.6.17\\bin\\scosche.sql";
private static class LogWorker extends SwingWorker<TableModel, String> {
private final File file;
private final DefaultTableModel model;
private LogWorker(File file, DefaultTableModel model) {
this.file = file;
this.model = model;
model.setColumnIdentifiers(new Object[]{file.getAbsolutePath()});
}
#Override
protected TableModel doInBackground() throws Exception {
BufferedReader br = new BufferedReader(new FileReader(file));
String s;
while ((s = br.readLine()) != null) {
publish(s);
}
return model;
}
#Override
protected void process(List<String> chunks) {
for (String s : chunks) {
model.addRow(new Object[]{s});
}
}
#Override
protected void done() {}
}
private void display() {
JFrame f = new JFrame("DisplayLog");
JLabel m=new JLabel("time");
JButton jb=new JButton("run");
f.add(jb,BorderLayout.BEFORE_FIRST_LINE);
f.add(m, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
JProgressBar jpb = new JProgressBar();
f.add(jpb, BorderLayout.NORTH);
f.add(new JScrollPane(table));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
LogWorker lw = new LogWorker(new File(NAME), model);
lw.addPropertyChangeListener((PropertyChangeEvent ev) -> {
SwingWorker.StateValue s = (SwingWorker.StateValue) ev.getNewValue();
jpb.setIndeterminate(s.equals(SwingWorker.StateValue.STARTED));
});
lw.execute();
int timeDelay = 0;
ActionListener time;
time = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
m.setText(System.currentTimeMillis() / 1000 + "seconds");
}
};
Timer timer=new Timer(timeDelay, time);
timer.start();
if(lw.isDone())
timer.stop();
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new DisplayLog().display();
});
}
}
It looks like the rows that comprise your table come from the network with variable, indeterminate latency. As shown here, you can load data into your TableModel in the background of a SwingWorker. As shown here, you can calculate intermediate progress in a way that makes sense for your application and display it in a PropertyChangeListener.