【问题标题】:Swing: popover with arrowSwing:带箭头的弹出框
【发布时间】:2012-04-23 10:56:04
【问题描述】:

我想创建一个带有箭头、圆角和阴影效果的 Google Chrome 收藏夹式弹出框。在 Java Swing 中。最好的方法是什么? SplashScreen?或者只是一个普通的AWT Window?其他想法?谢谢!

【问题讨论】:

    标签: java swing popover rounded-corners


    【解决方案1】:

    有几个选项,每个选项都有自己的优点和缺点......

    1. 创建自定义形状的窗口 - 使用这种方法,一些系统将能够在形状窗口后面创建额外的阴影,这也适用于大多数系统(即使在 linux JDK 上也应该适用)。这种方法的坏处(实际上使它无法使用)是没有锯齿的形状边界线 - 如果您创建一些椭圆形窗口,它的边会显得粗糙。

    2. 创建一个非不透明的未装饰窗口,绘制形状 - 这种方法将解决 (1) 方法的主要问题。您可以在完全透明的窗口上绘制您正在绘制的形状。这个的坏处是它只适用于 Win 和 Mac 系统。在(大多数情况下)任何 linux 系统上,您都会得到一个矩形窗口和大量关于不受支持的操作的错误。

    3. 在 java 窗口中创建一个自定义形状的弹出窗口,并将其放置在窗口分层或玻璃窗格上。这将使您完全避免任何兼容性问题并获得 (2) 方法的好处。不过,这种方法有一个不好的地方——您只能在窗口根窗格边界中显示这样的弹出窗口。在大多数情况下,这仍然比其他两种方式要好得多,因为它使用的资源更少,不会创建额外的窗口,而且您可以控制弹出窗口的每个部分。

    关于第三种方法 - 您可以查看我在自己的项目中创建的 TooltipManager WebLookAndFeel - 它使用窗口玻璃窗格来显示具有阴影效果的自定义形状的半透明工具提示。很快我将添加窗口 PopupManager,它允许快速创建“内部”窗口弹出窗口。

    以下是一些方法示例:

    前面所有示例中使用的一些代码

    创建形状的方法:

    private static Area createShape ()
    {
        Area shape = new Area ( new RoundRectangle2D.Double ( 0, 20, 500, 200, 20, 20 ) );
    
        GeneralPath gp = new GeneralPath ( GeneralPath.WIND_EVEN_ODD );
        gp.moveTo ( 230, 20 );
        gp.lineTo ( 250, 0 );
        gp.lineTo ( 270, 20 );
        gp.closePath ();
        shape.add ( new Area ( gp ) );
    
        return shape;
    }
    

    允许通过拖动组件移动窗口的鼠标适配器:

    public static class WindowMoveAdapter extends MouseAdapter
    {
        private boolean dragging = false;
        private int prevX = -1;
        private int prevY = -1;
    
        public WindowMoveAdapter ()
        {
            super ();
        }
    
        public void mousePressed ( MouseEvent e )
        {
            if ( SwingUtilities.isLeftMouseButton ( e ) )
            {
                dragging = true;
            }
            prevX = e.getXOnScreen ();
            prevY = e.getYOnScreen ();
        }
    
        public void mouseDragged ( MouseEvent e )
        {
            if ( prevX != -1 && prevY != -1 && dragging )
            {
                Window w = SwingUtilities.getWindowAncestor ( e.getComponent () );
                if ( w != null && w.isShowing () )
                {
                    Rectangle rect = w.getBounds ();
                    w.setBounds ( rect.x + ( e.getXOnScreen () - prevX ),
                            rect.y + ( e.getYOnScreen () - prevY ), rect.width, rect.height );
                }
            }
            prevX = e.getXOnScreen ();
            prevY = e.getYOnScreen ();
        }
    
        public void mouseReleased ( MouseEvent e )
        {
            dragging = false;
        }
    }
    

    第一种方法示例:

    public static void main ( String[] args )
    {
        JFrame frame = new JFrame ();
        frame.setUndecorated ( true );
    
        JPanel panel = new JPanel ();
        panel.setBackground ( Color.BLACK );
        WindowMoveAdapter wma = new WindowMoveAdapter ();
        panel.addMouseListener ( wma );
        panel.addMouseMotionListener ( wma );
        frame.getContentPane ().add ( panel );
    
        Area shape = createShape ();
        AWTUtilities.setWindowShape ( frame, shape );
        frame.setSize ( shape.getBounds ().getSize () );
        frame.setLocationRelativeTo ( null );
    
        frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        frame.setVisible ( true );
    }
    

    如您所见 - 圆形的边角非常粗糙且不好看

    第二种方法:

    public static void main ( String[] args )
    {
        JFrame frame = new JFrame ();
        frame.setUndecorated ( true );
    
        final Area shape = createShape ();
        JPanel panel = new JPanel ()
        {
            protected void paintComponent ( Graphics g )
            {
                super.paintComponent ( g );
    
                Graphics2D g2d = ( Graphics2D ) g;
                g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON );
    
                g2d.setPaint ( Color.BLACK );
                g2d.fill ( shape );
            }
        };
        panel.setOpaque ( false );
        WindowMoveAdapter wma = new WindowMoveAdapter ();
        panel.addMouseListener ( wma );
        panel.addMouseMotionListener ( wma );
        frame.getContentPane ().add ( panel );
    
        AWTUtilities.setWindowOpaque ( frame, false );
        frame.setSize ( shape.getBounds ().getSize () );
        frame.setLocationRelativeTo ( null );
    
        frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        frame.setVisible ( true );
    }
    

    现在它应该看起来很完美——唯一的问题是它只能在 Windows 和 Mac 上正常工作(至少在 1.6.x JDK 中)。至少大约一个月前,当我上次在各种操作系统上检查它时。

    第三种方法

    public static void main ( String[] args )
    {
        JFrame frame = new JFrame ();
    
        JPanel panel = new JPanel ( new BorderLayout () );
        panel.setOpaque ( false );
        WindowMoveAdapter wma = new WindowMoveAdapter ();
        panel.addMouseListener ( wma );
        panel.addMouseMotionListener ( wma );
        frame.getContentPane ().add ( panel );
    
        panel.add ( new JButton ( "Test" ) );
    
        final Area shape = createShape ();
    
        JPanel glassPane = new JPanel ( null )
        {
            public boolean contains ( int x, int y )
            {
                // This is to avoid cursor and mouse-events troubles
                return shape.contains ( x, y );
            }
        };
        glassPane.setOpaque ( false );
        frame.setGlassPane ( glassPane );
        glassPane.setVisible ( true );
    
        JComponent popup = new JComponent ()
        {
            protected void paintComponent ( Graphics g )
            {
                super.paintComponent ( g );
    
                Graphics2D g2d = ( Graphics2D ) g;
                g2d.setRenderingHint ( RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON );
    
                g2d.setPaint ( Color.BLACK );
                g2d.fill ( shape );
            }
        };
        popup.addMouseListener ( new MouseAdapter ()
        {
            // To block events on the popup
        });
        glassPane.add ( popup );
        popup.setBounds ( shape.getBounds () );
        popup.setVisible ( true );
    
        frame.setSize ( 800, 500 );
        frame.setLocationRelativeTo ( null );
    
        frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
        frame.setVisible ( true );
    }
    

    这是放置在玻璃板上的弹出窗口的简单示例。如您所见,它仅存在于 JFrame 内部,但有别名,可在任何类型的操作系统上正常工作。

    【讨论】:

    • 我已经注意到问题并更改了文本,但感谢您的提示 :)
    • 我选择了您的第三个示例的变体。玻璃板是垃圾! :)
    • @JonasByström 实际上,要在主应用程序框架内显示弹出窗口之类的内容,最好使用 LayeredPane 而不是 GlassPane。确切地说 - JLayeredPane.PALETTE_LAYER 是您需要将组件放置在框架内的 LayeredPane 上的层。 LayeredPane 更好,因为您不会遇到轻量级弹出窗口(如组合框/菜单/工具提示)与玻璃窗格上的组件重叠的问题。玻璃板可能会出现这些问题,因为它是框架内的最上层 - 只有另一个窗口可以高于玻璃板上的任何东西。
    • @JonasByström 查看本教程以查看整个 JFrame/JDialog/JWindow 层结构:docs.oracle.com/javase/tutorial/uiswing/components/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多