【问题标题】:Swing: Create a UWP ("Metro")–like buttonSwing:创建一个类似 UWP(“Metro”)的按钮
【发布时间】:2017-09-28 06:21:26
【问题描述】:

我想设计一个看起来像 UWP 的 Swing 按钮 - 就像这样 [Windows 设置应用程序]:

这是我目前所拥有的:

使用以下代码:

Font f = new Font("Segoe UI", Font.PLAIN, 20);
Color gray = new Color(204, 204, 204);
button.setFont(f);
button.setBackground(gray);
button.setContentAreaFilled(false);
button.setFocusPainted(false);
button.setFocusable(false);
button.setForeground(Color.BLACK);
button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));

无论button.setContentAreaFilled(Boolean b); 属性设置为false 还是true,都无法更改背景颜色,因为EmptyBorder 继承了Windows Swing 按钮的默认颜色。

悬停(悬停时颜色变化)也停止运行,属性设置为false

设置button.setContentAreaFilled(true); 会得到以下结果,这也不是很理想,因为按钮的背景仍然没有改变默认颜色(+它有一个轮廓):

我的问题基本上是:如何修改我的代码以获得以下类似 UWP 的设计?

默认值:

悬停:

【问题讨论】:

    标签: java swing netbeans uwp


    【解决方案1】:

    您可以通过多种方式“可能”做到这一点

    • 创建工厂方法以应用模仿功能所需的属性和侦听器
    • 创建一个从 JButtonAbstractButton 扩展的新类,并在自包含包中提供您需要的核心功能/属性
    • 您可以提供自己的 UI 委托,并自定义核心按钮的外观和感觉

    每种方法都有其优点和缺点,您需要决定哪种方法更能满足您的整体需求。

    例如,将自定义外观委托提供给现有代码库要容易得多,因为您无需更改代码,只需在您想使用时安装外观即可

    以下是使用外观委托的示例,这只是概念验证,可能还有很多额外的功能/工作需要完成 - 例如,我想提供更多提示使用的颜色和滚动边框的粗细,例如

    import java.awt.Color;
    import java.awt.Font;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.Insets;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javaapplication24.Test.MetroLookAndFeel;
    import javax.swing.AbstractButton;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.Border;
    import javax.swing.border.CompoundBorder;
    import javax.swing.border.EmptyBorder;
    import javax.swing.border.LineBorder;
    import javax.swing.plaf.basic.BasicButtonUI;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane() {
                setBorder(new EmptyBorder(10, 10, 10, 10));
                JButton fancyPB = new JButton("Restart Now");
                fancyPB.setUI(new MetroLookAndFeel());
    
                JButton normalPB = new JButton("Restart Now");
    
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.insets = new Insets(4, 4, 4, 4);
    
                add(fancyPB, gbc);
                add(normalPB, gbc);
            }
    
        }
    
        public class MetroLookAndFeel extends BasicButtonUI {
    
            // This could be computed properties, where the border color
            // is determined based on other properties
            private Border focusBorder = new CompoundBorder(new LineBorder(Color.DARK_GRAY, 3), new EmptyBorder(7, 13, 7, 14));
            private Border unfocusedBorder = new EmptyBorder(10, 14, 10, 14);
    
            @Override
            protected void installDefaults(AbstractButton b) {
    
                super.installDefaults(b);
                Font f = new Font("Segoe UI", Font.PLAIN, 20);
                Color gray = new Color(204, 204, 204);
                b.setFont(f);
                b.setBackground(gray);
                b.setContentAreaFilled(false);
                b.setFocusPainted(false);
                // This seems like an oddity...
                b.setFocusable(false);
                b.setForeground(Color.BLACK);
    //            b.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
                b.setBorder(unfocusedBorder);
            }
    
            @Override
            protected void installListeners(AbstractButton b) {
                super.installListeners(b);
                b.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseEntered(MouseEvent e) {
                        ((JButton)e.getSource()).setBorder(focusBorder);
                    }
    
                    @Override
                    public void mouseExited(MouseEvent e) {
                        ((JButton)e.getSource()).setBorder(unfocusedBorder);
                    }                
                });
            }
    
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      这是一个例子

      import java.awt.Color;
      import java.awt.Font;
      import java.awt.event.MouseAdapter;
      import java.awt.event.MouseEvent;
      
      import javax.swing.BorderFactory;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.WindowConstants;
      import javax.swing.border.CompoundBorder;
      import javax.swing.border.LineBorder;
      
      public class WinButton {
      
          public static void main(String[] args) {
              final JButton button = createWinButton("Restart now");
              JFrame frm = new JFrame("Test");
              JPanel layoutPanel = new JPanel();
              layoutPanel.add(button);
              frm.add(layoutPanel);
              frm.setSize(200, 200);
              frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
              frm.setLocationRelativeTo(null);
              frm.setVisible(true);
          }
      
          private static JButton createWinButton(String text) {
              final JButton button = new JButton(text);
              Font f = new Font("Segoe UI", Font.PLAIN, 20);
              Color gray = new Color(204, 204, 204);
              button.setFont(f);
              button.setBackground(gray);
              button.setContentAreaFilled(false);
              button.setFocusPainted(false);
              button.setFocusable(false);
              button.setForeground(Color.BLACK);
              button.setOpaque(true);
              button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
              button.addMouseListener(new MouseAdapter() {
                  @Override
                  public void mouseEntered(MouseEvent e) {
                      Color borderColor = new Color(100, 100, 100);
                      button.setBorder(new CompoundBorder(new LineBorder(borderColor, 3), BorderFactory.createEmptyBorder(7, 11, 7, 11)));
                  }
      
                  @Override
                  public void mouseExited(MouseEvent e) {
                      button.setBorder(BorderFactory.createEmptyBorder(10, 14, 10, 14));
                  }
              });
              return button;
          }
      }
      

      【讨论】:

        【解决方案3】:

        为了操纵鼠标事件,例如悬停,您必须自己处理这些事件,其中一种方法是创建自己的按钮。


        编辑

        按照 Sergiy Medvynskyy 的回答,将鼠标侦听器添加到您的按钮会是一种更好的做法,因为不需要修改按钮类本身。


        您可能还需要使用 LaF(UIManager 外观和感觉),因为它可能会给 UI 组件添加不想要的效果。

        请注意,这会影响所有界面组件。

        以下是一个基本示例。

        按钮:

        import java.awt.Color;
        import java.awt.Font;
        import java.awt.event.MouseEvent;
        import javax.swing.BorderFactory;
        import javax.swing.JButton;
        import javax.swing.border.Border;
        
        public class UWPButton extends JButton{
        
            private final Border regularBorder;
            private final Border hoverBorder;
        
            private final Color bgColor = new Color(204, 204, 204);
            private final Color transparent;
            private final Font metroFont = new Font("Segoe UI", Font.PLAIN, 20);
        
            public UWPButton() {
                super();
        
                // thickness of the button's borders
                int thickness = 4;
        
                // Create a transparent color, to use for the regular border
                // could also be the bgColor (204 204 204)
                Color c = new Color(1f, 0f, 0f, 0f);
                transparent = c;
        
                // Creates a compound border to be present on the button majority of the time.
                // uses an invisible line border on the outside in order to change border smoothly
                // the inside border is just an empty border for insets.
                regularBorder = BorderFactory.createCompoundBorder(
                        BorderFactory.createLineBorder(transparent, thickness), //outside
                        BorderFactory.createEmptyBorder(10, 14, 10, 14));       //inside
        
                // Creates a compound border which will be used when the mouse hovers the button.
                // the outside border has a darker colour than the background
                // the inside border is just an empty border for insets.
                hoverBorder = BorderFactory.createCompoundBorder(
                        BorderFactory.createLineBorder(bgColor.darker(), thickness), //outside
                        BorderFactory.createEmptyBorder(10, 14, 10, 14));            //inside
        
                // configures the button
                initButton();
            }
        
            // Here is where the mouse events are treated.
            @Override
            protected void processMouseEvent(MouseEvent e) {
                // Gets the ID of the event, 
                // if it is a mouse entering the button, sets the new border
                // if it is the mouse exiting the button, resets the border to the default.
                switch(e.getID()){
                    case MouseEvent.MOUSE_ENTERED:
                        this.setBorder(hoverBorder);
                        break;
                    case MouseEvent.MOUSE_EXITED:
                        this.setBorder(regularBorder);
                        break;
                }
        
                // the parent then does all the other handling.
                super.processMouseEvent(e); 
            }
        
        
            // Configures the button.
            private void initButton(){
        
                setFont(metroFont);
                setBorder(regularBorder);
                setBackground(bgColor);
        
                setFocusPainted(false);
            }
        
        }
        

        更改 LaF:如果您通过 IDE 拖放创建框架,您的 main 中应该有类似的内容(来自 netbeans):

        将“Nimbus”更改为“Metro”(或删除 LaF 设置),这样界面管理器就不会渲染所有 Nimbus 内容。 注意:没有“Metro”LaF,这只是为了禁用应用的LaF。

        // other stuff above
        //
        // change "Nimbus" for something else or just remove this try-catch block
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
        
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) {
                java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        // other stuff bellow
        

        有关 LaF(外观和感觉)的更多信息: https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html

        【讨论】:

          猜你喜欢
          • 2017-07-28
          • 2015-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-02
          • 2016-05-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多