0

I am working on a small project (with NetBeans' Java GUI Builder) in which I have 100 TextFields which does exact same thing on different data. They are arranged in 10 rows and 10 columns, there (Variable) names are like:

txt11,  txt12,  ... txt110
txt21,  txt22,  ... txt210
  .       .     ...   .
txt101, txt102, ... txt1010

so, that there names can easily generated by the row and column number as well as I can easily extract the row and the column of a TextField to which it belongs (But I can not).

Since they perform similar task I can write a single method which takes row and col as arguments and get executed whenever user writes something in any of the TextField. In order to accomplish the task I have to find out the row and the column they belong.

I tried to use following code inside the event listener ( I have added same event listener for all JTextField ):

private void TextFieldAnswerTyped(java.awt.event.KeyEvent evt) {
    String name = ((JTextField)evt.getSource()).getName();

    int row,col;
    if(name.endsWith("10"))
    {
        col=10;
        name=name.substring(0, name.length()-2);
    }
    else
    {
        col=Integer.parseInt(name.substring(name.length()-1,name.length()));
        name=name.substring(0, name.length()-1);
    }

    if(name.endsWith("10"))
        row=10;
    else
        row=Integer.parseInt(name.substring(name.length()-1,name.length()));

    checkForCell(row, col); //Performs the task
}

Everytime the event occures it gives me null as the name. Am I doing any mistake here or Is there any good alternative.

Abhishek Kashyap
  • 3,332
  • 2
  • 18
  • 20
  • 1
    You look to be making extra work for yourself unnecessarily. Why not consolidate it all and just use a JTable? – Hovercraft Full Of Eels Aug 14 '16 at 14:20
  • You may be confusing the name field, which is obtained by calling `getName()`, with the variable name, something totally different. If you can't use a JTable (again my main recommendation), then put all your JTextFields within a 2-dimensional array, and iterate through the array to get column and row position. – Hovercraft Full Of Eels Aug 14 '16 at 14:22
  • 2
    Any design (software wise, but also UI wise) that requires you (or your users) do deal with 100 text fields is simply: **broken**. Do as Hovercraft suggests; and A) consider using a different UI component and B) learn about arrays or something. Naming variables a1, a2, ... and so on; and then use the name to do reflection back to identify something ... really really bad idea. Seriously: this is how "maintenance nightmares" come into existence. – GhostCat Aug 14 '16 at 14:26
  • @HovercraftFullOfEels Sorry for the delay. – Abhishek Kashyap Aug 16 '16 at 15:08

1 Answers1

3

Your problem is that you're confusing the variable name with the JTextField's name field. Calling getName() on the JTextField returns the latter, its name field, which as you're finding out is set by default to null. Several solutions present themselves:

  • You could try to use reflection to get the object that the variable you're interested in refers to -- a bad, brittle and kludgy idea -- just don't do it.
  • You could in fact set the name field to be the same as the variable name, by calling setName(...) on the JTextField after creating it -- another bad kludge
  • Better would be to place all your JTextField objects into a 1 or 2 dimensional array or collection. By doing this, it would be easy to find out which row and column your field is in.
  • Perhaps the best solution (hard to tell without knowing more requirements) would be to use a JTable, one with 10 rows and 10 columns.

Your question sounds like it may in fact be an XY Problem where you ask "how do I fix this code" when the real solution is to use a different approach entirely. Please do tell us more about your "problem space" -- what overall problem that you're trying to solve, since again I think that there likely is a better solution out there for you.


Another issue: you appear to be trying to use a KeyListener with JTextFields, something I also do not recommend as this can interfere with the text fields native key processing. Instead you're almost always better off using a DocumentListener or a DocumentFilter.


Another option is to set the Swing component method, putClientProperty(...), to have your JTextFields "know" which row and column they're in. An example of this which uses the above method plus it's getter equivalent, and which uses an ActionListener in the JTextField to get the data is as follows:

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

@SuppressWarnings("serial")
public class ManyFields extends JPanel {

    private static final int ROWS = 10;
    private static final int COLS = ROWS;
    private static final int GAP = 2;
    private static final int TF_COLS = 5;
    public static final String ROW = "row";
    public static final String COL = "col";
    private JTextField[][] fieldGrid = new JTextField[ROWS][COLS];

    public ManyFields() {
        setLayout(new GridLayout(ROWS, COLS, GAP, GAP));
        setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));

        TFieldListener tFieldListener = new TFieldListener();

        for (int row = 0; row < fieldGrid.length; row++) {
            for (int col = 0; col < fieldGrid[row].length; col++) {
                JTextField tField = new JTextField(TF_COLS);
                tField.putClientProperty(ROW, row);
                tField.putClientProperty(COL, col);
                tField.addActionListener(tFieldListener);

                add(tField);
                fieldGrid[row][col] = tField;
            }
        }
    }

    private class TFieldListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            JTextField field = (JTextField) e.getSource();
            int row = (int) field.getClientProperty(ROW);
            int col = (int) field.getClientProperty(COL);

            // or another way to get row and column using the array:
            int row2 = -1;
            int col2 = -1;
            for (int r = 0; r < fieldGrid.length; r++) {
                for (int c = 0; c < fieldGrid[r].length; c++) {
                    if (field == fieldGrid[r][c]) {
                        row2 = r;
                        col2 = c;
                    }
                }
            }
            // now here row2 and col2 are set

            String text = field.getText();

            String title = String.format("Text for Cell [%d, %d]", col, row);
            String message = "text: " + text;
            int messageType = JOptionPane.INFORMATION_MESSAGE;
            JOptionPane.showMessageDialog(ManyFields.this, message, title, messageType);
            field.transferFocus(); // move to next component
        }
    }

    private static void createAndShowGui() {
        ManyFields mainPanel = new ManyFields();

        JFrame frame = new JFrame("Many Fields");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

But again, likely a JTable would offer a better solution, however it's hard to know exactly how to create this without more requirement information.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thanks @HovercraftFullOfEel‌​s for **putClientProperty**. Following answer also helped me in searching components by name http://stackoverflow.com/questions/4958600/get-a-swing-component-by-name#17806100 both together solved my problem. – Abhishek Kashyap Aug 16 '16 at 15:05