日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

让JTextField添加“自动完成”功能

發(fā)布時間:2025/3/14 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 让JTextField添加“自动完成”功能 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

? ? ?在越來越重視“用戶體驗”的今天,一個簡單的文本框也演進的越來越智能了。比如Google的搜索,當我們輸入搜索關(guān)鍵字的過程中,文本框就會動態(tài)的下拉列出最常輸入的近似文字,以便我們快速輸入要查詢的內(nèi)容。當然一直抄襲Google的百度自然也是一樣。類似的例子還有很多,例如一般的郵件客戶端,在敲入地址時,也會動態(tài)列出符合要求的地址,方便快速錄入,也會減少出錯。

? ? ?那么,Swing的文本框要做到這一點是否容易呢?網(wǎng)上的例子也能搜索到一些,不過要么功能做的太簡單,要么實現(xiàn)的代碼太繁瑣羅嗦。還有一些商業(yè)的Swing組件,則完全是要付費的。本文結(jié)合了2BizBox免費ERP軟件開發(fā)中的實踐,嘗試了一種非常簡單、有效的方法來制作這一效果。

? ? ? 首先仔細觀察這種效果:它外觀上、本質(zhì)上,都完全是一個文本框,而不是下拉框。所以,我們不想把它做成下拉框,也就是不想從JComboBox繼承。另外,下拉列表提示的出現(xiàn),是完全異步、動態(tài)的,它僅僅作為提示,不能干預正常的文本框的輸入。最后,那個下拉列表的外觀和行為則完全是一個JComboBox的下拉列表行為。所以,這個“可自動完成的JTextField”應當是一個JTextField和JComboBox下拉列表部分的結(jié)合體。

? ? ? 經(jīng)過以上分析,思路基本確定:它本質(zhì)是一個JTextField,但是又結(jié)合利用了一個JComboBox的下拉列表。二者合而為一即可。那么是從誰繼承呢?JTextField嗎?

? ? ? 仔細想想,繼承并不是最好的方法。俗話說:繼承是混蛋。能不繼承就不要繼承。為啥呢?繼承,意味著別人只能繼承你的類,才能使用這一功能。假如你的項目已經(jīng)寫了一萬多個界面,想給這里面的一些文本框增加這種智能提示功能,難道要對所有代碼進行修改,讓那些東西重新繼承你的類嗎?這無疑是個爛主意。所以,那些剛學會OO的童鞋,總是喜歡動不動就要繼承的思路,并不妥當。如果我們只是提供一個Util方法,對已經(jīng)存在的普通JTextField實例處理一下,就可以具有智能提示,豈不是更好?

? ? ? 要做到JTextField和JComboBox這兩個組件的結(jié)合,這里使用了非常“怪異”的一個絕招,你絕對想不到:把一個JComboBox塞到JTextField的身體里面,并讓它看不見。看一下代碼:

JTextField txtInput = new JTextField(); JComboBox cbInput = new JComboBox(); txtInput.setLayout(new BorderLayout()); txtInput.add(cbInput, BorderLayout.SOUTH);

? ? ?什么?把JTextField設置一個layout?并且還add一個JComboBox且放在SOUTH?我相信你絕對聞所未聞這種事情。怎么看都是怪胎啊。不要緊,把JComboBox的高度變成0,別人就看不出破綻了:

JComboBox cbInput = new JComboBox(model) {public Dimension getPreferredSize() {return new Dimension(super.getPreferredSize().width, 0);} };

? ? ?雖然combo看不見,但是它實實在在存在于文本框的身體里,且位于其下方。我們的思路是:當文本框輸入內(nèi)容時,我們判斷下拉框中是否有符合要求的列表,如果有,就馬上主動彈出下拉;否則就讓下拉消失。 監(jiān)控文本框輸入并不難:給它的document增加listener就行了。這里我們使用了“不區(qū)分大小寫”、“和輸入字符串開頭相同的項”的規(guī)則進行過濾。將所有備選字符串置于單獨一個數(shù)組中,每次用戶輸入后,動態(tài)過濾出符合條件的字符串,動態(tài)添加到JComboBox中,并將其下拉列表Popup出來即可:

txtInput.getDocument().addDocumentListener(new DocumentListener() {public void insertUpdate(DocumentEvent e) {updateList();}public void removeUpdate(DocumentEvent e) {updateList();}public void changedUpdate(DocumentEvent e) {updateList();}private void updateList() {setAdjusting(cbInput, true);model.removeAllElements();String input = txtInput.getText();if (!input.isEmpty()) {for (String item : items) {if (item.toLowerCase().startsWith(input.toLowerCase())) {model.addElement(item);}}}cbInput.setPopupVisible(model.getSize() > 0);setAdjusting(cbInput, false);} });

? ? ?此外,為了更方便操作,我們再增加幾個快捷鍵:當輸入ESC,主動關(guān)掉下拉列表;當輸入回車或空格,直接把第一項符合要求的字符串輸入文本框:

txtInput.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {setAdjusting(cbInput, true);if (e.getKeyCode() == KeyEvent.VK_SPACE) {if (cbInput.isPopupVisible()) {e.setKeyCode(KeyEvent.VK_ENTER);}}if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {e.setSource(cbInput);cbInput.dispatchEvent(e);if (e.getKeyCode() == KeyEvent.VK_ENTER) {txtInput.setText(cbInput.getSelectedItem().toString());cbInput.setPopupVisible(false);}}if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {cbInput.setPopupVisible(false);}setAdjusting(cbInput, false);} });

? ? ? 還有一個非常重要的技術(shù)要點要進行說明。在popup列表彈出的時候,我們希望用箭頭能夠上下移動選擇條目,但是又同時希望當前的光標和焦點不要離開文本框。這個好像非常難實現(xiàn)啊!請看我們是如何做到的:在監(jiān)控到上下箭頭輸入時候,把當前的鍵盤事件的source動態(tài)修改為JComboBox,然后派發(fā)給JComboBox。也就是說,本來事件是輸入到文本框的,我們把郵遞員攔截下來,把收件人改一下,繼續(xù)交給郵遞員進行派發(fā)。這樣,就做到“移花接木”了:

if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {e.setSource(cbInput);cbInput.dispatchEvent(e);if (e.getKeyCode() == KeyEvent.VK_ENTER) {txtInput.setText(cbInput.getSelectedItem().toString());cbInput.setPopupVisible(false);} }

? ? ?最后,為了演示效果,我們放一些數(shù)據(jù)到下拉列表中。放什么呢?自己造假數(shù)據(jù)太麻煩了,干脆用Java中的“所有國家”的數(shù)據(jù)吧,簡單省事:

Locale[] locales = Locale.getAvailableLocales(); for (int i = 0; i < locales.length; i++) {String item = locales[i].getDisplayName();items.add(item); }

? ? ?最后看一下效果,完全符合我們的預期:

? ? ? 以下是完整代碼:

import java.awt.*; import java.awt.event.*; import java.util.*;import javax.swing.*; import javax.swing.event.*;import twaver.*;public class Test {public static void main(String[] args) throws Exception {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());JFrame frame = new JFrame();frame.setTitle("Auto Completion Test");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setBounds(200, 200, 500, 400);ArrayList<String> items = new ArrayList<String>();Locale[] locales = Locale.getAvailableLocales();for (int i = 0; i < locales.length; i++) {String item = locales[i].getDisplayName();items.add(item);}JTextField txtInput = new JTextField();setupAutoComplete(txtInput, items);txtInput.setColumns(30);frame.getContentPane().setLayout(new FlowLayout());frame.getContentPane().add(txtInput, BorderLayout.NORTH);frame.setVisible(true);}private static boolean isAdjusting(JComboBox cbInput) {if (cbInput.getClientProperty("is_adjusting") instanceof Boolean) {return (Boolean) cbInput.getClientProperty("is_adjusting");}return false;}private static void setAdjusting(JComboBox cbInput, boolean adjusting) {cbInput.putClientProperty("is_adjusting", adjusting);}public static void setupAutoComplete(final JTextField txtInput, final ArrayList<String> items) {final DefaultComboBoxModel model = new DefaultComboBoxModel();final JComboBox cbInput = new JComboBox(model) {public Dimension getPreferredSize() {return new Dimension(super.getPreferredSize().width, 0);}};setAdjusting(cbInput, false);for (String item : items) {model.addElement(item);}cbInput.setSelectedItem(null);cbInput.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (!isAdjusting(cbInput)) {if (cbInput.getSelectedItem() != null) {txtInput.setText(cbInput.getSelectedItem().toString());}}}});txtInput.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {setAdjusting(cbInput, true);if (e.getKeyCode() == KeyEvent.VK_SPACE) {if (cbInput.isPopupVisible()) {e.setKeyCode(KeyEvent.VK_ENTER);}}if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {e.setSource(cbInput);cbInput.dispatchEvent(e);if (e.getKeyCode() == KeyEvent.VK_ENTER) {txtInput.setText(cbInput.getSelectedItem().toString());cbInput.setPopupVisible(false);}}if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {cbInput.setPopupVisible(false);}setAdjusting(cbInput, false);}});txtInput.getDocument().addDocumentListener(new DocumentListener() {public void insertUpdate(DocumentEvent e) {updateList();}public void removeUpdate(DocumentEvent e) {updateList();}public void changedUpdate(DocumentEvent e) {updateList();}private void updateList() {setAdjusting(cbInput, true);model.removeAllElements();String input = txtInput.getText();if (!input.isEmpty()) {for (String item : items) {if (item.toLowerCase().startsWith(input.toLowerCase())) {model.addElement(item);}}}cbInput.setPopupVisible(model.getSize() > 0);setAdjusting(cbInput, false);}});txtInput.setLayout(new BorderLayout());txtInput.add(cbInput, BorderLayout.SOUTH);} }

  

轉(zhuǎn)載于:https://www.cnblogs.com/twaver/archive/2012/06/12/2546432.html

總結(jié)

以上是生活随笔為你收集整理的让JTextField添加“自动完成”功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。