【问题标题】:Swing: restrict keyboard focus traversal to specific widgetsSwing:将键盘焦点遍历限制为特定的小部件
【发布时间】:2019-03-19 15:34:58
【问题描述】:

我有一个 JFrame,其中包含各种常见的面板和小部件,并且我有一个 JPanel,我将其用作该 JFrame 的 glassPane。我希望能够在可见时将键盘焦点遍历限制到 glassPane 中的组件。

我的问题可能会也可能不会因为后台线程启动一个进程导致进度对话框出现并随后消失这一事实而复杂化,这会从我的 glassPane 中的小部件中窃取焦点,但将其返回到我下方的某个小部件玻璃窗格。

我尝试将 JFrame 的焦点遍历策略设置为仅允许 glassPane 被聚焦的策略,但这似乎没有任何效果。 (也许我做错了?)

任何帮助将不胜感激。

【问题讨论】:

  • 您的应用程序听起来相当复杂。出于好奇,您在玻璃板中展示了什么样的组件?为什么是玻璃板?
  • glassPane 包含一个滚动的 TextPane、一个 JCheckBox 和一个 JButton。它用于显示 EULA,迫使用户在使用应用程序之前接受其条款。当我使用模态 JDialog 时,我的用户抱怨,所以我正在研究替代方案。
  • 也许我做错了 - 也许,如果没有看到你的代码就很难判断 :-) 也就是说,管理玻璃窗格 很棘手(会'不要期望自定义 ftp 会过度有用,因为它不负责 mouseEvents),因此您可以考虑使用 JLayer(从 jdk7 开始,或 JXLayer 用于 jdk6)。

标签: java swing


【解决方案1】:

正如我在评论中提到的,我可能会选择 J/X/Layer - 但如果自定义 FTP 确实是您的上下文中唯一缺少的部分,那么这是一个解决方案。

要从焦点遍历中排除组件,请在自定义 FTP 的接受方法中拒绝它们。下面是一个示例,如果 glassPane 可见,则拒绝所有不是 glassPane 子级的组件(注意:这过于简单,因为它只处理直接子级,实际代码必须沿着父链向上走,直到它碰到玻璃板与否)

public static class FTP extends LayoutFocusTraversalPolicy {

    @Override
    protected boolean accept(Component comp) {
        JFrame window = (JFrame) SwingUtilities.windowForComponent(comp);
        if (hasVisibleGlassPane(window)) {
            return comp.getParent() == window.getGlassPane();
        }
        return super.accept(comp);
    }

    private boolean hasVisibleGlassPane(JFrame window) {
        return window != null && window.getGlassPane() != null
                && window.getGlassPane().isVisible();
    }

}

【讨论】:

  • 太好了,谢谢。我完全忘了研究 FocusTraversalPolicy 的子类,只是直接扩展它。为了记录,我最终选择了另一种解决方案,但这实际上回答了所提出的问题。
【解决方案2】:

我将把我的评论作为一个答案,因为我相信它可以解决您的问题:

也许更好的选择是使用CardLayout 来简单地交换视图而不是玻璃窗格或对话框,因为这种情况似乎不适合玻璃窗格的使用。如果您使用 CardLayout,您不必想出一个 kludge(摆弄焦点遍历)来修复另一个 kludge 的副作用(使用玻璃窗格来处理它不打算用于的事情)。

如果您不熟悉它,CardLayout 可以让您轻松地交换 GUI 中的组件,并且通常用于交换持有复杂 GUI 的 JPanel,当用户从一个主要程序状态切换到另一个主要程序状态时。我认为这对你的目的来说是完美的,并且可以让你不必担心你的注意力问题。

【讨论】:

    【解决方案3】:

    当 glassPane 可见时,尝试在要忽略的 JFrame(或者最坏的情况下,在其中的所有组件上)调用 .setFocusable(false)

    【讨论】:

      【解决方案4】:

      当我与客户讨论时,他给了我同样的要求。所以我决定在我的项目中使用 JLayer 和 LayeredPane,并将所有这些组件作为我实现以下代码的简单解决方案,它可能会对您的项目有所帮助。

      public YourConstructor() {
          yes.addFocusListener(new FocusAdapter() {
              public void focusLost(FocusEvent fe) {
                  if (!no.hasFocus()) {
                      no.requestFocusInWindow();
                  }
              }
          });
      
          no.addFocusListener(new FocusAdapter() {
              public void focusLost(FocusEvent fe) {
                  if (!yes.hasFocus()) {
                      yes.requestFocusInWindow();
                  }
              }
          });
      }
      
      @Override
      public void setVisible(boolean visibility) {
          super.setVisible(visibility);
          if (visibility) {
              yes.requestFocusInWindow();
          }
      }
      

      【讨论】:

        【解决方案5】:

        以 kleopatra 的回答为基础,并帮助处于类似情况的其他人;我有一堆我不想集中的自定义挥杆组件(有时仅在它们不可编辑时)。我最终得到了这个:

        /**
         * A custom focus traversal policy to make focus traversal inside a container to ignore some swing components.<br /><br />
         * 
         * <b>Ignored components:</b><br />
         * - <code>CustomComponent1</code> components<br />
         * - <code>CustomComponent2</code> components that are not editable<br /><br />
         * 
         * <b>Usage:</b><br /><br />
         * <code>Container.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());</code>
         */
        public class CustomFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
        
            private static final long serialVersionUID = 1L;
        
            protected boolean accept(Component c) {
        
                if(c instanceof CustomComponent1) {
                    return false;
                }
        
                if(c instanceof CustomComponent2) {
        
                    CustomComponent2 t = (CustomComponent2) c;
        
                    if(!t.isEditable()) {
                        return false;
                    }
                }
        
                return super.accept(c);
            }
        
        }
        

        请注意,需要为每个 Container 设置策略(我为创建的每个 Window 都设置了该策略):

        Window window = new JFrame(); // Or JDialog; both subclasses of Container and Window
        window.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-26
          • 1970-01-01
          • 1970-01-01
          • 2012-11-17
          • 2011-10-08
          相关资源
          最近更新 更多