I am trying to implement a Document Listener in my program. So far every time the user types in a new word, I am getting the whole text and saving it. What I want to do is to get only the new word/words typed in and process them. Can you give me suggestions how I can do that?
How about document at http://docs.oracle.com/javase/tutorial/uiswing/events/documentlistener.html ?
Basically, the parameter DocumentEvent from insertUpdate event contains the text. You will have to retrieve the texts from the object e.
My suggestion is to try out some code and we'll see how much you know about DocumentListener, ok?
Below is proposed code similar to yours above:
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
//public class DocumentListener { // Fix: not a good name for the class since it is part of JVM
public class DocumentEventDemo extends JFrame { // easier than extends JPanel
JPanel txtPanel, centerPanel;
GridLayout grid;
JTextField txtField;
JTextArea txtArea;
JFrame frame;
JComponent newContentPane;
FlowLayout flow;
public DocumentEventDemo() {
txtArea = new JTextArea();
txtArea.getDocument().addDocumentListener(new MyDocumentListener());
txtArea.getDocument().putProperty("txtArea", "MyDocumentListener");
// txtField = new JTextField(10); // 10 chars max
// txtField.setText("12345");
centerPanel = new JPanel();
grid = new GridLayout(2,1,1,1);
txtPanel = new JPanel();
flow = new FlowLayout(FlowLayout.CENTER);
txtPanel.setLayout(flow);
//Adding control GUI fields to the only panel
// txtPanel.add(txtArea);
// txtPanel.add(txtField);
// Forming the center view with GUI controls
centerPanel.setLayout(grid);
// centerPanel.add(txtPanel);
centerPanel.add(txtArea);
// Add Panels to the Frame
getContentPane().add(centerPanel,"Center");
this.setSize(500,200);
this.validate();
this.setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// newContentPane = getRootPane();
// newContentPane.setOpaque(true);
// frame.setContentPane(newContentPane);
}
//MyEditor editor = new MyEditor(); // what is MyEditor?
//javax.swing.event.DocumentListener mydocumentListener = new javax.swing.event.DocumentListener()
// Make a class to define the inherited abstract methods, which are also events.
class MyDocumentListener implements DocumentListener {
String[] word=new String[50];
String text;
int i=0;
int y;
int l;
int len;
public void changedUpdate(DocumentEvent documentEvent) {
System.out.println("The text has been changed.");
}
public void insertUpdate(DocumentEvent documentEvent) {
try {
GetWord(documentEvent);
} catch (BadLocationException ex) {
Logger.getLogger(DocumentListener.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void removeUpdate(DocumentEvent documentEvent) {
System.out.println("A character has been removed!");
}
private void GetWord(DocumentEvent documentEvent) throws BadLocationException {
//get the type of event
DocumentEvent.EventType type = documentEvent.getType();
//check what is the event, hence what is the user doing
if (type.equals(DocumentEvent.EventType.INSERT)) {
Document source = documentEvent.getDocument();
y=documentEvent.getOffset();
l=documentEvent.getLength();
len=source.getLength();
text = source.getText(y,l);
if(text.equals(" "))
{
for (int z=0;z<len;z++)
{
System.out.print(word[z]);
}
System.out.println("\n");
Arrays.fill(word,null);
i=0;
}
word[i]=text;
i++;
} else {
System.out.println("A character has been removed!");
}
}
}; // DocumentListener class instantiation
// editor. editArea.getDocument().addDocumentListener(mydocumentListener);
public static void main(String args[]){
new DocumentEventDemo();
}
} // TOP class
Notes:
My most outer class extends JFrame, which creates the window & listeners the easy way, I think.
DocumentEventDemo is a constructor that creates the UI controls and looks.
I created a class that implements DocumentListener. This way I can override the abstract events.
My main function is on the bottom inside the class DocumentEventDemo, like yours actually.
I do not see the code for class MyEditor. Therefore I replaced that with JTextArea, behaves like an editor.
Your code GetWord seems to be working well. Congratulations!
Your technique of using System.out.println is not working since the app is windows GUI application instead of console, which works well with System.out.
Obviously, you still have work to do with the Listener functions for changed and remove.
Have fun!
Tommy Kwee
Related
Using a JTabbedPane, I'd like to capture the moment just immediately before the change in the selection of a new tab is effective and perform an action. It'd be something analogous to a focus-lost event for a swing component. The aim is to save the text of a few JTextFields into an external file when the tab is changed, so everytime the user clicks a different tab, the values of the current tab are written into the external file.
I've been using the ChangeListener to track the change of tabs, but haven't found a way to do what I need. Any ideas in how to achieve it in the next simple example?
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TabSample {
static void add(JTabbedPane tabbedPane, String label) {
JButton button = new JButton(label);
tabbedPane.addTab(label, button);
}
public static void main(String args[]) {
JFrame frame = new JFrame("TabSample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane tabbedPane = new JTabbedPane();
String titles[] = { "Geometry", "Materials", "Analysis"};
for (int i = 0, n = titles.length; i < n; i++) {
add(tabbedPane, titles[i]);
}
ChangeListener changeListener = new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) {
JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource();
int index = sourceTabbedPane.getSelectedIndex();
System.out.println("Tab changed to: " + sourceTabbedPane.getTitleAt(index));
}
};
tabbedPane.addChangeListener(changeListener);
frame.add(tabbedPane, BorderLayout.CENTER);
frame.setSize(400, 150);
frame.setVisible(true);
}
}
One possible way, and I'm not 100% sure that this is acceptable to do or not, but is to create your own model for the JTabbedPane, a model which implements SingleSelectionModel (check the SingleSelectionModel API), that overrides the setSelectedIndex(int index) method, the method that I'm pretty sure Swing uses when it wants to tell the JTabbedPane to change tabs. If you create a class that extends from the DefaultSingleSelectionModel, a concrete class that implements the above interface and override this method, you could make method calls before the super's method is called, and thus make GUI calls before the tab changes. For example, your setSelectedIndex method could look like this:
#Override
public void setSelectedIndex(int index) {
if (activated) {
String text = String.format("Before change, old index: %d; new index: %d", super.getSelectedIndex(), index);
JOptionPane.showMessageDialog(gui, text);
}
super.setSelectedIndex(index);
}
And using your code above could be implemented like:
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.DefaultSingleSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TabSample {
static void add(JTabbedPane tabbedPane, String label) {
JButton button = new JButton(label);
tabbedPane.addTab(label, button);
}
public static void main(String args[]) {
JFrame frame = new JFrame("TabSample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MySingleSelectionModel selectionModel = new MySingleSelectionModel(frame);
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.setModel(selectionModel);
String titles[] = { "Geometry", "Materials", "Analysis"};
for (int i = 0, n = titles.length; i < n; i++) {
add(tabbedPane, titles[i]);
}
ChangeListener changeListener = new ChangeListener() {
public void stateChanged(ChangeEvent changeEvent) {
JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource();
int index = sourceTabbedPane.getSelectedIndex();
System.out.println("Tab changed to: " + sourceTabbedPane.getTitleAt(index));
}
};
tabbedPane.addChangeListener(changeListener);
frame.add(tabbedPane, BorderLayout.CENTER);
frame.setSize(400, 150);
frame.setVisible(true);
selectionModel.setActivated(true);
}
private static class MySingleSelectionModel extends DefaultSingleSelectionModel {
private Component gui;
private boolean activated = false;
public MySingleSelectionModel(Component gui) {
this.gui = gui;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
#Override
public void setSelectedIndex(int index) {
if (activated) {
String text = String.format("Before change, old index: %d; new index: %d",
super.getSelectedIndex(), index);
JOptionPane.showMessageDialog(gui, text);
}
super.setSelectedIndex(index);
}
}
}
Note that I use a boolean field, activated to activate the behavior change so that it doesn't fire on GUI creation. I call setActivated(true) on the model after displaying the GUI.
Regarding your edit:
The aim is to save the text of a few JTextFields into an external file when the tab is changed, so everytime the user clicks a different tab, the values of the current tab are written into the external file.
I should have known, it was an XY Problem after all, where you ask how to solve a specific code problem when the best solution is to use a completely different approach. In the future, please give us all pertinent information with the original question so we can avoid wasting time with unnecessary solutions.
There is in fact no need here to do anything before the tab changes since it is perfectly fine to get the data when the tabs change. Your solution is to use a ChangeListener, and there's no need to go through the gymnastics of what I posted above.
I am new to java.
Can someone tell me how to add ActionListener with my code?
Do I need to make a different function for it? I want to retrieve value from textfield which is entered by user. I am getting error.
Please explain me the background logic behind when to make function of methods that already exists in java or can we use them directly? My code is:
Also tell me how by pressing ENTER I can get value attached with text field in string?
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JComboBox;
import javax.swing.JButton;
import javax.swing.*;
import javax.swing.JList;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Clientgui
{
public static void main(String[] args)
{
JFrame guiFrame=new JFrame();
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Client GUI");
guiFrame.setSize(30,30);
guiFrame.setLocationRelativeTo(null);
final JPanel comboPanel = new JPanel();
JLabel Msg = new JLabel("Type Message");
JTextField textbox=new JTextField(10);
comboPanel.add(Msg);
comboPanel.add(textbox);
textbox.addActionListener(this);
String text = textbox.getText();
//textArea.append(text + newline);
//textbox.selectAll();
textbox.setText("Enter message here");
//final JPanel comboPanel1 = new JPanel();
//JLabel listLb2 = new JLabel("Connect");
//comboPanel.add(listLb2 );
JButton connect=new JButton("Connect");
guiFrame.add(comboPanel,BorderLayout.NORTH);
guiFrame.add(connect,BorderLayout.SOUTH);
guiFrame.setVisible(true);
}
}
You need an instance of something that implements ActionListener, you are getting a compilation error here -
textbox.addActionListener(this); // <-- no instance "this".
// You may have new Clientgui(), but
// Clientgui does not implement ActionListener.
As mentioned by Elliott Frisch You can add the Action to the instance of something that implements ActionListener which you can achieve in two way
textbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//Write your action here.
}
});
OR
public class Clientgui implements ActionListener{
// content of class goes here
textbox.addActionListener(this);
// content of class goes here
}
In order to bind the enter key with your text box you should implements KeyListener
textbo.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
System.out.println("ENTER key pressed");
}
}
});
i want to know how to set the color of a button (in example, but i will need to set every component color) with the apple look and feel.
I found an answer in stackoverflow that suggest to change to the standard look and feel, that works for me, but i prefer not to change because I like apple's one.
Is there any solution?
I know there is because I saw many apps written in java that have colored buttons and also that use particular styles or images as background.
Can you tell me a solution?
Extend the Jbutton class and in that override the repaint() method and call setBackground(COLOR.ORANGE), to change the button color.
Now use this class to create all your buttons. If you wish to change color of a specific button, call the setBackground(COLOR.ORANGE) method on that specific button. Hope this helps. Have a look at the code below
package solutions;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.InputVerifier;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class VerifierTest extends JFrame {
private static final long serialVersionUID = 1L;
public VerifierTest() {
final JTextField tf = new JTextField("TextField1");
getContentPane().add(tf, BorderLayout.NORTH);
tf.setInputVerifier(new PassVerifier());
final JTextField tf2 = new JTextField("TextField2");
getContentPane().add(tf2, BorderLayout.SOUTH);
tf2.setInputVerifier(new PassVerifier());
final JButton b = new JButton("Button");
b.setBackground(Color.ORANGE);
b.setVerifyInputWhenFocusTarget(true);
getContentPane().add(b, BorderLayout.EAST);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!tf.getInputVerifier().verify(tf)) {
JOptionPane.showMessageDialog(tf.getParent(), "illegal value: " + tf.getText(), "Illegal Value",
JOptionPane.ERROR_MESSAGE);
}
if (b.isFocusOwner()) {
System.out.println("Button clicked");
}
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Frame frame = new VerifierTest();
frame.setSize(400, 200);
frame.setVisible(true);
}
class PassVerifier extends InputVerifier {
#Override
public boolean verify(JComponent input) {
final JTextField tf = (JTextField) input;
String pass = tf.getText();
if (pass.equals("Manish")) {
return true;
} else {
return false;
}
}
}
}
Comment the line "b.setBackground(Color.ORANGE);" and see the difference.
Please have a look at the following code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.text.*;
public class Form1 extends JFrame
{
private JTextPane textPane;
private JPanel south;
private JScrollPane scroll;
private String content;
public String documentType;
private DefaultStyledDocument document;
int start, end, offset1,length1;
private JButton button;
JFrame frame;
public Form1()
{
//Declaring the instance variables
textPane = new JTextPane();
textPane.setMinimumSize(new Dimension(100,100));
button = new JButton("Bold");
button.addActionListener(new StyledEditorKit.BoldAction());
button.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B,KeyEvent.CTRL_MASK),"key");
button.getActionMap().put("key", new StyledEditorKit.BoldAction());
document = (DefaultStyledDocument) textPane.getDocument();
//Creating the main window
south = new JPanel();
south.setLayout(new FlowLayout());
south.add(button);
scroll = new JScrollPane(textPane);
getContentPane().add(scroll,"Center");
getContentPane().add(south,"South");
setSize(800,600);
validate();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class Action extends AbstractAction
{
public void actionPerformed(ActionEvent ae)
{
new StyledEditorKit.BoldAction();
}
}
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run() {
Form1 f = new Form1();
f.setVisible(true);
}
});
}
}
In here, user can enter any text, and when he select a text and click on "Bold" button, the text will be bold. However, I need to do it using CTRL+B also. As you can see, my attempt is not giving any response to that key event. I even tried adding it to a seperate class which extends AbstractAction, but still no good. How can I implement the CTRL+B here? Please help...
When key bindings don't work for me, the first place I look is the InputMap -- am I sure that I'm using the right one? Well, are you sure? The default one uses JComponent.WHEN_FOCUSED and thus only works if your component has the focus.
If you want it to work at other times, say when the bound component is visible and in a focused window but doesn't necessarily have the focus itself, perhaps you should try different condition parameters. Try using JComponent.WHEN_IN_FOCUSED_WINDOW to start with.
i.e.,
InputMap inputMap = myComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
I have an array of JButtons which form a keypad interface. After six numbers are entered I want to disable the keypad so that no further numbers can be entered by the user.
I have written the code and the buttons do disable until the mouse hovers above any of them, then the buttons seem to re-enable themselves and run actionEvents added to them.
The full code is available here.
Possible things that I think are wrong.
There is some sort of MouseListener which is ignoring when I set button.setEnabled(false);
I haven't separated attributes from the buildGUI(); correctly, I only did this anyway so that the inner class could access them.
Possibly something to do with the gridLayout as disabling the buttons seems to work for my services JPanel buttons.
The problem lies in how you instantiated your Frame (CashMachine), not (directly) with its implementation.
You are calling buildGUI twice, one in the object's constructor, and then in the Driver class that instantiates the object. As a result, you are creating (and laying out) two sets of buttons.
When the buttons of the first set were eventually disabled, your mousing activity was revealing the second set of buttons. And a flaw in your ActionListener implementation can cause inputCount to take on values greater than 6, so buttons in the second set were not eventually disabled like those from the first set.
buildGUI should be private; it should be called in the CashMachine constructor, and not by your Driver class.
Conversely, in my opinion, CashMachine.setVisible should be called by the Driver class, and not by the CashMachine constructor.
The code works just fine I guess.
One possible source of confusion in your program is mixing number keys with control keys, Clear and Enter. Consider handling number keys separately with a single listener, as suggested in the NumberButton class shown below. Then you can handle the Clear and Enter buttons as desired. Also, using a List<NumberButton> makes the enable and disable loops easier.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class KeyPadPanel extends JPanel implements ActionListener {
private static final int MAX = 6;
private final List<NumberButton> numbers = new ArrayList<NumberButton>();
private final JTextArea text = new JTextArea(1, MAX);
private final JButton clear = new JButton("Clear");
private final JButton enter = new JButton("Enter");
public KeyPadPanel() {
super(new BorderLayout());
JPanel display = new JPanel();
text.setEditable(false);
display.add(text);
this.add(display, BorderLayout.NORTH);
JPanel pad = new JPanel(new GridLayout(4, 4));
for (int i = 0; i < 10; i++) {
NumberButton n = new NumberButton(i);
numbers.add(n);
if (i > 0) {
pad.add(n);
}
}
pad.add(clear);
pad.add(numbers.get(0));
pad.add(enter);
clear.addActionListener(this);
enter.addActionListener(this);
this.add(pad, BorderLayout.CENTER);
}
#Override
public void actionPerformed(ActionEvent e) {
text.setText("");
enableButtons();
}
private void enableButtons() {
for (NumberButton n : numbers) {
n.setEnabled(true);
}
}
private void disableButtons() {
for (NumberButton n : numbers) {
n.setEnabled(false);
}
}
private class NumberButton extends JButton implements ActionListener {
public NumberButton(int number) {
super(String.valueOf(number));
this.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
NumberButton b = (NumberButton) e.getSource();
if (text.getText().length() < MAX) {
text.append(b.getText());
}
if (text.getText().length() == MAX) {
disableButtons();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new KeyPadPanel());
f.pack();
f.setVisible(true);
}
});
}
}
Examining the class files was helpful! The problem is in the Driver class:
the buildGUI() method is being called 2 times: once in the constructor of CashMachine and second in the main method after calling the constructor.
public static void main(String args[])
{
CashMachine cashmachine = new CashMachine();
cashmachine.buildGUI();
}
This way you end up with the double number of buttons, that is, a pair of buttons at each position. But only one of each is being disabled.
Just remove the call to buildGUI from main (or from the constructor).
(I would change buildGUI to private as it should not be called from outside the class...)