Enter edit mode when typing something when Jtable’s cell is focused

Asked

Viewed 443 times

1

I have a celleditor in the format of textarea, and when you’re focused, I squeeze some word to type in celula. It opens the edit mode, but does not write anything, which may be wrong?

working class

package javaapplication10;

/**
 *
 * @author Gabriel
 */
public class Funcionario {

    private String nome;

    public Funcionario(String nome, int idade, int matricula) {
        this.nome = nome;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

}

class Funciotablemodel

import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;

public class FuncionarioTableModel extends AbstractTableModel {

    private String colunas[] = {"nome"};
    private ArrayList<Funcionario> funcionarios;
    private final int COLUNA_NOME = 0;


    public FuncionarioTableModel(ArrayList<Funcionario> funcionarios) {
        this.funcionarios = funcionarios;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
    }

    @Override
    public int getRowCount() {
        return funcionarios.size();
    }

    @Override
    public int getColumnCount() {
        return colunas.length;
    }

    @Override
    public String getColumnName(int indice) {
        return colunas[indice];
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        switch (columnIndex) {
            case COLUNA_NOME:
                return String.class;
                        default:
                return String.class;
        }
    }


    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        Funcionario funcionario = this.funcionarios.get(rowIndex);

        switch (columnIndex) {
            case COLUNA_NOME:
                return funcionario.getNome();
        }
        return null;
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        Funcionario funcionario = this.funcionarios.get(rowIndex);
        switch (columnIndex) {
            case COLUNA_NOME:
                funcionario.setNome(String.valueOf(aValue));
                break;
        }
        fireTableDataChanged();
    }
}

class Jtableexemple

package javaapplication10;

/**
 *
 * @author Gabriel
 */
import java.awt.Component;
import java.awt.EventQueue;
import java.util.ArrayList;
import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;

public class JTableExample extends JFrame {

    private JTable tabela;
    private JScrollPane scrollPainel;

    public JTableExample() {
        renderizarTela();
    }

    private void renderizarTela() {


    Funcionario f1 = new Funcionario("",0,0);
    Funcionario f2 = new Funcionario("", 0, 0);
    Funcionario f3 = new Funcionario("", 0, 0);
    Funcionario f4 = new Funcionario("", 0, 0);

        ArrayList<Funcionario> funcionarios = new ArrayList<>();
        funcionarios.add(f1);
        funcionarios.add(f2);
        funcionarios.add(f3);
        funcionarios.add(f4);

        FuncionarioTableModel model = new FuncionarioTableModel(funcionarios);

        this.tabela = new JTable(model);
        this.scrollPainel = new JScrollPane(tabela);
        tabela.getColumnModel().getColumn(0).setCellEditor(new TextAreaEditor());
                tabela.setRowHeight(50);
        this.add(scrollPainel);
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }



    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                JTableExample tb = new JTableExample();
                tb.setLocationRelativeTo(null);
                tb.setVisible(true);
            }
        });
    }
}

// celleditor
    class TextAreaEditor extends DefaultCellEditor {

        protected JScrollPane scrollpane;
        protected JTextArea textarea;

        public TextAreaEditor() {
            super(new JCheckBox());
            scrollpane = new JScrollPane();
            textarea = new JTextArea();
            textarea.setLineWrap(true);
            textarea.setWrapStyleWord(true);
//            textarea.setBorder(new TitledBorder("This is a JTextArea"));
            scrollpane.getViewport().add(textarea);

            // colocar para editar em 2 click
            setClickCountToStart(2);
            //
        }

        public Component getTableCellEditorComponent(JTable table, Object value,
                boolean isSelected, int row, int column) {
            textarea.setText((String) value);

            return scrollpane;
        }

        public Object getCellEditorValue() {
            return textarea.getText();

        }
    }
  • What’s the problem? Your code worked normally here

  • Excuse the delay Articuno! When he is focused on the cell and I try to type some writing or number, he opens the edition, but does not type. In case I double click it type. In that case I needed q when typing something it already activated the editing without giving both click

  • Remove the line you added to enter the edit with two clicks then.

  • In case I need to have her too.. You have to have both options. But the stranger is the same as he said, when you’re focused, try to type something and still not type.

  • Dude you’re doing this celleditor wrong. Notice that you pass a Jcheckbox in the constructor but then return a Jscrollpane. If you are going to use components that are not a text field, a combo or a checkbox, you cannot use defaultcelleditor, you need to implement a celleditor from scratch. Your class TextAreaEditor needs to be reset from scratch, so it will never work properly.

  • Well, Articuno, I don’t understand much. Scroll I put so that when you finish typing and the writing does not fit in the cell, it automatically increases the cell. ja this checkbox I have no idea. could you give me a hand to clean this? I really appreciate

  • I made a template but there is a problem, how do you intend to finish editing in the field? Enter does not work in the textarea, it breaks line.

  • It was good with Tab, but tab also does not work neh?

  • I managed to do, but without two clicks in the field, it opens straight for editing.

  • with the 2 clicks is not possible?

  • usually people always click on another cell to lose focus and can tab and in this case will already open another field

  • It’s possible, but it doesn’t pay for the work. You have to choose, if it’s two clicks, I can’t help because I’d have to rewrite a lot and for something so simple I don’t think it’s worth the trouble.

  • I get it, it might be the way you said

  • I believe I will answer yes

Show 9 more comments

1 answer

4


Your class TextAreaEditor is inheriting from DefaultCellEditor, and as the vast majority of classes DefaultXXX from the swing api, they are only for basic component functionality, so that you can implement these features without having to rewrite from scratch. As in the case of the cited class, it allows you to change the standard editor of editable cells (which is a text field) to other basic components, such as JCheckBox, JComboBox or the very JTextField with some possibility of customization. It is no wonder that if you access the documentation, you will see that this class only has 3 specific constructors, who expect just one of the 3 components mentioned.

I don’t know if it was a deliberate attempt to circumvent the class constraint or just ignorance, but in the class builder TextAreaEditor you do:

public TextAreaEditor() {
    super(new JCheckBox());

...

With this, you indicate to DefaultCellEditor that the default editor will be a checkbox, but then tries to pass a JScrollPane. This has everything to go wrong, as can be seen in your own example.

Recommended for cases where you need to implement an editor other than those supported by DefaultCellEditor is to write an editor that extends from AbstractCellEditor, class that has some standard implementations that lets you write a Celleditor with other components, and also need to implement TableCellEditor, to make the class compatible with the table.

Considering what you’d already done in your class, I did this:

public class TextAreaEditor extends AbstractCellEditor implements TableCellEditor {

    JTextArea textArea = new JTextArea();
    JScrollPane scroll;

    public TextAreaEditor() {

        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        textArea.setBorder(new TitledBorder("This is a JTextArea"));

        // altera o comportamento padrao do TAB para que transfira o foco
        // neste caso, vai transferir para a celular seguinte
        textArea.getInputMap().put(KeyStroke.getKeyStroke("TAB"), "transferFocus");

        scroll = new JScrollPane(textArea);
        
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex,
            int colIndex) {
        
        String text = value == null ? "" : value.toString();
        textArea.setText(text);

        return scroll;
    }

    @Override
    public Object getCellEditorValue() {
        return textArea.getText();
    }
}

Despite being commented, it is worth mentioning the line below:

textArea.getInputMap().put(KeyStroke.getKeyStroke("TAB"), "transferFocus");

Because we are creating a custom cell editor for the table, we also need to define a way to end editing. Usually just add one KeyListener editor and, when detecting ENTER, invoke the method stopCellEditing(), but as it is a JTextArea, which is a multiline field, it is hoped that it will be possible to write multiline texts, and capturing the ENTER we would take that characteristic from the component.

The line above remaps the function of the TAB, that by default within a textarea jumps 4 spaces forward (as occurs in most text editors), and sets the key to transfer the editor’s focus to the next component.

We could also organize the focus, but this is more complex and totally avoids the initial doubt. If you want to read more about focus, you can read in the documentation the tutorial for How to use focus subsystem and also this answer here on the website.

Applying the above class as column or whole table editor, we will already have cells with a textarea as an editor, but as the goal is also to allow you to go into editing mode when typing is started, you need to change a table behavior, so that you correctly redirect the focus to the text area. The simplest way I could find to do this was by changing the method changeSelection:

this.tabela = new JTable(model) {
    @Override
    public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {

        //inicia a edicao na celula da tabela
        //e quando o editor for o scrollpane, transfere
        //o foco para a textarea dentro dela
        if(editCellAt(rowIndex, columnIndex)) {
            Component editor = getEditorComponent();

            if(editor instanceof JScrollPane)
            ((JScrollPane)editor).getViewport().getView().requestFocusInWindow();
        }
        super.changeSelection(rowIndex, columnIndex, toggle, extend);
    }
};

When changing the selection in the table, this method is called, and in it, I check if the current cell is editable and start the editing mode, through the method editCellAt(). If so, I check if the editor is a JScrollPane, which is what interests us, and when it is, I get the focus transferred to the component inserted in it, in our case it will be itself JTextArea.

With this, the field already opens in edit mode, either by clicking on it or switching with TAB. See working:

inserir a descrição da imagem aqui


Extra

The previous text already answers the question, but the fact that you put a custom editor in the table also forces you to write a way in which the table will render the information from that cell. If you don’t write one redenderer, all the text will be displayed in a single line, because the redenderer cell pattern(just like default editor) is a simple text field.

Since I already had a ready-made example and had to use it to test the answer codes, follow the Renderer to the editor above, with some adaptations for correct display of multiline texts (relevant excerpts commented):

class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {

    public TextAreaCellRenderer() {
        setLineWrap(true);
        setWrapStyleWord(true);
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        
        
        setText((value == null) ? "" : value.toString());
        
        // ajusta o tamanho da celular conforme o texto digitado
        setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
        //ajusta o tamanho da linha da celula, conforme o texto digitado
        //para evitar que o texto seja cortado pela altura da linha
        if (table.getRowHeight(row) < getPreferredSize().height) {
            table.setRowHeight(row, getPreferredSize().height);
        }
        return this;
    }
}

Just apply the same column in the editor:

tabela.getColumnModel().getColumn(0).setCellRenderer(new TextAreaCellRenderer());

In addition to supporting multiline text, the table will also render the text correctly. See working:

inserir a descrição da imagem aqui

Of course, you can add a border to the Renderer and decrease the font if desired, but that’s up to you. : D

  • 1

    Our @Articuno, I didn’t expect all this, man.. You got a 1000 for this huge job. I just have to thank you for everything you’ve done. Thank you very much!

  • Do you want to charge for a private lesson? D

  • 1

    @Congratulations on the excellent answer :D

Browser other questions tagged

You are not signed in. Login or sign up in order to post.