Search code examples
javaswingjtablekey-bindings

How to exclude columns from tab key navigation?


By pressing tab the focus is moved to the next cell. I'd like to change this behavior, so that certains columns are excluded from tab key navigation. Let's say a table consists of 5 columns, then only the columns 1 and 3 should be considered for navigation. From what I've read FocusTraversalPolicy is used for this purpose. However, implementing this behaviour seems rather complicated as no column and row indicides are provided. So how do I return the correct Components?

public class Table extends JTable{
int columnCount = 5;
int[] tab = { 1, 3 };  
    public Table(){
        ...
        this.setFocusTraversalPolicy(new FocusTraversalPolicy() {

        @Override
        public Component getLastComponent(Container arg0) {
             return null;
        }

        @Override
        public Component getFirstComponent(Container arg0) {
            return null;
        }

        @Override
        public Component getDefaultComponent(Container arg0) {
            return null;
        }

        @Override
        public Component getComponentBefore(Container arg0, Component arg1) {
            return null;
        }

        @Override
        public Component getComponentAfter(Container arg0, Component arg1) {
            return null;
        }
    }); 
    } 
}

Solution

  • From what I've read FocusTraversalPolicy is used for this purpose

    Table columns are not real components so the FocusTraversalPolicy has no meaning once a table gains focus. JTable provides Actions to move from cell to cell.

    You might be able to use the concepts found in Table Tabbing. For example:

    public class SkipColumnAction extends WrappedAction
    {
        private JTable table;
        private Set columnsToSkip;
    
        /*
         *  Specify the component and KeyStroke for the Action we want to wrap
         */
        public SkipColumnAction(JTable table, KeyStroke keyStroke, Set columnsToSkip)
        {
            super(table, keyStroke);
            this.table = table;
            this.columnsToSkip = columnsToSkip;
        }
    
        /*
         *  Provide the custom behaviour of the Action
         */
        public void actionPerformed(ActionEvent e)
        {
            TableColumnModel tcm = table.getColumnModel();
            String header;
    
            do
            {
                invokeOriginalAction( e );
    
                int column = table.getSelectedColumn();
                header = tcm.getColumn( column ).getHeaderValue().toString();
            }
            while (columnsToSkip.contains( header ));
        }
    }
    

    To use the class you would do:

    Set<String> columnsToSkip = new HashSet<String>();
    columnsToSkip.add("Column Name ?");
    columnsToSkip.add("Column Name ?");
    new SkipColumnAction(table, KeyStroke.getKeyStroke("TAB"), columnsToSkip);
    new SkipColumnAction(table, KeyStroke.getKeyStroke("shift TAB"), columnsToSkip);
    

    The key point is that you have to replace the default tabbing Action of the table with one of your own.