如何防止JColorChooser 吸收JComponent 的KeyEvents?
How to prevent JColorChooser from absorbing KeyEvents on JComponent?
这是我的问题。
我在滑块中间有一个 JComponent,它本身位于框架中 BorderLayout 的中间。
我用我的 JComponent draw/write 里面的东西。
当我在边框布局的一侧实现 JColorChooser 时,我的问题就来了。它应该改变图纸的颜色。一旦我在 JColorChooser 上选择了一种颜色,我的 JComponent 就停止响应 KeyEvents 并且我不能再写任何东西了。
这里我的代码简化了:
我的主要 :
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
public class TestPhotoComponent {
public static void main(String[] args) {
JFrame frame = new JFrame("PhotoComponent test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel p1 = new JPanel();
p1.setPreferredSize(new Dimension(Integer.MAX_VALUE, 100));
p1.setBackground(Color.GREEN);
frame.add(p1, BorderLayout.NORTH);
JPanel p2 = new JPanel();
p2.setPreferredSize(new Dimension(Integer.MAX_VALUE, 100));
p2.setBackground(Color.GREEN);
frame.add(p2, BorderLayout.SOUTH);
JPanel p3 = new JPanel();
p3.setPreferredSize(new Dimension(100, Integer.MAX_VALUE));
p3.setBackground(Color.GREEN);
frame.add(p3, BorderLayout.WEST);
JPanel p4 = new JPanel();
p4.setPreferredSize(new Dimension(200, Integer.MAX_VALUE));
p4.setBackground(Color.GREEN);
frame.add(p4, BorderLayout.EAST);
PhotoComponent pc = new PhotoComponent(new Dimension(500, 300));
pc.setFocusable(true);
JScrollPane sp = new JScrollPane(pc);
sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
sp.getViewport().setBackground(Color.CYAN);
frame.add(sp, BorderLayout.CENTER);
final JColorChooser colorChooser = new JColorChooser();
colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Color color = colorChooser.getColor();
if (color != null)
pc.setColor(color);
}
});
p4.add(colorChooser);
frame.setFocusable(true);
frame.setPreferredSize(new Dimension(900, 600));
frame.pack();
frame.setVisible(true);
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
pc.keyListener.keyTyped(e);
}
});
}
}
我的 JComponent :
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class PhotoComponent extends JComponent {
Point position = null;
ArrayList<Text> textList = new ArrayList<>();
Text currentText = null;
Color color = Color.BLACK;
KeyAdapter keyListener;
public PhotoComponent(Dimension dimension){
this.setFocusable(true);
this.setVisible(true);
this.setPreferredSize(dimension);
addListeners();
}
void addListeners(){
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
currentText = new Text(e.getPoint());
textList.add(currentText);
}
});
this.keyListener = new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
super.keyTyped(e);
if(currentText != null){
currentText.addCharacter(e.getKeyChar());
repaint();
}
}
};
this.addKeyListener(this.keyListener);
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color oldColor = g2.getColor();
g2.setColor(Color.WHITE);
g2.fill(this.getBounds());
g2.setColor(this.color);
for(Text t : textList){
t.draw(g2);
}
g2.setColor(oldColor);
}
public void setColor(Color color) {
this.color = color;
}
}
还有我的文本对象
import java.awt.*;
public class Text {
Point position = null;
String content = "";
public Text(Point position){
this.position = position;
}
public void addCharacter(char newCharacter){
this.content += newCharacter;
}
public void draw(Graphics2D g2){
g2.drawString(content, position.x, position.y);
}
}
框架中 KeyListener 的小操作是因为 JComponent 不响应 KeyEvents。
我希望我已经解决了我的问题并提前致谢
这是因为您的组件失去了焦点,所以它将不再接收鼠标事件。我更改了您的鼠标侦听器以包含一个 requestFocusInWindow
,这是 JComponent 方法。焦点将返回到您的 PhotoComponent,您可以在单击面板后再次键入。
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
currentText = new Text(e.getPoint());
textList.add(currentText);
requestFocusInWindow();
}
});
The little manipulation with the KeyListener in the frame is because the JComponent doesn't respond to KeyEvents.
这不是必需的。不要在框架中添加 KeyListener。
仅将 KeyListener 添加到 PhotoComponent。
KeyEvent 仅被调度到具有焦点的组件。因此,当框架可见时,您希望 PhotoComponent 具有焦点。
您可以通过添加:
/*
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
pc.keyListener.keyTyped(e);
}
});
*/
pc.requestFocusInWindow();
I don't understand why it didn't work from the changeListener though
有时 Swing 组件会向 EDT 添加代码,因此代码不会按您期望的顺序执行。所以 PhotoComponent 可能会获得焦点,但颜色选择器可能会夺回它。
所以在您的 ChangeListener 中,您首先尝试:
pc.requestFocusInWindow();
在这种情况下它不起作用。
所以你的变化监听器中的逻辑应该是:
public void stateChanged(ChangeEvent e) {
Color color = colorChooser.getColor();
if (color != null)
pc.setColor(color);
SwingUtilities.invokeLater(() -> pc.requestFocusInWindow());
}
这将确保代码在所有颜色选择器逻辑执行完毕后执行。
另外你的 setColor() 方法应该是:
this.color = color;
repaint();
当组件的 属性 发生变化时,Swing 组件应该负责重新绘制自身,就像当文本发生变化时重新绘制组件一样。
此外,在编写侦听器时,无需对事件调用 super()
。适配器的默认实现 类 什么都不做。
这是我的问题。 我在滑块中间有一个 JComponent,它本身位于框架中 BorderLayout 的中间。 我用我的 JComponent draw/write 里面的东西。
当我在边框布局的一侧实现 JColorChooser 时,我的问题就来了。它应该改变图纸的颜色。一旦我在 JColorChooser 上选择了一种颜色,我的 JComponent 就停止响应 KeyEvents 并且我不能再写任何东西了。
这里我的代码简化了:
我的主要 :
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
public class TestPhotoComponent {
public static void main(String[] args) {
JFrame frame = new JFrame("PhotoComponent test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel p1 = new JPanel();
p1.setPreferredSize(new Dimension(Integer.MAX_VALUE, 100));
p1.setBackground(Color.GREEN);
frame.add(p1, BorderLayout.NORTH);
JPanel p2 = new JPanel();
p2.setPreferredSize(new Dimension(Integer.MAX_VALUE, 100));
p2.setBackground(Color.GREEN);
frame.add(p2, BorderLayout.SOUTH);
JPanel p3 = new JPanel();
p3.setPreferredSize(new Dimension(100, Integer.MAX_VALUE));
p3.setBackground(Color.GREEN);
frame.add(p3, BorderLayout.WEST);
JPanel p4 = new JPanel();
p4.setPreferredSize(new Dimension(200, Integer.MAX_VALUE));
p4.setBackground(Color.GREEN);
frame.add(p4, BorderLayout.EAST);
PhotoComponent pc = new PhotoComponent(new Dimension(500, 300));
pc.setFocusable(true);
JScrollPane sp = new JScrollPane(pc);
sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
sp.getViewport().setBackground(Color.CYAN);
frame.add(sp, BorderLayout.CENTER);
final JColorChooser colorChooser = new JColorChooser();
colorChooser.getSelectionModel().addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Color color = colorChooser.getColor();
if (color != null)
pc.setColor(color);
}
});
p4.add(colorChooser);
frame.setFocusable(true);
frame.setPreferredSize(new Dimension(900, 600));
frame.pack();
frame.setVisible(true);
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
pc.keyListener.keyTyped(e);
}
});
}
}
我的 JComponent :
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class PhotoComponent extends JComponent {
Point position = null;
ArrayList<Text> textList = new ArrayList<>();
Text currentText = null;
Color color = Color.BLACK;
KeyAdapter keyListener;
public PhotoComponent(Dimension dimension){
this.setFocusable(true);
this.setVisible(true);
this.setPreferredSize(dimension);
addListeners();
}
void addListeners(){
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
currentText = new Text(e.getPoint());
textList.add(currentText);
}
});
this.keyListener = new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
super.keyTyped(e);
if(currentText != null){
currentText.addCharacter(e.getKeyChar());
repaint();
}
}
};
this.addKeyListener(this.keyListener);
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Color oldColor = g2.getColor();
g2.setColor(Color.WHITE);
g2.fill(this.getBounds());
g2.setColor(this.color);
for(Text t : textList){
t.draw(g2);
}
g2.setColor(oldColor);
}
public void setColor(Color color) {
this.color = color;
}
}
还有我的文本对象
import java.awt.*;
public class Text {
Point position = null;
String content = "";
public Text(Point position){
this.position = position;
}
public void addCharacter(char newCharacter){
this.content += newCharacter;
}
public void draw(Graphics2D g2){
g2.drawString(content, position.x, position.y);
}
}
框架中 KeyListener 的小操作是因为 JComponent 不响应 KeyEvents。 我希望我已经解决了我的问题并提前致谢
这是因为您的组件失去了焦点,所以它将不再接收鼠标事件。我更改了您的鼠标侦听器以包含一个 requestFocusInWindow
,这是 JComponent 方法。焦点将返回到您的 PhotoComponent,您可以在单击面板后再次键入。
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
currentText = new Text(e.getPoint());
textList.add(currentText);
requestFocusInWindow();
}
});
The little manipulation with the KeyListener in the frame is because the JComponent doesn't respond to KeyEvents.
这不是必需的。不要在框架中添加 KeyListener。
仅将 KeyListener 添加到 PhotoComponent。
KeyEvent 仅被调度到具有焦点的组件。因此,当框架可见时,您希望 PhotoComponent 具有焦点。
您可以通过添加:
/*
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
pc.keyListener.keyTyped(e);
}
});
*/
pc.requestFocusInWindow();
I don't understand why it didn't work from the changeListener though
有时 Swing 组件会向 EDT 添加代码,因此代码不会按您期望的顺序执行。所以 PhotoComponent 可能会获得焦点,但颜色选择器可能会夺回它。
所以在您的 ChangeListener 中,您首先尝试:
pc.requestFocusInWindow();
在这种情况下它不起作用。
所以你的变化监听器中的逻辑应该是:
public void stateChanged(ChangeEvent e) {
Color color = colorChooser.getColor();
if (color != null)
pc.setColor(color);
SwingUtilities.invokeLater(() -> pc.requestFocusInWindow());
}
这将确保代码在所有颜色选择器逻辑执行完毕后执行。
另外你的 setColor() 方法应该是:
this.color = color;
repaint();
当组件的 属性 发生变化时,Swing 组件应该负责重新绘制自身,就像当文本发生变化时重新绘制组件一样。
此外,在编写侦听器时,无需对事件调用 super()
。适配器的默认实现 类 什么都不做。