使用FocusTraversalPolicy时,JSpinner AutoSelect不起作用 [英] JSpinner AutoSelect not working when using FocusTraversalPolicy

查看:396
本文介绍了使用FocusTraversalPolicy时,JSpinner AutoSelect不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在重新构建我的应用程序时,我开始为每个应用程序JPanel添加FocusTraversalPolicies。在那期间我注意到一些奇怪的事使用@MadProgrammers为JSpinners实现一个FocusListener来自动选择它的文本(可以在这里找到),我想成为能够通过一系列的JSpinners制表。



作为一个MCVE(必须要很长时间才能显示出问题并包含所有需要的东西),我制作了一个小程序,我正面临的问题:

$ p $ 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);



类MyJPanel继承了JPanel {
private static final SelectOnFocusgainedHandler FOCUSHANDLERINSTANCE = new SelectOnFocusgainedHandler();
ArrayList< JSpinner> spinners = new ArrayList<>();
JButton addTF = new JButton(Add TextField); (jsp);
FocusTraversalPolicy ftp = new FocusTraversalOnArray(())
public MyJPanel(){
addTF.addActionListener((list) - > {
for(JSpinner jsp:spinners) (JSpinner jsp:spinners){
add(jsp));

spinners.add(new JSpinner());
(new JSpinner [0])) ;
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>列表与LT; T> findAllChildren(JComponent组件,Class< T> clazz){
List< T> lstChildren = new ArrayList<>(5); (Component Comp:component.getComponents()){
if(clazz.isInstance(comp)){
lstChildren.add((T)comp);
} else if(comp instanceof JComponent){
lstChildren.addAll(findAllChildren((JComponent)comp,clazz));
}
}
返回Collections.unmodifiableList(lstChildren);



class SelectOnFocusgainedHandler扩展了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 Runnable(){
$ b $ @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();;




$ b 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;
下一个返回;

private组件循环(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循环;
}
}
}
int initialIndex = index;
while(true){
int newIndex = indexCycle(index,delta);
if(newIndex == initialIndex){
break;
}
index = newIndex;
//
组件组件= m_Components [newIndex]; $()b $ b 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);






$ b

这是以下两行导致Listener正常工作的意义,当至少有一行出现时,focusGained方法根本不执行(通过简单的命令行输出测试):

  setFocusCycleRoot(真); 
setFocusTraversalPolicyProvider(true);

然而,根据容器的实现,当两个属性都设置为时,我只能使用FocusTraversalPolicy true:

从容器源代码片段:

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

// ...
}

有两种方法可以同时使用它们吗?或者两者中的任何一种都有可能通过Spinners选项卡并同时自动选择它们?

注意: I我知道上面的程序完全不需要FocusTraversalPolicy,但是据说这是为了示范的目的!在我的应用程序中,给定的默认策略并不是什么都需要的。
解决方案

呵呵....

p>

这里的问题与我以前所期望的完全不同... MadProgrammer的focusListener工作得非常好,设置了在TraversalPolicy中使用正确组件的情况。

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



这意味着,而不是 ArrayList< JSpinner> 来创建FocusTraversalPolicy我使用了一个额外的 ArrayList< JFormattedTextField> ,我填充了遍历 JSpinner的foreach循环与以下内容:
$ b $ $ pre $ spinnameTextFields.add(((JSpinner.DefaultEditor)jsp.getEditor() ).getTextField());

以及之后的循环:

<$ p $焦点转换策略ftp = new FocusTraversalOnArray(spinnersTextFields.toArray(new Component [0]));


While restructuring my application, I began to add FocusTraversalPolicies to each of my Applications JPanels. During that I noticed something odd. Using @MadProgrammers implementation of a FocusListener for JSpinners to autoselect the text of it (can be found here), I wanted to be able to tab through a series of JSpinners.

As an MCVE (had to be that long to show the problem and include everything needed) I made up a smaller program showing the problem I am facing:

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);
    }
}

Thing is that the following two lines cause the Listener not to work properly anymore meaning, that the focusGained method is not executed at all (tested by simple command-line-output) when at least one of the lines is present:

setFocusCycleRoot(true);
setFocusTraversalPolicyProvider(true);

However according to the implementation of the Container, I can only use the FocusTraversalPolicy when both attributes are set to true:

Snippet from the source of Container:

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

Is there a way to use both of them at the same time? Or any alternative to each of both to have the possibility to tab through the Spinners and get them autoselected at the same time?

Note: I am aware that above program does not need a FocusTraversalPolicy at all, but as said, it is for demonstrational purpose! In my application the default Policy given, is not at all, what is needed.

解决方案

Oh well....

The problem here was a completely different one than previously expected by me... The focusListener by MadProgrammer is working perfectly fine, set the case that one uses the correct components in the TraversalPolicy.

Obviously (now that I know it) the TextField of the JSpinner is focused and not the spinner in total. So using the respective TextFields in the FocusTraversalOnArray instead of the JSpinners lead to the correct and desired behaviour.

Meaning that instead of the ArrayList<JSpinner> to create the FocusTraversalPolicy I used an additional ArrayList<JFormattedTextField> which I filled in the foreach loop that iterates over the JSpinner with the following:

spinnersTextFields.add(((JSpinner.DefaultEditor) jsp.getEditor()).getTextField());

and after that loop:

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

这篇关于使用FocusTraversalPolicy时,JSpinner AutoSelect不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆