打造自己的相片上传工具【二】

原创作品,转载的话写对出处(http://www.jnan.org)和作者(jnduan)即可,别无要求。

==================================

上一节里,分析了整个程序的大体架构和规划,下面就开始按照规划一步一步的实现和完善程序的功能。

首先要解决的就是左侧的树状磁盘目录,它是所有功能的发起点。我们先来看看这棵树应该长什么样子吧:





如上图所示:

整个树中需要注意以下几点:

1.为了提高用户的体验,根节点为“桌面”。然后依次罗列“桌面”的子文件夹和“我的文档”、“我的电脑”等若干特殊节点(这些节点可不是桌面的子文件夹,呵呵)。

在windows下编程,这样的一个树状目录是相当容易获得的,读取注册表,或者调用shell32.dll,都可以方便的得到类似“桌面”、“我的文档”一类的路径,当然你会说,java使用jni也可以调用dll啊,没错,不过这样就和windows平台绑死了,失去了跨平台的优势。所以,我们只能手工的造一颗这样的文件树出来了。

2.树节点的渲染

swing包下的JTree默认的呈现器(Renderer)是相当简陋的一个东西,只能显示文字。因此我们需要实现一个Renderer来达到在文字前显示图片,并且在选中时是蓝底白字,未选中时是白底黑字的效果。

3.树节点的展开

树的建立是一个递归的过程,但是我们不能一次性就把整棵树建立完毕(这是一个相当慢且耗费系统资源的过程),只能是在某节点被展开时,遍历其所有的子结点对应的文件夹,将其子结点所包含的子结点加载其中。

上面的话可能不好理解,举个例子:

如果根目录为c:\,其下有文件夹a和文件夹b,a下有aa和ab,b下有ba和bb。aa下又有aaa,那么,当树初始化时,应该先遍历c:,得到a和b。因此,c:节点前是一个“+”形状,代表其有子结点,可以展开的标记。当c:节点展开时,由于此时不知道a和b是否有子结点,无法确定显示“+”还是“-”,因此需要在c:展开时遍历c:的子结点a和b来确定子结点a和b的渲染状态。a和b展开时类推。

4.树节点的点击

当某个节点被点击时,需要得到该节点对应的文件夹下的所有图片,因此需要在节点中记录该节点对应的绝对路径(当然你可以反向遍历该节点的父节点得到路径,不过麻烦)

经过了以上分析,这棵树的关键技术点已经明确了:

1.每个结点对应的bean类应至少包含以下成员:

[java]
private ImageIcon m_icon;//这就是它的图标

private ImageIcon m_expandedIcon;//这是节点展开的图标

private String path;//这是节点显示的文字

private String fullPath;//这是节点对应文件夹的绝对路径
[/java]

2.呈现器Renderer这可是关键中的关键

首先,它肯定要实现javax.swing.tree.TreeCellRenderer这个接口,否则,swing可不知道怎么来渲染你的节点

其次,为了同时显示图片和文字,我采用了继承JLabel的方法,你要是有什么更好的办法,也请告诉我,分享一下。

下图是Renderer类的结构




其中:

m_bkNonSelectionColor定义了节点未选中时的背景色

m_bkSelectionColor定义了节点选中时的背景色

m_borderSelectionColor定义了文字边框的颜色

m_selected是boolean值,记录该节点是否被选中

m_textNonSelectionColor定义了文字未被选中时的颜色

m_textSelectionColor定义了文字被选中时的颜色

下面再简单介绍一下几个方法:

IconCellRenderer ()是构造器,除了显式调用父类构造器外,还担负着初始化那些成员的作用

getTreeCellRendererComponent可以得到呈现器渲染的对象,并且根据该对象的状态(是否选中,是否展开等)来设置该对象的一些属性。

paintComponent就不用多介绍了,你继承了JLabel,必然要重写paint方法咯。

篇幅所限,我就不大段大段的贴代码了,因为我一直认为copy代码学不到任何东西,搞懂代码的设计思路和实现逻辑才是王道。感兴趣的朋友可以单独和我交流讨论。

有了bean和呈现器,我们的树就可以出来了。建树的逻辑上面已经介绍过,所以下面我就简单的贴一些代码吧,表达一下具体的实现逻辑,懒得详细讲了。。。

[java]
//建树算法: 先得到用户的主目录,然后建立桌面目录 由于我的文档、我的电脑比较特殊,需要手动添加。 然后再由鼠标的点击事件触发目录的展开操作

// 下面的代码定义了树所需要的一些图标:
private static final ImageIcon ICON_DESKTOP = new ImageIcon(
"images/desktop.png");
private static final ImageIcon ICON_COMPUTER = new ImageIcon(
"images/computer.png");
private static final ImageIcon ICON_PICTURE = new ImageIcon(
"images/picture.png");
private static final ImageIcon ICON_MUSIC = new ImageIcon(
"images/music.png");
private static final ImageIcon ICON_DISK = new ImageIcon("images/disk.png");
private static final ImageIcon ICON_CD = new ImageIcon("images/cd.png");
private static final ImageIcon ICON_DOCUMENTS = new ImageIcon(
"images/documents.png");
private static final ImageIcon ICON_FOLDER = new ImageIcon(
"images/folder.png");

// 构造器代码主要的动作就是设置一下布局,把tree放到JScrollPane里,初始化树
public JWSDataTreePanel() {
this.setLayout(new GridLayout(1, 1));
leftPanel = new JPanel();
leftPanel.setLayout(new GridLayout(1, 1));
initItemTreeNodes();
itemTree = new JTree(m_model);
itemTree.setCellRenderer(new IconCellRenderer());
itemTree.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
itemTreeMouseClicked(evt);
}
});
itemTree
.addTreeExpansionListener(new javax.swing.event.TreeExpansionListener() {
public void treeCollapsed(
javax.swing.event.TreeExpansionEvent evt) {
}

public void treeExpanded(
javax.swing.event.TreeExpansionEvent evt) {
itemTreeExpanded(evt);
}
});
treeScrollPane = new JScrollPane();
treeScrollPane.getViewport().setView(itemTree);
leftPanel.add(treeScrollPane);
itemTree.setShowsRootHandles(true);
this.add(leftPanel);
}

private void initItemTreeNodes() {
workPath = System.getProperty("user.home");
File rootFile = new File(workPath + File.separator + "桌面");
IconData rootIconData = new IconData(ICON_DESKTOP, null, "桌面");
rootIconData.setFullPath(rootFile.getPath());
DefaultMutableTreeNode desktopNode = new DefaultMutableTreeNode(
rootIconData);
System.out.println(workPath);
if (rootFile.exists()) {
createTree(rootFile, desktopNode);
}
// 需要添加我的文档、我的电脑两个节点,此处尚未处理他们的子结点
File documentsFile = new File(workPath + File.separator
+ "My Documents");
IconData documentIconData = new IconData(ICON_DOCUMENTS, null, "我的文档");
documentIconData.setFullPath(documentsFile.getPath());
DefaultMutableTreeNode documentNode = new DefaultMutableTreeNode(
documentIconData);
desktopNode.add(documentNode);
createTree(documentsFile, documentNode);
DefaultMutableTreeNode computerNode = new DefaultMutableTreeNode(
new IconData(ICON_COMPUTER, null, "我的电脑"));
desktopNode.add(computerNode);
File[] disks = File.listRoots();
if (disks.length > 0) {
for (File disk : disks) {
IconData iconData = new IconData(ICON_DISK, null, disk
.getPath());
iconData.setFullPath(disk.getPath());
computerNode.add(new DefaultMutableTreeNode(iconData));
}
}
m_model = new DefaultTreeModel(desktopNode);
}

//递归建树 只列出文件夹。对于每个节点,还要看下面是否有文件夹来确定它前面的能否展开的标志
private void createTree(File file, DefaultMutableTreeNode node) {
DefaultMutableTreeNode subRoot = node;
if (file.isDirectory()) {
File[] subFiles = file.listFiles(DIRECTORY_FILTER);
for (File f : subFiles) {
// System.out.println(f.getAbsolutePath());
DefaultMutableTreeNode newNode = null;
if (f.isDirectory()) {
IconData iconData = new IconData(ICON_FOLDER, null, f
.getName());
iconData.setFullPath(f.getAbsolutePath());
newNode = new DefaultMutableTreeNode(iconData);
} else {
continue;
}
subRoot.add(newNode);
}
} else {
return;
}
}

//处理树展开事件,实现前文所述的逻辑
private void itemTreeExpanded(TreeExpansionEvent evt) {
// TODO Auto-generated method stub
TreePath path = evt.getPath();
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) path
.getLastPathComponent();
Enumeration e = selectedNode.children();
while (e.hasMoreElements()) {
DefaultMutableTreeNode subNode = (DefaultMutableTreeNode) e
.nextElement();
IconData obj = (IconData) subNode.getUserObject();
createTree(new File(obj.getFullPath()), subNode);
}
}

//处理鼠标点击树事件
private void itemTreeMouseClicked(java.awt.event.MouseEvent evt) {
DefaultMutableTreeNode selectedNode = null;
IconData iconNode = null;
selectedNode = (DefaultMutableTreeNode) itemTree
.getLastSelectedPathComponent();
if (selectedNode != null) {
IconData obj = (IconData) selectedNode.getUserObject();
System.out.println(obj.getFullPath());
File file = new File(obj.getFullPath());
File[] fs = file.listFiles();
for (File f : fs) {
System.out.println("\t" + f.getAbsolutePath());// 这里把展开的文件夹里的文件路径都打印出来,日后就是获取图片了,呵呵
}
}
}
[/java]

程序运行图:




嘿嘿,比较逼真吧。

内容预告:

下一节,将介绍图片放大、缩小、旋转等操作的实现



加载评论框需要翻墙