Swing getText won't read user input - java

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();
}

Related

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.

JDialogPane with Focused password input field [duplicate]

When my application loads, which is made using netbeans, the first JTextField is automatically focused, and in this JTextField, I have written "Enter your Username", it will go away when the user clicks on this field, but when the application is loaded, this field is focused, means I can't see "Enter your Username", how to unfocus it on startup ?
A log-in would be best done in a modal dialog, but that introduces problems in that the method requestFocusInWindow() must be called after the component is visible, but that is blocked by the fact the dialog is modal!
This example uses Rob Camick's RequestFocusListener (as presented in Dialog Focus) to manage focus after the dialog is visible.
Note: That is how it appears before the user does anything. The password field is focused by default.
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class LoginRequired {
LoginRequired() {
JFrame f = new JFrame("Login Required");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setResizable(false);
f.setSize(400, 300); // not recommended, but used here for convenience
f.setLocationByPlatform(true);
f.setVisible(true);
showLogin(f);
}
private void showLogin(JFrame frame) {
JPanel p = new JPanel(new BorderLayout(5,5));
JPanel labels = new JPanel(new GridLayout(0,1,2,2));
labels.add(new JLabel("User Name", SwingConstants.TRAILING));
labels.add(new JLabel("Password", SwingConstants.TRAILING));
p.add(labels, BorderLayout.LINE_START);
JPanel controls = new JPanel(new GridLayout(0,1,2,2));
JTextField username = new JTextField("Joe Blogs");
controls.add(username);
JPasswordField password = new JPasswordField();
password.addAncestorListener(new RequestFocusListener(false));
controls.add(password);
p.add(controls, BorderLayout.CENTER);
JOptionPane.showMessageDialog(
frame, p, "Log In", JOptionPane.QUESTION_MESSAGE);
System.out.println("User Name: " + username.getText());
System.out.println("Password: " + new String(password.getPassword()));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new LoginRequired();
});
}
}
/**
* Convenience class to request focus on a component.
*
* When the component is added to a realized Window then component will
* request focus immediately, since the ancestorAdded event is fired
* immediately.
*
* When the component is added to a non realized Window, then the focus
* request will be made once the window is realized, since the
* ancestorAdded event will not be fired until then.
*
* Using the default constructor will cause the listener to be removed
* from the component once the AncestorEvent is generated. A second constructor
* allows you to specify a boolean value of false to prevent the
* AncestorListener from being removed when the event is generated. This will
* allow you to reuse the listener each time the event is generated.
*/
class RequestFocusListener implements AncestorListener
{
private boolean removeListener;
/*
* Convenience constructor. The listener is only used once and then it is
* removed from the component.
*/
public RequestFocusListener()
{
this(true);
}
/*
* Constructor that controls whether this listen can be used once or
* multiple times.
*
* #param removeListener when true this listener is only invoked once
* otherwise it can be invoked multiple times.
*/
public RequestFocusListener(boolean removeListener)
{
this.removeListener = removeListener;
}
#Override
public void ancestorAdded(AncestorEvent e)
{
JComponent component = e.getComponent();
component.requestFocusInWindow();
if (removeListener)
component.removeAncestorListener( this );
}
#Override
public void ancestorMoved(AncestorEvent e) {}
#Override
public void ancestorRemoved(AncestorEvent e) {}
}
textField.setFocusable(false);
textField.setFocusable(true);
If, and only if, textField had focus, the next component in TAB-order order will get focus automatically. The effect is the same as a TAB press.
(not tested in a GUI with just one focusable component :) )
Use requestFocusInWindow() to set focus on some other component rather then your JTextfield first.
But i'd suggest not to alter the native focus system, rather setText(String s) on the JTextField after initComponents() call in the constructor (assumed to be in netbeans).
Further optional reading: How to Use the Focus Subsystem
I think giving keyboard focus to the username field is the correct behavior, assuming that's what the user needs to do first. Instead of clearing on focus, why not clear only after the user types something?:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.Document;
public class PlaceholderTextField extends JTextField {
public static void main(final String[] args) {
final PlaceholderTextField tf = new PlaceholderTextField("");
tf.setColumns(20);
tf.setPlaceholder("All your base are belong to us!");
final Font f = tf.getFont();
tf.setFont(new Font(f.getName(), f.getStyle(), 30));
JOptionPane.showMessageDialog(null, tf);
}
private String placeholder;
public PlaceholderTextField() {
}
public PlaceholderTextField(
final Document pDoc,
final String pText,
final int pColumns)
{
super(pDoc, pText, pColumns);
}
public PlaceholderTextField(final int pColumns) {
super(pColumns);
}
public PlaceholderTextField(final String pText) {
super(pText);
}
public PlaceholderTextField(final String pText, final int pColumns) {
super(pText, pColumns);
}
public String getPlaceholder() {
return placeholder;
}
#Override
protected void paintComponent(final Graphics pG) {
super.paintComponent(pG);
if (placeholder.length() == 0 || getText().length() > 0) {
return;
}
final Graphics2D g = (Graphics2D) pG;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(getDisabledTextColor());
g.drawString(placeholder, getInsets().left, pG.getFontMetrics()
.getMaxAscent() + getInsets().top);
}
public void setPlaceholder(final String s) {
placeholder = s;
}
}
If you really just want to remove focus, some options:
component.setFocusable(false);
KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();

JFrame does not draw content when called inside ActionListener

I am trying to make a set of 2 GUIs: one, when a button is clicked, calls another, which, based on which button is clicked in the second GUI, returns a value to the first GUI. Unfortunately, when called from the first GUI's actionPerformed method, the second GUI appears blank. This does not, however, happen when JOptionPane is used.
What does JOptionPane do that allows it to work inside the actionPerformed method, and why does my example code not work inside the actionPerformed method?
The code for the first GUI, which calls the second GUI, is as follows:
public class OpeningGUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private Container container;
private JButton btn, btn2;
/**
* Constructor for class OpeningGUI - establish the JFrame
* Loads the window and moves it to the center of the screen.
*/
public OpeningGUI() {
// when mama ain't happy, ain't nobody happy
super("Dominion Launcher");
//UI components get established here
container = getContentPane(); // Container is the abstract concept of the area inside a window
container.setLayout(new BorderLayout());
container.add(getCenterPanel(), BorderLayout.CENTER);
setSize(700, 300);
pack();
setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2,
(int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Sets the game mode based on which button is clicked.
* Click stops return method from waiting.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource() == btn) {
SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
System.out.println(sd.getSelectedIndex());
}
if(e.getSource() == btn2) {
JOptionPane.showConfirmDialog(null, "See it works, right");
}
}
/**
* Sets up the center panel with buttons to select game mode.
* #return the center panel.
*/
public JPanel getCenterPanel() {
JPanel temp = new JPanel();
btn = new JButton("SelectionDialog tester");
temp.add(btn);
btn.addActionListener(this);
btn2 = new JButton("JOptionPane tester");
temp.add(btn2);
btn2.addActionListener(this);
return temp;
}
/**
* Main method of OpeningGUI. Used to run the program.
* #param args command-line arguments. Unused.
*/
public static void main(String[] args) {
new OpeningGUI();
}
}
The code for the second GUI is as follows:
public class SelectionDialog extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private Container container;
private JButton confirmBtn;
private JButton[] buttons;
private ArrayList<Integer> selectionIndecies;
private CountDownLatch wait;
private String message;
private int numNeeded;
private boolean isMaximum;
/**
* Constructor for the SelectionDialog class.
* Selects from an ArrayList of buttons.
* #param message Message to display.
* #param num number to select.
*/
public SelectionDialog(String message, int num) {
super("Please Select Buttons");
this.message = message;
numNeeded = num;
isMaximum = false;
setupUI();
}
/**
* Establishes the JFrame and sets values for some fields.
*/
private void setupUI() {
selectionIndecies = new ArrayList<Integer>();
wait = new CountDownLatch(1);
//UI components get established here
container = getContentPane(); // Container is the abstract concept of the area inside a window
container.setLayout(new BorderLayout());
container.add(getTopPanel(), BorderLayout.NORTH);
container.add(getCenterPanel());
pack();
setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth()/2 - this.getWidth()/2,
(int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/2 - this.getHeight()/2);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
/**
* Changes color of buttons and adds or removes them from the selected arrays.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource() == confirmBtn) {
if((!isMaximum && selectionIndecies.size() <= numNeeded)
|| selectionIndecies.size() == numNeeded) {
wait.countDown();
dispose();
}
}
for(int i = 0; i < buttons.length; i++) {
if(e.getSource() == buttons[i]) {
if(!buttons[i].getBackground().equals(Color.ORANGE)) {
buttons[i].setBackground(Color.ORANGE);
buttons[i].setBorderPainted(false);
selectionIndecies.add(new Integer(i));
repaint();
}
else {
buttons[i].setBackground(Color.LIGHT_GRAY);
selectionIndecies.remove(new Integer(i));
repaint();
}
}
}
}
/**
* Creates the top panel of the GUI.
* Contains the prosperity check box, the number of players selector,
* and the card counter and confirm button.
* #return the top panel.
*/
private JPanel getTopPanel() {
JPanel topPanel = new JPanel();
JLabel temp = new JLabel(message + " ");
topPanel.add(temp);
confirmBtn = new JButton("Done");
topPanel.add(confirmBtn);
confirmBtn.addActionListener(this);
return topPanel;
}
/**
* Determines which buttons were selected.
* Waits until Ok has been clicked and a proper number of buttons had been selected.
* #return an array of indecies of the buttons selected.
*/
public ArrayList<Integer> getSelectedIndex() {
try {
wait.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Collections.sort(selectionIndecies);
return selectionIndecies;
}
/**
* Sets up center panel with ArrayList of buttons,
* and panels of buttons.
*/
private JScrollPane getCenterPanel() {
JPanel centerPanel = new JPanel();
buttons = new JButton[6];
for(int i = 0; i < 6; i++) {
JButton temp = new JButton("Button " + i);
temp.addActionListener(this);
temp.setVisible(true);
centerPanel.add(temp);
buttons[i] = temp;
}
return new JScrollPane(centerPanel);
}
/**
* Main method of the SelectionDialog class. For testing only.
* #param args command line arguments. Unused.
*/
public static void main(String[] args) {
SelectionDialog sd = new SelectionDialog("Select up to 3 buttons, then click done when selection complete", 3);
System.out.println(sd.getSelectedIndex());
}
}
This code is completely runnable with the two classes I have posted and appropriate import statements. The second GUI can also be run independently to show what should appear when called from the first GUI, and the first GUI contains an example JOptionPane that works normally.
Please help me figure out why the actionPerformed method stops only some GUIs from rendering, while others work normally!
You're blocking the EDT! actionPerformed is executed on the EDT, so getSelectedIndex is also and wait.await() blocks it. Notice that once this happens, the first frame doesn't respond also (and minimizing and un-minimizing the frames will not even paint them). Even if the 2nd frame were to show, it would not respond to user interaction because the first actionPerformed did not return.
I don't understand why you need the CountDownLatch. getSelectedIndex can only execute once confrimBtn is pressed, so just return the selected buttons at that point. This isn't the only solution - your design will eventually dictate the interaction between the classes.
In SelectionDialog's actionPerformed write:
if (e.getSource() == confirmBtn) {
if ((!isMaximum && selectionIndecies.size() <= numNeeded) || selectionIndecies.size() == numNeeded) {
Collections.sort(selectionIndecies);
OpeningGUI.publishSelectedIndex(selectionIndecies);
dispose();
}
}
and remove the getSelectedIndex method.
In OpeningGUI, add the following method
public static void publishSelectedIndex(ArrayList<Integer> list) {
System.out.println(list);
}
and remove from its actionPerformed the call to getSelectedIndex.
Notes:
Instead of the screen size calculation for setLocation, you can use setLocationRelativeTo(null).
Calling setSize when you call pack right after it makes the first call redundant.
No need to specify the generic type on the right-hand-side:
selectionIndecies = new ArrayList<>();
Swing should be started on the EDT (see here).
You would probably do better with a dialog instead of another JFrame.
Use different ActionListeners for buttons that function differently instead of checking the source with each call.

Dynamically initializing JButtons with unique ImageIcons

This is my first attempt at using a GUI layout in Java. I am trying to create a simple memory card game, where the user flips over two cards, and tries to get a match, and if there's no match they flip back over, if there's a match they stay flipped until you get all the matches. I might have made it hard on myself though in that I made the whole game dynamic to the constructor variables that define the number of columns and rows of cards. I thought this was better than hard-coding a certain value in, but now I'm having trouble putting the images in my img folder onto my cards.
I understand that variables of variables are not permitted in Java & this is really hard for me to adapt to as a ColdFusion developer. Can you help me think of ways to accomplish this this the proper way in Java?
Here is a simplified version of my code.
import javax.swing.JFrame;
public class MemoryApp
{
public static void main(String args[])
{
//Creates a new game with 3 columns and 4 rows
final CardGame myGame = new CardGame(3, 4);
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI(myGame.getCols(), myGame.getRows());
}
});
}
private static void createAndShowGUI(int c, int r) {
//Create and set up the window.
Window frame = new Window("GridLayoutDemo", c, r);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
frame.addComponentsToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
Card game class:
public class CardGame
{
private int cols, rows;
public CardGame(int c, int r)
{
cols = c;
rows = r;
}
public int getCols(){
return cols;
}
public int getRows(){
return rows;
}
}
The window with the grid layout:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
public class Window extends JFrame
{
private int cols, rows;
GridLayout windowGrid = new GridLayout(1,1);
public Window(String t, int c, int r)
{
super(t);
cols = c;
rows = r;
windowGrid.setColumns(c);
windowGrid.setRows(r);
}
public void addComponentsToPane(final Container pane)
{
final JPanel compsToExperiment = new JPanel();
compsToExperiment.setLayout(windowGrid);
JPanel controls = new JPanel();
controls.setLayout(new GridLayout(cols,rows));
int countCard = (cols * rows) / 2;
/**
* Add buttons to experiment with Grid Layout.
* This is where I'd like to be able to loop through
* countCard and create the required number of buttons
* as well as put images on the buttons like so:
*
* ImageIcon image1 = new ImageIcon(getClass().getResource("card1.png"));
* through
* ImageIcon image1 = new ImageIcon(getClass().getResource("card" & cardCount & ".png"));
*
* Below is how I would attempt this in ColdFusion- I know
* the variable of variables syntax is invalid, it is just
* to show you what I mean.
*/
// for(int i = 0; i < countCard; i++;)
// {
// compsToExperiment.add(new JButton("../img/card" & i & ".png"));
// ImageIcon variables["image" & i] = new ImageIcon(getClass().getResource("card" & i & ".png"));
// imageButton.setIcon(variables["image" & i]);
// etc. with ButtonClickEventHandler, haven't gotten that far yet
// }
pane.add(compsToExperiment, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.CENTER);
}
}
Based on the code commented-out code that you posted so far, one approach could be like this:
public class Window extends JFrame
{
...
// A java.util.List that stores all the buttons, so
// that their icons may be changed later
private List<JButton> buttons = new ArrayList<JButton>();
// A java.util.List that stores all the ImageIcons that
// may be placed on the buttons
private List<ImageIcon> imageIcons = new ArrayList<ImageIcon>();
public void addComponentsToPane(final Container pane)
{
...
for(int i = 0; i < countCard; i++;)
{
// String concatenation is done with "+" in Java, not with "&"
String fileName = "card" + i + ".png";
// Create the icon and the button containing the icon
ImageIcon imageIcon = new ImageIcon(getClass().getResource(fileName));
JButton button = new JButton(imageIcon);
// Add the button to the main panel
compsToExperiment.add(button);
// Store the button and the icon in the lists
// for later retrieval
imageIcons.add(imageIcon);
buttons.add(button);
// Attach an ActionListener to the button that will
// be informed when the button was clicked.
button.addActionListener(createActionListener(i));
}
}
private ActionListener createActionListener(final int cardIndex)
{
return new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
clickedCardButton(cardIndex);
}
};
}
private void clickedCardButton(int cardIndex)
{
System.out.println("Pressed button for card "+cardIndex);
// Here you can change the icons of the buttons or so...
JButton button = buttons.get(cardIndex);
ImageIcon imageIcon = imageIcons.get(cardIndex);
....
}
You mentioned that this is your first attempt to build a GUI with Java. So I assume that this is only intended for "practicing". If your intention was to build a "real application", you should rather consider some Model-View-Controller (MVC) approach for this.

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
}
});

Categories

Resources