2009年1月18日 星期日

JTextComponent 即時搜尋

以下範例介紹如何實做出 如Firefox 即時搜尋功能
並且將搜尋到的文字以高亮度表示. 

 2009-01-19_005344

 

JTextComponentWithSearchText.java
001 package sui.text;
002 
003 import java.awt.BorderLayout;
004 import java.awt.Color;
005 import java.awt.Dimension;
006 import java.awt.event.ActionEvent;
007 import java.awt.event.ActionListener;
008 import java.awt.event.MouseAdapter;
009 import java.awt.event.MouseEvent;
010 import java.util.regex.Matcher;
011 import java.util.regex.Pattern;
012 import java.util.regex.PatternSyntaxException;
013 
014 import javax.swing.JFrame;
015 import javax.swing.JScrollPane;
016 import javax.swing.JTextField;
017 import javax.swing.JTextPane;
018 import javax.swing.event.DocumentEvent;
019 import javax.swing.event.DocumentListener;
020 import javax.swing.text.BadLocationException;
021 import javax.swing.text.Document;
022 import javax.swing.text.JTextComponent;
023 import javax.swing.text.SimpleAttributeSet;
024 import javax.swing.text.StyleConstants;
025 
026 public class JTextComponentWithSearchText implements DocumentListener, ActionListener{
027   /** 文本元件.*/
028   private JTextComponent textComponent;
029   /** 背景顏色.*/
030   private Color selectBackgroundColor;
031   /** 比對器.*/
032   private Matcher matcher;
033   /** 符合比對文字的總數.*/
034   private int matcherCount;
035   /** 滑鼠事件.*/
036   private MouseAdapter mouseAdapter = new MouseAdapter() {
037     public void mouseClicked(MouseEvent e) {
038       // 當滑鼠連點二下時,執行搜尋.
039       if (e.getClickCount() == 2) {
040         invokeSearch();
041       }
042     }
043    
044     public void mouseReleased(MouseEvent e) {
045       invokeSearch();
046     }
047    
048     /**
049      * 呼叫搜尋.
050      */
051     private void invokeSearch() {
052       // 紀錄目前選取的範圍和文字.
053       int start = textComponent.getCaret().getDot();
054       int end = textComponent.getCaret().getMark();
055       String searchText = textComponent.getSelectedText();
056       if (searchText == null) {
057         return;
058       }
059       JTextField field = new JTextField(searchText);
060       startNewSearch(field.getDocument());
061       setSelectionText(start, end, true);
062     }
063   };
064   /** 搜尋runable.*/
065   private Runnable searchRunnable = new Runnable() {
066     public void run() {
067       highlighterSearchWord();
068     }
069   };
070   /** 搜尋緒*/
071   private Thread thread = new Thread(searchRunnable);;
072  
073   /**
074    * 建立一個搜尋的本文元件.
075    * @param textComponent 本文元件.
076    */
077   public JTextComponentWithSearchText(JTextComponent textComponent) {
078     this(textComponent, textComponent.getSelectionColor());
079   }
080  
081   /**
082    * 建立一個搜尋的文本元件, 並且設定搜尋文字的背景.
083    * @param textComponent 本文元件.
084    * @param color 文字背景顏色.
085    */
086   public JTextComponentWithSearchText(JTextComponent textComponent, Color color) {
087     this.textComponent = textComponent;
088     selectBackgroundColor = color;
089     textComponent.addMouseListener(mouseAdapter);
090   }
091  
092   /**
093    * 當輸入元件按下Enter時,將焦點移至下一個符合的文字.
094    */
095   public void actionPerformed(ActionEvent e) {
096     nextSearch();
097   }
098  
099   /**
100    * 當文字改變時,重新搜尋本文內符合的文字.
101    */
102   public void changedUpdate(DocumentEvent e) {
103     startNewSearch(e.getDocument());
104   }
105  
106   /**
107    * 當文字改變時,重新搜尋本文內符合的文字.
108    */
109   public void insertUpdate(DocumentEvent e) {
110     startNewSearch(e.getDocument());
111   }
112  
113   /**
114    * 當文字改變時,重新搜尋本文內符合的文字.
115    */
116   public void removeUpdate(DocumentEvent e) {
117     startNewSearch(e.getDocument());
118   }
119  
120   /**
121    * 搜尋本文內符合的文字.
122    * @param query_doc 搜尋文件的Document.
123    */
124   private void startNewSearch(Document query_doc) {
125     // 停止舊有的搜尋緒.
126     stopSearchThread();
127     try {
128       // 取得比對的文字.
129       String query = query_doc.getText(0, query_doc.getLength());
130      
131       // 取得本文內的文件.
132       Document content = textComponent.getDocument();
133       String body = content.getText(0, content.getLength());
134      
135       // 將給定的正則表達式編譯到網要中.
136       Pattern pattern;
137       pattern = Pattern.compile(query);
138       matcher = pattern.matcher(body);
139 
140      
141       // 創建比對給定輸入與此網要的比對器.
142       if (matcher != null) {
143         thread.start();
144         thread.join();
145       }
146     } catch (PatternSyntaxException e) {
147       // TODO: handle exception
148     } catch (InterruptedException e) {
149       // TODO: handle exception
150     } catch (BadLocationException e) {
151       // TODO: handle exception
152     } catch (Exception e) {
153       System.out.println("exception : " + e);
154       e.printStackTrace();
155     }
156   }
157  
158   /**
159    * 將搜尋符合的文字以高亮度表示.
160    */
161   private void highlighterSearchWord() {
162     matcherCount = 0;
163     if (textComponent instanceof JTextPane) {
164       JTextPane textPane = ((JTextPane) textComponent);
165       // 清除上一次搜尋的結果.
166       // 1.選取全部的文字.
167       setSelectionText(0, textComponent.getText().length(), false);
168       // 2.設定文字背景顏色為textComponent的初始值.
169       setAttributeSetBackground(textPane, textComponent.getBackground(), false);
170       // 執行搜尋
171       while (matcher.find()) {
172         // 將符合的文字以高亮度顯示.
173         // 1.選取符合的文字.
174         setSelectionText(matcher.start(), matcher.end(), false);
175         // 2.設定文字背景.
176         setAttributeSetBackground(textPane, selectBackgroundColor, false);
177         matcherCount++;
178       }
179      
180       // 將游標重新指定到第一個符合的文字.
181       if (matcher.find(0)) {
182         setSelectionText(matcher.start(), matcher.end(), true);
183       }
184     } else {
185       // 當textComponent不為JTextPane時,只需指定第一個符合的文字.
186       nextSearch();
187     }
188   }
189  
190   /**
191    * 尋找下一個符合的文字.
192    */
193   private void nextSearch() {
194     if (matcher != null) {
195       if (matcher.find()) {
196         setSelectionText(matcher.start(), matcher.end(), true);
197       }
198     }
199   }
200  
201   /**
202    * 設定被選取文字的背景顏色.
203    * @param component 本文元件.
204    * @param color 背景顏色.
205    * @param replace 是否清除原本文字的背景顏色.
206    */
207   private void setAttributeSetBackground(JTextPane component, Color color, boolean replace) {
208     SimpleAttributeSet attributeSet = new SimpleAttributeSet();
209     StyleConstants.setBackground(attributeSet, color);
210     component.setCharacterAttributes(attributeSet, replace);
211   }
212  
213   /**
214    * 設定選取文字的起迄.
215    * @param start 起.
216    * @param end 迄.
217    * @param isVisible 是否顯示選取.
218    */
219   private void setSelectionText(int start, int end, boolean isVisible) {
220     textComponent.getCaret().setDot(start);
221     textComponent.getCaret().moveDot(end);
222     textComponent.getCaret().setSelectionVisible(isVisible);
223   }
224  
225   /**
226    * 取得選取文字高亮度的顏色.
227    * @return 選取文字高亮度的顏色.
228    */
229   public Color getSelectBackgroundColor() {
230     return selectBackgroundColor;
231   }
232  
233   /**
234    * 設定選取文字高亮度的顏色.
235    * @param selectBackgroundColor 選取文字高亮度的顏色.
236    */
237   public void setSelectBackgroundColor(Color selectBackgroundColor) {
238     this.selectBackgroundColor = selectBackgroundColor;
239   }
240 
241   /**
242    * 中斷目前的執行緒,並加入新的執行緒.
243    */
244   private void stopSearchThread() {
245     thread.interrupt();
246     thread = new Thread(searchRunnable);
247   }
248 
249   /**
250    * 取得符合文字的總數.
251    * @return 符合文字的總數.
252    */
253   public int getMatcherCount() {
254     return matcherCount;
255   }
256  
257   /**
258    * 測試.
259    * @param args
260    */
261   public static void main(String[] args) {
262     JTextPane area = new JTextPane();
263     JScrollPane pane = new JScrollPane(area);
264     JTextComponentWithSearchText searchText = new JTextComponentWithSearchText(area, Color.YELLOW);
265    
266     JTextField field = new JTextField();
267     field.getDocument().addDocumentListener(searchText);
268     field.addActionListener(searchText);
269    
270     JFrame frame = new JFrame();
271     frame.getContentPane().add(BorderLayout.NORTH, field);
272     frame.getContentPane().add(BorderLayout.CENTER, pane);
273     frame.setPreferredSize(new Dimension(800,600));
274     frame.pack();
275     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
276     frame.setVisible(true);
277   }
278 }

1.當要增加註解在Excel Cell 中,透過WritableCellFeatures Class的setComment(String)加入註解

WriteExcelWithComment.java
package tw.com.haoxiao.excel.write;

import java.io.File;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableCellFeatures;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;


public class WriteExcelWithComment {
  private static String excelURL = "C:/ExcelWithComment.xls";
 
  public static void main(String[] args)throws Exception {
    // 建立Excel File
    File excelFile = new File(excelURL);
    WritableWorkbook writeable = Workbook.createWorkbook(excelFile);
   
    // 建立Excel Content
    WritableSheet sheet = writeable.createSheet("Commnet", 0);
    Label label = new Label(1,1,"Write Comment");
   
    // 產生註解
    WritableCellFeatures cellFeatures = new WritableCellFeatures();
    cellFeatures.setComment("增加一個註解");
   
    // 將註解加入至Label
    label.setCellFeatures(cellFeatures);
   
    // add Cell
    sheet.addCell(label);
   
    // Output and Close
    writeable.write();
    writeable.close();
  }
}

以下範例展示

1.如何取得Excel 所有的Sheet and Name

2.取得Excel 的Row and Column

3.取得每一個Sheet中的內容

4.搜尋每一個Sheet中,是否含有相同的字串

ReadExcelAPI.java
package tw.com.haoxiao.excel.read;

import java.io.File;
import java.util.Arrays;

import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;

public class ReadExcelAPI {
  // 檔案目錄
  private static String excelURL = "C:/ExcelTest.xls";
  // 搜尋的字串
  private static String searchString = "Apple";
 
  public static void main(String[] args)throws Exception {
    // 宣告File指向Excel的路徑
    File excelFile = new File(excelURL);
   
    // 透過 Workbook.getWorkbook 取得Excel
    Workbook workbook = Workbook.getWorkbook(excelFile);
   
    // 取得Excel 所有的Sheet Name
    System.out.println("======Sheet Name======");
    String[] sheetsByName = workbook.getSheetNames();
    System.out.println(Arrays.asList(sheetsByName));

    // 取得Excel 所有的Sheet
    System.out.println("======Sheet Name======");
    Sheet[] sheets = workbook.getSheets();
    for(int sheetIndex = 0; sheetIndex < sheets.length; sheetIndex++) {
      System.out.println("Index " + sheetIndex + " : " + sheets[sheetIndex].getName());
    }
   
    // 取得Rows and Columns 長度
    for(int sheetIndex = 0; sheetIndex < sheets.length; sheetIndex++) {
      int cols = sheets[sheetIndex].getColumns();
      int rows = sheets[sheetIndex].getRows();
      System.out.println("======" + sheets[sheetIndex].getName() + "======");
      System.out.println("Rows : " + rows);
      System.out.println("Cols : " + cols);
    }
   
    // 取得每一個Sheet的表格內容
    for(int sheetIndex = 0; sheetIndex < sheets.length; sheetIndex++) {
      int cols = sheets[sheetIndex].getColumns();
      int rows = sheets[sheetIndex].getRows();
      System.out.println("======" + sheets[sheetIndex].getName() + "======");
      for(int y = 0; y < rows; y++) {
        for(int x = 0; x < cols; x++) {
          System.out.print(sheets[sheetIndex].getCell(x, y).getContents() + "\t");
        }
        System.out.println();
      }
    }
   
    // 尋找 Cell的內容
    System.out.println("======搜尋======");
    for(int sheetIndex = 0; sheetIndex < sheets.length; sheetIndex++) {
      Sheet sheet = sheets[sheetIndex];
      Cell searchCell = sheet.findCell(searchString);
      if(searchCell != null) {
        System.out.println("找到" + searchString + "在" + sheet.getName()
            + "的(" + searchCell.getColumn() + ","
            + searchCell.getRow() + ")");
      }
    }
   
    // 關閉
    workbook.close();
  }
}

要使用Java讀取Excel也不難,只需要幾個步驟

1.建立要讀取Excel的路徑

2.透過Workbook.getWorkbook(File)取得Excel

3.取得Sheet

4.取得Sheet 中的 Cell

5.取得 Cell中的內容

6.關閉

 

ReadExcel.java
package tw.com.haoxiao.excel.read;

import java.io.File;

import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;

public class ReadExcel {
  private static String excelURL = "C:/ExcelTest.xls";
 
  public static void main(String[] args)throws Exception {
    // 宣告File指向Excel的路徑
    File excelFile = new File(excelURL);
   
    // 透過 Workbook.getWorkbook 取得Excel
    Workbook workbook = Workbook.getWorkbook(excelFile);
   
    // 取得Excel Sheet(頁籤)
    Sheet sheetByIndex = workbook.getSheet(0);
    Sheet sheetByName = workbook.getSheet("第一個頁籤");
   
    // 取得 Cell
    Cell cell1 = sheetByIndex.getCell(0, 0);
    Cell cell2 = sheetByIndex.getCell(1, 0);
    Cell cell3 = sheetByName.getCell(0, 1);
    Cell cell4 = sheetByName.getCell(1,1);
   
    // 取得 Cell的值
    String s1 = cell1.getContents();
    String s2 = cell2.getContents();
    String s3 = cell3.getContents();
    String s4 = cell4.getContents();
   
    // Print Cell Value
    System.out.println("Row 1 : " + s1 + "\t" + s2);
    System.out.println("Row 2 : " + s3 + "\t" + s4);
   
    // 關閉
    workbook.close();
  }

}

 
http://jexcelapi.sourceforge.net/ 下載 JExcelApi 的library
之後要使用Java建立一個Excel檔案就很簡單,只要幾個步驟。
1.建立一個File 指定Excel輸出的位置
2.透過Workbook.createWorkbook() 開啟一個Excel
3.產生一個Sheet, 並且命名
4.使用jxl,write.Label , Number, ... 建立每一個Cell的值
5.將 Cell 加入至 Sheet中
6.呼叫WritableWorkbook的write(), 將檔案寫入至硬碟中
7.關閉
WriteExcel.java
package tw.com.haoxiao.excel.write;

import java.io.File;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;

public class WriteExcel {
  private static String excelURL = "C:/ExcelTest.xls";
 
  public static void main(String[] args)throws Exception {
    System.out.println("Create Excel to " + excelURL);
   
    // 建立輸入 Excel 檔案的目錄
    File execlFile = new File(excelURL);

    // 建立 Excel 檔, 此時只是暫存於記憶體中, 還沒有真正寫入於實體的硬碟中
    WritableWorkbook workbook = Workbook.createWorkbook(execlFile);
   
    // 建立一個頁籤, 以及名稱
    WritableSheet sheet = workbook.createSheet("第一個頁籤", 0);
   
    // 宣告 Cell 內的值, String, Number
    Label label1 = new Label(0,0,"Hello");
    Label label2 = new Label(1,0,"Excel");
    Label label3 = new Label(0,1,"pi=");
    Number number = new Number(1,1,3.14159);
   
    // 將 Cell 加入至 Sheet
    sheet.addCell(label1);
    sheet.addCell(label2);
    sheet.addCell(label3);
    sheet.addCell(number);
   
    // 將 Excel 寫入至硬碟中
    workbook.write();
   
    // 關閉
    workbook.close();
   
    System.out.println("Excel already create succes...");
  }
}

2008年8月9日 星期六

Java - I/O - 1

1.藉由I/O可以在程式執行過程中存取外部資料(如檔案、Socket、Console、記憶體等等)
2.File Class 不是一個標準的I/O類別,在java.io類別函式中也是唯一的non-stream class。不能讀取檔案、不能寫入檔案。
3.File Class 用於收集檔案(目錄)相關資訊,如建立檔案(並非寫入檔案內容)、修改檔名、設定/讀取檔案屬性、測試檢查檔案是否存在、列出檔案目錄等等

以下為 File 的部份操作實例
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package io;

import java.io.*;

/**
 *
 * @author Administrator
 */
public class NewClass {
    private static String path = "C:/haoxiao/io/";
    
    public static void main(String[] args)throws IOException {
        
        //建立目錄,並判斷目錄是否存在
        System.out.println("================建立目錄,並判斷目錄是否存在================");
        File mkdir = new File(path);
        if(mkdir.mkdirs()) {
            System.out.println("目錄 C:/haoxiao/io/ 建立完成");
        } else {
            System.out.println("目錄 C:/haoxiao/io/ 已存在");
        }
        
        //建立檔案
        System.out.println("================建立檔案================");
        File file = new File(path , "Test.txt");
        if(!file.exists()) {
            System.out.println("檔案尚未建立...");
            if(file.createNewFile()) {
                System.out.println("檔案建立成功");
            } else {
                System.out.println("檔案建立失敗");
            }
        } else {
            System.out.println("檔案已經建立");
        }
        
        //更改檔名及路徑
        System.out.println("================更改檔名及路徑================");
        if(!file.exists()) {
            System.out.println("檔案不存在");
        }else{
            System.out.println("檔案更名中...");
            if(!new File(path+"changName.txt").exists()) {
                if(file.renameTo(new File(path + "changName.txt"))) {
                    System.out.println("檔案更名成功");
                } else {
                    System.out.println("檔案更名失敗");
                }
            } else {
                System.out.println("檔案已存在");
            }
        }

        showFile(path);
        
        //刪除檔案
        System.out.println("================刪除檔案================");
        if(file.exists()) {
           file.delete(); 
        }
        showFile(path);
        
        System.out.println("================實做Linux ls指令================");
        path = "C:/windows";
        File file1 = new File(path);
        showDetailFile(file1);
        
    }
    
    public static void showFile(String path) {
        for(String filename : new File(path).list()) {
            System.out.println("Filename : " + filename);
        }
    }
    
    public static void showDetailFile(File file) {
        String fileStrings[] = file.list();
        for(String fileString : fileStrings) {
            File file1 = new File(file.getAbsoluteFile() + File.separator + fileString);
            String isDir = isDirectory(file1);
            String isHidden = isHidden(file1);
            String canRead = canRead(file1);
            String canWrite = canWrite(file1);
            String canExecute = canExecute(file1);
            System.out.println(isDir + "\t" +canRead+canWrite+canExecute + "\t" + isHidden+ fileString + "\t " + file1.lastModified());
        }
    }
    
    public static String canRead(File file) {
        if(file.canRead()) {
            return "r";
        } else {
            return "_";
        }
    }
    
    public static String canWrite(File file) {
        if(file.canWrite()) {
           return "w";
        } else {
            return "_";
        }
    }
    
    public static String canExecute(File file) {
        if(file.canExecute()) {
            return "x";
        } else {
            return "_";
        }
    }
    
    public static String isHidden(File file) {
        if(file.isFile()) {
            return "";
        } else {
            return ".";
        }
    }
    
    public static String isDirectory(File file) {
        if(file.isDirectory()) {
            return "d";
        } else {
            return "_";
        }
    }
}