Java 焦点所有者临时更改为null
我对Swing开发还很陌生,希望我的问题不是一个愚蠢的问题 我有以下问题。我正在使用Java 焦点所有者临时更改为null,java,swing,focus,Java,Swing,Focus,我对Swing开发还很陌生,希望我的问题不是一个愚蠢的问题 我有以下问题。我正在使用KeyboardFocusManager跟踪焦点,监听属性permanentFocusOwner的更改。但是,当焦点从一个控件更改为另一个控件时,我将permanentFocusOwner属性中间更改为null 当焦点位于其中一个面板或其子面板内时,我当前的UI逻辑正在对控件进行一些更改。但是,获取中间值null会破坏此逻辑 我在谷歌上搜索了关于这个问题的信息,没有找到任何相关信息 问题是,这种行为是否出于设计,
KeyboardFocusManager
跟踪焦点,监听属性permanentFocusOwner
的更改。但是,当焦点从一个控件更改为另一个控件时,我将permanentFocusOwner
属性中间更改为null
当焦点位于其中一个面板或其子面板内时,我当前的UI逻辑正在对控件进行一些更改。但是,获取中间值null
会破坏此逻辑
我在谷歌上搜索了关于这个问题的信息,没有找到任何相关信息
问题是,这种行为是否出于设计,以及是否有某种方法可以绕过中间空值
以下是再现上述行为的最小应用程序:
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,(跳过)]
永久焦点所有者更改为:null
permanentFocusOwner已从:null更改为 permanentFocusOwner更改为:javax.swing.JButton[,41,18,41x26,(跳过)]
(可选部分,关于代码意图)
我的目标是使某些内容看起来像一个列表视图,其中条目在获得焦点时展开并显示更多信息(在失去焦点时折叠)。展开的视图包含一些附加按钮
JList
似乎不是合适的控件,因为(1)它不允许单击按钮,(2)它的条目具有恒定的高度,而我希望条目在焦点上动态展开JTable
及其编辑模式似乎也不是一个合适的解决方案,至少因为条目大小不变
因此,我使用带有垂直框布局的plain
JPanel
作为容器,订阅模型更改并手动更新视觉效果。问题是,当我单击按钮时,包含的列表项会失去焦点。如果焦点暂时不会更改为null
,我可以检测到焦点仍然停留在列表项中。KeyboardFocusManager正在为大多数属性触发两个事件(从beans规范开始,它不应该-永远也找不到原因,只是猜测焦点的异步性质可能就是原因)
要根据newVaue进行操作,请等待第二个作为解决方法,将上一个“真正的”焦点所有者存储为事件处理程序中的成员
if ((e.getOldValue() != null) && (e.getNewValue() == null))
prev_owner = e.getOldValue();
然后,当你聚焦到目标上时,你会有一个对象的句柄。仅当实际组件实际接收焦点时(即getNewValue()
为非空时),才处理高亮显示更改
(该行为似乎与中所述一致,即前一个组件先失去焦点,然后目标组件获得焦点。它不是原子的,因此有一段时间没有任何东西真正具有焦点。但我不是专家,因此这可能会有所不同。)
我的目标是使某些东西看起来像一个列表视图,其中条目在获得焦点时展开并显示更多信息
另一种选择是,我有时使用a:在左边,我将(可聚焦)展开按钮放在a或垂直面板上;在右边,我放置了展开视图
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.
ButtonModel可以通过使用JButton实现这一点,非常好的输出是通过使用JToggleButton实现的,或者仍然有保留JPanel+MouseListener()的原始想法
+1,但关于目标的更多细节可能会提供更好的解决方案。@垃圾神:添加了一些关于代码意图的细节。我不禁猜测这是窗口如何获得焦点的跨平台变化的结果。谢谢您的建议!虽然这将导致一个不同的用户界面,但它绝对是一个不错的设计。(我以前没有听说过
Outline
)顺便问一下,有没有一种简单的方法可以将JPanel/Box子项绑定到模型,就像JTable的项可以绑定到TableModel一样?我通常在JPanel
构造函数中给按钮一个。actionPerformed()
方法查询应用程序的数据模型并更新扩展视图。操作
可以从按钮、菜单、工具栏或焦点侦听器中调用。@垃圾神我想了一些,非常好的建议,但调用了我…,然后我以我所知道的最古老的想法结束,请参阅我的帖子,以前的投票+1,@trashgood:我现在正在使用我的模型对象。它似乎包含了事件列表列表
。好吧,为每个控件准备一个抽象模型会很好,但我并不反对手动构建一些东西——至少这会让我了解TableModel
和其他现有模型是如何工作的。(我有使用.NET/WPF/MVVM的经验,这似乎大致相同。)非常感谢您的代码!然而,我看到了一些关于我想要实现什么的问题。首先,通过按钮激活切换扩展。这与用户使用鼠标进行对焦相同,但如果通过键盘(例如,通过选项卡)设置对焦,则不会激活该按钮。其次,当焦点离开面板时,面板应该立即折叠。如果没有鼠标侦听器,则ButtonModel可以使用javax.swing.Timer(用于延迟显示,或者如果有多个操作,或者可能来自最近的JComponents的一致性)为我工作。键绑定(不是TAB,而是F1、F2或F5)可以解决这一问题,但我仍然认为最好将JWindow实现为容器,而不是跳转到JPanelI。我不确定我到底理解哪个鼠标
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.
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();
}
}