2

I have been researching JTables fairly thoroughly, and have found some brilliant resources that explain how to set the CellRenderer and CellEditor for columns, but have found that trying to set the Renderer and Editor of a single cell is a little less forthcoming.

The general idea of my table is to have two columns, where the left column displays variable names, and the right column allows for the modification of the associated variables. Most cells I have been able to perform validation on, and even found out how to use the DefaultCellRenderer and DefaultCellEditor the way that I required, but trying to do this with custom renderers and editors I am struggling with. Below I have tried to indicate how a table under my design would look: (Left hand value is the variable name, the right hand is the value)

x 0

y 0

isDestructable [checkbox]

image [clickable cell]


The image value should allow the user to click the cell, which will then bring up a JFileChooser, and extract the response and render a String representation inside the cell. I have numerous snippets of code written, but trying to link them together is the bit that I am really lost on. Below I have provided the code I have so far relating to these functions:

/*
Code that creates the table and passes a custom model to it. Code for setting the other
types of renderers and editors I have already got working have been omitted
*/
table = new JTable(new MyDataModel(columnNames, values))
{
    public TableCellRenderer getCellRenderer(int row, int col)
    {
        if(col == 1)
        {
            if(table.getModel().getValueAt(row, 0).equals("image"))
            {
                //This is where I would want to set my renderer I believe
            }
        }

        return super.getCellRenderer(row, col);
    }

    public TableCellEditor getCellEditor(int row, int col)
    {
        if(col == 1)
        {
            if(table.getModel().getValueAt(row, 0).equals("image"))
            {
                //This is where I would want to set my editor I believe
            }
         }

         return super.getCellEditor(row, col);
     }
};

/*
As an inner class, I have created the following editor that should display a JFileChooser
when the user clicks on the cell
*/
private class ButtonTableCellEditor extends AbstractTableCellEditor implements TableCellEditor
{
    private JFrame mainframe;
    private JFileChooser jfc;

    public ButtonTableCellEditor(JFrame frame)
    {
        mainframe = frame;
        jfc = new JFileChooser();
    }

    @Override
    public void addCellEditorListener(CellEditorListener arg0)
    {

    }

    @Override
    public void cancelCellEditing()
    {
        //Hide the JFileChooser here
        super.cancelCellEditing();
    }

    @Override
    public Object getCellEditorValue()
    {
        //Here we need to return the filename that has been returned by the JFileChooser
        return "test";
    }

    @Override
    public boolean isCellEditable(EventObject arg0)
    {
        return true;
    }

    @Override
    public void removeCellEditorListener(CellEditorListener arg0)
    {
        //Not sure that I will actually need to use this at all
    }

    @Override
    public boolean shouldSelectCell(EventObject arg0)
    {
        int returned = jfc.showOpenDialog(mainFrame);

        if(returned == JFileChooser.APPROVE_OPTION)
        {
            System.out.println("File was fine");
        }
        else
        {
            stopCellEditing();
        }

        return true;
    }

    public boolean stopCellEditing()
    {
        return false;
    }

    public Component getTableCellEditorComponent(JTable table, Object value, boolean arg2, int row, int col)
    {
         //This is something I know that is incredibly important based off other examples, but
         //I am unsure what to do in here in my case
         return null;
     }
}

All I really need help with (I think) is getting the ButtonTableCellEditor linked with the single cell I require (condition is if the left hand column equals "image", and what to do with the getTableCellEditorComponent() in my situation. Any help would be hugely appreciated, I have been stuck for a little while now.

Also, this is my first question (I have read numerous questions, and tried to take as much note as possible), so if there is any feedback in the way that I have posed the question so that later questions may be better structured, that would be most welcome. Thanks all!

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Stevo
  • 397
  • 6
  • 26
  • Why not use [JTable#setDefaultRenderer](https://docs.oracle.com/javase/7/docs/api/javax/swing/JTable.html#setDefaultRenderer(java.lang.Class,%20javax.swing.table.TableCellRenderer)) ... Extending the class just to customise the renderer/editors like this is ... kinda annoying .... – MadProgrammer Dec 06 '14 at 03:54
  • Don't return null from getTableCellEditorComponent, return the component you displayed on the table... – MadProgrammer Dec 06 '14 at 03:55
  • I did have `table.setDefaultRenderer(JComboBox.class, new ComboRenderer());` to set the rendering of my JComboBox but I'm not sure that I want the "button" to render as a button. I think it appearing as a String should be sufficient; it is mainly the functionality that I am focusing on (for now). In regards to the null return in getTableCellEditorComponent, that was just a placeholder so that the code compiled. Do you think that this is necessary if I decide to simply intercept the event in which the cell is clicked? Is that a viable way of doing things? And thank you for the quick response! – Stevo Dec 06 '14 at 04:53
  • Instead of putting the "action" in the table, you could use a `ListSelectionListener` on the table, which could change the state of other compoents in the UI – MadProgrammer Dec 06 '14 at 05:24
  • I have referred to this question: [Intercept JTable selection change events](http://stackoverflow.com/questions/3059489/intercept-jtable-selection-change-events) and found in the answer where the person is overriding changeSelection. If I were to do this and check to see if the right hand column had been clicked, and the left hand column contained the required String, I could then display the JFileChooser. I could then update the cell with the returned file name. I don't see why this wouldn't work now, but is this a good design? And thank you for pointing me towards a `ListSelectionListener`. – Stevo Dec 06 '14 at 05:48

1 Answers1

0

Based on the answer provided in Intercept JTable Selection ChangeEvents, and the extremely useful direction by MadProgrammer to look into ListSelectionListeners, I have been able to come up with the following solution:

table = new JTable(new CustomModel(columnNames, values))
{
    /*
    I have omitted the other functions I have overridden here as they are not necessary for the
    solution*/

    @Override
    public void changeSelection(int row, int col, boolean toggle, boolean extend)
    {
        if(col == 1 && table.getModel().getValueAt(row, 0).equals("image"))
        {
            /*
            imageChooser is the name of the JFileChooser I am using, and I am passing the
            main frame of my application to centre the window that appears
            */
            int returned = imageChooser.showOpenDialog(frame);

            if(returned == JFileChooser.APPROVE_OPTION)
                table.getModel().setValueAt(imageChooser.getSelectedFile().getName(), row, col);
        }

        super.changeSelection(row, col, toggle, extend);
    }
}

I do not believe this solution is very scalable, but for the purpose of my program, it works extremely well. I have successfully been able to open the JFileChooser whenever the appropriate cell is clicked, and have the String inside the cell change to represent the name of the file that was selected.

The cell essentially acts as a button without being rendered as one. Depending on the UI appearance requirements, this may not always be appropriate as it does simply look like an ordinary table cell, potentially leading to Usability issues.

EDIT:

A more scalable approach is to determine the type of the value in column 1 with something like "instanceof", as this will apply to all objects of that type.

Community
  • 1
  • 1
Stevo
  • 397
  • 6
  • 26