I have a jList object named jListCustSearchResults which holds a number of CustomerEntity objects and shows them as a list for a user to to select a customer.
The first method below is an actionperformed mthod of a JButton which triggers the update sequence of this JList when clicked. It invokes another function below named fillCustomerList to refill the JList object with the new Customers retrieved from the database.
The problem here is that so mentioned jList object is not updated in the gui. Instead it's completely empty. As an alternative solution, I placed the method refillCustomerList into a SwingWorker object within its doBackground method in order that the update does not happen in EDT. However, the jLIst is still not updated with the new content on the GUI. Why do you think it's not updated?
At the bottom of this message I placed the SwingWorker variant of my implementation. The jList is still not updated in the gui (I also invoked repaint()).
private void jTextFieldCustomerSearchWordActionPerformed(ActionEvent evt) {
int filterType = jComboBoxSearchType.getSelectedIndex() + 1;
String filterWord = jTextFieldCustomerSearchWord.getText();
try {
controller.repopulateCustomerListByFilterCriteria(filterType, filterWord);
} catch (ApplicationException ex) {
Helper.processExceptionLog(ex, true);
}
refillCustomerList();
}
private void refillCustomerList() {
if (jListCustSearchResults.getModel().getSize() != 0) {
jListCustSearchResults.removeAll();
}
jListCustSearchResults.setModel(new javax.swing.AbstractListModel() {
List<CustomerEntity> customerList = controller.getCustomerList();
#Override
public int getSize() {
return customerList.size();
}
#Override
public Object getElementAt(int i) {
return customerList.get(i);
}
});
jListCustSearchResults.setSelectedIndex(0);
}
==========================
WITH SWING WORKER variant:
private void jTextFieldCustomerSearchWordActionPerformed(ActionEvent evt) {
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
public void done() {
repaint();
}
#Override
protected Void doInBackground() throws Exception {
int filterType = jComboBoxSearchType.getSelectedIndex() + 1;
String filterWord = jTextFieldCustomerSearchWord.getText();
try {
controller.repopulateCustomerListByFilterCriteria(filterType, filterWord);
} catch (ApplicationException ex) {
Helper.processExceptionLog(ex, true);
}
refillCustomerList();
return null;
}
};
worker.execute();
}
Related
I have a base class mainframe and i have keeping the JButton as final static Which its BGcolor going to be changed by a extended class of mainframe namely dataframe. Initially i need to set the BGColor of the JButton to red. Then I need to change it to some other colors from the dataframe. I can able to set the BGColor from the mainframe but not from the dataframe(extended class). I've used mainframe.Button_name.setBackground(color.yellow); but still its not changing
`enter code here`
public class mainframe {
final static JButton Button_name = new JButton("Hi");
public static void main(String[] args)
{
public void run()
{
Button_name.setBackground(color.Red); //This is working
}
}
}
class dataframe extends mainframe implements Runnable
{
public void run()
{
//doing other things
while(some condition)
{
if (another_condition)
{
//from here i need to change that Buttons color
// i've tried this
mainframe.Button_name.setBackground(color.yellow); //Not working
}
}
}
}
Kindly anyone help with this issue
So you want to change the state of a UI component from a different thread in a different class. There are multiple ways you might be able to do this, but first, I would start by defining away for those classes to be able to only effect the change you want them to.
Exposing the entire frame, component or even button is not a good idea, people have a habit of changing things you don't want them to, so instead, we define a simple contract which states what they are allowed to do, for example...
public interface Colorable {
public void setColor(Color color);
}
This immediately decouples your code, meaning that any code that wants to change the state of your UI (or change the color of something else) can do so, without relying on the physical implementation.
Thread
First, we're going to have a look at using a Thread to change the UI...
public class ColorChanger {
private Colorable colorable;
public ColorChanger(Colorable colorable) {
this.colorable = colorable;
}
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
colorable.setColor(Color.GREEN);
} else {
colorable.setColor(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
}
});
t.start();
}
}
This is a pretty basic class, it requires an instance of Colorable and will change the state of the color for every 100 counts, based on if it's an even or odd hundred
We use a simple JPanel as our base test class, when you click the button, the ColorChanger is created and started.
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChanger changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChanger(TestPane.this);
changer.start();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method has a bunch of code in it, this is to ensure that the updates to the UI are executed only from within the context of the Event Dispatching Thread.
SwingWorker
An alternative is to use a SwingWorker, which operates very similarly to a Thread, expect it has the ability to publish content to the EDT
public class ColorChangerWorker extends SwingWorker<Void, Color> {
private Colorable colorable;
public ColorChangerWorker(Colorable colorable) {
this.colorable = colorable;
}
#Override
protected void process(List<Color> chunks) {
colorable.setColor(chunks.get(chunks.size() - 1));
}
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
publish(Color.GREEN);
} else {
publish(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
return null;
}
}
You will note here, that when we want to change the color we call publish. The process method is called to let us know that there is more data to be processed, but here, we're only interested in the last change.
And out TestPane...
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChangerWorker changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChangerWorker(TestPane.this);
changer.execute();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method remains unchanged, this is deliberate, when you test this class, you will note that "Not in the EDT" is never printed, basically meaning we could do away with all that code and just call btn.setBackground(color);, but I want you to see the difference.
The Button...
Now, when I run this code, I get the following output...
Wait a minute, that buttons background is filled?! Actually it is, but many button implementations have a secondary "content area" filling
You can turn this off using something like...
btn.setContentAreaFilled(false);
btn.setOpaque(true);
Which will result in something like...
I have a gui, that is having a Login prompt added.
while(notValidLogIn){
LoginPrompt.getDetails() //a static method that
}
Hwoever, the loginPrompt is a Jdialog, with a parent JFrame. How can I stop looping of cancel clicked, I could put System.exit(0) in cancel action performed. But don't want to stop everything, I want something like :
while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}
In a recent project I was working on, I've implemented an event based solution. The idea is JDialog notify to its parent JFrame how the login process went and this last one may or may not continue its execution. This way I have no loops and keep separate responsibilities: The schema would be something like this:
LoginEvent:
This is the event itself. Not that complicated:
class LoginEvent extends EventObject {
public static final int LOGIN_SUCCEEDED = 0;
public static final int LOGIN_FAILED = 1;
public static final int LOGIN_DIALOG_CLOSED = 2;
private int id;
public LoginEvent(Object source, int id) {
super(source);
this.id = id;
}
public int getId() {
return id;
}
}
LoginListener
An interface to handle these LoginEvents:
public interface LoginListener extends EventListener {
public void handleLoginEvent(LoginEvent evt);
}
Login Dialog
This class has to mantain a List with subscribed LoginListeners:
class LoginDialog {
List<LoginListener> listeners = new ArrayList<>();
JDialog dialog;
JButton accept;
JButton cancel;
public void show() {
//create and show GUI components
}
public void close() {
if(dialog != null) {
dialog.dispose();
}
}
...
public void addLoginListener(LoginListener loginEventListener) {
if(!listeners.contains(loginEventListener)) {
listeners.add(loginEventListener);
}
}
public void removeLoginListener(LoginListener loginEventListener) {
listeners.remove(loginEventListener);
}
public void dispatchLoginEvent(LoginEvent evt) {
for(LoginListener loginListener: listeners) {
loginListener.handleLoginEvent(evt);
}
}
}
Adding action listeners to accept and cancel buttons:
accept.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// validate login data
if(loginValid) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_SUCCEEDED));
} else {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_FAILED));
}
}
});
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_DIALOG_CLOSED));
}
});
Subscribing a LoginListener
In your JFrame:
final LoginDialog dialog = new LoginDialog();
dialog.addLoginListener(new LoginListener() {
#Override
public void handleLoginEvent(LoginEvent evt) {
if(evt.getId() == LoginEvent.LOGIN_SUCCEEDED {
dialog.close();
//continue execution
return;
}
if(evt.getId() == LoginEvent.LOGIN_FAILED) {
JOptionPane.showMessageDialog(null, "Login failed!");
return;
}
if(evt.getId() == LoginEvent.CLOSE_LOGIN_DIALOG) {
dialog.close();
// do something when this dialog is closed
}
}
};
dialog.show();
while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}
If this loop is inside another thread other than the EDT(event dispatch thread), then you can use SwingUtilities.invokeAndWait(new Runnable()) function: invokeAndWait() blocks the current thread until the EDT is done executing the task given by it. This option is particularly used while we want to await an execution of a thread for taking confirmation from user or other input using JDialogue/JFileChooser etc
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
Note: re-stating for emphasizing: you should ensure that this loop is executing inside another Thread: such as using an extended class of Runnable, or by means of anonymous class:
new Thread()
{
// other code of your context
public void run()
{
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
}
}.start();
I have 2 classes defined below:
public class TextsManager extends Thread {
LinkedList<String> lstOfPendingStr = new LinkedList<String>();
boolean stopLoop = false;
JTextArea txtArea;
public void run()
{
while (!stopLoop)
{
while (!lstOfPendingStr.isEmpty())
{
String tmp = lstOfPendingStr.getFirst();
this.txtArea.append(tmp);
lstOfPendingStr.removeFirst();
}
try {
Thread.sleep(0); // note: I had to force this code
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void AddNewStr(String newStr)
{
this.lstOfPendingStr.add(newStr);
}
}
And
public class ClientApp {
private JFrame frame;
private JTextField textField;
private JTextArea textArea;
static private TextsManager txtManager;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientApp window = new ClientApp();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientApp() {
initialize();
/*
* Client app
*/
txtManager = new TextsManager(textArea);
txtManager.start();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
textArea = new JTextArea();
textField = new JTextField();
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
txtManager.AddNewStr(textField.getText() + "\n");
textField.setText("");
}
}
});
}
}
The program will read User Input from textField, pass it into TextsManager.lstOfPendingStr. Then, on each loop inside TextsManager.run(), it will check for existed members in lstOfPendingStr and output them via txtArea.
The problem is that if I removed the code Thread.sleep(0) inside run(), the run() then apparently stopped working. Despite lstOfPendingStr had been successfully updated with new elements, codes inside the loop while(!lstOfPendingStr.isEmpty()) would not ever to be called.
I put hard codes such as System.out.println or Thread.sleep(0) (as in the provided code) inside the while(!stopLoop), then it worked fine.
Although, I managed to solve the problem by forcing the thread to sleep for a few miliseconds, I want to know the reason behind this issue.
I appreciate your wisdom.
Regard :)
You have a couple of problems.
You are calling methods on lstOfPendingStr from two threads, but initialized it with LinkedList, which is not thread-safe. You should use a thread safe class, LinkedBlockingQueue seems the best options as far as I understood from your code.
Inside the thread you are calling JTextArea#append(). As all AWT/Swing methods, you can not call them from arbitrary threads, but only from the AWT thread. Wrap the call inside an invokeLater block.
The fact that sleep appears to make your code work is just a sign of the concurrency problems.
I need a listener that will constantly check if a static boolean value has been changed so that I can repaint a component on a frame. Can someone please help me I really don't know much about listeners and haven't worked with them much? Help will be greatly appreciated.
edit(more clarity): I have two separate classes in which on class is the "main frame" the second class is an extension of JLabel and implements MouseListner for a "clickable photo". The "main frame" creates instances of the photo and when the photo is clicked the "main frame" is supposed to paint on the panel a description of the photo. This is "main frame"
MenuBar menuBar;
static AnnotationVisual a;
Picture pic;
Picture pic2;
GalleryScreen(int rows, int columns){
this.setBorder(BorderFactory.createEmptyBorder(500,500,0,0));
pic = new Picture("pic1", "Z:/My Documents/Downloads/Ball.jpg", new Coordinate(0,0));
pic2 = new Picture("pic2", "Z:/My Documents/Downloads/hoop.jpg" , new Coordinate(1,0));
this.add(pic);
this.add(pic2);
a = new AnnotationVisual();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
if(a.shouldAnnotate()){
FontMetrics size= g.getFontMetrics();
if(getWidth()>=(a.dispX()+size.stringWidth(a.annotationText()))){
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.setColor(Color.black);
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(a.annotationText())+5,15);
g.drawString(a.annotationText(), a.dispX(), a.dispY());
}else{
String sub="";
int letters=0;
g.setColor(Color.white);
g.fillRect(a.dispX()-3,a.dispY()-12,getWidth(),15);
g.setColor(Color.black);
for(int i=0;i<a.annotationText().length();i++){
if(a.dispX()+letters+16<=getWidth()){
sub+=a.annotationText().substring(i,i+1);
letters=size.stringWidth(sub);
}else{
sub=sub+"...";
i=a.annotationText().length();
}
}
g.drawRect(a.dispX()-3,a.dispY()-12,size.stringWidth(sub)+3,15);
g.drawString(sub,a.dispX(),a.dispY());
}
}
}
public static AnnotationVisual getA()
{
return a;
}
This is "clickable photo"
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.*;
public class Picture extends JLabel implements MouseListener
{
String myAnnotation;
String filePath;
Coordinate imageCoord;
private boolean wasDoubleClick;
private Timer timer;
EditAnnotation newEdit;
AnnotationVisual newVisual;
public Picture(String annotation, String filePath, Coordinate coord)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
myAnnotation=annotation;
this.filePath = filePath;
imageCoord = coord;
newEdit = new EditAnnotation(annotation);
newVisual = new AnnotationVisual();
}
public Picture(String filePath)
{
super(new ImageIcon(filePath));
this.addMouseListener(this);
this.filePath = filePath;
newEdit = new EditAnnotation();
newVisual = new AnnotationVisual();
}
public String getAnnotation()
{
return myAnnotation;
}
public AnnotationVisual getAnnotationVisual()
{
return newVisual;
}
public void setAnnotation(String annotation)
{
myAnnotation=annotation;
}
public Coordinate getCoordinate()
{
return imageCoord;
}
public void setCoordinate(Coordinate coord)
{
imageCoord = coord;
}
public Dimension getSize()
{
return new Dimension(super.getIcon().getIconWidth(), super.getIcon().getIconHeight());
}
public void mouseClicked(MouseEvent e)
{
final int scrLocX = (int)e.getLocationOnScreen().getX();
final int scrLocY = (int)e.getLocationOnScreen().getY();
if (e.getClickCount() == 2)
{
wasDoubleClick = true;
}
else if(e.getClickCount() == 1)
{
Integer timerinterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
timer = new Timer(timerinterval.intValue(), new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
if (wasDoubleClick)
{
GalleryScreen.getA().deleteAnnotation();
myAnnotation = newEdit.getAnnotation();
newEdit.show(myAnnotation);
wasDoubleClick = false;
}
else
{
GalleryScreen.getA().deleteAnnotation();
GalleryScreen.getA().showAnnotation(scrLocX, scrLocY , myAnnotation);
}
}
});
timer.setRepeats(false);
timer.start();
}
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
}
AnnotationVisual is the thing that supposed to pop up when single clicked
You're probably better off making the boolean private, and only allowing it to be changed through a setter method. The setter method, when called, should then repaint the component.
The point of listeners is to invert the logic. You don't constantly check if a value is changed. You notify the listener when you change the value.
So, instead of Foo.bar = 5, you invoke Foo.setBar(5), where in addition to the assignment, you call barListener.valueChanged(value)
As a sidenote - avoid storing state in static variables.
You don't set a listener on a field in Java, you set it on a property. While properties (according to the JavaBeans spec) can be fields, they're usually done as pairs of methods (one getter, one setter; the latter being not needed for read-only fields) as that lets you hook extra logic in to be called when the property is accessed. Such as firing a listener callback to say that the value has changed. (You could use a thread to monitor for that sort of thing, but that's really nasty and error-prone. Wasteful too.)
One thing to be aware of though: you don't know what thread the value will have been modified from. Take care when invoking back into Swing…
I have an editable JTable and have set a DefaultCellEditor like so:
colModel.getColumn( 1 ).setCellEditor( new DefaultCellEditor( txtEditBox ) {
// ...
#Override
public void cancelCellEditing() {
super.cancelCellEditing();
// handling the event
}
// ...
}
However, when pressing escape while editing a cell in this column, though the editing mode is finished, this method is not called. Any ideas why? Am I doing something wrong? Is there a way to handle this (other than manually adding a KeyListener that is)?
The official way: You can register a CellEditorListener: AbstractCellEditor.addCellEditorListener(...). If the editing is canceled, editingCanceled(ChangeEvent e) should be called. Due to a SUN bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6788481, editingCanceled is not called :(
As workaround you can register your own action for the ESCAPE key and handle it yourself. But it will not work for resize events.
Another solution (quick and dirty;-)): Overwrite the methode JTable.removeEditor() and insert your code after the super call.
I had this problem too. I wrote another workaround that involves ActionListener and FocusListener. This is it:
public class TableEditorListenerHelper {
// dealing with events
private final EventListenerList listeners = new EventListenerList();
private ChangeEvent changeEvent;
// cell editor that we're helping
private CellEditor editor;
// transient state
private boolean editing = false;
private JTable table;
public TableEditorListenerHelper(CellEditor editor, JTextField field) {
this.editor = editor;
field.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
field.addFocusListener(new FocusListener() {
#Override public void focusGained(FocusEvent e) {
editing = true;
}
#Override public void focusLost(FocusEvent e) {
JTable table = TableEditorListenerHelper.this.table;
if (editing && isEditing(table)) {
fireEditingCanceled();
}
}
private boolean isEditing(JTable table) { // a hack necessary to deal with focuslist vs table repaint
return table != null && table.isEditing();
}
});
}
public void setTable(JTable table) {
this.table = table;
}
public void addCellEditorListener(CellEditorListener l) {
listeners.add(CellEditorListener.class, l);
}
public void removeCellEditorListener(CellEditorListener l) {
listeners.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listeners.getListeners(CellEditorListener.class);
}
protected void fireEditingCanceled() {
for (CellEditorListener l : getCellEditorListeners()) {
l.editingCanceled(getOrCreateEvent());
}
resetEditingState();
}
protected void fireEditingStopped() {
for (CellEditorListener l : getCellEditorListeners()) {
l.editingStopped(getOrCreateEvent());
}
resetEditingState();
}
private void resetEditingState() {
table = null;
editing = false;
}
private ChangeEvent getOrCreateEvent() {
return changeEvent = changeEvent == null ? new ChangeEvent(editor) : changeEvent;
}
Here you can find a little more complete solution.
Another way fix this bug:
jTable.addPropertyChangeListener("tableCellEditor", e -> {
Object o = e.getOldValue();
if (o instanceof DefaultCellEditor) {
((DefaultCellEditor) o).cancelCellEditing();
}
});