1

I am using JTextField inside JTable cells; I use a TabelModel which has the dynamic data of the JTable. Now, when I click a button, I am reading a cell value. Problem is the cell which has the present focus doesn't return the updated value. For example, consider this program:

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class MyTable extends JFrame {

    DefaultTableModel tmodel = new DefaultTableModel(new Object[][]{
            {"some"}, {"any"}, {"even"}, {"text"}, {"and"}, {""}},
        new Object[]{"Column 1"});

    public MyTable() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTable table = new JTable(tmodel);
        table.setDefaultRenderer(Object.class, new MyRenderer());
        getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
        getContentPane().setLayout(new GridLayout(2, 2));
        JButton jb = new JButton("click me"); //button to display last cell data
        jb.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                JOptionPane.showMessageDialog(
                    null, table.getModel().getValueAt(5, 0));
            }
        });
        getContentPane().add(jb);
    }

    public static void main(String arg[]) {
        new MyTable().setVisible(true);
    }
}

class MyRenderer implements TableCellRenderer {

    public Component getTableCellRendererComponent(JTable table, Object value,
        boolean isSelected, boolean hasFocus, int row, int column) {
        JTextField editor = new JTextField();
        if (value != null) {
            editor.setText(value.toString());
        }
        return editor;
    }
}

Here I am leaving the last field empty. When I read it by

table.getModel().getValueAt(5, 0)

I get null. Now I change the value at 5,0, and again click the button, but now again I get null. Now surprisingly, I edit some other cell, again click button; now I get the correct data at cell 5,0! Why is this? Is this a bug? I tried several possibilities!

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Ashok Raj
  • 444
  • 6
  • 25
  • 2
    1) For better help sooner, post an [SSCCE](http://sscce.org/). (e.g. should include explicit imports) 2) Please use a consistent and logical indent for code blocks. – Andrew Thompson Mar 26 '12 at 15:12
  • check if `getValueAt(1, 0)` returns `any`, maybe the problem is with your indexes? – maialithar Mar 26 '12 at 15:29
  • @h4b0 no no, I checked it multiple times. The cell which has focus returns the old value not the updated one. – Ashok Raj Mar 26 '12 at 15:30
  • Unless I am misunderstanding your problem, your example works perfectly for me. It always correctly returns the value contained in the cell that is requested in getValueAt(int, int) – Michael Mar 26 '12 at 15:45
  • As ymene says pressing "enter" key solves the issue. but asking user to press enter for each cell edit is not a solution. anyone know how to commit it with code – Ashok Raj Mar 26 '12 at 15:57
  • you are doing something wrong in the code you are _not_ showing, or the other way round: the code you are showing is unrelated to the description of whatever problem you perceive :-) So yes, it's a bug - in _your_ code or in _your_ expectation. Show an SSCCE, then we might be able to help you finding that bug. – kleopatra Mar 27 '12 at 10:45
  • @kleopatra The real code is in a secure client Virtual machine. The Example is perfect in depicting my problem. Maybe running my code can make you more clear on it. – Ashok Raj Mar 27 '12 at 13:40
  • +1 for [sscce](http://sscce.org/). – trashgod Mar 27 '12 at 19:37

3 Answers3

5

There are several points to consider within your example:

First you should have a look in Java - Tutorials to learn about the concept of Renderer and Editor within JTables, because you kinda mix things up here. Within your Renderer you use a JTextField which doesn't make sense, because this object is just used to present the data within your tableModel. Normally a JLabel is used for presenting, since you wont ever edit a value within a Renderer.

Moreover your example (eventhough I haven't tried it yet) should work, since a JTable already has a DefaultEditor. Maybe you just forgot to commit the value with pressing enter before you clicked the button to check which value is in?

I really can just recommend for reading this tutorial and search for some examples on how this is handled normally. You should find a lot of working examples.

crusam
  • 6,140
  • 6
  • 40
  • 68
  • The rendering is just a quick example of my real project. No prob with that. You are right. pressing enter commits the change, but how to do it via code? I did search a lot of examples. JTable & rendering & editing is not the issue. Getting the value is. – Ashok Raj Mar 26 '12 at 15:47
  • Your example doesnt seem to fullfil your usecase. What is it exactly you want your example to do? While you are editing the value in your cell, the data isn't saved in your TableModel, hence you can't ask the model for the current value, unless you commit( with pressing enter, or changing focus) the value. Do you need the value already while editing the cell? – crusam Mar 26 '12 at 15:57
  • @ymene ya i want the current value by committing, it's possible.I was not able to work it out by changing focus (actually button should take the focus when i click it rit?). i need the value when button is clicked after editing the cell. terminateEditOnFocusLost is the solution :) – Ashok Raj Mar 27 '12 at 18:17
5

I think that every type of Arrays start with 0 (zero), then output to the JoptionPane is correct (return empty String), little bit modified your original code, added most important Swing rulles

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class MyTable extends JFrame {

    private static final long serialVersionUID = 1L;
    private DefaultTableModel tmodel = new DefaultTableModel(new Object[][]{
                {"some"}, {"any"}, {"even"}, {"text"}, {"and"}, {"xxxxxxxxxxxxxxxxxxxxxxx"}},
            new Object[]{"Column 1"});

    public MyTable() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final JTable table = new JTable(tmodel);
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        add(new JScrollPane(table), BorderLayout.CENTER);
        JButton jb = new JButton("click me"); //button to display last cell data
        jb.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent arg0) {
                JOptionPane.showMessageDialog(null, table.getModel().getValueAt(5, 0));
            }
        });
        add(jb, BorderLayout.SOUTH);
        pack();
        setVisible(true);
    }

    public static void main(String arg[]) {
        EventQueue.invokeLater(new Runnable() {//added initial thread

            @Override
            public void run() {
                MyTable myTable = new MyTable();
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • 1
    @Ashok Raj be sure desing is about success, most important for compound JComponents, I leaving answering Renderer/Editor issue, because (@ymene) touched that issues, by answering your question – mKorbel Mar 26 '12 at 15:48
  • I agree with you, but the above picture is just an quick little example, not my actual application. I can do a workaround but I really wanted to know to fix this issue – Ashok Raj Mar 26 '12 at 15:54
  • 1
    @Ashok Raj not clear what do you fix, 1) JTable, 2) JTable with Renderer, 3) JTable with Editor, 4) JTable with Renderer and Editor too, – mKorbel Mar 26 '12 at 16:01
  • problem is when i edit a cell, it's data is not immediately retrievable as long as the cell has focus. all the Rendered & editor & Jtable are fine. no issues with them – Ashok Raj Mar 26 '12 at 16:04
  • 2
    @Ashok Raj ahaaaaaaaaa :-) , you are right, you have to call `stopCellEditing`, better could be reading answers by ([@kleopatra](http://stackoverflow.com/search?q=user%3A203657+stopCellEditing)) or the same by ([@camickr](http://stackoverflow.com/search?q=user%3A131872+stopCellEditing)), maybe there are another questions and answers, but I linked `TOP two answerers` on this forum – mKorbel Mar 26 '12 at 16:13
  • @trashgod thank you are right, simple and clear, but I'm not still sure if OP required to call `stopCellEditing` from `JButton` ??? directly, really don't know, not sure about, maybe :-) – mKorbel Mar 26 '12 at 16:16
  • just can agree with you mKorbel, but from what I understood so far, trashgods link is really what the OP is looking for. First I guessed he wanted to go for some live validation. – crusam Mar 26 '12 at 16:20
  • @trashgod please ignore my last idiotic comment, JButton's click take Focus by default, sorry my bad ..... errrgggt – mKorbel Mar 26 '12 at 16:22
  • 1
    @mKorbel: I typically do both; I use `terminateEditOnFocusLost`, as well as `stopCellEditing()` in my enter/solve `Action`. – trashgod Mar 26 '12 at 16:23
  • @ymene for simple editor without InputMask works as we expecting, otherwise InputMask helt Focus until value isn't inputed correctly, but this issue could be about cancelCellEditing (maybe) too :-) – mKorbel Mar 26 '12 at 16:26
  • @mKorbel thanx a lot... terminateEditOnFocusLost is working good. stopCellEditing won't help as it stops further changes in the table. yet got a problem terminateEditOnFocusLost not working for the first time the app is run. any suggestions? – Ashok Raj Mar 27 '12 at 18:10
  • @AshokRaj: The credit goes to @kleopatra; more [here](http://stackoverflow.com/a/9898379/230513). – trashgod Mar 27 '12 at 22:04
5

Incorporating @kleopatra's suggestion and @mKorble's helpful revisions, here are a few additional ideas based on your example. Note in particular the use of setDefaultButton() and Action in conjunction with Key Bindings.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.table.DefaultTableModel;

/** @see https://stackoverflow.com/q/9874664/230513 */
public class MyTable extends JFrame {

    private static final String show = "Show";
    private DefaultTableModel tmodel = new DefaultTableModel(
        new Object[][]{
            {"edit"}, {"any"}, {"cell"}, {"text"}, {"and"}, {"edit me"}},
        new Object[]{
            "Column 1"});
    private JTable table = new JTable(tmodel);
    private Action showAction = new ShowAction(show);
    private JButton jb = new JButton(showAction);

    public MyTable() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationByPlatform(true);
        table.putClientProperty("terminateEditOnFocusLost", true);
        KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
        table.getInputMap(JTable.WHEN_FOCUSED).put(enter, show);
        table.getActionMap().put(show, showAction);
        add(new JScrollPane(table), BorderLayout.CENTER);
        table.setPreferredScrollableViewportSize(new Dimension(400, 200));
        JPanel panel = new JPanel();
        panel.add(jb);
        getRootPane().setDefaultButton(jb);
        add(panel, BorderLayout.SOUTH);
        pack();
    }

    private class ShowAction extends AbstractAction {

        private ShowAction(String name) {
            super(name);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
            }
            int row = table.getSelectedRow();
            if (row > -1) {
                JOptionPane.showMessageDialog(
                    MyTable.this, table.getModel().getValueAt(row, 0));
            }
        }
    }

    public static void main(String arg[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new MyTable().setVisible(true);
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • THAT is an AWESOME solution...it's working perfectly 100% as I wanted. Thanks everyone :) – Ashok Raj Mar 28 '12 at 05:03
  • polite cough ... the example is a bit convoluted with twirks that are neither needed nor recommended. a) All that's needed for the OPs example to work (now that I understood the problem, at last :-) is setting the terminateEdit client property b) the enter binding overwrites the table's own enter (which terminates the edit implicitly) c) never call a button's click if you don't really need it (instead, wire the actions) d) the safeguarding aginst not-yet committed edits feels upside down: should be done in show instead of enter – kleopatra Mar 28 '12 at 09:51
  • @kleopatra: Right on all counts; thanks. I plead careless cloning :-) – trashgod Mar 28 '12 at 10:55