Cell validation in JTable
Asked Answered
L

2

5

I have a JTable that needs cell validation for the cells where the user can input text. When a user enters invalid text the border of the cell turns red.

I've managed to get this working associating a two dimension array to flag if each cell has errors or not.

The problem is that the user must be able to reorder the table (by column). I have to store the error flag in the table model, not separatly. Anyone has ideas how to do this?

Lithometeor answered 25/4, 2013 at 21:10 Comment(2)
A user can only edit one cell at a time. Don't let them update the model until the data is valid. I don't see any need for a two dimensional array to keep track of error since all the data in the model should always be valid.Emboss
You could also orientate the "error" state to by indexed by the model. This way you can convert the view index back to the model index as required. By I like camickr's suggestGleesome
T
7

Also consider a custom TableCellEditor, seen here and below. Adding an InputVerifier, as shown here, is a good alternative.

As the user must be able to reorder the table by column:

JTable provides methods that convert from model coordinates to view coordinates — convertColumnIndexToView and convertRowIndexToView — and that convert from view coordinates to model coordinates — convertColumnIndexToModel and convertRowIndexToModel.

image

Terramycin answered 26/4, 2013 at 11:25 Comment(0)
W
4

I have tried one approach. We can use a TableCellRenderer and check the data in the cell and if the data is having error then just show it in RED. Here I have a StudentTableModel which will get the data to the table.

Error Check

After Reordering

The table shows the cell RED if the cell contains special character like '@', '#', '$'. You can still reorder the table but still the rendering will take care of it. AFAIK flags are not required for achieving this.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

import com.amarnath.DragDrop.StudentTableModel;

public class TableErrorCheck {

    private JFrame frame;
    private JTable table;


    private void createUI() {

        frame = new JFrame();
        table = new JTable();
        table.setModel(new StudentTableModel());
        table.getColumnModel().getColumn(1).setCellRenderer(new ErrorCellRenderer());

        frame.setLayout(new BorderLayout());
        frame.add(new JScrollPane(table), BorderLayout.CENTER);
        frame.setTitle("Table Error Check.");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                new TableErrorCheck().createUI();
            }
        };

        EventQueue.invokeLater(r);
    }

}

class ErrorCellRenderer extends DefaultTableCellRenderer {

    private static final long serialVersionUID = 1L;

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                row, column);

        if(value.toString().contains("@") || value.toString().contains("$") || value.toString().contains("#")) {
            component.setBackground(Color.RED);
        } else {
            component.setBackground(Color.WHITE);
        }

        return component;
    }
}


import java.util.ArrayList;
import java.util.List;

import javax.swing.table.AbstractTableModel;

public class StudentTableModel extends AbstractTableModel {

    private static final long serialVersionUID = 1L;

    private List<StudentDO> data;

    private List<String> columnNames;

    public StudentTableModel() {
        data = getTableData();
        columnNames = getTableColumnNames();
    }

    public List<StudentDO> getData() {
        return data;
    }

    public void setData(List<StudentDO> data) {
        this.data = data;
    }

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

    @Override
    public String getColumnName(int column) {
        return columnNames.get(column);
    }

    @Override
    public int getColumnCount() {
        return columnNames.size();
    }

    @Override
    public int getRowCount() {
        if(data == null) {
            return 0;
        }
        return data.size();
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        if(columnIndex == 0 || columnIndex == 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
        case 0:
            return data.get(rowIndex).isSelect();
        case 1:
            return data.get(rowIndex).getName();
        case 2:
            return data.get(rowIndex).getAge();
        default:
            return null;
        }
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        switch (columnIndex) {
        case 0:
            data.get(rowIndex).setSelect((Boolean) aValue);
            break;
        case 1:
            data.get(rowIndex).setName(aValue == null ? null : aValue.toString());
            break;
        case 2:
            data.get(rowIndex).setAge(aValue == null ? new Integer(0) : Integer.parseInt(aValue.toString()));
            break;
        default:
            break;
        }
    }

    /**
     * Add a row.
     * @param index
     * @param studentDO
     */
    public void addRow(int index, StudentDO studentDO) {
        data.add(index, studentDO);
        fireTableDataChanged();
    }

    private List<StudentDO> getTableData() {
        List<StudentDO> list = new ArrayList<StudentDO>();

        for(int i = 0; i< 5; i++) {
            StudentDO student = new StudentDO();
            student.setSelect(false);
            student.setName("Stu " + i);
            student.setAge(10 + i);
            student.setIdentifier("ToapTable");

            list.add(student);
        }

        return list;
    }

    private List<String> getTableColumnNames() {
        List<String> columnNames = new ArrayList<String>();
        columnNames.add("Select");
        columnNames.add("Name");
        columnNames.add("Age");

        return columnNames;
    }
}

public class StudentDO {

    private boolean select;
    private String name;
    private int age;
      // Removed Getters and Setters .
}

P.S: Please let me know whether this is the good approach or not.

Waybill answered 26/4, 2013 at 9:18 Comment(4)
@Che. Thanks. In fact, this is my approach. but there are 2 issues: 1)some cell validation rules depend on 2 cells 2)how can get iterate through the cell renderers and check the colour of the background? That's why i created a 2 dimensional array to check the errors... but that prevents row sorting (my question)Lithometeor
@AlexandreGoncalves If you need to do sorting then you need to save the row numbers.Waybill
@Che, i wanted to avoid saving the row numbers, to avoid messing things up... If i could access the renderer, i would know if it has background red (like your example) and then get the error status. but afaik that is not possible!Lithometeor
@AlexandreGoncalves Okay let me suggest something. What if you don't allow to sort on table columns unless they clear the errors in the cells. (Just think abt it .. :-))Waybill

© 2022 - 2024 — McMap. All rights reserved.