您现在的位置是:网站首页 > 代码编程 > JAVA开发JAVA开发
【原】Swing界面优化JTree详细图文教程
不忘初心 2017-11-07 围观() 评论() 点赞() 【JAVA开发】
简介:之前在iteye上发表过一篇关于jtree优化的帖子:swing界面优化进阶五,这几天在整理代码,发现之前写的案例中,有很多的bug和逻辑问题,顺手再次整理了一
之前在iteye上发表过一篇关于jtree优化的帖子:swing界面优化进阶五,这几天在整理代码,发现之前写的案例中,有很多的bug和逻辑问题,顺手再次整理了一下,小改动,性能有大的提升。
对比之前的不同之处:
1、鼠标离开时,将滑入的特效还原;
2、将UI渲染的重复代码,抽出来整理成了一个工具类;
3、在UI渲染中,重构了一部分逻辑,去除了不必要的UI渲染;
先来一张效果图:
好了,最后的效果并没有多大的差异,直接上代码了。
不打包了,图标之类的,在iteye上已经上传过一次了,这里就不再上传了,直接贴代码,大家可以拷贝直接运行。
一、重写我们自己的Node节点,这里的一个小改动,增加了一个boolean值来记录是否选中,这样就不需要每次getBackground()来判断是否选中了
package com.wolffy.node;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.Component;
import java.awt.Dimension;
/**
* Created by SongFei on 2017/11/6.
*/
public class MyTreeNode extends DefaultMutableTreeNode {
private static final long serialVersionUID = 1007068847268622569L;
/**
* 图片
*/
private Icon icon;
/**
* 文字
*/
private String name;
/**
* 签名
*/
private String sign;
/**
* 是否选中
*/
private boolean select;
private JPanel groupPanel;
private JPanel buddyPanel;
private JLabel iconLabel;
private JLabel nameLabel;
private JLabel signLabel;
public MyTreeNode() {
}
/**
* 初始化分组节点
*
* @param name 名称
*/
public MyTreeNode(Icon icon, String name) {
this.icon = icon;
this.name = name;
// 初始化UI
initCateGUI();
}
/**
* 初始化好友节点
*
* @param icon 头像
* @param nick 昵称
* @param sign 签名
*/
public MyTreeNode(Icon icon, String nick, String sign) {
this.icon = icon;
this.name = nick;
this.sign = sign;
// 初始化UI
initNodeGUI();
}
/**
* 自定义分组UI
*/
private void initCateGUI() {
groupPanel = new JPanel();
groupPanel.setLayout(null);
// groupPanel.setOpaque(false);
// 这里大家注意,当我们写好UI之后可能会发现他的颜色不太对,
// 这时候千万不要用上面那句,不然当我们想再次改变其颜色的时候,就生效不了
// 红绿蓝分别为255的这个颜色趋近于透明,我们可以用它来代替setOpaque
// groupPanel.setBackground(new Color(255,255,255));
// 突然发现置成null也可以
groupPanel.setBackground(null);
groupPanel.setPreferredSize(new Dimension(300, 25));
// groupPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
iconLabel = new JLabel(icon);
iconLabel.setBounds(6, 5, 20, 16);
groupPanel.add(iconLabel);
nameLabel = new JLabel(name);
nameLabel.setBounds(23, 0, 132, 28);
groupPanel.add(nameLabel);
}
/**
* 自定义好友UI
*/
private void initNodeGUI() {
buddyPanel = new JPanel();
buddyPanel.setLayout(null);
buddyPanel.setBackground(null);
buddyPanel.setPreferredSize(new Dimension(300, 50));
// buddyPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
iconLabel = new JLabel(icon);
iconLabel.setBounds(8, 4, 42, 42);
buddyPanel.add(iconLabel);
nameLabel = new JLabel(name);
nameLabel.setBounds(59, 5, 132, 19);
buddyPanel.add(nameLabel);
signLabel = new JLabel(sign);
signLabel.setBounds(59, 28, 132, 17);
buddyPanel.add(signLabel);
}
/**
* 将自定义UI返回给渲染器 <br/>
* 供渲染器调用,返回的必须是一个Component
*
* @return
*/
public Component getGroupView() {
return groupPanel;
}
/**
* 将自定义UI返回给渲染器 <br/>
* 供渲染器调用,返回的必须是一个Component
*
* @return
*/
public Component getBuddyView() {
return buddyPanel;
}
public JLabel getIconLabel() {
return iconLabel;
}
public JLabel getNameLabel() {
return nameLabel;
}
public JLabel getSignLabel() {
return signLabel;
}
public Icon getIcon() {
return icon;
}
public void setIcon(Icon icon) {
this.icon = icon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public boolean isSelect() {
return select;
}
public void setSelect(boolean select) {
this.select = select;
}
}
二、继承DefaultTreeCellRenderer,重写里面的getTreeCellRendererComponent()方法(关于renderer,在iteye中的帖子中已经说过了)
package com.wolffy.render;
import com.wolffy.node.MyTreeNode;
import com.wolffy.util.ImgUtils;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.Component;
/**
* Created by SongFei on 2017/11/6.
*/
public class MyTreeCellRenderer extends DefaultTreeCellRenderer {
private static final long serialVersionUID = -3617708634867111249L;
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
MyTreeNode node = (MyTreeNode) value;
// 根节点从0开始,依次往下
// 一级节点
if (node.getLevel() == 1) {
if (expanded) {
node.getIconLabel().setIcon(ImgUtils.getIcon("arrow_down.png"));
} else {
node.getIconLabel().setIcon(ImgUtils.getIcon("arrow_left.png"));
}
return node.getGroupView();
}
// 二级节点
if (node.getLevel() == 2) {
node.getIconLabel().setIcon(ImgUtils.getIcon("qq_icon.png"));
return node.getBuddyView();
}
return this;
}
}
三、继承BasicTreeUI,重写相关的方法(具体方法在iteye的帖子中也说过了,这里直接贴代码)
package com.wolffy.ui;
import com.wolffy.render.MyTreeCellRenderer;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.plaf.basic.BasicTreeUI;
import java.awt.Graphics;
/**
* Created by SongFei on 2017/11/6.
*/
public class MyTreeUI extends BasicTreeUI {
@Override
public void installUI(JComponent c) {
super.installUI(c);
JTree jTree = (JTree) c;
jTree.setRootVisible(false);// 这个一定要设置,否则会报错类型转换异常
jTree.setToggleClickCount(1);// 单击展开
jTree.setCellRenderer(new MyTreeCellRenderer());
}
// 去除JTree的垂直线
@Override
protected void paintVerticalLine(Graphics g, JComponent c, int x, int top, int bottom) {
}
// 去除JTree的水平线
@Override
protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left, int right) {
}
// 实现父节点与子节点左对齐
@Override
public void setLeftChildIndent(int newAmount) {
}
// 实现父节点与子节点右对齐
@Override
public void setRightChildIndent(int newAmount) {
}
}
四、准备工作已经做好了,直接新建一个jtree
package com.wolffy.frame;
import com.wolffy.node.MyTreeNode;
import com.wolffy.ui.MyTreeUI;
import com.wolffy.util.ImgUtils;
import com.wolffy.util.TreeUtils;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
/**
* Created by SongFei on 2017/10/24.
*/
public class JTreeFrame extends JFrame {
private static final long serialVersionUID = 1683067312766564107L;
/**
* 鼠标滑过
*/
private Color HOVER_COLOR = new Color(200, 200, 200, 100);
/**
* 鼠标点击
*/
private Color SELECT_COLOR = new Color(160, 160, 160, 100);
private JPanel jPanel;
private JTree jTree;
private DefaultMutableTreeNode root;
private DefaultTreeModel model;
public JTreeFrame() {
initGUI();
}
private void initGUI() {
setSize(700, 575);
//setUndecorated(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jPanel = new JPanel();
getContentPane().add(jPanel, BorderLayout.CENTER);
root = new DefaultMutableTreeNode();
model = new DefaultTreeModel(root);
for (int i = 1; i <= 3; i++) {
MyTreeNode cate = new MyTreeNode(ImgUtils.getIcon("arrow_left.png"), "我的分组" + i);
for (int j = 1; j <= 3; j++) {
MyTreeNode node = new MyTreeNode(ImgUtils.getIcon("avatar.png"), "好友" + i + "-" + j, "人生若只如初见");
cate.add(node);
}
root.add(cate);
}
jTree = new JTree(model);
jTree.setUI(new MyTreeUI());
jTree.addMouseListener(new MouseAdapter() {
@Override
public void mouseExited(MouseEvent e) {
TreeUtils.restoreUI(root, model, true);
}
@Override
public void mouseClicked(MouseEvent e) {
TreePath path = jTree.getSelectionPath();
if (path == null) {
return;
}
MyTreeNode node = (MyTreeNode) path.getLastPathComponent();
if (node == null) {
return;
}
// 除了好友节点,其他节点都没有点击选中功能
if (node.getLevel() != 2) {
return;
}
// 已选择了,就不管,避免重复绘制
if (node.isSelect()) {
return;
}
TreeUtils.restoreUI(root, model, false);
TreeUtils.setBackColor(model, node, SELECT_COLOR);
node.setSelect(true);
}
});
jTree.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
TreePath path = jTree.getPathForLocation(e.getX(), e.getY());
if (path == null) {
return;
}
MyTreeNode node = (MyTreeNode) path.getLastPathComponent();
if (node == null) {
return;
}
// 已选择了,就不管,避免重复绘制
if (node.isSelect()) {
return;
}
TreeUtils.restoreUI(root, model, true);
TreeUtils.setBackColor(model, node, HOVER_COLOR);
}
});
jPanel.add(jTree);
}
public static void main(String[] args) {
JTreeFrame jTreeFrame = new JTreeFrame();
jTreeFrame.setVisible(true);
}
}
上述代码中,添加鼠标事件的时候,大家需要注意一下mouseMoved()方法,此方法在MouseListener和MouseMotionListener都有,但是我们选择了MouseMotionListener中了,因为MouseListener中的mouseMoved()方法,他只在第一次鼠标进入到jtree时生效,当你将鼠标从一个节点移动到另外一个节点时,不会再次触发,而在MouseMotionListener中的mouseMoved()方法,只要你鼠标在移动,他就会不停的触发。
在iteye的帖子中,鼠标滑入之后,再次移出,jtree上的滑入特效还存在,当时没在意,这次看了一下QQ的做法,QQ是除了点击选中的那一个之外,其他的特效都恢复原状,而在mouseMoved()和mouseClick()的时候,也会重绘其他节点,这几处的代码有很多重复,所以抽了一个工具类TreeUtils.java
package com.wolffy.util;
import com.wolffy.node.MyTreeNode;
import javax.swing.JPanel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import java.awt.Color;
/**
* Created by SongFei on 2017/11/6.
*/
public class TreeUtils {
/**
* 设置Node背景颜色
*
* @param model 模型,需要用它来刷新jtree
* @param node 自定义的MyTreeNode节点
* @param color 颜色
*/
public static void setBackColor(DefaultTreeModel model, MyTreeNode node, Color color) {
JPanel panel = null;
if (node.getLevel() == 1) {
panel = (JPanel) node.getGroupView();
}
if (node.getLevel() == 2) {
panel = (JPanel) node.getBuddyView();
}
// 鼠标move的时候,此方法会被频繁调用,用颜色做一下比对,降低性能开销
if (panel != null && panel.getBackground() != color) {
panel.setBackground(color);
model.reload(node);
}
}
/**
* 重置jtree的UI,将一些操作效果都还原
*
* @param root 根节点
* @param model 模型
* @param retainSelect 是否保留已选的Node
*/
public static void restoreUI(DefaultMutableTreeNode root, DefaultTreeModel model, boolean retainSelect) {
int count = root.getChildCount();
if (count <= 0) {
return;
}
// 重置二级节点UI
for (int i = 0; i < count; i++) {
MyTreeNode group = (MyTreeNode) root.getChildAt(i);
restoreGroupUI(model, group);
// 重置三级节点UI
for (int j = 0; j < group.getChildCount(); j++) {
MyTreeNode buddy = (MyTreeNode) group.getChildAt(j);
// 保留已选择的Node
if (retainSelect && buddy.isSelect()) {
continue;
}
restoreBuddyUI(model, buddy);
}
}
}
/**
* 重置分组节点的UI
*
* @param model 模型
* @param node 自定义Node
*/
private static void restoreGroupUI(DefaultTreeModel model, MyTreeNode node) {
node.getGroupView().setBackground(null);
node.setSelect(false);
model.reload(node);
}
/**
* 重置好友节点的UI
*
* @param model 模型
* @param node 自定义Node
*/
private static void restoreBuddyUI(DefaultTreeModel model, MyTreeNode node) {
node.getBuddyView().setBackground(null);
node.setSelect(false);
model.reload(node);
}
}
基本上就这些了,上述代码,大家可以直接拷贝运行!
看完文章,有任何疑问,请加入群聊一起交流!!!
很赞哦! ()
相关文章
标签云
猜你喜欢
- IntelliJ IDEA 2019.2已经可以利用补丁永久破解激活了
- IntelliJ IDEA 2019.3利用补丁永久破解激活教程
- IntelliJ IDEA高版本最灵活的永久破解激活方法(含插件激活,时长你说了算)
- Jetbrains全家桶基于ja-netfilter的最新破解激活详细图文教程
- IntelliJ IDEA 2022.1永久破解激活教程(亲测可用,持续更新)
- 分享几个正版 IntelliJ IDEA 激活码(破解码、注册码),亲测可用,持续更新
- ja-netfilter到底需不需要mymap,2021.3.2版本激活失效?
- 如何激活idea2022.1及以上版本中的插件(亲测可用)
- 【史上最全】IntelliJ IDEA最新2022.1版本安装和激活视频教学(含插件)
- IntelliJ IDEA 2022.2 版本最新2099年永久激活方法,亲测可用,也可以开启新UI了。
站点信息
- 网站程序:spring + freemarker
- 主题模板:《今夕何夕》
- 文章统计:篇文章
- 标签管理:标签云
- 微信公众号:扫描二维码,关注我们