Java KeyListener Stops Working - java

- = UPDATE = -
It turns out the issue was not with Java but with my Apple keyboard. Holding down a letter key brings up a menu that breaks my Java programs. By disabling that menu popup, my KeyListener and my Key Bindings both work as they should. Thank you all for your answers.
Question
I have searched on Google and on StackOverflow for an answer to my question, but to no avail. All of the questions that I've found have the main class extending JComponent, JFrame, JPanel, etc., and not Canvas.
Now for my question:
I am having trouble getting my Java KeyListener to cooperate while my program runs. When I start my program, everything works as usual. However, as I start pressing keys and moving things around (with said keys), the program begins to delay and take more time for the key presses to register. All of a sudden, they KeyListener breaks altogether and I get no input (even a System.out.println statement in the keyPressed method shows no activity). I have three classes that have to do with my KeyListener in any way.
If it helps, the goal of this program is to use BufferedImage class to plot points from different mathematical functions, like a sine wave. I have commented the best I can without being Super-Comment-Man, but I can clarify on the purpose of any code to the best of my ability.
First, my Screen class (draws stuff on the JFrame with a BufferStrategy):
package com.elek.waves.graphics;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import com.elek.waves.graphics.math.Controller;
import com.elek.waves.graphics.math.Graph;
import com.elek.waves.input.Keyboard;
/**
* The engine of the entire Waves project. Processes the BufferedImage and puts the JFrame
* on the screen. Uses other classes to calculate where to put pixels (what color each pixel
* in the array should be) and get keyboard input.
*
* #author my name
* #version 1.0
*/
public class Screen extends Canvas {
/**
* Holds some *important* number that makes Java happy.
*/
private static final long serialVersionUID = 1L;
/**
* Constant (and static) dimensions of the window.
*/
public static final int WIDTH = 800, HEIGHT = 800;
/**
* Frame that will contain the BufferedImage and all its glory.
*/
private JFrame frame;
/**
* BufferedImage processes the pixel array and translates it into fancy screen magic.
*/
private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
/**
* Holds color data for each pixel on the screen. Each pixel has an integer value equivalent
* to the hexadecimal RGB value for whatever color this pixel is supposed to be.
*/
private int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
/**
* Graph object to draw the lines on.
*/
private Graph graph;
/**
* Controller object to control the graph.
*/
private Controller controller;
/**
* Keybaord object to use as a key-listener.
*/
private Keyboard key;
/* -- Constructor -- */
/**
* Creates a new Screen object. Initializes the JFrame object.
*/
public Screen() {
frame = new JFrame("Magic!");
graph = new Graph(pixels);
key = new Keyboard();
controller = new Controller(key, graph);
addKeyListener(key);
}
/* -- Methods -- */
/**
* Called once and only once by the main method. Repeatedly calls the update and render methods
* until the program stops running.
*/
private void start() {
this.requestFocus();
this.requestFocusInWindow();
while (true) {
update();
render();
}
}
/**
* Called by the start method repeatedly. First, clears the screen of the previous image in
* order to prevent ghost-imaging or blurring. Then, updates the pixel array to whatever it
* needs to be for the next iteration of the render method.
*/
private void update() {
// Update the keyboard input
key.update();
// Update the controller
controller.update();
// Clean up the screen and then graph the line
clearScreen();
graph.drawWave();
}
/**
* Called by the start method repeatedly. Draws the pixel array onto the JFrame using the
* BufferedImage magic.
*/
private void render() {
// Initialize buffer strategies
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(2);
return;
}
// Physically update the actual pixels on the image
Graphics g = (Graphics2D) bs.getDrawGraphics();
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
g.dispose();
bs.show();
}
/**
* Clears the screen by setting every pixel in the pixel array to black. Used to prevent
* ghost-images or blurring.
*/
public void clearScreen() {
for (int i = 0; i < pixels.length; i++)
pixels[i] = 0;
}
/**
* Main method to run the program. Creates a Screen object with a BufferedImage to display
* pixels however the other classes decide to. All this does is set up the JFrame with the
* proper parameters and properties to get it up and running.
*
* #param args A String array of random arguments that Java requires or it gets fussy
*/
public static void main(String[] args) {
// Create Screen object
Screen screen = new Screen();
screen.frame.add(screen);
screen.frame.pack();
screen.frame.setSize(WIDTH, HEIGHT);
screen.frame.setLocationRelativeTo(null);
screen.frame.setResizable(false);
screen.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screen.frame.setVisible(true);
screen.start();
}
}
Second, my Keyboard class (KeyListener that breaks):
package com.elek.waves.input;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* Gets the user's key strokes and determines which keys are down at a given time.
*
* #author my name
* #version 1.0
*/
public class Keyboard implements KeyListener {
/**
* Holds the state of 120 keys (true if they're down, false if they're not).
*/
private boolean[] keys = new boolean[120];
/**
* Holds the state of the "useful" keys (true if down, false if not).
*/
public boolean w, a, s, d, up, down, left, right;
/**
* Determines if the "useful" keys are down or not. Sets the variables to true if they're down and
* false if they're up.
*/
public void update() {
w = keys[KeyEvent.VK_W];
a = keys[KeyEvent.VK_A];
s = keys[KeyEvent.VK_S];
d = keys[KeyEvent.VK_D];
up = keys[KeyEvent.VK_UP];
down = keys[KeyEvent.VK_DOWN];
left = keys[KeyEvent.VK_LEFT];
right = keys[KeyEvent.VK_RIGHT];
}
/**
* Changes the state of the pressed key's corresponding boolean in the array to true.
*/
public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}
/**
* Changes the state of the pressed key's corresponding boolean in the array to false.
*/
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
public void keyTyped(KeyEvent e) {
}
}
Third, my Controller class (uses the KeyListener to control the program):
package com.elek.waves.graphics.math;
import com.elek.waves.input.Keyboard;
/**
* Controls the graph's transformation properties (stretching and shifting). Directly changes the
* transformation variables in the Graph class to achieve this.
*
* #author my name
* #version 1.0
*/
public class Controller {
/**
* Keyboard object to get the user's key-inputs.
*/
private Keyboard input;
/**
* Graph object that this Controller will control.
*/
private Graph graph;
/* -- Constructor -- */
/**
* Create a new Controller object with the specific keyboard input parameter.
* <pre>Sets the starting parameters as the following:
* Vertical Scale: 1
* Horizontal Scale: 1
* Vertical Shift = 0
* Horizontal Shift = 0</pre>
*
* #param input The Keybaord object from which the controller will get input
*/
public Controller(Keyboard input, Graph parent) {
// Initialize keybaord input and graph parent
this.input = input;
graph = parent;
// Initialize transformation variables
graph.vScale = 50;
graph.hScale = 0.05;
graph.vShift = 0;
graph.hShift = 0;
}
/* -- Methods -- */
/**
* Updates the shifting of the graph (moving around) and the scaling of the graph (stretching)
* from the keyboard input. <strong>WASD</strong> keys control shifting, and <strong>up, down,
* left, and right</strong> keys control stretching.
*/
public void update() {
// Update shifting
if (input.w) graph.vShift += 0.5;
if (input.s) graph.vShift -= 0.5;
if (input.a) graph.hShift -= 0.04;
if (input.d) graph.hShift += 0.04;
// Update scaling
if (input.up) graph.vScale += 0.5;
if (input.down) graph.vScale -= 0.5;
if (input.left) graph.hScale += 0.0001;
if (input.right) graph.hScale -= 0.0001;
}
}
I have found several helpful people saying to use KeyBindings as opposed to a KeyListener. However, I have used a KeyListener successfully in the past, and I'd like to get it to work again if possible. If KeyBindings are absolutely necessary, I supposed I can make the switch, but I'd prefer if that didn't have to be the case.
Thank you all in advance!

Canvas will suffer the same issues that all the other components suffer from, loss of keyboard focus, this is why we generally don't recommend KeyListener.
First you need to make the Canvas focusable, see Canvas#setFocusable
The next, more difficult issue, is requesting keyboard focus, you can use Canvas#requestFocusInWindow but any component which requires keyboard focus will steal it.
Depending on what you are doing, you might be able to simply place the call in the update loop, but you need to be aware that if you want to ask input from the user, within the same window, you will have issues (with the canvas stealing the focus)
Update
I had some issues with index of bounds due to the use of an array in the keyboard controller, which I switched over to Set instead...
public class Keyboard implements KeyListener {
/**
* Holds the state of 120 keys (true if they're down, false if they're
* not).
*/
// private boolean[] keys = new boolean[120];
/**
* Holds the state of the "useful" keys (true if down, false if not).
*/
private Set<Integer> keys;
/**
* Determines if the "useful" keys are down or not. Sets the variables
* to true if they're down and false if they're up.
*/
public void update() {
keys = new HashSet<>(8);
}
public boolean isKeyPressed(int key) {
return keys.contains(key);
}
public boolean isWPressed() {
return isKeyPressed(KeyEvent.VK_W);
}
public boolean isAPressed() {
return isKeyPressed(KeyEvent.VK_A);
}
public boolean isSPressed() {
return isKeyPressed(KeyEvent.VK_S);
}
public boolean isDPressed() {
return isKeyPressed(KeyEvent.VK_D);
}
public boolean isUpPressed() {
return isKeyPressed(KeyEvent.VK_UP);
}
public boolean isDownPressed() {
return isKeyPressed(KeyEvent.VK_DOWN);
}
public boolean isLeftPressed() {
return isKeyPressed(KeyEvent.VK_LEFT);
}
public boolean isRightPressed() {
return isKeyPressed(KeyEvent.VK_RIGHT);
}
/**
* Changes the state of the pressed key's corresponding boolean in the
* array to true.
*/
public void keyPressed(KeyEvent e) {
System.out.println("Pressed = " + e.getKeyCode());
keys.add(e.getKeyCode());
}
/**
* Changes the state of the pressed key's corresponding boolean in the
* array to false.
*/
public void keyReleased(KeyEvent e) {
System.out.println("Released = " + e.getKeyCode());
keys.remove(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {
}
}
I also added a small delay into the render loop so you're not chocking the system
private void start() {
setFocusable(true);
while (true) {
this.requestFocusInWindow();
update();
render();
try {
Thread.sleep(16);
} catch (InterruptedException ex) {
}
}
}

try this
import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class Main {
public static void main(String[] argv) throws Exception {
JTextField textField = new JTextField();
textField.addKeyListener(new MKeyListener());
JFrame jframe = new JFrame();
jframe.add(textField);
jframe.setSize(700, 700);
jframe.setVisible(true);
}
}
class MKeyListener extends KeyAdapter {
#Override
public void keyPressed(KeyEvent event) {
System.out.println(event.getKeyCode)
if(event.getKeyCode() = \\key code here\\){
System.out.println("True")
}else{System.out.println("False")
Allthough this was ran in package java but there shouldnt be anything wrong with it

Related

java GUI. keep getting loadImage(): Error: Image at images/f.png could not be found

i am working on hangman game project and i think im done with the code but everytime i load the game it gives me this error which i wrote when it cannot load the images file. im not sure if im doing something wrong to load the directory for the images file. here is the code...
this is ofcourse one of 5 classes but i want to be sure if im making a mistake loading the directory or is it something else. Also if u have any suggestions on how to improve this code im open to that..
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
/**
* A tile representing a letter to be guessed during a game.
* #author Jeff A.
*/
public class letterBlocks extends JLabel
{
/**
* Add a TileListener
* #param l The MouseListener to attach as a TileListener
*/
public void addTileListener(MouseListener l)
{
tileListener = l;
addMouseListener(tileListener);
}
/**
* The directory containing the letter images.
*/
private final String IMAGE_DIRECTORY;
/**
* The type of image (.jpg, .png, etc. to include the period).
*/
private final String IMAGE_TYPE;
/**
* The images of the letters in the window.
*/
private final char IMAGE_LETTER;
/**
* The preferred width of the tiles.
*/
private final int PREFERRED_WIDTH;
/**
* The preferred height of the tiles.
*/
private final int PREFERRED_HEIGHT;
/**
* The current path of the current image.
*/
private String path;
/**
* The current image being displayed.
*/
private BufferedImage image;
/**
* A custom MouseListener from the GameBoard class to change the tiles on
* a click.
*/
private MouseListener tileListener;
/**
* The default constructor.
*/
public letterBlocks() { this('A', "C:\\Users\\omare\\Dropbox\\My PC (LAPTOP-AGJUFI2M)\\Desktop\\Images", ".png"); }
/**
* Creates a new LetterTile given the letter to be displayed, the directory
* of the letter image series, and the image type.
* #param imageLetter The letter to be displayed on the tile.
* #param imageDirectory The directory holding the letter images.
* #param imageType The type of the letter images.
*/
public letterBlocks(char imageLetter, String imageDirectory, String imageType)
{
IMAGE_LETTER = imageLetter;
IMAGE_DIRECTORY = imageDirectory;
IMAGE_TYPE = imageType;
PREFERRED_WIDTH = PREFERRED_HEIGHT = 50;
setPreferredSize(new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT));
path = IMAGE_DIRECTORY + IMAGE_LETTER + IMAGE_TYPE;
image = loadImage(path);
}
/**
* Changes the tile's appearance and removes the mouse listener to prevent
* the tile from being clicked again.
* #return The tile's letter.
*/
public char guess()
{
loadNewImage("guessed");
removeTileListener();
return IMAGE_LETTER;
}
/**
* Loads a new image in the hangman image series.
* #param suffix The suffix of the image name.
*/
private void loadNewImage(String suffix)
{
path = IMAGE_DIRECTORY + IMAGE_LETTER + "_" + suffix + IMAGE_TYPE;
image = loadImage(path);
repaint();
}
/**
* Remove the tile's TileListener.
*/
public void removeTileListener() { removeMouseListener(tileListener); }
/**
* Loads an image from a file.
* #param imagePath The path to load an image from.
* #return A BufferedImage object on success, exits on failure.
*/
private BufferedImage loadImage(String imagePath)
{
BufferedImage img = null;
try
{
img = ImageIO.read(new File(imagePath));
}
catch (IOException ex)
{
System.err.println("loadImage(): Error: Image at "
+ imagePath + " could not be found");
System.exit(1);
}
return img;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image,
0,
0,
PREFERRED_WIDTH,
PREFERRED_HEIGHT,
null);
}
}
i tried to put the images in different files and that didnt work

How do I make a panel visible inside frame in Java AWT?

I am studying Java AWT to create GUI applications. I am working on the below code where I cannot make the panel visible inside the frame. Here is my code:
import java.awt.*;
import java.awt.event.*;
/**
*
* #author kiran
*/
public class UserInterface
{
Frame UI;
private static double UIWidth, UIHeight;
/**
* Constructs User Interface
*/
public UserInterface()
{
UI = new Frame("frame");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
UIWidth = screenSize.getWidth();
UIHeight = screenSize.getHeight();
buildFrame();
buildMessageInputArea();
}
/**
* returns the width of the UI
* #return returns the width of the UI
*/
public static double getUIWidth()
{
return UIWidth;
}
/**
* returns the width of the UI
* #return returns the width of the UI
*/
public static double getUIHeight()
{
return UIHeight;
}
/**
* Builds the frame
*/
private void buildFrame()
{
UI.setSize((int)UIWidth,(int)UIHeight*96/100);
UI.setVisible(true);
UI.setLayout(new FlowLayout());
UI.addWindowListener(new Actions());
}
private void buildMessageInputArea()
{
Panel current = new TextAreaPanel().getPanel();
current.setVisible(true);
UI.add(current);
}
}
class TextAreaPanel extends Frame
{
private Panel textAreaPanel;
TextArea msgInputArea;
public TextAreaPanel()
{
textAreaPanel = new Panel();
msgInputArea = new TextArea(1000,(int)UserInterface.getUIWidth() * 80/100);
}
private void addTextArea()
{
textAreaPanel.add(msgInputArea);
}
public Panel getPanel()
{
return textAreaPanel;
}
}
class Actions extends WindowAdapter
{
#Override
public void windowClosing(WindowEvent c)
{
System.exit(0);
}
}
How can I make the panel visible inside the frame?
How do I make a panel visible inside frame in Java AWT?
There were two fundamental problems with the code as seen, which can be fixed by changing the following:
Add the panel/text area to the GUI before setVisible(true) is called on the top level container.
While it is possible to add components to a container after it has been made visible, they require special handling, and it is not necessary in this case.
Add the text area to the panel!
Here is the code, turned into a Minimal, Complete, and Verifiable example by adding a main(String[]) method, with those two changes implemented, as well as more explanatory comments on other aspects of the code.
import java.awt.*;
import java.awt.event.*;
public class UserInterface {
Frame UI;
private static double UIWidth, UIHeight;
public static void main(String[] args) {
Runnable r = () -> {
new UserInterface();
};
EventQueue.invokeLater(r);
}
/**
* Constructs User Interface
*/
public UserInterface() {
UI = new Frame("frame");
// setting a GUI to full screen while accounting for the task
// bar can be achieved in a single line of code.
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
UIWidth = screenSize.getWidth();
UIHeight = screenSize.getHeight();
// these need to be called in the reverse order to ensure the
// components are added before the GUI is set visible.
buildMessageInputArea();
buildFrame();
}
/**
* returns the width of the UI
*
* #return returns the width of the UI
*/
public static double getUIWidth() {
return UIWidth;
}
/**
* returns the width of the UI
*
* #return returns the width of the UI
*/
public static double getUIHeight() {
return UIHeight;
}
/**
* Builds the frame
*/
private void buildFrame() {
UI.setSize((int) UIWidth, (int) UIHeight * 96 / 100);
UI.setVisible(true);
UI.setLayout(new FlowLayout());
UI.addWindowListener(new Actions());
}
private void buildMessageInputArea() {
Panel current = new TextAreaPanel().getPanel();
current.setVisible(true);
UI.add(current);
}
}
// does not need to be a fram
//class TextAreaPanel extends Frame {
class TextAreaPanel {
private Panel textAreaPanel;
TextArea msgInputArea;
public TextAreaPanel() {
textAreaPanel = new Panel();
// these number represent columns and rows, not pixels!
//msgInputArea = new TextArea(1000, (int) UserInterface.getUIWidth() * 80 / 100);
msgInputArea = new TextArea(40, 60);
// add the text area to the panel!
textAreaPanel.add(msgInputArea);
}
/** not called by anything else
private void addTextArea() {
textAreaPanel.add(msgInputArea);
}
**/
public Panel getPanel() {
return textAreaPanel;
}
}
// This can be achieved in a single line of code
class Actions extends WindowAdapter {
#Override
public void windowClosing(WindowEvent c) {
System.exit(0);
}
}
To add to / expand on the comments of #mKorbel & #camickr:
Why use AWT? See this answer for many good reasons to abandon AWT components in favor of Swing.
See Composition over inheritance.
The use of static in GUIs more commonly causes problems, than fixes them. Most (if not all) of the methods marked as static should be reduced to non-static with the code using an instance of the object to call the method.

Swing getText won't read user input

I've been trying to get the GUI going for the application I'm working on, and, it's been ages since doing anything in Swing. Most of the stuff has come back, but not this. I cannot understand why, across none of the different text components, if I type something into them, getText() will always return "" regardless. It'll return correctly if I use setText() as a test, but that's not really helpful.
This remains consistent across JTextArea/JTextField, various ways of referencing the text field (direct variable vs pulling from the HashMap), and regardless of what kind of thread does the accessing. At times I've used the debugger to try and look at things as they're happening through other test snippets, and still nothing. Since the edit, have trimmed a lot of these cases out, but still persists in the problem.
In all cases, user input is never acquired.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class LifePathUI2 {
final static boolean shouldWeightX = true;
private GridBagConstraints cons;
private BorderLayout windowLayout;
private GridBagLayout layout;
private JFrame mainWindow;
private JPanel mainPanel;
private JComponent currentComponent;
private JTextField characterName;
/**
* #throws HeadlessException
*/
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
init();
}
public void init()
{
mainWindow.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
mainWindow.setLayout(windowLayout);
cons.ipadx = 5;
cons.ipady = 5;
cons.anchor = GridBagConstraints.NORTHWEST;
cons.fill = GridBagConstraints.NONE;
cons.weighty = 1.0;
cons.weightx = 1.0;
// to make everything work right we add a mainPanel under the mainWindow
cons.gridheight = 1;
cons.gridwidth = 1;
mainWindow.add(mainPanel);
// Readers keep in mind if you don't set this below, strange behavior happens
mainPanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
mainPanel.setLayout(layout);
currentComponent = mainPanel;
addLabel(0,0,"Character Name");
characterName = addTextF(1,0,"",30,true);
endRow(2,0);
addButton(0,1,"Foo").addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println(characterName.getText());
}
});
endVertical(0,2);
mainWindow.setSize(1400, 900);
mainWindow.setVisible(true);
}
/**
* Inserts a spacer to signify the end of components for this row (effects layout so it doesn't center)
*/
private void endRow(int x, int y)
{
cons.weightx = 100.0;
addLabel(x,y,"");
cons.weightx = 1.0;
}
/**
* Inserts a spacer to signify the end of components vertically (effects layout so it doesn't center)
*/
private void endVertical(int x, int y)
{
cons.weighty = 100.0;
addLabel(x,y,"");
cons.weighty = 1.0;
}
/**
* Shorthand command to add label at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param text Display Text for label
*/
private JLabel addLabel(int x, int y, String text)
{
JLabel temp = new JLabel(text);
addC(temp,x,y);
return temp;
}
/**
* Shorthand command to add Button at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param text Display Text for Button
* #return The component created
*/
private JButton addButton(int x, int y, String text)
{
JButton temp = new JButton(text);
addC(temp,x,y);
return temp;
}
/**
* Shorthand command to add Text Field at coordinates with text
* #param x non-negative integer
* #param y non-negative integer
* #param value the default value for the text field
* #param cols Number of Columns
* #param updateListen If true will add listener that triggers UI updates when values change
* #return The component created
*/
private JTextField addTextF(int x, int y, String value, int cols, boolean updateListen)
{
JTextField temp = new JTextField(value, cols);
// this prevents the common issue of the text fields turning into slits
temp.setMinimumSize(temp.getPreferredSize());
addC(temp,x,y);
return temp;
}
/**
* Shorthand to add new components to the UI tab at given coords
* #param comp Component to add to UI
* #param x non-negative integer
* #param y non-negative integer
*
*/
private void addC(JComponent comp, int x, int y) {
cons.gridx = x;
cons.gridy = y;
currentComponent.add(comp,cons);
}
/**
* #param args
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
LifePathUI2 ui = new LifePathUI2();
ui.init();
}
});
}
}
I've searched every question here for swing stuff and none of them problems seem to correspond to this problem specifically. Was not entirely expecting that.
I apologize for the bigger snippet but since all of the parts immediately around creating the text fields, reading them, creating the GUI itself, all seem to match the examples online I've seen... I'm just smart enough to know there must be something odd I did here and just not seeing it.
EDIT: Simplifying example. Despite breaking this down to a much more simple test case, still no dice
EDIT2: Finished compression of things. I was actually expecting this to work because this is on the level of working code I've written before. But still nothing
A wild guess, but your update() method looks like it might be trying to extract the text from the Document that its listening to (you don't say), and if so, then use the DocumentEvent to get the Document and then extract the text. Something like:
private class TextChangeListener implements DocumentListener {
#Override
public void changedUpdate(DocumentEvent e) {
update(e);
}
#Override
public void insertUpdate(DocumentEvent e) {
update(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
update(e);
}
}
and
private void update(DocumentEvent e) {
String text;
try {
Document doc = e.getDocument();
text = e.getDocument().getText(0, doc.getLength());
// do something with text here! ************
System.out.println(text);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
Other than that, I'm lost in your code. Simplify. Refactor. Simplify some more. Refactor some more. Think smaller classes that can be fully tested in isolation.
Edit
You call init() TWICE, and that's the problem since it means that you create two of everything, one displayed and one not.
You first call it here:
public LifePathUI2() throws HeadlessException {
// ....
init();
}
and then call it again in your main:
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
LifePathUI2 ui = new LifePathUI2();
ui.init();
}
});
Call it only once.
Change this:
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
init();
}
to this:
public LifePathUI2() throws HeadlessException {
cons = new GridBagConstraints();
windowLayout = new BorderLayout();
layout = new GridBagLayout();
mainWindow = new JFrame();
mainPanel = new JPanel();
mainWindow.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
// init();
}

Unable to type or delete text in JTextfield

I'm learning Java using Art and Science of Java (using Java SE 6u45). Trying to Change the font of text by entering the new font type in a JTextField. But the problem is that I can't enter any text in JTextField. The problem is common to other swing components i've used like JButton, JCheckBox. But in the latter components I could see the effect of selection, even though the visual selection stays the same, meaning that the check box remains checked even after clicking but the code shows the result of an unchecked box.
But in case of JTextField, not even the effect is showing. Not also could i delete a test text i put in JTextField. Tried to use isEditable() , grabFocus() and isFocusable(). Could it be a Java bug ?
/**
* Example 10.9
*
* This program prints the given text in the font inputted by the user in JTextField
*/
package ASJ_Examples;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JLabel;
import javax.swing.JTextField;
import acm.graphics.GLabel;
import acm.program.GraphicsProgram;
public class FontSampler extends GraphicsProgram implements ActionListener{
/**
* Eclispe Generated
*/
private static final long serialVersionUID = -5734136235409079420L;
private static final String TEST_STRING = "This is a test";
private static final double LEFT_MARGIN = 3;
private static final int MAX_FONT_NAME = 10;
public void init(){
addJFontLabel();
addJFontTextField();
lastY = 0;
addGLabel();
}
/**
* Adds a text field to enter the required font
*/
private void addJFontTextField() {
String test = "new";
fontField = new JTextField(test, MAX_FONT_NAME); //added to see if Jtextfiled is responding
// fontField.setEnabled(true);
// fontField.setEditable(true);
fontField.addActionListener(this);
//added these to give focus to jtextfield but no effect
fontField.isEditable();
fontField.grabFocus();
fontField.isFocusable();
//add to window
add(fontField, SOUTH);
}
/**
* Adds JFontLAbel to denote the text input field
*/
private void addJFontLabel() {
add(new JLabel("Font"), SOUTH);
}
/**
* Adds the test label to canvas
*/
private void addGLabel() {
lastLabel = new GLabel(TEST_STRING);
add(lastLabel, 20, 20);
}
public void ActionPerformed(ActionEvent e){
if(e.getSource() == fontField){
GLabel label = new GLabel(TEST_STRING);
label.setFont(lastLabel.getFont()); //to display the text even if the suer entered a non-allowed font
label.setFont(fontField.getText()); //change the font to u4ser demanded font
addGlabel(label);
lastLabel = label;
}
}
/**
*adds a Glabel on the next line adjusting for heights
* #param label
*/
private void addGlabel(GLabel label) {
lastY += label.getHeight();
lastY += lastLabel.getDescent() - label.getDescent();
add(label, LEFT_MARGIN, lastY);
}
/**
* JTextField to enter font
*/
private JTextField fontField;
/**
* GLabel which is being worked on
*/
private GLabel lastLabel;
/**
*
*/
private double lastY;
}
try using fontField.requestFocus(); instead of fontField.grabFocus();
and fontField.setEditable(true); instead of fontField.isEditable();
fontField.setFocusable(true); instead of fontField.isFocusable();
btw fontField.setEditable(true); and fontField.setFocusable(true); are not necessary by default they are set to true.
As #andrew-thompson pointed out, the issue was mix of awt and swing. I suppose somehow the GComponent was overlaying Swing component JTextField which made it inaccessible. So the workaround is to create JPanel and a KeyListener to the JTextField as noted by #marco .
This is the code that works :
SSCCE Code
/**
*
* This program prints the given text in the font inputted by the user in JTextField
*/
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.JTextField;
import acm.graphics.GLabel;
import acm.program.GraphicsProgram;
public class FontSampler extends GraphicsProgram implements KeyListener{
private static final String TEST_STRING = "This is a test";
private static final double LEFT_MARGIN = 3;
private static final int MAX_FONT_NAME = 10;
public void init(){
initPanel(); //init panel
addJFontTextField(); //adds fontField to enter the font type
addGLabel(); //adds default label to GCanvas
lastY = 0; //default y offset for post-processed label
add(panel); //adds panel to canvas
}
/**
* initialises panel
*/
private void initPanel() {
panel = new JPanel();
panel.setLayout(new BorderLayout());
}
/**
* Adds a text field to enter the required font
*/
private void addJFontTextField() {
fontField = new JTextField( MAX_FONT_NAME);//added to see if Jtextfiled is responding
panel.add(fontField, BorderLayout.SOUTH);
fontField.addKeyListener(this); //adds key listener
}
/**
* Adds the test label to canvas
*/
private void addGLabel() {
lastLabel = new GLabel(TEST_STRING);
add(lastLabel, 20, 20);
}
/**
* Called when any key is pressed
*/
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){//check for enter key pressed
GLabel label = new GLabel(TEST_STRING);
label.setFont(lastLabel.getFont());
label.setFont(fontField.getText());
addGlabel(label);
lastLabel = label;
}
}
/**
*adds a Glabel on the next line adjusting for heights
* #param label
*/
private void addGlabel(GLabel label) {
lastY += label.getHeight();
lastY += lastLabel.getDescent() - label.getDescent();
add(label, LEFT_MARGIN, lastY);
}
/**
* JTextField to enter font
*/
private JTextField fontField;
/**
* GLabel which is being worked on
*/
private GLabel lastLabel;
/**
*
*/
private double lastY;
private JPanel panel;
}
Thanks :)
But in the latter components I could see the effect of selection, even though the visual selection stays the same, meaning that the check box remains checked even after clicking but the code shows the result of an unchecked box.
That sounds very strange. Maybe the GraphicsProgram class is doing something naughty. Cannot tell w/o its code though.
fontField.isEditable();
fontField.grabFocus();
fontField.isFocusable();
The first returns a boolean if the field is editable (it is by default).
The second should not be used by client programs, use fontField.requestFocusInWindow() instead. The third returns a boolean if the field is focusable (it is by default).
fontField.addActionListener(this);
An ActionListener on a JTextField does nothing. Try doing this:
fontfield.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void insertUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
}
#Override
public void changedUpdate(DocumentEvent e) {
// TODO Auto-generated method stub
}
});

Set the Tooltip Delay Time for a Particular Component in Java Swing

I'm trying to set tooltips on a JEditorPane. The method which I use to determine what tooltip text to show is fairly CPU intensive - and so I would like to only show it after the mouse has stopped for a short amount of time - say 1 second.
I know I can use ToolTipManager.sharedInstance().setInitialDelay(), however this will set the delay time for tooltips on all swing components at once and I don't want this.
If what you want is to make the tooltip dismiss delay much longer for a specific component, then this is a nice hack:
(kudos to tech at http://tech.chitgoks.com/2010/05/31/disable-tooltip-delay-in-java-swing/)
private final int defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay();
addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent me) {
ToolTipManager.sharedInstance().setDismissDelay(60000);
}
public void mouseExited(MouseEvent me) {
ToolTipManager.sharedInstance().setDismissDelay(defaultDismissTimeout);
}
});
Well, I would recommend doing the CPU intensive task on another thread so it doesn't interrupt normal GUI tasks.
That would be a better solution. (instead of trying to circumvent the problem)
*Edit* You could possibly calculate the tootips for every word in the JEditorPane and store them in a Map. Then all you would have to do is access the tootip out of the Map if it changes.
Ideally people won't be moving the mouse and typing at the same time. So, you can calculate the tootlips when the text changes, and just pull them from the Map on mouseMoved().
You can show the popup yourself. Listen for mouseMoved() events, start/stop the timer and then show popup with the following code:
First you need PopupFactory, Popup, and ToolTip:
private PopupFactory popupFactory = PopupFactory.getSharedInstance();
private Popup popup;
private JToolTip toolTip = jEditorPane.createToolTip();
then, to show or hide the toolTip:
private void showToolTip(MouseEvent e) {
toolTip.setTipText(...);
int x = e.getXOnScreen();
int y = e.getYOnScreen();
popup = popupFactory.getPopup(jEditorPane, toolTip, x, y);
popup.show();
}
private void hideToolTip() {
if (popup != null)
popup.hide();
}
This will give you adjustable delay and a lot of troubles :)
FWIW, here is code that is based on the post by Noel. It takes that prior art and wraps it in a new class where the default is stored statically. Just in case anyone may benefit:
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
/**
* Provides customizable tooltip timeouts for a {#link javax.swing.JComponent}.
*
* #see ToolTipManager#setDismissDelay(int).
*/
public final class CustomTooltipDelayer extends MouseAdapter
{
private static final int defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay();
private final int _delay;
/**
* Override the tooltip timeout for the given component in raw millis.
*
* #param component target
* #param delay the timeout duration in milliseconds
*/
public static CustomTooltipDelayer attach(JComponent component, int delay)
{
CustomTooltipDelayer delayer = new CustomTooltipDelayer(delay);
component.addMouseListener( delayer );
return delayer;
}
/**
* Override the tooltip timeout for the given component as a ratio of the JVM-wide default.
*
* #param component target
* #param ratio the timeout duration as a ratio of the default
*/
public static CustomTooltipDelayer attach(JComponent component, float ratio)
{
return attach( component, (int)(defaultDismissTimeout * ratio) );
}
/** Use factory method {#link #attach(JComponent, int)} */
private CustomTooltipDelayer(int delay)
{
_delay = delay;
}
#Override
public void mouseEntered( MouseEvent e )
{
ToolTipManager.sharedInstance().setDismissDelay(_delay);
}
#Override
public void mouseExited( MouseEvent e )
{
ToolTipManager.sharedInstance().setDismissDelay(defaultDismissTimeout);
}
}

Categories

Resources