首页 > 解决方案 > Java Swing - 显示玻璃窗格并使用事件而不会失去以前的焦点所有者

问题描述

我们正在尝试向用户显示一个乳白色JGlassPane,以表示由于进度条或当前正在显示的其他内容,当前不接受事件。虽然这通常会自动工作,因为我们正在显示一个模态对话框,我们还希望在JGlassPane没有 a 的情况下使用JDialog,这意味着只有 aJGlassPane和一个等待光标。

到目前为止,我们通过使用JGlassPane忽略事件并绘制半透明颜色的 a 来做到这一点。最初我们调用requestFocus. JGlassPane这样做的问题是,当玻璃板消失时,我们会丢失先前聚焦的组件。解决方案是简单地记住之前关注的组件。但是,在通过菜单调用Action触发 的 的情况下JGlassPane,焦点组件是 windows JRootPane。通常 swing 会在关闭菜单后正确恢复焦点,但是,在这种情况下,我们正在破坏此功能。有没有办法在不破坏焦点行为的情况下实现我们想要的?

在此演示中,您可以看到,在使用两个按钮之一后按 ESC 时正确返回焦点,但不是使用桌面上的上下文菜单。

public class FocusDemo
{
  //Temporary static veriable for demo purposes.
  private static final AtomicReference<Component> previouslyFocused = new AtomicReference<>();

  private static class ConsumingGlassPane extends JPanel
  {
    public ConsumingGlassPane()
    {
      setOpaque( false );
      setFocusable( true );

      addMouseListener( new MouseListener()
      {
        @Override
        public void mouseClicked( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mousePressed( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseReleased( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseEntered( @NonNull final MouseEvent e )
        {
          e.consume();
        }

        @Override
        public void mouseExited( @NonNull final MouseEvent e )
        {
          e.consume();
        }
      } );

      addKeyListener( new KeyListener()
      {

        @Override
        public void keyTyped( final KeyEvent e )
        {
          e.consume();
        }

        @Override
        public void keyReleased( final KeyEvent e )
        {
          e.consume();
        }

        @Override
        public void keyPressed( final KeyEvent e )
        {
          if ( e.getKeyCode() == KeyEvent.VK_ESCAPE )
          {
            setVisible( false );
            final Component component = previouslyFocused.get();
            if ( component != null )
            {
              component.requestFocusInWindow();
            }
          }
          e.consume();
        }
      } );

      // This component keeps the focus until is made hidden
      setInputVerifier( new InputVerifier()
      {
        @Override
        public boolean verify( final JComponent input )
        {
          return !isVisible();
        }
      } );
    }

    @Override
    protected void paintComponent( final Graphics g )
    {
      final Graphics2D g2 = (Graphics2D) g.create();
      g2.setColor( new Color( 240, 230, 230, 128 ) );
      g2.fillRect( 0, 0, getWidth(), getHeight() );
      g2.dispose();
    }
  }

  public static void main( final String[] args )
  {
    final JFrame frame = new JFrame();
    frame.setGlassPane( new ConsumingGlassPane() );
    final ActionListener showglasspane = __ ->
    {
      previouslyFocused.set( frame.getFocusOwner() );
      frame.getRootPane().getGlassPane().setVisible( true );
      frame.getRootPane().getGlassPane().requestFocus();
    };

    final JButton button1 = new JButton( "Show GlassPane" );
    final JButton button2 = new JButton( "Show GlassPane" );
    button1.addActionListener( showglasspane );
    button2.addActionListener( showglasspane );

    final Object[][] objects = { { 1, 2, 3 }, { 1, 2, 3 } };
    final String[] col = { "Lel", "Lol", "Lul" };
    final JTable table = new JTable( objects, col );
    final JMenuItem menuItem = new JMenuItem( "Show GlassPane" );
    menuItem.addActionListener( showglasspane );
    final JPopupMenu popup = new JPopupMenu();
    popup.add( menuItem );
    table.setComponentPopupMenu( popup );

    frame.setLayout( new GridBagLayout() );
    frame.add( button1 );
    frame.add( button2 );
    frame.add( table );
    frame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE );
    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }
}

标签: javaswingfocusglasspane

解决方案


推荐阅读