Note that there are some explanatory texts on larger screens.

plurals
  1. POjTabbedPane transfer focus to next sheet
    text
    copied!<h2>Problem Description</h2> <p>I am currently writing an application used to manually copy data from a sheet of paper to a database. This application has plenty of widgets where the user can enter the data. To keep the UI somewhat tidy, I decided to use a tabbed pane, separating the entry fields into logical units.</p> <p>The most important feature of the application is that is should be usable by keyboard alone. So you should be able to switch tabs with keystrokes. By default this is possible using <code>CTRL+PgUp/PgDown</code>. But, as an additional convenience, I would like to activate the next tab as soon as the user transfers focus <em>out of</em> the last widgets on the current tab.</p> <p>So, if the user has the focus on the last text field, and presses tab, I'd like to activate the next tab, and put the focus on the first widget therein.</p> <p>To solve this, I marked the jTabbedPane as <code>focusCycleRootProvider</code> and added a custom <code>FocusTraversalPolicy</code>. My current problem is this: As soon as I programatically activate the next tab (using <code>setSelectedIndex</code>), which happens in the <code>getComponentAfter</code> method, the method <code>getComponentAfter</code> is executed a second time. This defeats my current logic. I cannot seem to find a way to prevent this from happening. Any ideas?</p> <p>In the example below, you will see an <code>ArrayIndexOutOfBoundsException</code>. This happens because <code>getComponentAfter</code> is called twice. Once on the first tab, and once on the second tab. But twice with the same component as argument. This means, that the second time, the for-loop won't find a matching component, so the counter <code>i</code> will be as large as there are components <em>in the second tab</em> + 1. This causes the exception.</p> <h2>Test Executable</h2> <pre><code>/* * TestFrame.java * * Created on Apr 18, 2011, 4:37:52 PM */ package testrun; import java.awt.Component; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; /** * * @author malbert */ public class TestFrame extends javax.swing.JFrame { /** Creates new form TestFrame */ public TestFrame() { initComponents(); jTabbedPane1.setFocusTraversalPolicyProvider(true); jTabbedPane1.setFocusTraversalPolicy(new EasyTabberFocusTraversalPolicy(jTabbedPane1)); jTabbedPane1.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { super.focusGained(e); jTabbedPane1.setSelectedIndex(0); Component ca = jTabbedPane1.getFocusTraversalPolicy().getFirstComponent(jTabbedPane1); if (ca != null) { ca.requestFocusInWindow(); } } }); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // &lt;editor-fold defaultstate="collapsed" desc="Generated Code"&gt;//GEN-BEGIN:initComponents private void initComponents() { jTabbedPane1 = new javax.swing.JTabbedPane(); jPanel1 = new javax.swing.JPanel(); jTextField2 = new javax.swing.JTextField(); jTextField3 = new javax.swing.JTextField(); jPanel2 = new javax.swing.JPanel(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jTextField4 = new javax.swing.JTextField(); jTextField1 = new javax.swing.JTextField(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); jTextField2.setText("jTextField2"); jTextField3.setText("jTextField3"); javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(289, Short.MAX_VALUE)) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTextField3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(168, Short.MAX_VALUE)) ); jTabbedPane1.addTab("tab1", jPanel1); jButton1.setText("jButton1"); jButton2.setText("jButton2"); jTextField4.setText("jTextField4"); javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addComponent(jButton1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jButton2)) .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap(165, Short.MAX_VALUE)) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel2Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jButton1) .addComponent(jButton2)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTextField4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(162, Short.MAX_VALUE)) ); jTabbedPane1.addTab("tab2", jPanel2); jTextField1.setText("jTextField1"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE) .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 251, Short.MAX_VALUE) .addContainerGap()) ); pack(); }// &lt;/editor-fold&gt;//GEN-END:initComponents /** * @param args the command line arguments */ public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new TestFrame().setVisible(true); } }); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JTabbedPane jTabbedPane1; private javax.swing.JTextField jTextField1; private javax.swing.JTextField jTextField2; private javax.swing.JTextField jTextField3; private javax.swing.JTextField jTextField4; // End of variables declaration//GEN-END:variables } </code></pre> <h2>Traversal Policy</h2> <pre><code>package testrun; import java.awt.Component; import java.awt.Container; import javax.swing.JTabbedPane; import javax.swing.LayoutFocusTraversalPolicy; /** * * @author malbert */ public class EasyTabberFocusTraversalPolicy extends LayoutFocusTraversalPolicy { private final JTabbedPane container; private int currentTab = 0; public EasyTabberFocusTraversalPolicy(JTabbedPane container) { this.container = container; } @Override public Component getComponentAfter(Container aContainer, Component aComponent) { System.out.println("after " + aComponent); Component comp = container.getComponentAt(currentTab); if (Container.class.isInstance(comp)) { Component[] components = ((Container) comp).getComponents(); int i = 0; for (i = 0; i &lt; components.length; i++) { if (!components[i].isEnabled() || !components[i].isFocusable()) { continue; } if (components[i].equals(aComponent)) { break; } } if (i == components.length - 1) { // we reached the end. Go to the next tab! currentTab = currentTab + 1; Component fc = firstComponentInCurrentTab(); activateTab(currentTab); return fc; } else { return components[i + 1]; } } else { return comp; } } @Override public Component getComponentBefore(Container aContainer, Component aComponent) { System.out.println("before"); return super.getComponentBefore(aContainer, aComponent); } @Override public Component getFirstComponent(Container aContainer) { System.out.println("first"); return firstComponentInCurrentTab(); } @Override public Component getLastComponent(Container aContainer) { System.out.println("last"); return lastComponentInCurrentTab(); } private Component firstComponentInCurrentTab() { Component comp = container.getComponentAt(currentTab); if (comp instanceof Container) { Component[] components = ((Container) comp).getComponents(); if (components.length == 0) { return null; } return components[0]; } else { return comp; } } private Component lastComponentInCurrentTab() { Component comp = container.getComponentAt(currentTab); if (comp instanceof Container) { Component[] components = ((Container) comp).getComponents(); if (components.length == 0) { return null; } return components[components.length - 1]; } else { return comp; } } private void activateTab(int index) { // wrap around if (index &lt; 0) { index = container.getTabCount() - 1; } else if (index &gt; container.getTabCount() - 1) { index = 0; } currentTab = index; container.setSelectedIndex(index); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload