How to update JList from another Thread? - java

public class ListExample {
public static void main(String[] args) {
final List l=new List();
l.init();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
l.list.updateUI();
System.out.println("Asim");
}
});
}
}
public class List extends JFrame{
public DefaultListModel DLM=new DefaultListModel();
public JList list=new JList(DLM);
private JPanel jp=new JPanel();
private JScrollPane sp=new JScrollPane(list);
public void init(){
jp.add(sp);
super.add(jp);
super.setVisible(true);
super.setLayout(new FlowLayout());
super.setSize(400,500);
UL p=new UL();
p.UL_1();
}
public void Update_list(String[] n){
list.setListData(n);
list.updateUI();
}
}
public class UL extends List{
public void UL_1(){
t.start();
}
Thread t=new Thread(new Runnable() {
#Override
public void run() {
//To change body of implemented methods use File | Settings | File Templates.
String[] n={"Asim", "saif","Khan"};
List l=new List();
l.list.setListData(n);
list.updateUI();
}
});
}

Swing is a single threaded framework, this means that your are expected to only modify the UI elements from within the context of the Event Dispatching Thread. Equally, any long run task or other blocking process will prevent the EDT from processing new events.
While there are a number of ways you might achieve this, probably the simplest would be to use a SwingWorker
For example...
Populate JList with threads
Take a look at Concurrency in Swing for more details

You'd better not to write your java code in that bad-style.
Don not make xxxList extends JFrame ,which will cause misunderstanding.
To use a thread update a list,may be you can start a new thread,like follwing:
Before: not encouraged to use another non EDT thread,incur error.
import java.awt.FlowLayout;
import javax.swing.*;
/*
* This code is bad dealing with Swing component update
*
* Update a Swing component from A Non-EDT thread is not encouraged
* may incur error like:
*
* Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
*/
public class ListExampleBad {
public static void main(String[] args) {
final ListFrame listFrame=new ListFrame();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//use a thread to update list data
new Thread(new UpdateList(listFrame)).start();
System.out.println("Asim");
}
});
}
}
//list fram with JList
class ListFrame extends JFrame{
public ListFrame(){
jp.add(sp);
super.add(jp);
super.setVisible(true);
super.setLayout(new FlowLayout());
super.setSize(400,500);
}
public synchronized void updateList(String[] n){
list.setListData(n);
list.updateUI();
}
private static final long serialVersionUID = 1L;
private DefaultListModel<String> DLM=new DefaultListModel<String>();
private JList<String> list=new JList<String>(DLM);
private JPanel jp=new JPanel();
private JScrollPane sp=new JScrollPane(list);
}
//runnable dealing with data update
class UpdateList implements Runnable {
public UpdateList(ListFrame listFrame) {
this.ListFrame = listFrame;
}
#Override
public void run() {
// TODO Auto-generated method stub
if(SwingUtilities.isEventDispatchThread()) {
System.out.println("updating list from Event Dispatch Thread");
}else {
System.out.println("updating list NOT from Event Dispatch Thread");
}
String[] n={"Asim", "saif","Khan"};
ListFrame.updateList(n);
}
private ListFrame ListFrame;
}
Thank you who make comments, I update the code:
《core java》 guides us tow principle:
1)don't do a time-consuming job in EDT thread,using a SwingWorker.
2)Except from EDT,don not operate Swing componnet in other thread.
Besides,you can use SwingUtilities.isEventDispatchThread() to check if the job is doing in a EDT thread.
one way: use SwingUtilities.invokeLater
public class ListExampleBetter {
public static void main(String[] args) {
final ListFrameBad listFrame=new ListFrameBad();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//use SwingUtilities.invokeLater,it's ok
SwingUtilities.invokeLater(new UpdateList(listFrame));
}
}
another way:using SwingWoker
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.*;
/**
* This example illustrate updating JList from thread
*/
public class ListExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ListFrame listFrame = new ListFrame();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
listFrame.setLocationRelativeTo(null);
listFrame.setVisible(true);
}
});
}
}
//frame with JList
class ListFrame extends JFrame{
public ListFrame(){
super("Update JList Demo");
//initalize data field
dataToUpdate =new String[]{"Asim", "saif","Khan"};
DefaultListModel<String> DLM =new DefaultListModel<String>();
DLM.addElement("wait for update...");;
list =new JList<String>(DLM);
//build gui
JPanel btnPanel = new JPanel();
JButton btnUpdate = new JButton("Update");
btnPanel.add(btnUpdate);
JScrollPane sp=new JScrollPane(list);
this.add(btnPanel,BorderLayout.NORTH);
this.add(sp,BorderLayout.CENTER);
this.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
//deal with action
btnUpdate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ListUpdater updater = new ListUpdater();
updater.execute();
}
});
}
public synchronized void updateList(String[] n){
list.setListData(n);
list.updateUI();
}
//using Swingworker to update list
private class ListUpdater extends SwingWorker<Void,String>{
#Override
public Void doInBackground() {
for(String str :dataToUpdate ) {
publish(str);
}
return null;
}
#Override
public void process (List<String> datas) {
for(String str : datas) {
model.addElement(str);
}
}
#Override
public void done() {
if(SwingUtilities.isEventDispatchThread()) {
System.out.println("updating list from Event Dispatch Thread");
}else {
System.out.println("updating list NOT from Event Dispatch Thread");
}
list.setModel(model);
}
private DefaultListModel<String> model =new DefaultListModel<String>();
}
public String[] getDataToUpdate() {
return dataToUpdate;
}
public void setDataToUpdate(String[] dataToUpdate) {
this.dataToUpdate = dataToUpdate;
}
private static final long serialVersionUID = 1L;
private final int DEFAULT_WIDTH = 300,DEFAULT_HEIGHT = 300;
private JList<String> list ;
private String[] dataToUpdate ;
}

Related

How to show JList from a blockingQueue?

I'm using multi threading with Java, I have a thread that will post a message into a queue, a blockingQueue which is thread safe, and I have another thread, implementing a GUI with swing.
Everytime I'm checking whether the queue is empty or not, if not, I poll the message and add it to DefaultListModel, but the problem the display is not updated.
I made sure that the message polled is not empty.
This is the code:
For the implementation of the JList
historyListModel = new DefaultListModel<String>();
historyList = new JList<String>(historyListModel);
historyList.setAutoscrolls(true);
JScrollPane historyScroll = new JScrollPane(historyList);
add(historyScroll, BorderLayout.CENTER);
Adding message to the queue
private BlockingQueue<String> messageRes = new LinkedBlockingQueue<>();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String msgReceived = new String(packet.getData(), 0, packet.getLength());
messageRes.add(msgReceived);
The thread to update the display
private class update implements Runnable{
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
}
}
}
}
Then this thread is called in the GUI main window
new Thread(new update()).start();
In order to make some tests, I tried the following code and It worked
private class update implements Runnable{
#Override
public void run() {
while (true){
Thread.sleep(1000)
historyListModel.addElement("hello world);
}
}
}
The previous code allows me to update the display every 1 second.
I tried one more code in order to triangulate the error:
private class update implements Runnable{
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
historyListModel.addElement("hello world);
}
}
}
}
No display was updated with the previous code.
Could anyone propose any explanation to what is happening ?
Thank you.
Swing is not thread-safe and is single-threaded. This means you should not be updating the UI from outside of the context of the Event Dispatching Thread nor should you be executing long-running/blocking calls within its context.
In your case, a SwingWorker would probably be the best solution, as you can poll the queue for new messages and publish them to the UI in a safe manner.
See...
Concurrency in Swing
Worker Threads and SwingWorker
for more details.
One thing I did note during my testing was this...
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
}
}
}
was probably causing the UI to be overloaded, as poll will either return the next element or null if there are none. So it was a "wild loop". Instead, you should probably be using take which will wait till a new value is available.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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 JList<String> messageList;
private DefaultListModel<String> model;
public TestPane() {
model = new DefaultListModel<>();
messageList = new JList<>(model);
setLayout(new BorderLayout());
add(new JScrollPane(messageList));
Consumer consumer = new Consumer();
Producer producer = new Producer();
ConsumerWoker worker = new ConsumerWoker(consumer, model);
worker.execute();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
consumer.add(producer.next());
Thread.sleep(500);
}
} catch (InterruptedException ex) {
}
}
});
thread.start();
}
}
public class Producer {
private List<String> master = Arrays.asList(new String[]{
"Malachy Keller",
"Ruqayyah Galvan",
"Harris Nunez",
"Nojus Riggs",
"Joan Mercer",
"Lynda Solomon",
"Raiden Fitzpatrick",
"Sebastian Ahmed",
"Jo Short",
"Nabeel Howarth",
"Maira Garrett",
"Patrik Knights",
"Mimi Mcgill",
"Antonina Villanueva",
"Kenya Hyde",
"Aleksander Rigby",
"Hasan Gilmore",
"Jessica Mcculloch",
"Seth Black",
"Marjorie Brewer",
"Elliot Gay",
"Oluwatobiloba Bowman",
"Domonic Saunders",
"Braden Hale",
"Muneeb Rankin",
"Ruby Tapia",
"Iris Hines",
"Afsana Ponce",
"Beverly Soto",
"Presley Bloggs",
"Leopold Goddard",
"Missy Browne",
"Deniz Woodcock",
"Gwion Ferreira",
"Stanley Mccall",
"Jayda Christie",
"Nikhil Plummer",
"Stacy Crosby",
"Cally Henry",
"Lilliana Taylor",
"Dolcie Navarro",
"Merryn Reynolds",
"Annalise Boyce",
"Anaya Cisneros",
"Aimie Piper",
"Celine Pearson",
"Clayton Battle",
"Danielle Briggs",
"Maddison Couch",
"Jorden Keeling",
"Iylah Holmes",
"Bethaney Quintero",
"Dominique Brett",
"Rohit Benjamin",
"Edgar Rodgers",
"Petra Salgado",
"Myrtle Deleon",
"Letitia Sheridan",
"Wasim Chester",
"Leela Simpson",
"Aine Rojas",
"Ava Mclean",
"Jerry Caldwell",
"Fraser Prosser",
"Callum Vang",
"Yasmin Ochoa",
"Gaia Daly",
"Vanessa Mathews",
"Scarlett Brook",
"Rhiann Fox",
"Nansi Cote",
"Dwayne Rowley",
"Junior Lucas",
"Becky Rush",
"Lori Guthrie",
"Safa Reed",
"Merlin Cartwright",
"Misbah Trejo",
"Khaleesi Ellison",
"Lena Wood",
"Bluebell Coffey",
"Sherry Hutton",
"Abi Delacruz",
"Kwabena Bright",
"Anastazja Kumar",
"Bronwyn Huffman",
"Atif Burke",
"Arwen Kirby",
"Bobbie Noble",
"Blane Bauer",
"Zander Sparrow",
"Marius Wormald",
"Rajan Perez",
"Teejay Faulkner",
"Imaani Rodriquez",
"Safaa Middleton",
"Rafael Livingston",
"Oakley Swan",
"Samiya Kim",
"Glen Beasley"
});
private List<String> avaliableMessages;
public Producer() {
avaliableMessages = new ArrayList<>(100);
}
public String next() {
if (avaliableMessages.isEmpty()) {
avaliableMessages.addAll(master);
}
return avaliableMessages.remove(0);
}
}
public class Consumer {
private BlockingQueue<String> messages = new LinkedBlockingDeque<>();
public void add(String message) {
messages.add(message);
}
public String next() throws InterruptedException {
return messages.take();
}
}
public class ConsumerWoker extends SwingWorker<Void, String> {
private AtomicBoolean keepRunning = new AtomicBoolean(true);
private Consumer consumer;
private DefaultListModel model;
public ConsumerWoker(Consumer consumer, DefaultListModel model) {
this.consumer = consumer;
this.model = model;
}
public void stop() {
keepRunning.set(false);
}
public Consumer getConsumer() {
return consumer;
}
#Override
protected Void doInBackground() throws Exception {
while (keepRunning.get()) {
String message = getConsumer().next();
publish(message);
}
return null;
}
#Override
protected void process(List<String> chunks) {
for (String msg : chunks) {
model.addElement(msg);
}
}
}
}

How to close Threads before opening new Threads in Java?

I have problems with Java's Multi-Threading feature, so hopefully somebody can help me....
Here is my problem:
In the JPanel ExamplePanel which is located in the JFrame ExampleFrame I've added a ComponentListener which invokes the startPaint()-Method. This method should work in a new Thread. My Problem is that by resizing the window "former" Threads aren't closed, meanwhile new Threads are added....
So is there a way to resize the JPanel and to close at the same time the "old" threads, so that the number of threads is not growing, when I resize the JPanel?
I have tried something with a boolean exiter-variable, but it do not seemed to work...
here is the code:
package example;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Example2 {
public static void main(String[] args) throws InterruptedException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new ExampleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class ExampleFrame extends JFrame {
private static final long serialVersionUID = 1L;
ExamplePanel examplePanel = new ExamplePanel();
private Thread t=null;
private class ExamplePanel extends JPanel implements ComponentListener {
private static final long serialVersionUID = 1L;
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
#Override
public void componentResized(ComponentEvent e) {
startPaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
private void startPaint() {
t=new Thread(new Runnable() {
#Override
public void run() {
//System.out.println(Thread.currentThread().getName());
while (true) {
//System.out.println(Thread.activeCount());
}
}
});
t.start();
}
}
public ExampleFrame() {
examplePanel.addComponentListener((ComponentListener) examplePanel);
getContentPane().add(examplePanel);
}
}
if the calculations don't take long don't use an extra Thread.
if you need this extra Thread make sure that it doesn't run forever (no while (true) without returning at some point)
you can always interrupt your running Thread bfore creating the new one
if (t != null && t.isAlive()) {
t.interrupt();
}
and check in the while(true) loop if the Thread is interrupted
if (t.isInterrupted()) {
System.out.println("Thread ended");
return;
}
hope this helps

JFrame only shows components at first creation

When I start my application it opens a JFrame (the main window) and a JFilechooser to select an input directory, which is then scanned.
The scan method itself creates a new JFrame which contains a JButton and a JProgressBar and starts a new Thread which scans the selected Directory. Up until this point everything works fine.
Now I change the Directory Path in my Main Window, which calls the scan method again. This time it creates another JFrame which should contain the JProgressBar and the JButton but it shows up empty (The JFrame Title is still set).
update:
minimal example
public class MainWindow
{
private JFrame _frame;
private JTextArea _textArea;
private ProgressBar _progress;
public MainWindow() throws InterruptedException, ExecutionException
{
_frame = new JFrame("Main Window");
_textArea = new JTextArea();
_frame.add(_textArea);
_frame.setSize(200, 200);
_frame.setVisible(true);
_textArea.setText(doStuffinBackground());
_progress.dispose();
}
private String doStuffinBackground() throws InterruptedException,
ExecutionException
{
setUpProgressBar();
ScanWorker scanWorker = new ScanWorker();
scanWorker.execute();
return scanWorker.get();
}
private void setUpProgressBar()
{
// Display progress bar
_progress = new ProgressBar();
}
class ProgressBar extends JFrame
{
public ProgressBar()
{
super();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
add(progressBar);
setTitle("Progress Window");
setSize(200, 200);
toFront();
setVisible(true);
}
}
class ScanWorker extends SwingWorker<String, Void>
{
#Override
public String doInBackground() throws InterruptedException
{
int j = 0;
for (int i = 0; i < 10; i++)
{
Thread.sleep(1000);
j += 1;
}
return String.valueOf(j);
}
}
public static void main(String[] args) throws InvocationTargetException,
InterruptedException
{
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
// Start the main controller
try
{
new MainWindow();
}
catch (InterruptedException | ExecutionException e) {}
}
});
}
}
From the basic looks of your scan method, you are blocking the Event Dispatching Thread, when you scan the directory, which is preventing it from updating the UI.
Specifically, you don't seem to truly understand what Callable and FutureTask are actually used for or how to use them properly...
Calling FutureTask#run will call the Callable's call method...from within the current thread context.
Take a look at Concurrency in Swing for more details...
Instead of trying to use FutureTask and Callable in this manner, consider using a SwingWorker, which is designed to do this kind of work (and uses Callable and FutureTask internally)
Have a look at Worker Threads and SwingWorker for more details
Now, before you jump down my throat and tell me that "it works the first time I ran it", that's because you're not starting your UI properly. All Swing UI's should be create and manipulated from within the context of the Event Dispatching Thread. You main method is executed in, what is commonly called, the "main thread", which is not the same as the EDT. This is basically setting up fluke situation in where the first time you call scan, you are not running within the context of the EDT, allowing it to work ... and breaking the single thread rules of Swing in the process...
Take a look at Initial Threads for more details...
I would also consider using a JDialog instead of another frame, even if it's not modal, it makes for a better paradigm for your application, as it really should only have a single main frame.
Updated based on new code
So, basically, return scanWorker.get(); is a blocking call. It will wait until the doInBackground method completes, which means it's block the EDT, still...'
Instead, you should be making use of the publish, process and/or done methods of the SwingWorker
import java.util.ArrayList;
import java.util.List;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class MainWindow {
private JFrame _frame;
private JTextArea _textArea;
private ProgressBar _progress;
public MainWindow() {
_frame = new JFrame("Main Window");
_textArea = new JTextArea();
_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_frame.add(new JScrollPane(_textArea));
_frame.setSize(200, 200);;
_frame.setVisible(true);
doStuffinBackground();
}
private void doStuffinBackground() {
// _progress = new ProgressBar();
// ScanWorker scanWorker = new ScanWorker();
// scanWorker.execute();
// return scanWorker.get();
_progress = new ProgressBar();
ScanWorker worker = new ScanWorker(_textArea, _progress);
worker.execute();
_progress.setVisible(true);
}
class ProgressBar extends JDialog {
public ProgressBar() {
super(_frame, "Scanning", true);
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
progressBar.setStringPainted(false);
add(progressBar);
setTitle("Progress Window");
pack();
setLocationRelativeTo(_frame);
}
}
class ScanWorker extends SwingWorker<List<String>, String> {
private JTextArea textArea;
private ProgressBar progressBar;
protected ScanWorker(JTextArea _textArea, ProgressBar _progress) {
this.textArea = _textArea;
this.progressBar = _progress;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
textArea.append(value + "\n");
}
}
#Override
public List<String> doInBackground() throws Exception {
System.out.println("...");
int j = 0;
List<String> results = new ArrayList<>(25);
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
j += 1;
System.out.println(j);
results.add(Integer.toString(j));
publish(Integer.toString(j));
}
return results;
}
#Override
protected void done() {
progressBar.dispose();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MainWindow();
}
});
}
}

JFrame in separate class, what about the ActionListener?

I'm trying to separate my Swing GUI from my actual code. In short, I want the user to kick off a process (based on the user's selections); in this case, the JFrame will no longer be needed.
What I couldn't figure out is how to share the user's selection from the GUI.class with the Main.class.
Do you have any advice for me?
Here's my code:
public class Main {
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
GUI gui = new GUI(templates);
gui.setVisible(true);
}
});
// Kick off a process based on the user's selection
}
}
public class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI(Object[] objects) {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 100);
setLocationRelativeTo(null);
JPanel cp = new JPanel();
cp.setBorder(new EmptyBorder(10, 10, 10, 10));
setContentPane(cp);
JLabel lbl = new JLabel("Selection:");
cp.add(lbl);
final JComboBox<String> comboBox = new JComboBox<String>(new String[] { "One", "Two", "Three" });
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
dispose();
// Share the selected item with Main.class
}
});
cp.add(comboBox);
}
}
You could create an object to store the selection result and pass it in to the constructor of the GUI class. Set the selection result in that object before closing the UI and then your Main class could access the value:
public class SelectionResult {
private String selectionResult;
public void setSelectionResult(final String selectionResult) {
this.selectionResult = selectionResult;
}
public String getSelectionResult() {
return this.selectionResult;
}
}
Then, you could modify the GUI constructor like this:
private final SelectionResult selectionResult;
public GUI(Object[] objects, SelectionResult selectionResult) {
this.selectionResult = selectionResult;
...
Create a SelectionResult object in your Main class, and pass it to the constructor of the GUI class. In you GUI class ActionListener, you can then call the setSelectionResult() method with the selected value and that value will be available from the Main class.
You would need to add code to make your main method wait while you are waiting for the value to be set in the UI and then proceed with your logic based on the selection.
A Good way of doing this is use Callback mechanism.
Steps to follow:
create a callback interface
interface Callback {
void execute(Object result);
}
GUI class will implement Callback interface but without providing any implementation
Make GUI class abstract
abstract class GUI extends JFrame implements Callback
Now create an object of GUI class providing actual implementation of Callback interface
Here you can use Anonymous class
GUI gui = new GUI() {
#Override
public void execute(Object result) {
System.out.println("You have selected " + result);
}
};
You can pass any thing in execute() method of Callback.
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
dispose();
// Share the selected item with Main.class
// Callback
execute(comboBox.getSelectedItem());
}
});
Here Main class is responsible for capturing the response of Callback that is directed by GUI class.
Here is the code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
GUI gui = new GUI() {
#Override
public void execute(Object result) {
System.out.println("You have selected " + result);
}
};
gui.setVisible(true);
}
});
// Kick off a process based on the user's selection
}
}
interface Callback {
void execute(Object result);
}
abstract class GUI extends JFrame implements Callback {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 100);
setLocationRelativeTo(null);
JPanel cp = new JPanel();
cp.setBorder(new EmptyBorder(10, 10, 10, 10));
setContentPane(cp);
JLabel lbl = new JLabel("Selection:");
cp.add(lbl);
final JComboBox comboBox = new JComboBox(new String[] { "One", "Two", "Three" });
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
dispose();
// Share the selected item with Main.class
execute(comboBox.getSelectedItem());
}
});
cp.add(comboBox);
}
}

Thread interruption and ActionListener Java

I have a function graphics() that creates my JFrame and two JRadioButtons and adds ActionListeners to them. This graphics is called from main() and graphics itself calls game().
public void game() throws Exception
{
jTextArea1.setLineWrap(true);
jTextArea1.setWrapStyleWord(true);
jTextArea1.setText("This is private information.");
jRadioButton1.setVisible(true);
jRadioButton2.setVisible(true);
try {
t.sleep(40000);
repaint();
} catch (InterruptedException e) {
// We've been interrupted: no more messages.
return;
}
After displaying "This is private information." in the text Area, I want the program execution to pause for 40 seconds, or until the user presses the JRadioButton, whichever is earlier. So I added an ActionListener and called t.interrupt() inside it.
private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {
t.interrupt();
jRadioButton1.setVisible(false);
jRadioButton2.setVisible(false);
//System.out.println(t.interrupted());
jTextArea1.setText("Please wait...");
}
However, even after choosing the JRadioButton which should trigger the interrupt, that does not happen and t.interrupted returns false.
Any help would be appreciated.
Never, ever call Thread.sleep(...) on the Swing event thread as you will freeze the thread and effectively freeze your program. The solution is to consider use of a Swing Timer for the time-dependent portion of your requirement and using a SelectionListener for the JCheckBox or JRadioButton requirement.
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;
public class PausingExecution extends JPanel {
private static final String SELECTED_TEXT = "Snafus are Better!!!";
private static final String UNSELECTED_TEXT = "Fubars Rule!!";
private static final String TIMES_UP = "Time's Up!!!!";
private static final int TIMER_DELAY = 10 * 1000;
private JTextField messageField = new JTextField(UNSELECTED_TEXT, 10);
private JCheckBox checkBox = new JCheckBox("Click Me");
public PausingExecution() {
add(messageField);
add(checkBox);
checkBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent iEvt) {
if (iEvt.getStateChange() == ItemEvent.SELECTED) {
messageField.setText(SELECTED_TEXT);
} else {
messageField.setText(UNSELECTED_TEXT);
}
}
});
Timer mySwingTimer = new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
messageField.setText(TIMES_UP);
checkBox.setEnabled(false);
}
});
mySwingTimer.setRepeats(false);
mySwingTimer.start();
}
private static void createAndShowGui() {
PausingExecution mainPanel = new PausingExecution();
JFrame frame = new JFrame("PausingExecution");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Categories