快一个月没写总结了,这个星期总算做出了一个还算满意的成果,就来和大家分享一下经验吧。
?
首先将下思路:一开始当然是一个JFrame了,然后我把它分成左右两个部分,左边那部分是用来进行搜索相关设置的,包括需要搜索的内容,搜索的范围以及是否忽略大小写和是否搜索隐藏文件这些功能。而右边那个部分则当然是用来展示搜索结果的啦~~
?
?
这是我的程序运行之后的界面。
?
简单的功能就不说了,首先是左侧面板的两个JTextField,一个是输入需要搜索的内容,这个必须得自己手动输入,另外一个是搜索的路径,这个可以自己手动输入,也可以通过选择文件夹按钮来选择输出。当使用按钮输出时就需要用到一个文件类:JFileChooser。基本思想是当点击选择文件夹按钮后弹出一个对话框:(JFileChooser对象)jfc.showDialog(null,null);在这之前得设置只能选择文件夹:jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
最后当选择结束之后要得到选中的目录,而且在这里最好处理一个异常,即如果你什么都没选返回,就会抛出异常,所以我的代码是:
try {
?????selectedfile = jfc.getSelectedFile().getAbsolutePath();
????} catch (Exception ef) {
?????System.out.println("未选中任何文件");
????}
当然,如果你导出jar文件的话,catch语句可能就没用了。
最后记得与之相对应的JTextField对象.setText(selectedfile);
以上所有操作在一个匿名内部类JButton对象.addActionListener(new ActionListener(){.......});里面,这样基本就完成了,还有一点值得提醒的是:在匿名内部类中的所有外部变量都必须是final类型的,以后在各种匿名监听器中都会出现左侧面板的各种组件,所以在那些组件创建对象的时候都已经事先定义成final属性了。
?
?
点击搜索按钮后,然后就是进行系统的搜索了,这一点和在课堂练习时在控制台输出没什么区别,无非是创建File对象,然后列目录列文件,再进行递归等。至于多选框的区分大小写和不寻找隐藏的文件和文件夹则两个功能则需在递归的时候设置两个bool值当做开关,进行判断。下面是一点实现技巧。
区分大小写:如果不勾上的话当然就是正常的搜索了,只需要两个String,(搜索的路径).contains((搜索的内容));如果勾上的话就是需要区分大小写了,因为java提供的String判断方法只有.equalsIgnoreCase();即判断两个字符串完全相等,这显然和我们所要求的效果不一致,所以我的方法是先将两个字符串同时.toLowerCase();当然.toUpperCase();也是没问题的,当然这样的话每个遍历的路径都必须先进行这一操作,必然会导致程序运行慢下来,如果读者有更好的方法欢迎探讨。
不寻找隐藏的文件和文件夹:这里可以直接使用File类里的方法.isHidden();没多大问题,那么我们现在这里有一个小问题:我们是先判断一个文件是否为隐藏文件再判断我们隐藏文件的复选框是否选中了,还是先判断我们隐藏文件的复选框是否选中了再判断一个文件是否是隐藏文件呢?假设总共有x个文件。然后有y个隐藏文件(y<=x);如果选用第一种方法,总共需要判断(x+y)次,而第二种方法如果未选中的话,只需x次(对每个文件进行判断是否选中,可能可以再循环体外判断,这样就只需要判断一次,但是一个这是一个递归的方法,二个在体系内不仅要判断这一点还有其他的判断,三个我的个人水平还不是很高,所以暂时我就将他写入循环体内),而第二种方法选中的话就需要判断2x次,于是我们折中取1.5x次,那么哪种方法好呢?相信大家都知道一个电脑里的隐藏文件数量是远远比不上未隐藏的。即y《x-y;所以总体来说还是第一种方法好点。
最后,不管是哪种搜索,将每个符合要求的结果存入一个静态队列数组,交由右边的面板对象调用。记得将每次调用完后队列清空~~
?
?
?
现在来说一下右边面板的一些实现,首先由一个问题就是刚开始运行的时候JFrame已经加载完全了,之后需要对按钮操作使得结果显示出来,则需要一些动态方法。我的基本思路是每次搜索时先将右边面板所有组件卸载,然后进行添加,最后调用javax.swing.SwingUtilities.updateComponentTreeUI(Component);方法来刷新。我是用JTable来显示结果的,鉴于JTable的麻烦使用和我已经写了相当长的时间,就直接将按下搜索按钮后的代码复制过来吧。。
?
?
?
public void show() { Image image = Toolkit.getDefaultToolkit().createImage("showpanelXP.jpg"); ? Font fnt = new Font("", Font.CENTER_BASELINE, 12); // 移除所有组件 MainWindow.sp.removeAll(); MainWindow.sp.getGraphics().drawImage(image, 0, 0, null); /** * 得到SearchPanel的结果数组 */ final String str[] = SearchPanel.str; /** * 设置JTable单元格透明 */ final JTable jt = new JTable(50, 2) { // 设置JTable单元格透明函数 public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component c = super.prepareRenderer(renderer, row, column); if (c instanceof JComponent) { ((JComponent) c).setOpaque(false); } return c; } // 设置JTable单元格不可被编辑函数 public boolean isCellEditable(int row, int col) { return false; } }; jt.setOpaque(false); /** * 设置JTable一些其他相关信息 */ // 设置字体颜色及样式 jt.setForeground(new Color(255, 255, 255)); jt.setFont(fnt); // 用内部类方法设置所有单元格不可被编辑 jt.isCellEditable(0, 0); // 设置单元格高度,默认为16 jt.setRowHeight(60); // 设置不可多选行 jt.setRowSelectionAllowed(false); /** * error,由于DefaultTableMode中dtm重新设置了列名,所以以下程序并未获得真正的对象, * 保留以作警示,具体实现代码在dtm设置列名下一行 */ // 得到JTable中的列对象,并设置其宽度 // TableColumnModel tcm = jt.getColumnModel(); // TableColumn tc = tcm.getColumn(0); // tc.setPreferredWidth(500); JScrollPane jsp = new JScrollPane(jt); // 设置JScrollPane组件透明 jsp.setOpaque(false); jsp.getViewport().setOpaque(false); final DefaultTableModel dtm = (DefaultTableModel) jt.getModel(); dtm.setRowCount(0); dtm.setColumnIdentifiers(new String[] { "文件名(双击以打开)", "大小" }); // 设置列宽度 jt.getColumn("文件名(双击以打开)").setPreferredWidth(1050); for (int i = 0; i < str.length; i++) { String element[] = new String[] { str[i], count((new File(str[i]).length())) }; dtm.addRow(element); } MainWindow.sp.add(jsp); javax.swing.SwingUtilities.updateComponentTreeUI(MainWindow.sp); //给JTable添加监听器,使得双击可以打开该文件夹或文件 jt.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub if (e.getClickCount() == 2) { for (int i = 0; i < dtm.getRowCount(); i++) { if (jt.getSelectedRow() != -1) { File file = new File((String)jt.getValueAt(jt.getSelectedRow(), 0)); try { Desktop.getDesktop().open(file); } catch (IOException e1) { // TODO Auto-generated catch block JOptionPane.showMessageDialog(null, "无法打开指定的文件", "警告", 2); } break; } } } } @Override public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } @Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } }); }
?
这里有几个难点,一个就是JTable,DefaultTableMode,TableColumnMode,TableColumn,TableMode这几个类的相互转换以及它们各自方法的调用。不过我想熟练了之后这个就不是很难了。
然后是设置JTable行列不可被编辑,这个的实现方法是在创建JTable对象时定义的,以及不可多选行,这些在上述代码中都有注释。
另外还有得到JTable的行对象,我试过,结果无果,但是可以通过某些方法得到其中的Object内容,最后将其转换为String类型,再通过JTable的鼠标监听器,当点击两次时打开windows下的该文件或目录。有些文件是无法通过这个程序打开的,于是我在打开文件操作时用了try…catch语句,当打开失败时弹出警告对话框,不过令我郁闷的是我居然在测试mp4文件时它弹出了对话框,各种囧……
还有一些小问题像是字体设置啊,以及和北背景对应的字体颜色设置用到setForeground(…);函数等。。
?
?
最后重要加一点的就是背景设置,因为在程序运行时我们必须加载两个面板在setVisible(true);但是面板设置图片又必须在setVisible(true);后再getGraphics().drawImage( , , , );所以怎么看也不合逻辑,为了解决这一问题,我们需要采用重绘组件的方法,即先加载JFrame窗体,这样当然图片背景设置就不成功,因为得到的Graphics对象还是空的,不过让它自动重绘一下就OK了。在这里调用方法继续贴出来:
?
?
public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, 0, 0, null); }
?image对象在外部已经定义过。这样就解决了,当然这个解决指的是对于左边的面板,因为左边就那么几个组件,而右边的组件是一个JTable全部覆盖了,这就要设置表格透明,我一直认为迅雷7的下载界面很好看,现在大概这个效果也可以媲美了吧。设置一个组件透明的函数是setOpaque(false);当然对于JTable来说不是这么简单实现的。但对于JCheckBox和JLabel等这样就OK了,事实上左边的面板我也这么设置了,所以看起来没有遮盖,不过对于左边的JButton,这样设置也是不行的,所以它还是覆盖了。言归正传,对于JTable的设置透明方法,我上网参考了很多资料,所以我自身还是不太懂,只知道在创建该对象的时候重写其中的一个方法,在上述代码中有说明,然后在外面设置setOpaque(false);再设置JScrollPane对象setOpaque(false);大功告成~~~
?
JTable列的宽度设置也一直是困扰我的难题,不过还是被我找出了问题所在,在代码中也有说明~~
?
?
最后一点,是关于JTable第二列中的文件大小问题,这里只需要得到每个文件的大小,默认是Byte,你可以把它转换成1~1024的任意一个数加上他的单位再输出就行。如果你只需要得到整数,那非常简单,但是如果需要得到几位小数,那就有点曲折了,思路是:将得到的大小数据转换成double型,然后进行单位换算直至它的数值在1~1024,以及得到这时相应的单位。最后通过一个格式化函数将它格式化,我的格式化是输出两个小数。然后将结果输出就行了,以下是一个格式化函数,当传入的参数是File.length();时返回的就是两个的浮点数+单位~~~
?
?
// 格式化输出单位 public String count(long len) { int k = 0; double db = (double) len; String a[] = new String[] { "B", "KB", "MB", "GB", "TB" }; while (db >= 1024) { db /= 1024; k++; } DecimalFormat df = new DecimalFormat("#.00"); return df.format(db) + a[k]; }
?
?
好吧,花了两个多小时,终于大功告成了,欢迎大家批评指教,文件我本想打包成jar上传上来,后来发现(这是我首次打包)打包后文件的路径设置有所改变,所以暂时还无法完成,下次成功了再上传上来吧~~