以下範例介紹如何實做出 如Firefox 即時搜尋功能
並且將搜尋到的文字以高亮度表示.
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 } |