DocumentListener not working properly - java

im trying to code a DocumentListener in order to catch the change into a textField.
I have a panel which include another panel in which i put a textField, i have tried a lot of formulas but it didnt work
This is the panel
public JPanelTASAS() {
initComponents();
txtTASA.getDocument().addDocumentListener(new BecomingYellowDocumentListener(txtTASA));
}
private static class BecomingYellowDocumentListener implements DocumentListener {
private utilesGUIx.JTextFieldCZ textField;
public BecomingYellowDocumentListener(utilesGUIx.JTextFieldCZ textField) {
this.textField = textField;
}
#Override
public void insertUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
#Override
public void removeUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
#Override
public void changedUpdate(DocumentEvent e) {
textField.setBackground(Color.yellow);
System.out.println("Prueba");
}
}
The next one is the principal Panel where the other panel is included
public JPanelTRANSMISIONES() {
initComponents();
anadirPaneles();
}
With this code inside initComponents
jPanelTASAS1 = new gestionTrafico.forms.JPanelTASAS();
And for the record the code of utilesGUIx.JTextFieldCZ
public JTextFieldCZ() {
super();
enableEvents(AWTEvent.FOCUS_EVENT_MASK);
enableEvents(AWTEvent.KEY_EVENT_MASK);
setDocument(createDefaultModel());
}
public void setDocument(Document doc) {
if (doc != null) {
doc.putProperty("filterNewlines", Boolean.TRUE);
}
super.setDocument(doc);
}
Just to be clear, if if apply this listener to a JTextField of the Principal Panel it works, I think that the problem is adding a document listener to a panel inside another panel. Is it possible ?
Thank you very much in advance for your help
EDIT: I realice that if i change the value of the textfield hardcoding, the documentlistener works. But if i change the value of the textfield in the panel it doesnt.

Guessing: your problem is simply that just changing the background doesn't automatically trigger a repaint of the affected UI element.
In other words: you will probably see those messages on stdout; but in order for your UI to change, you should call repaint() on your frame or panel.
See here for some common solutions to common painting problems.
But given your latest comment, you are not even there yet. I guess you have to look into the details of using a DocumentListener more carefully, for example by studying this here.

Yes, it is definitively possible to add a document listener to a document of a text field located on a panel inside a second panel.
You are probably adding the listener to the wrong document and/or text field; but hard to say without seeing how you are doing it, could be something related to how the panels are constructed/added...
Example (SSCC), quick&dirty just to show that it is possible:
package test;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Panels {
public static void main(String[] args) {
new Panels();
}
private JFrame frame;
private JPanel firstPanel;
private JPanel secondPanel;
private Panels() {
initGUI();
frame.setVisible(true);
}
private void initGUI() {
JTextField secondField = new JTextField(20);
secondField.getDocument().addDocumentListener(new ColoringListener(secondField));
secondPanel = new JPanel();
secondPanel.setBorder(new TitledBorder("Second"));
secondPanel.add(secondField);
JTextField firstField = new JTextField(20);
firstField.getDocument().addDocumentListener(new ColoringListener(firstField));
firstPanel = new JPanel();
firstPanel.setBorder(new TitledBorder("First"));
firstPanel.add(firstField);
firstPanel.add(secondPanel);
frame = new JFrame();
frame.add(firstPanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
}
private static class ColoringListener implements DocumentListener {
private final JTextField field;
ColoringListener(JTextField field) {
this.field = field;
}
#Override
public void insertUpdate(DocumentEvent e) {
field.setBackground(Color.GREEN);
}
#Override
public void removeUpdate(DocumentEvent e) {
field.setBackground(Color.RED);
}
#Override
public void changedUpdate(DocumentEvent e) {
}
}
}
EDIT: I tried to use the same structures as used in the question. I don't like to have to pass the field to the listeners constructor and to add the listener to the field - error prone!

I got the answer.
The problem is that I am adding DocumentListener in the constructor, I change it and I call getDocument().addDocumentListener when the textField gain the Focus (txtTASAFocusGained).
Now it works properly.

Related

How to copy JButton action and change its references to underlying objects?

The following example creates a JFrame with JButton, JTextField and JLabel.
When the button is pressed it increments the value in the text field and label.
This example also creates a 2nd JFrame that is a copy of the first.
The button, text field and label is copied as well.
The issue at hand is the button on the copied frame still updates the text field and label on the original. The 'why' is fairly obvious and is because the code makes specific reference to the text field and label.
Although this isn't written in the best manner but it is a great example of the scenario in which I am addressing.
The objective is, without a major rewrite, what would be the least invasive way to have the copied button action update the copied test field and label instead of the original?
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class ButtonTextFieldLabel extends JFrame
{
JButton bnt1 = new JButton("B1");
JTextField tf1 = new JTextField("1");
JLabel lbl1 = new JLabel("100");
public ButtonTextFieldLabel()
{
super("Main Frame");
setLayout(null);
bnt1.setBounds(50,100,120,40);
tf1.setBounds(300,100, 80,40);
lbl1.setBounds(200,100,80,40);
bnt1.addActionListener(new ListenerHolder(this));
add(bnt1);
add(tf1);
add(lbl1);
setSize(500,500);
makeCopy(this);
setVisible(true);
}
private void makeCopy(ButtonTextFieldLabel originalObj)
{
JFrame copyFrame = new JFrame();
copyFrame.setTitle("Copy of " + originalObj.getTitle());
copyFrame.setSize(originalObj.getSize());
copyFrame.setLocation(originalObj.getX()+100, originalObj.getY()+100);
copyFrame.setLayout(null);
JButton copyBnt1 = new JButton();
copyBnt1.setBounds(originalObj.bnt1.getBounds());
copyBnt1.setLabel(originalObj.bnt1.getLabel());
copyFrame.add(copyBnt1);
for (ActionListener al : originalObj.bnt1.getActionListeners())
{
copyBnt1.addActionListener(al);
}
JTextField copyTf1 = new JTextField();
copyTf1.setBounds(originalObj.tf1.getBounds());
copyTf1.setText(originalObj.tf1.getText());
JLabel copyLbl1 = new JLabel();
copyLbl1.setBounds(originalObj.lbl1.getBounds());
copyLbl1.setText(originalObj.lbl1.getText());
copyFrame.add(copyBnt1);
copyFrame.add(copyTf1);
copyFrame.add(copyLbl1);
copyFrame.setVisible(true);
}
public void runThis()
{
tf1.setText( Integer.toString(Integer.parseInt(tf1.getText())+1) );
lbl1.setText( Integer.toString(Integer.parseInt(lbl1.getText())+1) );
}
}
class ListenerHolder implements ActionListener
{
ButtonTextFieldLabel ph;
public ListenerHolder(ButtonTextFieldLabel ph)
{
this.ph = ph;
}
#Override
public void actionPerformed(ActionEvent arg0)
{
ph.runThis();
}
}
public class TestBTL
{
public static void main(String[] args){
new ButtonTextFieldLabel();
}
}
You already know the reason for the problem -- you're copying the original ActionListener, complete with its reference to the original GUI components. The overall solution is not to copy the action listener but rather to create your GUI's to hold and maintain their own unique state. One solution is rather than try to copy components via kludge, to create a self-contained GUI object that holds and updates its own state. You can create multiple GUI's using a factory method if desired.
For example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TestBtl2 {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndDisplayFrame("Frame 1").setVisible(true);
createAndDisplayFrame("Frame 2").setVisible(true);
});
}
// Factory method
private static JFrame createAndDisplayFrame(String text) {
BtlPanel btlPanel = new BtlPanel();
JFrame frame = new JFrame(text);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(btlPanel);
frame.pack();
frame.setLocationByPlatform(true);
return frame;
}
}
class BtlPanel extends JPanel {
private int value = 0;
private JButton button1 = new JButton(new ButtonAction("Button 1"));
private JLabel label1 = new JLabel("00");
private JTextField textField1 = new JTextField("00");
public BtlPanel() {
textField1.setFocusable(false);
add(button1);
add(Box.createHorizontalStrut(20));
add(label1);
add(Box.createHorizontalStrut(20));
add(textField1);
setPreferredSize(new Dimension(300, 100));
}
public void incrementValue() {
value++;
String text = String.format("%02d", value);
label1.setText(text);
textField1.setText(text);
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
incrementValue();
}
}
}
Side Recommendations:
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Check out: The Use of Multiple JFrames, Good/Bad Practice?

Is there a way to halt a method until a button is pressed?

I have been coding for a card game and cannot get my method to wait for a button to be pressed.
The general code goes like this. When I run the code, doTask() has a segment where it needs to wait for a button to be pressed. However, the method does not wait for the button and just loops through.
I am currently thinking to have a while loop with a boolean (buttonIsPressed) which will be triggered true in actionPerformed(e). However, is there a simpler way to do this?
Any help would be appreciated.
Thank you.
public class Test {
public Test()
{
// all vars instantiated
while (!(taskLeft==0))
{
doTask();
taskLeft--;
}
}
private class Handler implements ActionListener
{
public void actionPerformed(ActionEvent arg0) {
// update information in doTask()
}
}
}
Yours is a classic XY Problem where you ask how to solve a specific code problem when the best solution is to use a completely different approach. You're thinking of how do I code this event-driven GUI program to work as a linear console program, and that's not how you should approach this. Instead look at changing object state and basing response of the object to events based on its state.
So get rid of the while loop, and instead do your task when the button is pushed based on the state of the GUI. The details of any solution will depend on the details of your problem, something you may wish to share with us.
So for instance, here taskLeft represents a "state" of the TaskEx object, and your Handler's response will depend on the state of this variable.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class TaskEx extends JPanel {
private int taskLeft = 10;
public void doTask() {
//
}
private class Handler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
doTask();
taskLeft--;
}
}
}
}
An actually functioning example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class TaskEx extends JPanel {
private int taskLeft = 10;
private JLabel taskCountLabel = new JLabel(String.valueOf(taskLeft));
public TaskEx() {
JPanel northPanel = new JPanel();
northPanel.add(new JLabel("Tasks Left:"));
northPanel.add(taskCountLabel);
JPanel centerPanel = new JPanel();
centerPanel.add(new JButton(new Handler("Push Me")));
setLayout(new BorderLayout());
add(northPanel, BorderLayout.PAGE_START);
add(centerPanel, BorderLayout.CENTER);
}
public void doTask() {
taskCountLabel.setText(String.valueOf(taskLeft));
}
private static void createAndShowGui() {
TaskEx mainPanel = new TaskEx();
JFrame frame = new JFrame("Task Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
private class Handler extends AbstractAction {
public Handler(String name) {
super(name);
putValue(MNEMONIC_KEY, (int) name.charAt(0));
}
#Override
public void actionPerformed(ActionEvent e) {
if (taskLeft > 0) {
taskLeft--;
doTask();
}
}
}
}

Java Listener not working unless i press enter how to enable it working once text box changed

I have sticked below listener inside JtextField when action performed event,
so as to perform action against any change made in text box once user make any change.
but the problem is the code not starting or working unless you press enter only then
the code execute the code, i need to know what i have to add and where to enable below code once text Filed changed instantly .I can see some similar help referring to Oracle listener help but am unable to manage so i need direct simple way.
private void jTextField1ActionPerformed(java.awt.event.ActionEvent evt) {
jTextField1.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
JOptionPane.showMessageDialog(null, "Change case");
}
public void insertUpdate(DocumentEvent de) {
JOptionPane.showMessageDialog(null, "Update Case");
}
public void removeUpdate(DocumentEvent de) {
JOptionPane.showMessageDialog(null, "Remove case");
}
});
// TODO add your handling code here:
}
ActionListener for a text field only listens for enter key being typed. So what your code essentialy does is: when enter key is pressed adds a new DocumentListener to the text field.
The DocumentListener is what you want so take that code (adding the document listener) out of the jTextField1ActionPerformed method and put it in the constructor of the class. Or have a private method, so as not to clutter the constructor.
Assuming you are using Netbeans GUI editor (looks like it from your method signature):
public class MyFrame exentds JFrame {
public MyFrame() {
initComponents();
addDocumentListenerToField();
}
private void addDocumentListenerToField() {
jTextField.getDocument().addDocumentListener(..);
}
}
UPDATE: DEMO
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class DocListeenerDemo extends JFrame {
private JTextField field;
private JLabel label;
public DocListeenerDemo() {
initComponents();
addDocumentListenerToField();
}
private void initComponents() {
setLayout(new GridLayout(0, 1));
field = new JTextField(20);
label = new JLabel("", SwingConstants.CENTER);
add(field);
add(label);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationByPlatform(true);
}
private void addDocumentListenerToField() {
field.getDocument().addDocumentListener(new DocumentListener(){
public void changedUpdate(DocumentEvent arg0) {doYourStuff();}
public void insertUpdate(DocumentEvent arg0) {doYourStuff();}
public void removeUpdate(DocumentEvent arg0) {doYourStuff();}
public void doYourStuff() {
label.setText(field.getText());
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
DocListeenerDemo demo = new DocListeenerDemo();
demo.setVisible(true);
}
});
}
}
I have not figure out a way to add the DocumentListener through the GUI tool. Sucks.
try using actionListener rather than documentListener

Running a method after switching cards in cardlayout

I'm sure someone has asked this question before, but my google-fu is not strong today.
I have a JFrame that uses a CardLayout as its manager. How do I run a "Start" method when I switch to each JPanel without using a switch?
The code I use to add the frames to the layout is:
/**
* Adds JPanels to the Card Layout.
* #param panel is the JPanel to add to the layout.
* #param windowName is the identifier used to recognise the Panel.
*/
public final void addToCards(final JPanel panel, final WindowNames windowName) {
view.getBasePanel().add(panel, windowName.getValue());
}
The code I use to switch the layout is:
/**
* Method to change the JPanel currently visible on the BasePanel.
* #param windowName is the name of the JPanel to change to.
*/
public final void changePanel(final WindowNames windowName) {
view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
}
Currently I have an ActionListener set that will call the switch code, but I can't work out how to call the "Start" method within the screen that it will be switching to.
I have an interface setup for each of the JPanels so that the method name will be identical in each.
You can just use a ComponentListener for the panel(s). When the panel becomes the view of the CardLayout, it will fire a component event and handled by componentShown in your listener (as well as the panel taken out of view, handling the componentHidden). Call your start() method there. This way you don't have to explicitly call the start() when the panel changes, as it be called for you.
See How to Write Component Listeners for more details.
Here is a simple example.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main {
private static final String PANEL_A = "panelA";
private static final String PANEL_B = "panelB";
CardLayout layout = new CardLayout();
JPanel panel = new JPanel(layout);
ComponentListenerPanel p1 = new ComponentListenerPanel(PANEL_A);
ComponentListenerPanel p2 = new ComponentListenerPanel(PANEL_B);
JButton b1 = new JButton(PANEL_A);
JButton b2 = new JButton(PANEL_B);
public Main() {
panel.add(p1, PANEL_A);
panel.add(p2, PANEL_B);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_A);
}
});
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
show(PANEL_B);
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(b1);
buttonPanel.add(b2);
JFrame frame = new JFrame();
frame.add(panel);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void show(String panelName) {
layout.show(panel, panelName);
}
private class ComponentListenerPanel extends JPanel {
private String panelName;
public ComponentListenerPanel(String panelName) {
this.panelName = panelName;
addComponentListener(new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent evt) {
stop();
}
#Override
public void componentShown(ComponentEvent evt) {
start();
}
});
}
public void start() {
System.out.println(panelName + " started");
}
public void stop() {
System.out.println(panelName + " stopped");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Main();
}
});
}
}
Note you haven't actually said where the start method is, so this code/answer is just assuming you have some start method in custom panel. Hope I guessed right. In the future, or even now, you should always post an MCVE so that we don't have to do all this guessing.
I have an interface setup for each of the JPanels so that the method name will be identical in each
So then the problem is getting the current panel that is visible when the panels are swapped so you can invoke the method.
Check out Card Layout Focus for a class that extends CardLayout to provide a few helper methods to add additional functionality for the CardLayout. You would use the getCurrentCard() method.
So your changePane(...) method might be something like:
public final void changePanel(final WindowNames windowName) {
//view.getCardLayout().show(view.getBasePanel(), windowName.getValue());
RXCardLayout layout = view.getCardLayout();
layout.show(view.getBasePanel(), windowName.getValue());
MyInterface panel = (MyInterface)layout.getCurrentCard();
panel.someMethod(...);
}
Of course you would also need to use the RXCardLayout as the layout manager for your main panel.

Why wont my custom JButton display its name?

Hi I am working on a project and I am having trouble adding JButtons. For some reason, it wont print its name.
This class is supposed to add the custom buttons
import java.awt.*;
import javax.swing.JPanel;
public class FightPanel extends JPanel {
public static final int WIDTH = 600;
public static final int HEIGHT = 600;
public FightPanel() {
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setLayout(new BorderLayout());
FightButton test = new FightButton("test");
add(test);
}
}
This class is custom button class
import javax.swing.JButton;
public class FightButton extends JButton {
private String name;
public FightButton(String name) {
this.name = name;
setName(name);
}
}
The setName() method does not control the text that is displayed on a button. It is basically just used by application code to identify a component.
The setText(...) is used to set the text that is displayed in the button.
did now know that you have to use the super constructor to do that.
You don't have to but that is the easiest way to do what you want since you are passing the text as a parameter to your class.
If you want to change the text AFTER the component has been created then you would use the setText() method.
All you're doing is setting a non-displayed property of your new class and leaving the name state of the original parent class unchanged. Solution: use the super's constructor to help you out:
public FightButton(String name) {
super(name);
}
In fact there really is no need for your FlightButton to have a name field since all that does is shadow the JButton's name field. In fact if this is all you add to FlightButton, I would get rid of this class and simply use JButton objects. Myself, that is what I usually do -- use plain vanilla JButtons, but use custom AbstractActions, and I feel that is likely where your energies should go.
Edit
You state in comment:
thank you i did now know that you have to use the super constructor to do that.
You don't have to, as you could also use setter methods and the like, but if you want your constructor to behave like a JButton's similar constructor, best to use the parent's constructor.
Edit 2
For example:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class FightPanel extends JPanel {
public static final int FP_WIDTH = 600;
public static final int FP_HEIGHT = 600;
public FightPanel() {
setLayout(new BorderLayout());
JButton test = new JButton(new FightAction("Test", KeyEvent.VK_T));
add(test);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(FP_WIDTH, FP_HEIGHT);
}
private static void createAndShowGui() {
FightPanel mainPanel = new FightPanel();
JFrame frame = new JFrame("FightPanel");
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();
}
});
}
}
class FightAction extends AbstractAction {
public FightAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand() + " pressed!");
}
}

Categories