使用FocusTraversalPolicy

时间:2017-02-07 10:55:57

标签: java swing jpanel focus

在重构我的应用程序时,我开始为每个应用程序JPanel添加FocusTraversalPolicies。在那期间,我注意到了一些奇怪使用@MadProgrammers实现一个FocusListener for JSpinners来自动选择它的文本(can be found here),我希望能够通过一系列JSpinner进行制表。

作为一个MCVE(必须那么久才能显示问题并包含所需的一切)我制作了一个较小的程序来显示我面临的问题:

import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.FocusTraversalPolicy;
import java.awt.KeyboardFocusManager;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.*;
import javax.swing.text.JTextComponent;

public class testsforSO extends JFrame {

    private static final long serialVersionUID = 1977580061768232581L;
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                testsforSO frame = new testsforSO();
                frame.pack();
                frame.setSize(800, 300);
                frame.setVisible(true); 
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });
    }

    public testsforSO(){
        setContentPane(new JPanel());
        MyJPanel myPanel = new MyJPanel();
        getContentPane().add(myPanel);
    }
}

class MyJPanel extends JPanel{
    private static final SelectOnFocusgainedHandler FOCUSHANDLERINSTANCE = new SelectOnFocusgainedHandler();
    ArrayList<JSpinner> spinners = new ArrayList<>();
    JButton addTF = new JButton("Add TextField");
    public MyJPanel(){
        addTF.addActionListener((list) -> {
            for (JSpinner jsp : spinners) remove(jsp);
            FocusTraversalPolicy ftp = new FocusTraversalOnArray(spinners.toArray(new JSpinner[0]));

            spinners.add(new JSpinner());
            for (JSpinner jsp : spinners) {
                add(jsp);
                installFocusListener(jsp);
            }
            setFocusTraversalPolicy(ftp);
            setFocusCycleRoot(true);
            setFocusTraversalPolicyProvider(true);
            revalidate();
            repaint();
        });
        add(addTF);
    }

private void installFocusListener(JSpinner spinner) {

        List<JTextComponent> lstChildren = findAllChildren(spinner, JTextComponent.class);
        if (lstChildren != null && lstChildren.size() > 0) {
            JTextComponent editor = lstChildren.get(0);
            editor.addFocusListener(FOCUSHANDLERINSTANCE);
        }
    }

    private <T extends Component> List<T> findAllChildren(JComponent component, Class<T> clazz) {
        List<T> lstChildren = new ArrayList<>(5);
        for (Component comp : component.getComponents()) {
            if (clazz.isInstance(comp)) {
                lstChildren.add((T) comp);
            } else if (comp instanceof JComponent) {
                lstChildren.addAll(findAllChildren((JComponent) comp, clazz));
            }
        }
        return Collections.unmodifiableList(lstChildren);
    }
}

class SelectOnFocusgainedHandler extends FocusAdapter {
    @Override
    public void focusGained(FocusEvent e){
        System.out.println("FocusGained");
        Component comp = e.getComponent();
        if (comp instanceof JTextComponent){
            final JTextComponent textComponent = (JTextComponent) comp;
            new Thread (new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep(25);

                    } catch(InterruptedException e){
                    }
                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            System.out.println((KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner().equals(textComponent)));
                            textComponent.selectAll();
                        }
                    });
                }
            }).start();;
        }
    }
}


class FocusTraversalOnArray extends FocusTraversalPolicy {
    private final Component m_Components[];

        public FocusTraversalOnArray(Component components[]) {
        m_Components = components;
    }

    private int indexCycle(int index, int delta) {
        int size = m_Components.length;
        int next = (index + delta + size) % size;
        return next;
    }
    private Component cycle(Component currentComponent, int delta) {
        int index = -1;
        loop : for (int i = 0; i < m_Components.length; i++) {
            Component component = m_Components[i];
            for (Component c = currentComponent; c != null; c = c.getParent()) {
                if (component == c) {
                    index = i;
                    break loop;
                }
            }
        }
        int initialIndex = index;
        while (true) {
            int newIndex = indexCycle(index, delta);
            if (newIndex == initialIndex) {
                break;
            }
            index = newIndex;
            //
            Component component = m_Components[newIndex];
            if (component.isEnabled() && component.isVisible() && component.isFocusable()) {
                return component;
            }
        }
        return currentComponent;
    }
        public Component getComponentAfter(Container container, Component component) {
        return cycle(component, 1);
    }
    public Component getComponentBefore(Container container, Component component) {
        return cycle(component, -1);
    }
    public Component getFirstComponent(Container container) {
        return m_Components[0];
    }
    public Component getLastComponent(Container container) {
        return m_Components[m_Components.length - 1];
    }
    public Component getDefaultComponent(Container container) {
        return getFirstComponent(container);
    }
}

事情是,以下两行导致Listener不再正常工作,这意味着当至少有一行存在时,focusGained方法根本不执行(通过简单的命令行输出测试): / p>

setFocusCycleRoot(true);
setFocusTraversalPolicyProvider(true);

但是根据Container的实现,当两个属性都设置为true时,我只能使用FocusTraversalPolicy:

来自Container的代码段:

 public FocusTraversalPolicy getFocusTraversalPolicy() {
       if (!isFocusTraversalPolicyProvider() && !isFocusCycleRoot()) {
           return null;
       }
    //....
 }

有没有办法同时使用它们?或者两者都可以选择是否有可能通过Spinners并同时自动选择?

注意:我知道上面的程序根本不需要FocusTraversalPolicy,但正如所说,这是出于演示目的!在我的应用程序中,给出的默认策略根本不需要。

1 个答案:

答案 0 :(得分:0)

哦,好吧....

这里的问题与我之前预期的完全不同...... MadProgrammer的focusListener工作得很好,设置了一个在TraversalPolicy中使用正确组件的情况。

显然(现在我知道了)JSpinner的TextField是专注的,而不是整个微调器。因此,使用FocusTraversalOnArray中的相应TextField而不是JSpinner会导致正确和期望的行为。

这意味着我使用了ArrayList<JSpinner>而不是ArrayList<JFormattedTextField>来创建FocusTraversalPolicy,而是使用了JSpinner来填充foreach循环,该循环使用以下内容迭代spinnersTextFields.add(((JSpinner.DefaultEditor) jsp.getEditor()).getTextField());

FocusTraversalPolicy ftp = new FocusTraversalOnArray(spinnersTextFields.toArray(new Component[0]));

然后循环:

Intent intent = new Intent(context, MyActivity.class);
intent.putExtra(key, "my_value"); //Used to send information to action class
PendingIntent pi = PendingIntent.getActivity(context, 0, intent,
                                 PendingIntent.FLAG_UPDATE_CURRENT);