1.2 swing之美化与拓展组件

993 阅读4分钟

引言:

众所周知,swing的默认样式已经控件是非常基础,并且不是特别好看的,本节将介绍如何美化界面,以及拓展控件

目录

  1. JFrame添加背景图以及应用图标
  2. 全局字体抗锯齿
  3. 自定义系统关闭窗口功能
  4. JCheckBox修改勾选图标
  5. JCombobox设置边框背景颜色
  6. JMenu修改字体居中显示
  7. JTextField输入框添加图标
  8. JTable样式居中背景调整或隐藏某一列
  9. ComboboxUI美化
  10. JButton配置隐藏文字聚焦框
  11. JPassword配置文本显示以及隐藏
一、JFrame添加背景图以及应用图标

代码入下:实现JFrame

public MyFrame() {
    super();
    setTitle("XXX");

    try {
        URL imgURL = MyFrame.class.getResource("/static/image/titleLogin.png");
        Image image = ImageIO.read(imgURL);
        setIconImage(image);
    } catch (IOException e) {
        e.printStackTrace();
    }
    try{
        URL imgURL = MyFrame.class.getResource("/static/image/backgroud.png");
        ImageIcon img = new ImageIcon(ImageIO.read(imgURL));
        JLabel imgLabel = new JLabel(img);//将背景图放在标签里。

        this.getLayeredPane().add(imgLabel, new Integer(Integer.MIN_VALUE));//注意这里是关键,将背景标签添加到jfram的LayeredPane面板里。
        imgLabel.setBounds(0,0,img.getIconWidth(), img.getIconHeight());//设置背景标签的位置
        Container cp=this.getContentPane();
        cp.setLayout(new BorderLayout());
        ((JPanel)cp).setOpaque(false);

    }catch(IOException e1){
        e1.printStackTrace();
    }
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    setBackground(new java.awt.Color(255, 255, 255));
    setPreferredSize(new Dimension(920,492));
    setSize(920,492);
    setLocationRelativeTo(null);
    setResizable(false);

}
二、全局字体抗锯齿

代码如下:需要注意要在JFrame之前调用UIs.setUI();

import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;


public class UIs {

    private static final String FALLBACK_FONT_FAMILY_NAME = Font.SANS_SERIF;
    private static final Map<String, String> FONT_FAMILY_NAMES = new HashMap<>();
    private static final String[] BEST_FONT_FAMILIES = {
            "微软雅黑", "arial", "sans-serif"
    };
    private static final int BEST_FONT_SIZE = 14; // 12px

    static {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fontFamilyNames = env.getAvailableFontFamilyNames();
        for (String fontFamilyName : fontFamilyNames) {
            FONT_FAMILY_NAMES.put(fontFamilyName.toLowerCase(), fontFamilyName);
        }
        if (!FONT_FAMILY_NAMES.containsKey("serif")) {
            FONT_FAMILY_NAMES.put("serif", Font.SERIF);
        }
        if (!FONT_FAMILY_NAMES.containsKey("sans-serif")) {
            FONT_FAMILY_NAMES.put("sans-serif", Font.SANS_SERIF);
        }
    }

    public static void enableAntiAliasing() {
        System.setProperty("awt.useSystemAAFontSettings", "on");
        System.setProperty("swing.aatext", "true");
    }

    public static String getLookAndFeel() {
        try {
            for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    return info.getClassName();
                }
            }
        } catch (Exception ignore) {
        }
        return UIManager.getCrossPlatformLookAndFeelClassName();
    }

    public static String getFontFamily(String[] fontFamilies) {
        for (String fontFamily : fontFamilies) {
            fontFamily = fontFamily.toLowerCase();
            if (FONT_FAMILY_NAMES.containsKey(fontFamily)) {
                return FONT_FAMILY_NAMES.get(fontFamily);
            }
        }
        return FALLBACK_FONT_FAMILY_NAME;
    }

    public static String[] getBestFontFamilies() {
        return BEST_FONT_FAMILIES;
    }

    public static int getBestFontSize() {
        return BEST_FONT_SIZE;
    }



    public static void setUI() {
        enableAntiAliasing();
        // set LookAndFeel
//        try {
//            UIManager.setLookAndFeel(getLookAndFeel());
//        } catch (Exception ignore) {
//        }
        // set DefaultFont
        String bestFontFamily = getFontFamily(getBestFontFamilies());
        for (Map.Entry<Object, Object> entry : UIManager.getDefaults().entrySet()) {
            if (entry.getValue() instanceof FontUIResource) {
                FontUIResource fontUIRes = (FontUIResource) entry.getValue();
                entry.setValue(new FontUIResource(
                        bestFontFamily,
                        fontUIRes.getStyle(),
                        getBestFontSize() > fontUIRes.getSize() ?
                                getBestFontSize() : fontUIRes.getSize()
                ));
            }
        }
    }
}

三、自定义系统关闭窗口功能

1.首先,重写JFrame的processWindowEvent

@Override
protected void processWindowEvent(WindowEvent pEvent) {
    if (pEvent.getID() == WindowEvent.WINDOW_CLOSING) {

        /** 防止用户多次点击“关闭”按钮造成重复保存 **/
        //处理JFrame关闭事件……
        CommonBtnEvent.exitSystemBtnHandler();
    } else {
        //忽略其他事件,交给JFrame处理
        super.processWindowEvent(pEvent);

    }

}

2.其次,书写自己的CommonBtnEvent方法

import lombok.extern.slf4j.Slf4j;

import javax.swing.*;

/**
 * @Author:hongyueyuan
 * @Date: 2022/3/19
 */
@Slf4j
public class CommonBtnEvent {
 
    public static void exitSystemBtnHandler()  {

        log.info("退出系统操作");
        
        int result = -1;
        try{
            LookAndFeel lookAndFeel=UIManager.getLookAndFeel();
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            result = JOptionPane.showConfirmDialog(null, "是否要退出系统?", "退出确认", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
            UIManager.setLookAndFeel(lookAndFeel);
        }catch(Exception e){
            log.error("",e);
        }

        if (result == JOptionPane.YES_OPTION){
               //退出系统
                System.exit(0);

        }
    }

}
四、 JCheckBox修改勾选图标

代码如下:直接设置属性setIcon与setSelectedIcon

import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * @Author:hongyueyuan
 * @Date: 2022/4/24
 */
public class JComCheckBox extends JCheckBox {


    public JComCheckBox() {

        try{
            setIcon(new ImageIcon(ImageIO.read(JComCheckBox.class.getResourceAsStream("/static/image/checkbox_unselect.png"))));
            setSelectedIcon(new ImageIcon(ImageIO.read(JComCheckBox.class.getResourceAsStream("/static/image/checkbox_select.png"))));
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
五、JCombobox设置边框背景颜色

代码如下:

import javax.swing.*;
import java.awt.*;

/**
 * @Author:hongyueyuan
 * @Date: 2022/4/24
 */
public class JComCombobox  extends JComboBox {


    public JComCombobox() {
        setFont(new java.awt.Font("微软雅黑", 0, 28)); // NOI18N
        setForeground(new java.awt.Color(38, 38, 38));
        setBorder(new javax.swing.border.LineBorder(new java.awt.Color(24, 144, 255), 2, true));
        setPreferredSize(new java.awt.Dimension(420, 74));
        setMinimumSize(new java.awt.Dimension(420, 74));
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D  g2d= (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        //消除画图锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        super.paintComponent(g2d);

    }



}
六、JMenu修改字体居中显示

特别注意:由于JMenuItem配置setHorizontalAlignment(SwingConstants.CENTER);没有效果;所以用下面的方式
1.首次,重写BasicMenuItemUI

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicMenuItemUI;
import java.awt.*;

/**
 * @Author:hongyueyuan
 * @Date: 2022/4/25
 */
public class CustomMenuUI extends BasicMenuItemUI {

    public static ComponentUI createUI(JComponent c) {
        return new CustomMenuUI();
    }

    @Override
    protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect, String text) {
        int w2 = menuItem.getBounds().width/2-2;
        textRect.translate(w2 - textRect.width/2, 0);
        Graphics2D  g2d= (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        //消除画图锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        super.paintText(g2d, menuItem, textRect, text);
    }
}

2、JMenuItem配置UI

JPopupMenu popupMenu = new JPopupMenu();

// 创建 一级菜单
JMenuItem classMenuItem = new JMenuItem("切换班级");
classMenuItem.setBackground(Color.white);
classMenuItem.setFont(new java.awt.Font("微软雅黑", 0, 14));
classMenuItem.setForeground(new Color(38,38,38));
classMenuItem.setPreferredSize(new Dimension(126,50));
classMenuItem.setSize(new Dimension(126,50));
classMenuItem.setMinimumSize(new Dimension(126,50));
classMenuItem.setMaximumSize(new Dimension(126,50));
classMenuItem.setHorizontalAlignment(SwingConstants.CENTER);
classMenuItem.setHorizontalTextPosition(SwingConstants.CENTER);
classMenuItem.setVerticalAlignment(SwingConstants.CENTER);
classMenuItem.setVerticalTextPosition(SwingConstants.CENTER);
classMenuItem.setUI(new CustomMenuUI());
popupMenu.add(classMenuItem);
七、JTextField输入框添加图标

特别注意:setMargin和setBorder不能同时存在,setBorder会被setMargin覆盖没有下效果,paintBorder方法
代码如下:

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

/**
 * @Author:hongyueyuan
 * @Date: 2022/3/9
 */
public class JTextFieldUser extends JTextField {

    private ImageIcon icon;

    @Override
    protected void paintBorder(Graphics g) {
        //设置外边框
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setColor(new Color(217,217,217));
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 1, 1);
        g2d.dispose();
    }

    public JTextFieldUser() throws IOException {

        // 获取当前路径下的图片
        setFont(new java.awt.Font("微软雅黑", 0, 16)); // NOI18N
        setToolTipText("账号"); // NOI18N
        setPreferredSize(new java.awt.Dimension(360, 50));
        setSelectionColor(new java.awt.Color(24, 144, 255));

        URL imgURL = JTextFieldUser.class.getResource("/static/image/user.png");
        BufferedImage image = ImageIO.read(imgURL);
        int imgwidth=image.getWidth();
        int imgheight=image.getHeight();
        icon=new ImageIcon(image);
         //配置文本输入向右偏移imgwidth*2的宽度
        Insets insets = new Insets(0, imgwidth*2, 0, 0);
        setMargin(insets);

    }


    @Override
    public void paintComponent(Graphics g) {
        Insets insets = getInsets();
        super.paintComponent(g);
        int iconWidth = icon.getIconWidth();
        int iconHeight = icon.getIconHeight();
        int Height = this.getHeight();
        //在文本框中画上之前图片
        icon.paintIcon(this, g, iconWidth/2, (Height - iconHeight) / 2);
    }

}

八、JTable样式居中背景调整或隐藏某一列 代码如下:

import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import sun.swing.table.DefaultTableCellHeaderRenderer;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.List;

/**
 * @Author:hongyueyuan
 * @Date: 2022/3/14
 */
@NoArgsConstructor
@Slf4j
public class StudentListTable {

    private static final String[] COL_NAMES = {"studentId", "学生名称", "状态"};

    public void initTable(List<PcStudentListResp> list, JTable jtable) throws IOException {
        jtable.setEnabled(false);
        jtable.setGridColor(new java.awt.Color(250, 250, 250));
        jtable.setBackground(new Color(255,255,255));
        jtable.setShowVerticalLines(false);

        String[] colNames = COL_NAMES;
        DefaultTableModel model = new DefaultTableModel(colNames, 0) {
            @Override
            public Class<?> getColumnClass(int column) {
                if (getRowCount() > 0) {
                    Object value = getValueAt(0, column);
                    if (value != null) {
                        return getValueAt(0, column).getClass();
                    }
                }

                return super.getColumnClass(column);
            }
        };


        int iconHeight = 25;
        URL penConnectUrl = StudentListPanel.class.getResource("/static/image/connect.png");
        BufferedImage penConnectImg = ImageIO.read(penConnectUrl);
        penConnectImg.getScaledInstance(iconHeight, iconHeight, Image.SCALE_FAST);

        ImageIcon penConnectIcon = new ImageIcon(penConnectImg);

        URL penDisconnectUrl = StudentListPanel.class.getResource("/static/image/disconnect.png");
        BufferedImage penDisconnectImg = ImageIO.read(penDisconnectUrl);
        penDisconnectImg.getScaledInstance(iconHeight, iconHeight, Image.SCALE_FAST);
        ImageIcon penDisconnectIcon = new ImageIcon(penDisconnectImg);
        if (list == null || list.size() == 0) {
            log.info("学生列表为空");
        } else {
            for (int i = 0; i < list.size(); i++) {
                Object[] rowData = null;
                if (list.get(i).getStatus().equals(PenConnectStatusEnum.CONNECT_STATUS.getCode())) {
                    rowData = new Object[]{list.get(i).getId(), list.get(i).getSmName(), penConnectIcon};
                } else {
                    rowData = new Object[]{list.get(i).getId(), list.get(i).getSmName(), penDisconnectIcon};
                }
                model.addRow(rowData);
            }
        }


        jtable.setModel(model);
        jtable.setRowHeight(45);
        DefaultTableCellRenderer tcr = new DefaultTableCellRenderer();//单元格渲染器
        tcr.setHorizontalAlignment(SwingConstants.CENTER);//居中显示
        jtable.setDefaultRenderer(Object.class, tcr);//设置渲染器
        jtable.getTableHeader().setPreferredSize(new Dimension(jtable.getWidth() / 2, 45));
        jtable.getTableHeader().setReorderingAllowed(false);
        jtable.getTableHeader().setResizingAllowed(false);
        //jtable.getTableHeader().setBackground(new Color(250,250,250));
        jtable.getTableHeader().setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
        DefaultTableCellRenderer tableRender = new DefaultTableCellRenderer();//单元格渲染器
        tableRender.setHorizontalAlignment(SwingConstants.CENTER);//居中显示
        tableRender.setBackground(new Color(250,250,250));
        tableRender.setFont(new java.awt.Font("微软雅黑", 1, 14));
        tableRender.setForeground(Color.BLACK);
        //tableRender.setBorder(BorderFactory.createLineBorder(Color.red));
        jtable.getTableHeader().setDefaultRenderer(tableRender);
        jtable.getTableHeader().setFont(new Font("微软雅黑", 1, 14));
        jtable.setEnabled(false);
        jtable.getTableHeader().setForeground(new Color(26,26,26));
        hideColumn(jtable, 0);

    }


    protected void hideColumn(JTable table, int index) {
        //隐藏某一列
        TableColumn tc = table.getColumnModel().getColumn(index);
        tc.setMaxWidth(0);
        tc.setPreferredWidth(0);
        tc.setMinWidth(0);
        tc.setWidth(0);
        table.getTableHeader().getColumnModel().getColumn(index).setMaxWidth(0);
        table.getTableHeader().getColumnModel().getColumn(index).setMinWidth(0);
    }


}
九、ComboboxUI美化

代码如下: 1.首先,拓展BasicComboBoxUI

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import java.awt.*;

/**
 * @Author:hongyueyuan
 * @Date: 2022/4/25
 */
public class CustomComboxUI extends BasicComboBoxUI {

    private static Color DEFAULT_COLOR = new Color(24, 144, 255);
    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        JComboBox comboBox = (JComboBox) c;
        comboBox.setFocusable(true);
        comboBox.setOpaque(false);

        comboBox.setRenderer(new MyListCellRenderer());

    }

    @Override
    protected JButton createArrowButton() {
        // 也可以使用BasicComboBoxUI里的arrowButton对象
        ImageIcon DOWN_ICON =null;
        try{
            DOWN_ICON = new ImageIcon(ImageIO.read(CustomComboxUI.class.getResourceAsStream("/static/image/downBtn.png")));
        }catch(Exception e){

        }
        JButton arrow = new JButton();
        // 设置自己定义的UI
        arrow.setUI(new MyButtonUI());
        // 设置下拉箭头图标
        arrow.setIcon(DOWN_ICON);
        // 设置无法获得焦点
        arrow.setFocusable(false);
        // 设置边距,调整图标位置
       // arrow.setMargin(new Insets(0, 20, 0, 0));
        return arrow;
    }

    @Override
    public void paint(Graphics g, JComponent c) {
        // 也可以使用BasicComboBoxUI里的combobox对象
        JComboBox comboBox = (JComboBox) c;

        hasFocus = comboBox.hasFocus();

        Rectangle r = rectangleForCurrentValue();

        // JComboBox的textfield的绘制,并不是靠Renderer来控制
        // 它会通过paintCurrentValueBackground来绘制背景
        // 然后通过paintCurrentValue去绘制显示的值
        Graphics2D g2d = (Graphics2D) g;
        if (!comboBox.isEditable()) {
            paintCurrentValueBackground(g2d, r, hasFocus);
            paintCurrentValue(g2d, r, hasFocus);
        } else {
            paintCurrentValueBackground(g2d, r, hasFocus);
        }

        // 获取焦点时,用不同颜色来区分
        if (comboBox.hasFocus()) {
            g2d.setColor(DEFAULT_COLOR);
        } else {
            g2d.setColor(Color.GRAY);
        }

        // 边框透明度
        //g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));

        // 绘制边框,后两个参数控制圆角
        // 边框也有占位,所以宽高都需要减去2,否则会导致边框不全
        g2d.drawRoundRect(0, 0, comboBox.getWidth() - 2, comboBox.getHeight() - 2, 2, 2);

    }

    @Override
    protected ComboPopup createPopup() {
        BasicComboPopup popup = (BasicComboPopup) super.createPopup();
        // 获取到popup,为其设置边框,和combobox的颜色保持同步
        popup.setBorder(BorderFactory.createLineBorder(DEFAULT_COLOR));
        return popup;
    }
}

2.拓展BasicButtonUI


import javax.swing.*;
import javax.swing.plaf.basic.BasicButtonUI;

/**
 * @Author:hongyueyuan
 * @Date: 2022/4/25
 */
public class MyButtonUI  extends BasicButtonUI implements SwingConstants {

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        JButton button = (JButton) c;
        button.setContentAreaFilled(false);//父类不用绘制内容
        button.setFocusPainted(false);//父类不用绘制焦点
        button.setBorderPainted(false);//父类不用绘制边框
    }
}

3.其次,JComboBox设置UI

gradeCombobox.setUI(new CustomComboxUI());
十、 JButton配置隐藏文字聚焦框

代码如下:

showPasswordBtn.setFocusPainted(false);
十一、 JPasswordField配置文本显示以及隐藏
jspf.setEchoChar(char(0));//这行代码可以显示明文
jspf.setEchoChar('*');//这行代码用于隐藏