积微成著 不积跬步,无以至千里

首页   >   web开发   >   Swing界面优化JComboBox详细图文教程

Swing界面优化JComboBox详细图文教程

之前写过一个SwingQQ的项目,在字体设置那些下拉选项中,没有找到如何优化UI,所以就还是用原生的组件,把整体界面的美观度都给拖累了,后来自己也在iteye上发表了几篇swing界面优化的帖子,但是始终还是没有将JComboBox组件给优化出来,最近研究了一下开源组件weblookandfeel,学习了一下实现思路,自己写个例子分享一下。

先给大家看一下之前那个惨不忍睹的界面(http://www.iteye.com/topic/1137293),就是选择颜色背景那块的下拉框,真的很丑

Java Swing界面优化JComboBox教程

简直辣眼睛,下面来分析一下如何优化:

jcombobox总共有三个部分组成:editor、button、popup,分别是编辑区域、下拉按钮、展示区域

Java Swing界面优化JComboBox教程

可以说,拉低美观的最主要原因就是这个按钮,其次是下拉框中的一些颜色搭配,就从这两个点入手了。

之前在iteye上也发表过其他组件优化的帖子(JTree的优化),里面提到了两个概念,一个是UI,一个是Renderer,这两个东西就是优化的代码所在了,在swing中,每个组件都会有一个UI,入口就是他里面的installUI()方法,也就是我们需要重写这个方法;Renderer的中文释义是“渲染”,他在swing中扮演渲染器的作用,UI负责调用它来渲染出每一个组件,里面会有一个get*CellRendererComponent()方法,这里用*号,是因为每个组件的名字都不一样,它会返回每个渲染后的单个元素。

好了,概念弄清楚了,下面我们来看看代码,根据上面所说,直接找到BasicComboBoxUI,里面有三个关于button的方法和一个关于popup(下拉框)的方法

/**
 * This public method is implementation specific and should be private. Do
 * not call or override.
 *
 * @see #createArrowButton
 */
public void configureArrowButton() {
	if ( arrowButton != null ) {
		arrowButton.setEnabled( comboBox.isEnabled() );
		arrowButton.setFocusable(comboBox.isFocusable());
		arrowButton.setRequestFocusEnabled(false);
		arrowButton.addMouseListener( popup.getMouseListener() );
		arrowButton.addMouseMotionListener( popup.getMouseMotionListener() );
		arrowButton.resetKeyboardActions();
		arrowButton.putClientProperty("doNotCancelPopup", HIDE_POPUP_KEY);
		arrowButton.setInheritsPopupMenu(true);
	}
}

/**
 * This public method is implementation specific and should be private. Do
 * not call or override.
 *
 * @see #createArrowButton
 */
public void unconfigureArrowButton() {
	if ( arrowButton != null ) {
		arrowButton.removeMouseListener( popup.getMouseListener() );
		arrowButton.removeMouseMotionListener( popup.getMouseMotionListener() );
	}
}

/**
 * Creates a button which will be used as the control to show or hide
 * the popup portion of the combo box.
 *
 * @return a button which represents the popup control
 */
protected JButton createArrowButton() {
	JButton button = new BasicArrowButton(BasicArrowButton.SOUTH,
								UIManager.getColor("ComboBox.buttonBackground"),
								UIManager.getColor("ComboBox.buttonShadow"),
								UIManager.getColor("ComboBox.buttonDarkShadow"),
								UIManager.getColor("ComboBox.buttonHighlight"));
	button.setName("ComboBox.arrowButton");
	return button;
}

/**
 * Creates the popup portion of the combo box.
 *
 * @return an instance of <code>ComboPopup</code>
 * @see ComboPopup
 */
protected ComboPopup createPopup() {
	return new BasicComboPopup( comboBox );
}

中间的方法可以不看,它是用来取消button的事件,主要就是第三个和第一个方法,第三个方法创建了一个Jbutton,第一个方法对这个Jbutton加入了一系列的配置和事件,它这个Jbutton是一个BasicArrowButton,我们可以不用,因为Jbutton很简单,完全可以自己来写;而最后一个方法则是用来重写popup的,框框没有过多要求,简洁大方就很好看了,所以只需要调整一下颜色即可。

so,直接上代码了,继承BasicComboBoxUI,重写相关的方法

package com.wolffy.ui;

import com.alee.laf.combobox.WebComboBoxUI;
import com.wolffy.render.MyListCellRenderer;

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

/**
 * Created by SongFei on 2017/10/24.
 */
public class MyComboBoxUI extends BasicComboBoxUI {

    private static ImageIcon DOWN_ICON = new ImageIcon(WebComboBoxUI.class.getResource("icons/down.png"));

    private static Color DEFAULT_COLOR = new Color(150, 207, 254);

    @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对象
        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;
    }
}

代码中有两个地方需要注意,一个是paint方法,一个是MyButtonUI,之所以将paint方法也重写一下,因为它的背景和值都是绘制出来的,这个方法会被频繁调用,这里可以控制一下它的焦点颜色;至于MyButtonUI则是为了代码更清晰,将公共的代码拆分出来

package com.wolffy.ui;

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

/**
 * Created by SongFei on 2017/11/1.
 */
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);//父类不用绘制边框
    }

}

大家可以看到,MyButtonUI中也是一对set,这些东西放到MyComboBoxUI的createArrowButton()方法中也是可以的。

按钮和整体的border,已经优化的差不多了,再来看看下拉框popup组件,因为它有很多item,所以是一个list集合的形式,它需要循环渲染,swing调用的是ListCellRenderer,上面说过了渲染器中,主要用到的方法是get*CellRendererComponent(),那么这里需要重写的方法就是getListCellRendererComponent()了。

下拉框中的内容,都是一行一行的,从左到右,只显示一个纯文本,我们可以用一个Jlabel来渲染,这个是最简单的,想要更多的效果,大家可以自行组合,比如说用Jpanel来渲染,Jpanel里面可以放N多组件

package com.wolffy.render;

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

/**
 * Created by SongFei on 2017/11/2.
 */
public class MyListCellRenderer implements ListCellRenderer {

    private DefaultListCellRenderer defaultCellRenderer = new DefaultListCellRenderer();

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {

        // 每一行,都转换成jlabel来处理
        JLabel renderer = (JLabel) defaultCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

        // 每一行的jlabel的颜色
        if (isSelected) {
            renderer.setBackground(new Color(150, 207, 254));
            renderer.setForeground(Color.WHITE);
        } else {
            renderer.setBackground(null);
        }

        // 字体靠左
        renderer.setHorizontalAlignment(JLabel.LEFT);

        // 左侧padding
        renderer.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));

        // render的宽高
        renderer.setPreferredSize(new Dimension(100, 25));

        // list背景色,也就是向下的按钮左边儿那一块儿
        list.setSelectionBackground(null);
        list.setBorder(null);

        return renderer;
    }

}

UI和Renderer都已经重写了,那么我们来写一个Frame调用一下,看看效果

package com.wolffy.frame;

import com.wolffy.ui.MyComboBoxUI;

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

/**
 * Created by SongFei on 2017/10/24.
 */
public class LoginFrame extends JFrame {

    private static final long serialVersionUID = 474825076033661007L;

    private JPanel jPanel;

    private JComboBox jComboBox;

    public LoginFrame() {
        initGUI();
    }

    private void initGUI() {
        setSize(700, 500);
        //setUndecorated(true);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        jPanel = new JPanel();
        jPanel.setLayout(null);
        jPanel.setBorder(BorderFactory.createLineBorder(Color.GRAY));
        getContentPane().add(jPanel, BorderLayout.CENTER);

        String[] texts = {"张三", "李四", "王五", "赵六", "田七"};
        jComboBox = new JComboBox<>(texts);
        //jComboBox.setEditable(true);
        jComboBox.setUI(new MyComboBoxUI());
        jComboBox.setBounds(100, 70, 100, 50);
        jPanel.add(jComboBox);
    }

    public static void main(String[] args) {
        LoginFrame loginFrame = new LoginFrame();
        loginFrame.setVisible(true);
        //loginFrame.pack();
    }

}

效果图如下:

JavaSwing界面优化JComboBox教程

和之前一比,简直不要太好看!

QQ群:积微成著官方群(686430774),验证消息:积微成著

站长Q:1347384268(加好友请注明来意)

分享到:

欢迎分享本文,转载请注明出处!

作者:不忘初心

发布时间:2017-11-03

永久地址:http://www.jiweichengzhu.com/article/073d9331de4f401098e5c934e84cdf45