论坛首页 Java企业应用论坛

Swing界面优化进阶五

浏览 13817 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2015-05-31   最后修改:2015-06-07
承接此贴:
http://www.iteye.com/topic/1137293
http://www.iteye.com/topic/1138102

JTree  一个给我印象最深刻的东西。

刚参加工作的时候那会儿,公司让我做一个即时通讯软件,当时真是被jtree玩死了,后来干脆自己用jlabel重新写了一个好友列表,说起来都是泪啊。。。

好了,不扯了,先来说一下优化思路:

1、重写cellRenderer渲染器;
2、消除节点之前的连线;
3、构建自己的Node节点(重绘UI);
4、居左对齐;
5、加上鼠标效果。

老规矩,还是先上图



看完大家没感觉吧,因为他这个排版还可以,勉强能接受。

接下来,我们需要将其原始图标给替换掉



上代码:继承DefaultTreeCellRenderer重写里面的getTreeCellRendererComponent方法,并调用重写的渲染器JTree.setCellRenderer(new DemoRenderer());

public class DemoRenderer extends DefaultTreeCellRenderer {

    // 大家可以将其看成一个jlabel
	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
        // 设置图标
		this.setIcon(new ImageIcon(getClass().getClassLoader().getResource("qq_icon.png")));
        // 设置文字
		this.setText(value.toString());
		return this;
	}
	
}

但是大家发现,这不是我们要的效果,我们想要灵活的控制图标





OK,我们来看下代码,思路就是根据不同级别控制一下图标

public class DemoRenderer extends DefaultTreeCellRenderer {

	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
		// 根节点从0开始,依次往下
		// 分组
		if (node.getLevel() == 1) {
			if (expanded) {
				this.setIcon(new ImageIcon(getClass().getClassLoader().getResource("arrow_down.png")));
			} else {
				this.setIcon(new ImageIcon(getClass().getClassLoader().getResource("arrow_left.png")));
			}
		}
		// 好友
		if (node.getLevel() == 2) {
			this.setIcon(new ImageIcon(getClass().getClassLoader().getResource("qq_icon.png")));
		}
		this.setText(value.toString());
		return this;
	}
	
}

看着前面的线条不太美观,咱们干掉它。很简单,有现成API:JTree.putClientProperty("JTree.lineStyle", "None");
效果图:



看起来好友头像有了,昵称也有了,可是个性签名呢?原始的node节点,我们可以将其看做一个jlabel,只有一张图表和一行文字,所以我们需要重新绘制节点UI了



主要是cellRender渲染器和node节点,上代码:

package course;

import java.awt.Component;

import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;

/**
 * 自定义CellRenderer
 * @author sf_dream
 * @date 2015年5月24日
 */
@SuppressWarnings("serial")
public class DemoRenderer extends DefaultTreeCellRenderer {

	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		// 大家这里注意,我为了节省时间,所以就没有写两个node类
		// 所有的代码写在了同一个类中,然后根据不同的节点来调用相应的方法
		DemoNode node = (DemoNode) value;
		if (node.getLevel() == 1) {
			if (expanded) {
				node.iconLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("arrow_down.png")));
			} else {
				node.iconLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("arrow_left.png")));
			}
			return node.getCateView();
		}
		if (node.getLevel() == 2) {
			node.iconLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource("qq_icon.png")));
			return node.getNodeView();
		}
		return this;
	}
	
}



package course;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.tree.DefaultMutableTreeNode;

/**
 * 自定义Node <br/>
 * 继承节点类DefaultMutableTreeNode <br/>
 * 在这里自定义我们自己的UI,以便cellRenderer中调用
 * 
 * @author sf_dream
 * @date 2015年5月24日
 */
@SuppressWarnings("serial")
public class DemoNode extends DefaultMutableTreeNode {

	/** 图片 */
	private Icon icon;
	/** 文字 */
	private String name;
	/** 签名 */
	private String sign;
	
	public JPanel cateContent;
	public JPanel nodeContent;
	
	public JLabel iconLabel;
	public JLabel nameLabel;
	public JLabel signLabel;
	
	/**
	 * 初始化分组节点
	 * @param name 名称
	 */
	public DemoNode(Icon icon, String name) {
		this.icon = icon;
		this.name = name;
		// 初始化UI
		initCateGUI();
	}

	/**
	 * 初始化好友节点
	 * @param icon 头像
	 * @param nick 昵称
	 * @param sign 签名
	 */
	public DemoNode(Icon icon, String nick, String sign) {
		this.icon = icon;
		this.name = nick;
		this.sign = sign;
		// 初始化UI
		initNodeGUI();
	}
	
	/**
	 * 自定义分组UI
	 */
	private void initCateGUI() {
		cateContent = new JPanel();
		cateContent.setLayout(null);
		cateContent.setOpaque(false);
		cateContent.setPreferredSize(new Dimension(258, 25));
		//cateContent.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		
		iconLabel = new JLabel(icon);
		iconLabel.setBounds(6, 5, 20, 16);
		cateContent.add(iconLabel);
		
		nameLabel = new JLabel(name);
		nameLabel.setBounds(23, 0, 132, 28);
		cateContent.add(nameLabel);
	}
	
	/**
	 * 自定义好友UI
	 */
	private void initNodeGUI() {
		nodeContent = new JPanel();
		nodeContent.setLayout(null);
		nodeContent.setOpaque(false);
		nodeContent.setPreferredSize(new Dimension(258, 50));
		//nodeContent.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
		
		iconLabel = new JLabel(icon);
		iconLabel.setBounds(8, 4, 39, 42);
		nodeContent.add(iconLabel);
		
		nameLabel = new JLabel(name);
		nameLabel.setBounds(59, 5, 132, 19);
		nodeContent.add(nameLabel);
		
		signLabel = new JLabel(sign);
		signLabel.setBounds(59, 28, 132, 17);
		nodeContent.add(signLabel);
	}
	
	/**
	 * 将自定义UI返回给渲染器	<br/>
	 * 供渲染器调用,返回的必须是一个Component
	 * @return
	 */
	public Component getCateView() {
		return cateContent;
	}
	
	/**
	 * 将自定义UI返回给渲染器	<br/>
	 * 供渲染器调用,返回的必须是一个Component
	 * @return
	 */
	public Component getNodeView() {
		return nodeContent;
	}

	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;
	}

}


看完大家是不是觉着大功告成了呢,咱再来整个边框看看到底好了没有。



线去掉了,但是位置么有让出来(先前因为背景都是白色,所以我们看的不太明显)

解决办法:继承BasicTreeUI,重写里面的左右对齐方法,
调用:JTree.setUI(new DemoTreeUI());

package course;

import java.awt.Graphics;

import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicTreeUI;

/**
 * 重写UI,主要为了左对齐
 * @author sf_dream
 * @date 2015年5月25日
 */
public class DemoTreeUI extends BasicTreeUI {

	// 去除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.putClientProperty("JTree.lineStyle", "None")了

再来看看效果,对齐了有木有



现在我们只需要将宽度跟界面匹配起来,就是在我们重绘的节点UI里面设置宽度,可以设置宽一点儿都没事,反正超出界面的部分也看不到,所以不用担心



好了,我们的界面基本上已经优化完毕,再来加点儿鼠标特效,我这里就简单点儿,鼠标滑入显示边框,点击选中变色。大家这里注意一下,事件还是放在jtree上处理,而不是放在我们重绘的UI上面,当时我就掉到这个坑里面了

特效:
1、鼠标滑入,当前三级(好友)节点显示边框,其他节点去掉边框;
2、鼠标点击,当前三节(好友)几点显示边框并且改变背景颜色,其他节点去掉边框并恢复颜色;

思路很简单,上面说过大家应该都能明白,有一个问题,大家帮忙思考一下,若是大家有更好的办法,可以教我一下,大家看到这些特效,改变自己就要恢复其他,所以这里就涉及到循环了,循环中除去根节点还有二三级节点,在节点过多的情况下,嵌套for循环十分影响效率,但是自己重绘UI的时候又加不上事件,非得加在jtree上,很让人头疼。。。

上图:


















  • 大小: 45.3 KB
  • 大小: 36.2 KB
  • 大小: 27.9 KB
  • 大小: 36.3 KB
  • 大小: 28.1 KB
  • 大小: 38.3 KB
  • 大小: 37.9 KB
  • 大小: 37.9 KB
  • 大小: 37.7 KB
  • 大小: 38.7 KB
  • 大小: 39.7 KB
  • 大小: 9.4 KB
   发表时间:2015-06-01  
楼主厉害!学习!
0 请登录后投票
   发表时间:2015-06-02  
写的挺不错,希望楼主能持续更新
0 请登录后投票
   发表时间:2015-06-04  
小湿妹,加油
0 请登录后投票
   发表时间:2015-06-05  
必须得顶。。。。。。
0 请登录后投票
   发表时间:2015-06-05  
swing功力深厚!
0 请登录后投票
   发表时间:2015-06-06  
cyberniuniu 写道
楼主厉害!学习!


大神,我还是比较佩服你的QQ气泡啊
0 请登录后投票
   发表时间:2015-06-06  
jiangchao419 写道
swing功力深厚!


一点儿都不深厚,还要你们帮忙思考问题。。。
0 请登录后投票
   发表时间:2015-06-06  
godtiger 写道
写的挺不错,希望楼主能持续更新


这个jtree组件讲解的差不多了,感觉没啥多说的了
0 请登录后投票
   发表时间:2015-06-06  
gao_xianglong 写道
必须得顶。。。。。。


谢赞
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics