【问题标题】:How to prevent JColorChooser from absorbing KeyEvents on JComponent?如何防止 JColorChooser 吸收 JComponent 上的 KeyEvents?
【发布时间】:2021-12-09 21:12:06
【问题描述】:

这是我的问题。 我在滑块中间有一个 JComponent,它本身位于框架中 BorderLayout 的中间。 我使用我的 JComponent 在其中绘制/写入内容。

当我在边框布局的一侧实现 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。 我希望我能解决我的问题并提前感谢

【问题讨论】:

  • 我需要在我的问题中编辑什么?我尝试在 JColorChooser 的 changeListener 中从框架和 PhotoComponent 添加一个 requestFocus ,但它没有工作......
  • SwingUtilities.invokeLater(() -> pc.requestFocusInWindow());在更改侦听器工作中,但我也在 mousePressed 中使用它来解决我的 KeyListener 需要使用 JFrame 进行操作的问题。但是将 requestFocus 放在 changeListener 中让我避免了必须点击组件来输入键
  • 感谢跟进,我怀疑 invokeLater 会起作用。还有一些其他方法可以跟踪焦点,使用焦点侦听器等。

标签: java swing keylistener jcomponent jcolorchooser


【解决方案1】:

在框架中对 KeyListener 的小操作是因为 JComponent 不响应 KeyEvents。

这不是必需的。不要在框架中添加 KeyListener。

只将 KeyListener 添加到 PhotoComponent。

KeyEvent 仅被分派给具有焦点的组件。因此,您希望您的 PhotoComponent 在框架可见时获得焦点。

您可以通过添加:

/*
        frame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                pc.keyListener.keyTyped(e);
            }
        });
*/

pc.requestFocusInWindow();

我不明白为什么它在 changeListener 中不起作用

有时 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()。适配器类的默认实现什么都不做。

【讨论】:

    【解决方案2】:

    这是因为您的组件失去了焦点,因此它将不再接收鼠标事件。我更改了您的鼠标侦听器以包含 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();
                }
            });
    

    【讨论】:

    • 确实有效!谢谢 !我不明白为什么它不能从 changeListener 工作...
    • @AlyaVie 很难说,因为可能会发生几件事。也许 your 更改侦听器被触发,然后颜色选择器的一部分请求焦点。您可以尝试使用“EventQueue.invokeLater(()-> requestFocus());”在您的更改侦听器中。这将在当前事件序列的末尾发布操作。
    • 来自 requestFocus() 的 API 文档:...developers are strongly encouraged to use requestFocusInWindow when possible. 是的,您需要使用 invokeLater()。
    • @camickr 我想我使用了一些我希望开发人员使用“requestFocusInWindow”的应用程序。在这种情况下,由于它发生在点击事件上,因此基本上不会被注意到,但这是一个很好的信息。
    猜你喜欢
    • 2011-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-23
    相关资源
    最近更新 更多