Jtable editabile con celle JtextField - problemi di Focus con altri componenti

di il
3 risposte

Jtable editabile con celle JtextField - problemi di Focus con altri componenti

Buongiorno a tutti, sono nuovo del forum.
Spero qualcuno di voi possa darmi una mano su un problema che mi porto dietro da tempo e di cui non riesco a trovare soluzione ammesso che alla fine non sia un bug di java...
Spiego il progetto Java:

Il problema risiede nell'interfaccia che è composta da:
- JInternalFrame con all'interno
- JTabbedPane con all'interno
- Componenti quali JtextField, JPanel con all'interno JTable, ComboBox etc
Lo spostamento da un componente all'altro, usando la tastiera, è gestito da una classe FocusTraversalPolicy legata al JTabbedPane;
in questa classe si gestisce l'avanzamento di componente in componente con i tasti TAB e ShiftTAB;
tutto funziona meravigliosamente pure nel caso di componente Jtable se non si è ancora in Edit di cella.
Preciso che al componente Jtable ho disattivato i tasti Tab e ShiftTAB perchè non siano utilizzati per movimenti tra celle e possono essere utilizzati per passaggio da JTable a componente successivo o precedente al JTable stesso.

Queste le istruzioni che disattivano i 2 tasti come spostamento celle nel JTable
ActionMap myTableActionMap = myTable.getActionMap();
myTableActionMap.put("selectPreviousColumnCell", new myPreviousFocusHandler());
myTableActionMap.put("selectNextColumnCell", new myNextFocusHandler());
public class myPreviousFocusHandler extends AbstractAction
{
public void actionPerformed(ActionEvent evt)
{
KeyboardFocusManager myManager = KeyboardFocusManager.getCurrentKeyboardFocusManage r();
myManager.focusPreviousComponent();
}
}
public class myNextFocusHandler extends AbstractAction
{
public void actionPerformed(ActionEvent evt)
{
KeyboardFocusManager myManager = KeyboardFocusManager.getCurrentKeyboardFocusManage r();
myManager.focusNextComponent();
}
}


Il problema si manifesta quando sono in edit di cella del JTable (componente JTextField) e premo il tasto ShiftTab.
Non si comporta come mi aspetterei e soprattutto si comporta in modo diverso rispetto a come si comporta avendo premuto il tasto TAB.
Nel caso di edit di cella (JTextField) nel JTable se si preme il tasto TAB il programma si comporta come è nelle mie attese:
- Passa il controllo al FocusTraversalPolicy del JtabbedPane
- Passa da StopCellEditing della cella in edit
- Passa da getCellEditorValue della cella in edit
- Per debug passa pure da un FocusLost di un Listener sulla JTable dove si rileva che il prossimo componente è quello corretto a cui passa il focus
- Attiva il focus sul componente successivo
Nel caso di edit di cella (JTextField) nel JTable se si preme il tasto ShiftTAB il programma si comporta come non è nelle mie attese e diversamente dal tasto TAB:
- NON Passa il controllo al FocusTraversalPolicy del JtabbedPane
- NON Passa da StopCellEditing della cella in edit
- NON Passa da getCellEditorValue della cella in edit
- Passa invece da FocusLost di un Listener sulla JTable dove si rileva che il prossimo componente è lo stesso JTextField che gestisce l'edit di cella.
- Rimane attiva la stessa cella della JTable in uno stato non si capisce se di edit o meno.

Spero di essere stato chiaro
L'intero sorgente è troppo esteso per poter essere pubblicato ma non ho problemi a riportarne i pezzi che possono interessare la discussione.

3 Risposte

  • Re: Jtable editabile con celle JtextField - problemi di Focus con altri componenti

    Buongiorno
    Ripropongo un estratto del problema che ho esposto nel primo post sperando che con un esempio pratico qualcuno mi possa dare una mano

    Facendo girare l'esempio (trascurato ma funzionale a dimostrare il mio problema) si notano in console i passaggi nel focustraversalpolicy

    Prova1: si preme semplicemente TAB o ShiftTab
    In console si vedono i passaggi nei metodi getComponentAfter e getComponentBefore; in particolare si vede che, non essendo le celle in edit, nel caso del secondo componente, viene ritornato il componente JScrollPane. Va bene cosi

    Prova2: si clicca su una cella JTable, si scrive qualcosa (per entrare in edit) e si preme TAB
    In console si vede il passaggio nel metodo getComponentAfter ; in particolare si vede che, avendo il focus la JTable, viene ritornato il componente JTable. Va bene cosi

    Prova3: si clicca su una cella JTable, si scrive qualcosa (per entrare in edit) e si preme ShiftTAB
    Non succede niente almeno a livello di focustraversalpolicy
    Se si preme ancora ShiftTab
    In console si vede il passaggio nel metodo getComponentBefore ; in particolare si vede che viene ritornato il componente JTextField usato per l'edit della cella!!!!
    
    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.FocusTraversalPolicy;
    import java.awt.KeyboardFocusManager;
    import java.awt.event.ActionEvent;
    
    
    import javax.swing.AbstractAction;
    import javax.swing.ActionMap;
    import javax.swing.DefaultCellEditor;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JRootPane;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.JTextField;
    import javax.swing.SwingUtilities;
    
    public class Prova2
    {
        public static final Double VideoDefaultFontIncrementWidth = 0.92;
        public static final Double VideoDefaultFontIncrementHeight = 1.5;
        
        public static void main(String[] args)
        {
            SwingUtilities.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {
                    new Prova2();
                }
            });
        }
    
    
        public Prova2()
        {
            JFrame frame = new JFrame("Prova");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            //
            Object[] columnHeaders = {"Codice", "Nome", "Prezzo", "Data", "Noedit"};
            Object rows[][] = { 
                { "A", "About", 44.36, null, "aa" }, 
                { "B", "Boy", 44.84, null, "aa" }, 
                { "C", "Cat", 463.63, null, "aa" }, 
                { "D", "Day", 27.14, null, "aa" }, 
                { "E", "Eat", 44.57, null, "aa" }, 
                { "F", "Fail", 23.15, null, "aa" }, 
                { "G", "Good", 4.40, null, "aa" }, 
                { "H", "Hot", 24.96, null, "aa" }, 
                { "I", "Ivey", 5.45, null, "aa" }, 
                { "J", "Jack", 49.54, null, "aa" }, 
                { "K", "Kids", 280.00, null, "aa" }, 
                { "2A", "About", 44.36, null, "aa" }, 
                { "2B", "Boy", 44.84, null, "aa" }, 
                { "2C", "Cat", 463.63, null, "aa" }, 
                { "2D", "Day", 27.14, null, "aa"}, 
                { "2E", "Eat", 44.57, null, "aa" }, 
                { "2F", "Fail", 23.15, null, "aa" }, 
                { "2G", "Good", 4.40, null, "aa" }, 
                { "2H", "Hot", 24.96, null, "aa" }, 
                { "2I", "Ivey", 5.45, null, "aa" }, 
                { "2J", "Jack", 49.54, null, "aa" }, 
                { "2K", "Kids", 280.00, null, "aa" } };
            //
            // text 1
            JTextField Text1 = new JTextField("");
            Text1.setName("Text1");
            frame.add(Text1, BorderLayout.NORTH);
            //
            // JTable soluzione 2
            JTable myTable = new JTable(rows, columnHeaders);
            myTable.setName("Table");
            myTable.setFillsViewportHeight(true);
            myTable.putClientProperty("autoStartsEdit", true);
            myTable.putClientProperty("terminateEditOnFocusLost", true);
            ////myTable.setAutoCreateColumnsFromModel(false);
            ////myTable.getSelectionModel().addListSelectionListener(new myListSelectionHandler());
            ////myTable.addFocusListener(new myTableFocusListener());
                // disabilita CtrlTab e CtrlShiftTab come uscita di campo jTable
                // che tornano ad essere sentiti come traversal focus del tabbedpane per tab successivo e precedente
            ////myTable.setFocusTraversalKeysEnabled(false);
                // disabilita Tab e ShiftTab come spostamento tra celle
                // che tornano ad essere sentiti come traversal focus del tabbedpane per campo successivo e precedente
            ActionMap myTableActionMap = myTable.getActionMap();
            myTableActionMap.put("selectPreviousColumnCell", new myPreviousFocusHandler());
            myTableActionMap.put("selectNextColumnCell", new myNextFocusHandler());
            for (int i = 0; i < myTable.getColumnCount()-1; i++)
            {
                JTextField myTextField = new JTextField();
                myTable.getColumn(columnHeaders[i]).setCellEditor(new DefaultCellEditor(myTextField));
            }
            JScrollPane pane = new JScrollPane(myTable);
            frame.add(pane, BorderLayout.CENTER);        
            //
            // text2
            JTextField Text2 = new JTextField("");
            Text2.setName("Text1");
            frame.add(Text2, BorderLayout.SOUTH);
            //
            // frame
            frame.setFocusTraversalPolicy(new MyFocusTraversalPolicy());
            frame.setSize(800, 400);
            frame.setVisible(true);
        }
        
        public static class MyFocusTraversalPolicy extends FocusTraversalPolicy
        {
            @Override
            public Component getComponentAfter(Container arg0, Component arg1)
            {
                System.out.println("getComponentAfter: "+arg1.toString());
                JRootPane myRootPane=(JRootPane)arg0.getComponent(0);
                JPanel myPanel = (JPanel) myRootPane.getLayeredPane().getComponent(0);
                if (arg1 instanceof JPanel == true)
                {
                    return getFirstComponent(arg0);
                }
                int myNewPosition = 0;
                myNewPosition = myComponentPosition(myPanel, arg1);
                myNewPosition = myComponentNext(myPanel, myNewPosition, 1);
                if (myNewPosition > -1)
                {
                    return myComponent(myPanel, myNewPosition);
                }
                else
                {
                    return getFirstComponent(arg0);
                }
            }
            @Override
            public Component getComponentBefore(Container arg0, Component arg1)
            {
                System.out.println("getComponentBefore: "+arg1.toString());
                JRootPane myRootPane=(JRootPane)arg0.getComponent(0);
                JPanel myPanel = (JPanel) myRootPane.getLayeredPane().getComponent(0);
                if (arg1 instanceof JPanel == true)
                {
                    return getFirstComponent(arg0);
                }
                int myNewPosition = 0;
                myNewPosition = myComponentPosition(myPanel, arg1);
                myNewPosition = myComponentNext(myPanel, myNewPosition, -1);
                if (myNewPosition > -1)
                {
                    return myComponent(myPanel, myNewPosition);
                }
                else
                {
                    return getLastComponent(arg0);
                }
            }
            @Override
            public Component getDefaultComponent(Container arg0)
            {
                System.out.println("getDefaultComponent: ");
                JRootPane myRootPane=(JRootPane)arg0.getComponent(0);
                JPanel myPanel = (JPanel) myRootPane.getLayeredPane().getComponent(0);
                return myPanel.getComponent(0);
            }
            @Override
            public Component getFirstComponent(Container arg0)
            {
                System.out.println("getFirstComponent: ");
                JRootPane myRootPane=(JRootPane)arg0.getComponent(0);
                JPanel myPanel = (JPanel) myRootPane.getLayeredPane().getComponent(0);
                return myPanel.getComponent(0);
            }
            @Override
            public Component getLastComponent(Container arg0)
            {
                System.out.println("getLastComponent: ");
                JRootPane myRootPane=(JRootPane)arg0.getComponent(0);
                JPanel myPanel = (JPanel) myRootPane.getLayeredPane().getComponent(0);
                return myPanel.getComponent(myPanel.getComponentCount()-1);
            }
            // ritorna la posizione del componente dato
            private int myComponentPosition(Container parm_Container, Component parm_Component)
            {
                for (int i = 0; i < parm_Container.getComponentCount(); i++)
                {
                    if (parm_Container.getComponent(i) == parm_Component)
                    {
                        return i;
                    }
                }
                return -1;
            }
            // ritorna la posizione del componente successivo o precedente rispetto al dato
            private int myComponentNext(Container parm_Container, int parm_Position, int parm_Increment)
            {
                for (int i = parm_Position + parm_Increment; i > -1 && i < parm_Container.getComponentCount(); i = i + parm_Increment)
                {
                    if (parm_Container.getComponent(i).isEnabled() && parm_Container.getComponent(i).isFocusable())
                    {
                        return i;
                    }
                }
                return -1;
            }
    
    
            // ritorna il componente alla posizione
            private Component myComponent(Container parm_Container, int parm_Position)
            {
                return parm_Container.getComponent(parm_Position);
            }
        }
        
        
        public class myPreviousFocusHandler extends AbstractAction
        {
            private static final long serialVersionUID = 1L;
    
    
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                KeyboardFocusManager myManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
                myManager.focusPreviousComponent();
            }
        }
    
    
        public class myNextFocusHandler extends AbstractAction
        {
            private static final long serialVersionUID = 1L;
    
    
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                KeyboardFocusManager myManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
                myManager.focusNextComponent();
            }
    
    
        }
        
    }
    
  • Re: Jtable editabile con celle JtextField - problemi di Focus con altri componenti

    PaoloDaPrato ha scritto:


    Ripropongo un estratto del problema che ho esposto nel primo post sperando che con un esempio pratico qualcuno mi possa dare una mano
    Ecco, con un esempio di codice che riproduce la questione si può sicuramente capire meglio il problema. Lo guardo e provo meglio appena posso in giornata.
  • Re: Jtable editabile con celle JtextField - problemi di Focus con altri componenti

    Buongiorno.
    Nessuno riesce a darmi una mano?
    Grazie
Devi accedere o registrarti per scrivere nel forum
3 risposte