How to define the order in which components will receive focus through TAB?

Asked

Viewed 844 times

6

To organize the component focus selection order up to version 1.4 of java, we used the method setNextFocusableComponent(). However, it was discontinued from the aforementioned version.

I would like to know how to do in the latest versions to control the focus order of components on the screen through the key TAB. For example, the code below has 4 distinct components, and they get focus on the order that were added on the screen:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class NextFocusTest extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel contentPane;
    private JPanel panel;
    private JTextField firstComp;
    private JComboBox<String> thirdComp;
    private JTextField secondComp;
    private JButton fourthComp;

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            NextFocusTest frame = new NextFocusTest();
            frame.setVisible(true);
        });
    }

    public NextFocusTest() {
        initComponents();
    }

    private void initComponents() {

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(350, 200));
        this.contentPane = new JPanel();
        this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        this.contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(this.contentPane);

        this.panel = new JPanel();
        this.panel.setBorder(new EmptyBorder(0, 20, 0, 20));
        this.panel.setLayout(new GridLayout(2, 2, 20, 5));
        this.contentPane.add(this.panel, BorderLayout.NORTH);

        this.firstComp = new JTextField();
        this.firstComp.setPreferredSize(new Dimension(100, 20));
        this.panel.add(this.firstComp);

        this.thirdComp = new JComboBox<String>();
        this.thirdComp.setModel(new DefaultComboBoxModel<String>(new String[] { "teste 1", "teste 2", "teste 3" }));
        this.thirdComp.setPreferredSize(new Dimension(100, 20));
        this.panel.add(this.thirdComp);

        this.secondComp = new JTextField();
        this.secondComp.setPreferredSize(new Dimension(100, 20));
        this.panel.add(this.secondComp);

        this.fourthComp = new JButton("OK");
        this.fourthComp.setPreferredSize(new Dimension(90, 23));
        this.panel.add(this.fourthComp);

        pack();
    }
}

Alternating the focus with TAB:

inserir a descrição da imagem aqui

How do I manually set the component focus order, for example by following the order below?

  • first text field
  • second text field
  • combobox
  • button
  • 1

    Have you tried the method setNextFocusableComponent. Take an example: firstComp.setNextFocusableComponent(secondComp);

  • @acklay right at the beginning of the question I quote him, is discontinued.

  • 1

    haha... just read the title. xD mals

1 answer

4


Use the class FocusTraversalPolicy to define the order of focus of the components of a container. To do so, extend the class FocusTraversalPolicy, implement the policy of focus and attribute using setFocusTraversalPolicy. The code based on your example would look something like this:

import java.awt.*;
import java.util.LinkedList;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class NextFocusTest extends JFrame {

  private static final long serialVersionUID = 1L;
  private JPanel contentPane;
  private JPanel panel;
  private JTextField firstComp;
  private JComboBox<String> thirdComp;
  private JTextField secondComp;
  private JButton fourthComp;

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      NextFocusTest frame = new NextFocusTest();
      frame.setVisible(true);
    });
  }

  public NextFocusTest() {
    initComponents();
  }

  private void initComponents() {

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setPreferredSize(new Dimension(350, 200));
    this.contentPane = new JPanel();
    this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    this.contentPane.setLayout(new BorderLayout(0, 0));
    setContentPane(this.contentPane);

    this.panel = new JPanel();
    this.panel.setBorder(new EmptyBorder(0, 20, 0, 20));
    this.panel.setLayout(new GridLayout(2, 2, 20, 5));
    this.contentPane.add(this.panel, BorderLayout.NORTH);

    this.firstComp = new JTextField();
    this.firstComp.setPreferredSize(new Dimension(100, 20));
    this.panel.add(this.firstComp);

    this.thirdComp = new JComboBox<>();
    this.thirdComp.setModel(new DefaultComboBoxModel<String>(new String[]{"teste 1", "teste 2", "teste 3"}));
    this.thirdComp.setPreferredSize(new Dimension(100, 20));
    this.panel.add(this.thirdComp);

    this.secondComp = new JTextField();
    this.secondComp.setPreferredSize(new Dimension(100, 20));
    this.panel.add(this.secondComp);

    this.fourthComp = new JButton("OK");
    this.fourthComp.setPreferredSize(new Dimension(90, 23));
    this.panel.add(this.fourthComp);

    pack();
    setFocusTraversalPolicy(new PoliticaFoco());
  }

  class PoliticaFoco extends FocusTraversalPolicy {

    private final java.util.List<Component> componentes;
    private int focado = 0;

    public PoliticaFoco() {
      this.componentes = new LinkedList<>();
      this.componentes.add(thirdComp);
      this.componentes.add(firstComp);
      this.componentes.add(fourthComp);
      this.componentes.add(secondComp);
    }

    @Override
    public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
      this.focado = (this.focado + 1) % this.componentes.size();

      return this.componentes.get(focado);
    }

    @Override
    public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
      this.focado = (this.componentes.size() + this.focado - 1) % this.componentes.size();

      return this.componentes.get(focado);
    }

    @Override
    public Component getDefaultComponent(Container focusCycleRoot) {
      return this.componentes.get(0);
    }

    @Override
    public Component getLastComponent(Container focusCycleRoot) {
      return this.componentes.get(this.componentes.size() - 1);
    }

    @Override
    public Component getFirstComponent(Container focusCycleRoot) {
      return this.componentes.get(0);
    }
  }
}

In the implementation of FocusTraversalPolicy, we implement the methods getComponentAfter, getComponentBefore, getDefaultComponent and getLastComponent that determine the component that will receive the focus in each of the situations. To control the order I used a List which receives the components in order to control the focus. The disadvantage is that for each component added, it will be necessary to include also in the implemented class.


You can also create the generic focus policy as follows:

import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.util.LinkedList;

public class PoliticaFocoGenerica extends FocusTraversalPolicy {

  protected final java.util.List<Component> componentes = new LinkedList<>();
  private int focado = 0;

  @Override
  public Component getComponentAfter(Container focusCycleRoot, Component aComponent) {
    this.focado = (this.focado + 1) % this.componentes.size();

    return this.componentes.get(focado);
  }

  @Override
  public Component getComponentBefore(Container focusCycleRoot, Component aComponent) {
    this.focado = (this.componentes.size() + this.focado - 1) % this.componentes.size();

    return this.componentes.get(focado);
  }

  @Override
  public Component getDefaultComponent(Container focusCycleRoot) {
    return this.componentes.get(0);
  }

  @Override
  public Component getLastComponent(Container focusCycleRoot) {
    return this.componentes.get(this.componentes.size() - 1);
  }

  @Override
  public Component getFirstComponent(Container focusCycleRoot) {
    return this.componentes.get(0);
  }
}

Only by defining the components in their respective orders, as follows:

class PoliticaFoco extends PoliticaFocoGenerica {

  public PoliticaFoco() {
    this.componentes.add(thirdComp);
    this.componentes.add(firstComp);
    this.componentes.add(fourthComp);
    this.componentes.add(secondComp);
  }
}

And in his container:

...
setFocusTraversalPolicy(new PoliticaFoco());
...
  • Do I really need to create a separate class? Don’t you have some container method that I can set to? Or be based on the layout manager?

  • 1

    @Negative article, after version 1.4 this method was discontinued and replaced by FocusTraversalPolicy. There’s even a tutorial based on the website of Oracle

  • I get it. Not too much, but is it possible to "generalize" this class? So I could repurpose anywhere without having to copy and add the components at hand.

  • @Article Yes, I put an example. But in reality the component you will always have to define, after all the goal is to change the order manually, no?

  • In the example, you used Linkedlist. This choice is optional (I can choose the Collection list I want) or there is some need to use this one specifically?

  • @Article is optional, including in the tutorial example of Oracle is used a array simple. I used the List for judging the implementation easier to understand

  • 2

    It worked! I decided to create a constructor in the focus policy class, so she could receive a list of components. Thank you :)

  • @Very interesting article to do this way, is more "closed", avoiding later errors!

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.