焦点所有者临时更改为null

时间:2011-09-10 14:13:36

标签: java swing focus

我对Swing开发很新,希望我的问题不是一个愚蠢的问题。

我遇到了以下问题。我正在使用KeyboardFocusManager跟踪焦点,监听属性permanentFocusOwner更改。但是,当焦点从一个控件更改为另一个控件时,我将permanentFocusOwner属性中间更改为null

当焦点位于其中一个面板或其子面板内时,我当前的UI逻辑正在对控件进行一些更改。但是,获得中间null打破了这种逻辑。

我在Google上搜索了有关此问题的信息,但未发现任何相关信息。

问题是,这种行为是否是设计的,以及是否有某种方法可以解决中间空值。

这是复制上述行为的最小应用程序:

import java.awt.*;
import java.beans.*;
import javax.swing.*;

public class FocusNullTest extends JFrame {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                FocusNullTest self = new FocusNullTest();
                self.setVisible(true);
            }
        });
    }

    public FocusNullTest() {
        setSize(150, 100);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container contentPane = getContentPane(); 
        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));

        contentPane.add(new JButton("1"));
        contentPane.add(new JButton("2"));

        KeyboardFocusManager focusManager =
            KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener(
                "permanentFocusOwner",
                new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent e) {
                        System.out.println("permanentFocusOwner changed from: "
                                           + e.getOldValue());
                        System.out.println("permanentFocusOwner changed to  : "
                                           + e.getNewValue());
                    }
                });
    }
}

日志输出为:

  

(程序启动,焦点自动设置为按钮1)
  permanentFocusOwner已更改为:null
  permanentFocusOwner更改为:javax.swing.JButton [,0,18,41x26,(跳过)]
  (点击按钮2)
  permanentFocusOwner已更改为:javax.swing.JButton [,0,18,41x26,(跳过)]
   permanentFocusOwner更改为:null
   permanentFocusOwner已更改为:null
  permanentFocusOwner更改为:javax.swing.JButton [,41,18,41x26,(跳过)]


(可选部分,关于代码意图)
我的目标是创建一个看起来像列表视图的东西,其中条目扩展并在它们获得焦点时显示更多信息(并在它们丢失时折叠回来)。展开的视图包含一些额外的按钮。

JList似乎不是合适的控件,因为(1)它不允许点击按钮,(2)它的条目具有恒定的高度,而我希望条目动态扩展在焦点上。 JTable及其编辑模式似乎也不是一个合适的解决方案,至少因为常量条目大小。

所以我使用带有垂直框布局的普通JPanel作为容器,并订阅模型更改并手动更新视觉效果。问题是,当我单击按钮时,包含列表项失去焦点。如果焦点不会暂时变为null,我可以检测到焦点仍然在列表项中。

4 个答案:

答案 0 :(得分:2)

KeyboardFocusManager为大多数属性触发两个事件(从bean规范开始,它不应该 - 从未找到原因,只是猜测焦点的异步性质可能是某种原因)

   firePropertyChange(someProperty, oldValue, null)
   firePropertyChange(someProperty, null, newValue)

根据newVaue做的事情,等待第二次

答案 1 :(得分:2)

作为解决方法,将最后一个“真正的”前一焦点所有者存储为事件处理程序中的成员。

if ((e.getOldValue() != null) && (e.getNewValue() == null))
 prev_owner = e.getOldValue();

然后,当您实际聚焦于目标时,您将拥有该对象的句柄。仅当实际组件实际获得焦点时(即getNewValue()非空时)处理突出显示更改。

(行为似乎与The AWT Focus Subsystem中所描述的一致,在前一个组件首先失去焦点,然后目标组件得到它的意义上。它不是原子的,所以有一段时间在哪里没有什么实际上有焦点。但我不是专家,所以这可能会有所不同。)

答案 2 :(得分:2)

  

我的目标是制作一个看起来像列表视图的内容,其中条目会扩展,并在获得焦点时显示更多信息。

另一种选择,我有时使用JSplitPane:在左侧,我将(可聚焦的)展开按钮放在JTableOutline或垂直Box的面板;在右边,我把展开的视图。

答案 3 :(得分:2)

My goal is to make something looking like a list view, where the entries 
expand and display more information when they get focus (and collapse back 
when they lose it). The expanded view contains some additional buttons.

enter image description here enter image description here

ButtonModel可以通过使用JButton来做到这一点,非常好的输出是使用JToggleButton还是仍然有原创的想法与JPanel + MouseListener()

import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class CollapsablePanelTest {

    public static void main(String[] args) {
        CollapsablePanel cp = new CollapsablePanel("test", buildPanel());
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new JScrollPane(cp));
        f.setSize(360, 300);
        f.setLocation(200, 100);
        f.setVisible(true);
    }

    public static JPanel buildPanel() {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(2, 1, 2, 1);
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        JPanel p1 = new JPanel(new GridBagLayout());
        p1.setBackground(Color.blue);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        p1.add(new JButton("button 1"), gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p1.add(new JButton("button 2"), gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        p1.add(new JButton("button 3"), gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p1.add(new JButton("button 4"), gbc);
        return p1;
    }

    private CollapsablePanelTest() {
    }
}

class CollapsablePanel extends JPanel {

    private static final long serialVersionUID = 1L;
    private boolean selected;
    private JPanel contentPanel_;
    private HeaderPanel headerPanel_;

    private class HeaderPanel extends JButton /*JToggleButton //implements MouseListener*/ {

        private static final long serialVersionUID = 1L;
        private String __text;
        private Font __font;
        private BufferedImage open, closed;
        private final int OFFSET = 30, PAD = 5;

        public HeaderPanel(String text) {
            //addMouseListener(this);
            __text = text;
            setText(__text);
            __font = new Font("sans-serif", Font.PLAIN, 12);
            // setRequestFocusEnabled(true);
            setPreferredSize(new Dimension(200, 30));
            int w = getWidth();
            int h = getHeight();
            /*try {
            open = ImageIO.read(new File("images/arrow_down_mini.png"));
            closed = ImageIO.read(new File("images/arrow_right_mini.png"));
            } catch (IOException e) {
            e.printStackTrace();
            }*/
            getModel().addChangeListener(new ChangeListener() {

                @Override
                public void stateChanged(ChangeEvent e) {
                    ButtonModel model = (ButtonModel) e.getSource();
                    if (model.isRollover()) {
                        toggleSelection();
                    } else if (model.isPressed()) {
                        toggleSelection();//for JToggleButton
                    }
                }
            });
        }

        /*@Override
        protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        int h = getHeight();
        ///if (selected)
        //g2.drawImage(open, PAD, 0, h, h, this);
        //else
        //g2.drawImage(closed, PAD, 0, h, h, this);
        // Uncomment once you have your own images
        g2.setFont(font);
        FontRenderContext frc = g2.getFontRenderContext();
        LineMetrics lm = font.getLineMetrics(__text, frc);
        float height = lm.getAscent() + lm.getDescent();
        float x = OFFSET;
        float y = (h + height) / 2 - lm.getDescent();
        g2.drawString(__text, x, y);
        }
        @Override
        public void mouseClicked(MouseEvent e) {
        toggleSelection();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }*/
    }

    public CollapsablePanel(String text, JPanel panel) {
        super(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(1, 3, 0, 3);
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        selected = false;
        headerPanel_ = new HeaderPanel(text);
        setBackground(Color.orange);
        contentPanel_ = panel;
        add(headerPanel_, gbc);
        add(contentPanel_, gbc);
        contentPanel_.setVisible(false);
        JLabel padding = new JLabel();
        gbc.weighty = 1.0;
        add(padding, gbc);
    }

    public void toggleSelection() {
        selected = !selected;
        if (contentPanel_.isShowing()) {
            contentPanel_.setVisible(false);
        } else {
            contentPanel_.setVisible(true);
        }
        validate();
        headerPanel_.repaint();
    }
}