在重构我的应用程序时,我开始为每个应用程序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,但正如所说,这是出于演示目的!在我的应用程序中,给出的默认策略根本不需要。
答案 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);