【问题标题】:Java blocking focus from JComponentJava 阻止来自 JComponent 的焦点
【发布时间】:2013-11-16 22:40:08
【问题描述】:

我为我的应用程序制作了一个 gui。 JFrame 有 2 个 JPanel,panel1 和 panel2。 panel1 就是这样,一个带有自定义绘画的 JPanel,每 5 毫秒重新绘画一次。

panel2 是我第一次尝试 CardLayout 实现:它包含 JPanels subPanel1 和 subPanel2。 subPanel1 包含一个 JComboBox 并添加到 panel2:panel2.add(subPanel1);

subPanel2 有 .setLayout(new CardLayout()); 命令,我在其中添加了 3 个新的 JPanel,以及适当的 itemListener 和所有内容。当然我也加了:panel2.add(subPanel2);

现在来解决问题:关注 Java 中的组件。我了解方法setFocusable(boolean)requestFocus()。但我不能让他们以任何合乎逻辑的方式行事。

首先,它们的根本问题:当组合框获得焦点时,我根本无法取消焦点(尝试用光标到处点击)。

以下是我进行的实验:

1) 在整个应用程序中没有任何代码说明焦点,组合框从焦点开始,无论面板 1 和面板 2 以哪个顺序添加到 JFrame。

2) 如果我设置panel1.setFocusable(true);(在其构造函数中),它将从焦点开始

3) 如果我设置panel1.setFocusable(false); 并请求关注它,它不会得到它。 (只有按预期工作的东西)

4) 如果我将 panel2、subPanel1 或 subPanel2 单独或任意组合设置为不可聚焦,它们仍然可以接收焦点(组合框,即唯一能够注册焦点的组件)。

5) 如果我将组合框设置为不可聚焦,我仍然可以使用框的 itemListener 在 CardLayout 中的卡片之间滚动,但焦点不会粘在它上面。事实上 panel1 仍然注册键盘输入

所以我真的对整个“专注”的事情感到非常困惑。也许这不是我认为的那样?我想要做的是完全阻止与 panel2 的所有交互,直到一个标志(每 5 毫秒评估一次)为真。我是否正确地假设与 JPanel 不同,JComboBox 在单击时自动具有 mousebuttonListener 以获得焦点?如果不是,那么如何完全禁用 JComboBox 和当前卡片显示的所有组件? 不可聚焦组件中的组件仍然可聚焦是正常行为吗?

【问题讨论】:

  • 听起来您真正想要使用的是.setEnabled(false),但请注意,您不能只将面板设置为禁用,您还需要为面板中的每个组件都这样做。跨度>
  • @JavaDevil 看起来确实是我想要的,谢谢 :)

标签: java swing combobox focus


【解决方案1】:

我尝试了许多不同的方法来解决类似的问题。

遍历组件层次结构并禁用每个组件的问题是您破坏了任何上下文并规避了对这些组件的管理。

基本上,如果某些子组件在您重新启用容器时需要保持禁用状态,您就会突然陷入多重责任交织的问题......

迄今为止我发现的最佳解决方案是简单地使用JXLayer,它提供了“可锁定”层概念,允许您“锁定”单个容器,防止用户与之交互,例如...... .

这是直接取自 JXLayer 演示代码...

/**
 * Copyright (c) 2006-2008, Alexander Potochkin
 * All rights reserved.
 */

package org.jdesktop.jxlayer.demo;

import com.jhlabs.image.BlurFilter;
import com.jhlabs.image.EmbossFilter;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.demo.util.LafMenu;
import org.jdesktop.jxlayer.plaf.effect.BufferedImageOpEffect;
import org.jdesktop.jxlayer.plaf.effect.LayerEffect;
import org.jdesktop.jxlayer.plaf.ext.LockableUI;
import org.jdesktop.swingx.painter.BusyPainter;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;

/**
 * A demo to show the abilities of the {@link LockableUI}.
 * 
 * @see BusyPainterUI  
 */
public class LockableDemo extends JFrame {
    private JXLayer<JComponent> layer;
    private LockableUI blurUI = 
            new LockableUI(new BufferedImageOpEffect(new BlurFilter()));
    private EnhancedLockableUI embossUI = 
            new EnhancedLockableUI(new BufferedImageOpEffect(new EmbossFilter()));
    private LockableUI busyPainterUI = new BusyPainterUI();

    private JCheckBoxMenuItem disablingItem =
            new JCheckBoxMenuItem(new AbstractAction("Lock the layer") {

                public void actionPerformed(ActionEvent e) {
                    blurItem.setEnabled(!disablingItem.isSelected());
                    embossItem.setEnabled(!disablingItem.isSelected());
                    busyPainterItem.setEnabled(!disablingItem.isSelected());

                    blurUI.setLocked(disablingItem.isSelected());
                    embossUI.setLocked(disablingItem.isSelected());
                    busyPainterUI.setLocked(disablingItem.isSelected());
                }
            });

    private JRadioButtonMenuItem blurItem = new JRadioButtonMenuItem("Blur effect");
    private JRadioButtonMenuItem embossItem = new JRadioButtonMenuItem("Unlock button");
    private JRadioButtonMenuItem busyPainterItem = new JRadioButtonMenuItem("BusyPainter");

    public LockableDemo() {
        super("Lockable layer demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JComponent view = createLayerPanel();
        layer = new JXLayer<JComponent>(view);

        layer.setUI(blurUI);
        add(layer);
        add(createToolPanel(), BorderLayout.EAST);
        setJMenuBar(createMenuBar());
        setSize(380, 300);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new LockableDemo().setVisible(true);
            }
        });
    }

    private JMenuBar createMenuBar() {
        JMenu menu = new JMenu("Menu");

        disablingItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.ALT_MASK));

        menu.add(disablingItem);
        menu.addSeparator();

        blurItem.setSelected(true);
        blurItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, InputEvent.ALT_MASK));
        menu.add(blurItem);

        embossItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, InputEvent.ALT_MASK));
        menu.add(embossItem);

        busyPainterItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3, InputEvent.ALT_MASK));
        menu.add(busyPainterItem);

        ButtonGroup group = new ButtonGroup();
        group.add(blurItem);
        group.add(embossItem);
        group.add(busyPainterItem);

        ItemListener menuListener = new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                if (blurItem.isSelected()) {
                    layer.setUI(blurUI);
                } else if (embossItem.isSelected()) {
                    layer.setUI(embossUI);
                } else if (busyPainterItem.isSelected()) {
                    layer.setUI(busyPainterUI);
                }
            }
        };

        blurItem.addItemListener(menuListener);
        embossItem.addItemListener(menuListener);
        busyPainterItem.addItemListener(menuListener);

        embossUI.getUnlockButton().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                disablingItem.doClick();
            }
        });

        JMenuBar bar = new JMenuBar();
        bar.add(menu);

        bar.add(new LafMenu());
        return bar;
    }

    private JComponent createLayerPanel() {
        JComponent panel = new JPanel();
        panel.add(new JCheckBox("JCheckBox"));
        panel.add(new JRadioButton("JRadioButton"));
        panel.add(new JTextField(15));
        JButton button = new JButton("Have a nice day");
        button.setMnemonic('H');
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(LockableDemo.this,
                        "actionPerformed");
            }
        });
        panel.add(button);
        panel.add(new JTextField(15));
        panel.add(new JCheckBox("JCheckBox"));
        panel.add(new JRadioButton("JRadioButton"));
        panel.add(new JTextField(15));
        panel.add(new JCheckBox("JCheckBox"));
        panel.add(new JRadioButton("JRadioButton"));
        panel.setBorder(BorderFactory.createEtchedBorder());
        return panel;
    }

    private JComponent createToolPanel() {
        JComponent box = Box.createVerticalBox();
        JCheckBox button = new JCheckBox(disablingItem.getText());
        button.setModel(disablingItem.getModel());
        box.add(Box.createGlue());
        box.add(button);
        box.add(Box.createGlue());
        JRadioButton blur = new JRadioButton(blurItem.getText());
        blur.setModel(blurItem.getModel());
        box.add(blur);
        JRadioButton emboss = new JRadioButton(embossItem.getText());
        emboss.setModel(embossItem.getModel());
        box.add(emboss);
        JRadioButton translucent = new JRadioButton(busyPainterItem.getText());
        translucent.setModel(busyPainterItem.getModel());
        box.add(translucent);
        box.add(Box.createGlue());
        return box;
    }


    /**
     * Subclass of the {@link LockableUI} which shows a button
     * that allows to unlock the {@link JXLayer} when it is locked  
     */
    public static class EnhancedLockableUI extends LockableUI {
        private JButton unlockButton = new JButton("Unlock");

        public EnhancedLockableUI(LayerEffect... lockedEffects) {
            super(lockedEffects);
            unlockButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    setLocked(false);
                }
            });
            unlockButton.setVisible(false);
        }

        public AbstractButton getUnlockButton() {
            return unlockButton;
        }

        @Override
        @SuppressWarnings("unchecked")
        public void installUI(JComponent c) {
            super.installUI(c);
            JXLayer<JComponent> l = (JXLayer<JComponent>) c;
            l.getGlassPane().setLayout(new GridBagLayout());
            l.getGlassPane().add(unlockButton);
            unlockButton.setCursor(Cursor.getDefaultCursor());
        }

        @Override
        @SuppressWarnings("unchecked")
        public void uninstallUI(JComponent c) {
            super.uninstallUI(c);
            JXLayer<JComponent> l = (JXLayer<JComponent>) c;
            l.getGlassPane().setLayout(new FlowLayout());
            l.getGlassPane().remove(unlockButton);
        }

        public void setLocked(boolean isLocked) {
            super.setLocked(isLocked);
            unlockButton.setVisible(isLocked);
        }
    }

    /**
     * Subclass of the {@link LockableUI} which uses the {@link BusyPainterUI}
     * from the SwingX project to implement the "busy effect" 
     * when {@link JXLayer} is locked.  
     */
    public static class BusyPainterUI extends LockableUI 
            implements ActionListener {
        private BusyPainter busyPainter;
        private Timer timer;
        private int frameNumber;

        public BusyPainterUI() {
            busyPainter = new BusyPainter() {
                protected void doPaint(Graphics2D g, JComponent object,
                                       int width, int height) {
                    // centralize the effect
                    Rectangle r = getTrajectory().getBounds();
                    int tw = width - r.width - 2 * r.x;
                    int th = height - r.height - 2 * r.y;
                    g.translate(tw / 2, th / 2);
                    super.doPaint(g, object, width, height);
                }
            };
            busyPainter.setPointShape(new Ellipse2D.Double(0, 0, 20, 20));
            busyPainter.setTrajectory(new Ellipse2D.Double(0, 0, 100, 100));
            timer = new Timer(100, this);
        }

        @Override
        protected void paintLayer(Graphics2D g2, JXLayer<? extends JComponent> l) {
            super.paintLayer(g2, l);
            if (isLocked()) {
                busyPainter.paint(g2, l, l.getWidth(), l.getHeight());
            }
        }

        @Override
        public void setLocked(boolean isLocked) {
            super.setLocked(isLocked);
            if (isLocked) {
                timer.start();
            } else {
                timer.stop();
            }
        }

        // Change the frame for the busyPainter
        // and mark BusyPainterUI as dirty 
        public void actionPerformed(ActionEvent e) {
            frameNumber = (frameNumber + 1) % 8;
            busyPainter.setFrame(frameNumber);
            // this will repaint the layer
            setDirty(true);
        }
    }
}

【讨论】:

    【解决方案2】:

    听起来你真正想要使用的是.setEnabled(false)

    如果您需要将面板中的所有组件设置为禁用,那么您可以使用这样的方法来执行此操作:(可能不是JComponents 的最佳方法,但如果需要可以轻松修改,但这确实工作)

    public static void setContainerAndChildrenEnabled(Container c, boolean b)
    {
        Component[] allComps = c.getComponents();
        for(Component com : allComps)
        {
            com.setEnabled(b);
            if(com instanceof Container)
                setContainerAndChildrenEnabled((Container) com, b);
        }
    }
    

    然后使用您要设置的面板调用它,并使用 true 或 false 来启用/禁用。这也将为Container 中的每个Component 递归调用setEnabled()

    从文档中要注意两点:

    1. 注意:禁用轻量级组件不会阻止它接收 MouseEvents。

    2. 注意:禁用重量级容器会阻止该容器中的所有组件接收任何输入事件。但是禁用轻量级容器只会影响这个容器。

    isLightweight()

    【讨论】:

    • 这是一种危险的方法,因为它会丢弃所有上下文。如果需要禁用容器的一个或多个子容器,但您启用了整个容器,会发生什么情况?
    • @MadProgrammer 是的,在这种情况下,这不是很好/有用,这只是一个全有或全无的实现,并认为它们可能是我不知道的含义。但是,我以前从未遇到过它们,但是当我在面板中的面板中有面板并且想要禁用/启用父面板中的所有内容时发现它很有用。
    • 花了两周时间寻找为什么某些组件没有启用/禁用,而我认为它们应该是......因为这个确切的例子(我们有一些后台加载过程会禁用并独立重新启用字段)
    • @MadProgrammer 2 周听起来很痛苦,感谢您的提醒。可能需要查看JXLayer
    • 我只需要禁用所有内容或什么都不做,所以这对我来说非常适合!谢谢=]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 2018-03-12
    • 1970-01-01
    • 2011-03-15
    相关资源
    最近更新 更多