境:SpringBoot2.3.9.RELEASE + QQ郵箱 + JDK8
進入設置---》帳戶
開啟功能
生成授權碼
這里的授權碼用于配置郵件服務的密碼。
spring:
mail:
host: smtp.qq.com
port: 465
default-encoding: UTF-8
username: xxxx@qq.com
password: xxxxx #這里是上一步生成的授權碼
properties:
mail.smtp.ssl.enable: true #開啟SSl
mail.smtp.connectiontimeout: 5000
mail.debug: true #調試模式,這樣在發送郵件時會輸出詳細信息
這里properties支持如下屬性(smtp)
@Resource
private JavaMailSender sender ;
@Test
public void testSimpleMailSend() {
// 構建一個郵件對象
SimpleMailMessage message = new SimpleMailMessage();
// 設置郵件主題
message.setSubject("測試郵件");
// 設置郵件發送者,這個跟application.yml中設置的要一致
message.setFrom("123456@qq.com");
// 設置郵件接收者,可以有多個接收者,中間用逗號隔開,以下類似
message.setTo("123456@qq.com");
// 設置郵件抄送人,可以有多個抄送人
message.setCc("76007509@qq.com");
// 設置隱秘抄送人,可以有多個
message.setBcc("123456@qq.com");
// 設置郵件發送日期
message.setSentDate(new Date());
// 設置郵件的正文
message.setText("這是簡單的郵件");
// 發送郵件
sender.send(message);
}
測試:
控制臺輸出了詳細信息,同時收到了郵件。
@Test
public void testSendAttachMail() throws Exception {
MimeMessage mimeMessage = sender.createMimeMessage();
// true表示構建一個可以帶附件的郵件對象
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("有附件的郵件");
helper.setFrom("123456@qq.com");
helper.setTo("123456@qq.com");
helper.setSentDate(new Date());
helper.setText("我有附件");
helper.addAttachment("JavaMail規范文檔", new File("D:\\java\\opensource\\javamail\\JavaMail-1.6.pdf"));
sender.send(mimeMessage);
}
控制臺
帶附件的郵件發送時,最好吧debug關閉,不然控制臺一直輸出內容,比較慢。
@Test
public void testSendImageMail() throws Exception {
MimeMessage mimeMessage = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("帶圖片的郵件");
helper.setFrom("348792955@qq.com");
helper.setTo("348792955@qq.com");
helper.setSentDate(new Date());
// src='cid:p01' 占位符寫法 ,第二個參數true表示這是一個html文本
helper.setText("<p>這封郵件包含兩種圖片,"
+ "分別如下</p><p>第一張圖片:</p><img src='cid:p01'/><p>第二張圖片:</p><img src='cid:p02'/>", true);
// 第一個參數指的是html中占位符的名字,第二個參數就是文件的位置
helper.addInline("p01",new FileSystemResource(new File("D:\\images\\1.jpg")));
helper.addInline("p02",new FileSystemResource(new File("D:\\images\\2.jpg")));
sender.send(mimeMessage);
}
完畢!!!
ECMAScript 6引入了模板字面量,對字符串的操作進行了增強方式:
真正的多行字符串。
可以將變量或JavaScript表達式嵌入到占位符中并將其作為字符串的一部分輸出到結果中。
模板字面量的基礎語法就是用反引號(`)來替換字符串的單、雙引號,例如:
let message = `Hello World`;
這句代碼使用模板字面量語法創建了一個字符串,并賦值給message變量,這時變量的值與一個普通的字符串并無差異。
如果想要在字符串中使用反引號,那么用反斜杠(\)將它轉義即可,如下所示:
let message = `Hello\` World`;
在模本字面量中,不需要轉義單、雙引號。
在ECMAScript 5中,如果一個字符串字面量要分為多行書寫,那么可以采用兩種方式來實現:在一行結尾的時候添加斜杠(\)表示承接下一行的代碼,或者使用加號(+)來拼接字符串,如下所示:
let message = "Hello \
World";
let greeting = "Welcome"
+ " you";
console.log(message);
console.log(greeting);
這兩種實現方式,前者是利用JavaScript的語法bug來實現,后者是利用字符串的拼接操作來實現。當把字符串message和greeting打印到控制臺時,這兩個字符串均未進行顯示,前者使用反斜杠(\)只是代表行的延續,并未真正插入新的一行。如果要輸出新的一行,需要手動加入換行符,如下所示:
let message = "Hello \n\
World";
let greeting = "Welcome"
+ "\n"
+ " you";
console.log(message);
console.log(greeting);
在ECMAScript 6中,使用模板字面量語法,可以很方便地實現多行字符串的創建。如果需要在字符串中添加新的一行,只需要在代碼中直接換行即可,如下所示:
let message = `Hello
World`;
console.log(message);
輸出結果如下:
Hello
World
要注意:在反引號中的所有空白字符(包括但不限于空格、換行、制表符)都屬于字符串的一部分。
在一個模板字面量中,可以將JavaScript變量或者任何合法的JavaScript表達式嵌入到占位符中并將其作為字符串的一部分輸出到結果中。
占位符由一個左側的 ${ 和右側的 } 符號組成,中間可以包含變量或JavaScript表達,例如:
let name = "zhangsan";
let message =`Hello, ${name}`;
console.log(message);
let amount = 5;
let price = 86.5;
let total = `The total price is ${price * amount}`;
console.log(total);
模板字面量本身也是JavaScript表達式,因此也可以在一個模板字面量中嵌入另一個模板字面量,如下所示:
let name = "lisi";
let message = `Hello, ${
`my name is ${name}`
}.`;
console.log(message); //輸出:Hello, my name is lisi.
Vue.js從入門到實戰
現在終于可以開始介紹Swing用戶界面組件了。首先,介紹具有用戶輸入和編輯文本功能的組件。文本域(JTextField)組件和文本區(JTextArea)組件用于獲取文本輸入。文本域只能接收單行文本輸入而文本區可以接收多行文本輸入。
這兩個類都繼承于JTextComponent類。由于JTextComponent類是抽象類,所以不能構造這個類的對象。另外,在Java中常會看到這種情況,當查看API文檔時,發現自己正在尋找的方法實際上來自父類JTextComponent,而不是來自派生類自身。例如,在一個文本域和文本區內獲取(get)、設置(set)文本的方法實際上都是JTextComponent類中的方法。
javax.swing.text.JTextComponent 1.2
? void setText(String t)
改變文本組件的文本。
參數:t 新文本
? String getText( )
返回文本組件中的文本。
? void setEditable(boolean b)
確定用戶是否可以編輯JTextComponent的內容。
把文本域添加到窗口中的常用辦法是把它添加到面板或者其他容器中,這與添加按鈕完全一樣:
JPanel panel = new JPanel( );
JTextField textField = new JTextField("Default input", 20);
panel.add(textField);
這段代碼將添加一個文本域,同時通過放入一個字符串“Default input”來對它進行初始化。
構造器的第二個參數設置了文本域的寬度。在這個例子中,寬度值為20“列”。但是,這里所說的列不是一個精確的測量單位。一列就是在當前使用的字體下一個字符的寬度。如果希望文本域最多能夠輸入n個字符,就應該把寬度設置為n列。在實際中,這樣做效果不是很好,應該將最大輸入長度再多設1~2個字符。列數只是給AWT設定首選(preferred)大小的一個提示。如果布局管理器需要縮放這個文本域,它會調整文本域的大小。在JTextField的構造器中設定的列寬度并不是用戶能輸入的字符個數的上限。用戶可以輸入一個更長的字符串,但是當文本長度超過文本域長度時輸入就會滾動。用戶通常不喜歡滾動文本域,因此應該盡量把文本域設置得大一些。如果需要在運行時重新設置列數,可以使用setColumns方法。
提示:使用setColumns方法改變文本框的大小之后,需要調用包含這個文本框的容器的revalidate方法。
textField.setColumns(10);
panel.revalidate( );
revalidate方法會重新計算容器內所有組件的大小,并且對它們重新進行布局。調用revalidate方法以后,布局管理器會重新設置容器的大小,然后就可以看到改變尺寸后的文本域了。
revalidate方法是JComponent類中的方法。它并不是馬上就改變組件大小,而是標記該組件需要改變大小。這樣就避免了多個組件都要改變大小時帶來的重復計算。
但是,如果想重新計算一個JFrame中的所有組件,就需要調用validate方法—JFrame沒有擴展JComponent。
通常情況下,希望用戶在文本域中添加文本(或者編輯已經存在的文本)。文本域一般初始為空白。只要不為JTextField構造器提供字符串參數,就可以建立一個空白的文本域。
JTextField textField = new JTextField(20);
可以在任何時候調用setText方法來改變文本域中的內容。這個方法是從前面提到的JTextComponent中繼承而來的。例如:
textField. setText("Hello!");
并且,在前面已經提到,可以調用getText方法提取用戶鍵入的文本。這個方法返回用戶輸入的文本。如果想要把getText方法返回的文本域中的數據內容的前后空格去掉,就應該調用trim方法:
String text = textField.getText( ).trim( );
如果想要改變顯示文本的字體,就使用setFont方法。
javax.swing.JTextField 1.2
? JTextField(int cols)
構造一個指定列數的空JTextField。
參數:cols 文本域中的列數
? JTextField(String text, int cols)
構造一個指定列數、指定初始字符串的JTextField。
參數:text 顯示文本
cols 列數
? void setColumns(int cols)
告知文本域使用的列數。
參數:cols 列數
javax.swing.JComponent 1.2
? void revalidate( )
重新計算組件的位置和大小。
java.awt.Component 1.0
? void validate( )
重新計算組件的位置和大小。如果組件是容器,容器的位置和大小將被重新計算。
標簽是容納文本的組件。它們沒有任何修飾(例如沒有邊界),也不響應用戶輸入。可以利用標簽標識組件。例如,與按鈕不同,文本域沒有標簽標識它們。要想用標識符標識這種不帶標簽的組件,應該
1)用相應的文本構造JLabel組件。
2)將標簽組件放置在離需要標識的組件足夠近的地方,這樣用戶就會知道標簽標識的組件。
JLabel的構造器允許指定初始文本和圖標,也可以選擇內容的排列方式。可以用SwingConstants接口中的常量來指定排列方式。這個接口定義了幾個很有用的常量,如LEFT、RIGHT、CENTER、NORTH、EAST等。JLabel是實現該接口的一個Swing類。因此,可以指定右對齊標簽:
JLabel label = new JLabel("Minutes", SwingConstants.RIGHT);
或者
JLabel label = new JLabel("Minutes", JLabel.RIGHT);
利用setText和setIcon方法可以在運行期間設置標簽的文本和圖標。
提示:從JDK 1.3開始,可以在按鈕、標簽和菜單項上使用無格式文本和HTML文本。
我們不推薦在按鈕上使用HTML文本—這樣會影響觀感。但是HTML文本在標簽中是非常有效的。只要簡單地將標簽字符串放置在<html>. . .</html>中即可:
label = new JLabel("<html><b>Required</b>entry:</html>");
警告—包含HTML標簽的第一個組件需要延遲一段時間才能顯示出來,這是因為需要加載相當復雜的HTML顯示代碼。
與其他組件一樣,標簽也可以放置在容器中。這就是說,可以利用前面講述的技巧把標簽放置在任何需要的地方。
javax.swing.JLabel 1.2
? JLabel(String text)
構造左對齊文本的標簽。
參數:text 標簽中的文本
? JLabel(Icon icon)
構造左對齊圖標的標簽。
參數:icon 標簽中的圖標
? JLabel(String text, int align)
構造指定文本和排列方式的標簽。
參數:text 標簽中的文本
align 一個SwingConstants的常量LEFT、CENTER或者RIGHT
? JLabel(String text, Icon icon, int align)
構造具有文本和圖標的標簽。圖標位于文本的左側。
參數:text
標簽中的文本
icon
標簽中的圖標
align
一個SwingConstants的常量LEFT、CENTER或者RIGHT
? void setText(String text)
設定標簽的文本。
參數:text
標簽中的文本
? void setIcon(Icon icon)
設定標簽的圖標。
參數:icon
標簽中的圖標
讓我們應用文本域來完成一些操作。圖9-12展示了運行例9-2的應用程序的外觀。這個程序顯示了一個時鐘,并且帶有兩個文本域用來輸入小時和分鐘。只要這兩個文本域中的內容發生改變,時鐘就會跟著改變。
跟蹤文本域的每一次變化需要費點功夫。首先,需要注意,監視擊鍵并不是一個好主意。有些擊鍵(如箭頭鍵)并不改變文本。而且,對于某些觀感來說,鼠標動作也會導致文本改變。在本章開頭已經看到,Swing文本域以一種非常通用的方法來實現:在文本域中看到的字符串只是底層數據結構(模型)的可視化表現(視圖)。當然,對于一個簡單的文本域來說,二者之間沒有太大的區別。
視圖是顯示字符串,而模型是字符串對象。但是同樣的體系結構也用于更高級的編輯組件。這些組件可以通過字體、段落以及其他更加復雜的數據結構標識的屬性來提供格式化文本。所有文本組件的模型都由Document接口描述,既包括無格式的文本,也包括格式化的文本(例如HTML)。當數據改變后,可以要求文檔(而非文本組件)給予通告,這將需要安裝一個文檔監聽器(document listener):
textField.getDocument( ).addDocumentListener(listener);
當文本發生改變后,下面所列的DocumentListener方法中的一個會被調用:
void insertUpdate(DocumentEvent event)
void removeUpdate(DocumentEvent event)
void changedUpdate(DocumentEvent event)
當添加或者刪除字符時,應該調用前兩個方法。文本域根本不會調用第三個方法。對于復雜的文檔類型來說,當一些內容發生變化時(例如改變格式),就會調用第三個方法。遺憾的是,沒有任何回調方法會通告文本已改變—通常無需考慮它是如何改變的。而且這里也沒有適配器類,因此文檔監聽器必須實現這三個方法。下面是程序代碼:
private class ClockFieldListener implements DocumentListener
{
public void insertUpdate(DocumentEvent event) { setClock( ); }
public void removeUpdate(DocumentEvent event) { setClock( ); }
public void changedUpdate(DocumentEvent event) {}
}
setClock方法使用getText方法從文本域中獲得當前用戶輸入的字符串。不過,得到的只是字符串。
需要使用我們熟悉的(也許是笨拙的)方法將它們轉換成整數,如下所示:
int hours = Integer.parseInt(hourField.getText( ).trim( ));
int minutes = Integer.parseInt(minuteField.getText( ).trim( ));
但是當用戶在文本域中輸入了非整數字符串,如“two”或者文本域中為空時,這段代碼就會產生錯誤。在這種情況下,可以捕獲parseInt方法拋出的NumberFormatException異常,如果文本域中的內容不是數字,就不更新時鐘了。在下一節中,將會看到如何在第一時間阻止用戶的無效輸入。
注意:除了監聽文檔事件以外,還可以把動作事件監聽器添加到文本域中。當用戶按下ENTER鍵時,該動作監聽器就會得到通知。我們不推薦此方法,因為用戶常常忘記在輸入完數據后再敲一下回車鍵。如果使用一個動作監聽器,就應該同時也安裝一個焦點監聽器,這樣當用戶離開文本域時就會得到通知。
最后,說明ClockPanel構造器如何設置首選的大小:
當框架的pack方法計算框架大小時,將使用面板首選的大小。
例9-2 TextTest.java
javax.swing.JComponent 1.2
? void setPreferredSize(Dimension d)
設置組件首選的大小。
javax.swing.text.Document 1.2
? int getLength( )
返回當前文檔中的字符個數。
? String getText(int offset, int length)
返回文檔中給定部分所包含的文本。
參數:offset 文本的起始偏移量
Length 需要的字符串長度
? void addDocumentListener(DocumentListener listener)
注冊監聽器來監聽文檔改變時的通知。
javax.swing.event.DocumentEvent 1.2
? Document getDocument( )
獲得作為事件源的文檔。
javax.swing.event.DocumentListener 1.2
? void changedUpdate(DocumentEvent event)
當一個屬性或者屬性集發生改變時被調用。
? void insertUpdate(DocumentEvent event)
當文檔發生一個插入操作時被調用。
? void removeUpdate(DocumentEvent event)
當文檔的一部分被刪除時被調用。
密碼域是一種特殊的文本域。為了避免有某種企圖的人看到密碼,用戶輸入的字符不顯示出來。每個輸入的字符都用回顯字符(echo character)表示,通常用星號“*”取代。Swing提供了JPasswordField類來實現這樣的文本域。
密碼域是另一個模型-視圖-控制器體系模式的例子。密碼域采用與規則的文本域相同的模型來存儲數據,但是它的視圖改為顯示回顯字符,而不是實際的字符。
javax.swing.JPasswordField 1.2
? JPasswordField(String text, int columns)
構造一個新的密碼域。
參數:text 將要顯示的文本,沒有相應的文本則為null
columns 列數
? void setEchoChar(char echo)
為密碼域設置回顯字符。注意,特殊的觀感可以選擇自己的回顯字符。0表示重置為默認的回顯字符。
參數:echo 代替文本字符顯示的回顯字符
? char[ ] getPassword( )
返回密碼域中的文本。為了安全起見,在使用之后應該覆寫返回的數組內容。(密碼并不是以字符串String型返回的,因為字符串一直保留在虛擬機上,直到垃圾回收。)
在上一個例子中,希望用戶輸入數字,而不是任意的字符串。就是說只允許用戶輸入0~9的數字加上連字符(-)。并且如果是連字符,必須是輸入的第一個符號。
從表面上看,對輸入進行檢驗十分簡單。我們可以為文本域安裝一個鍵盤監聽器,并且銷毀所有非數字或連字符的鍵盤事件。遺憾的是,這只是一種簡單的方法,盡管常常推薦大家用這種方法處理輸入檢驗,但實際上效果并不好。首先,不是每個由有效輸入字符組合的字符串都是有效的數值,比如--3和3-3就是非法的,盡管它們都由有效輸入字符組成。其次,也是更重要的是,有些改變文本的方法與擊鍵無關。在某些觀感上,一些特定的鍵組合用于實現剪切、復制和粘貼文本操作。例如,在Metal觀感上,組合鍵CTRL+V把緩沖區中的內容粘貼到文本域中。所以,需要監視以保證用戶粘貼的是一個有效的字符。很明顯,試圖通過過濾擊鍵來確保文本域中內容的有效性是一件很困難的事情。當然這也不是應用程序程序員要關注的問題。
也許你會感到奇怪,在JDK 1.4之前,沒有輸入數值的組件。從本書的第1版開始,我們就提供了一個用于輸入格式化整型的文本域IntTextField。(在每個新版本中,都會改變一些不成熟的驗證機制的實現。)最終,在JDK 1.4中,Swing設計者面對問題,提供了一個多功能的JFormattedTextField類,這個類不僅可以用于輸入數值,也可以用于輸入時間和更加復雜的格式化數值,如IP地址。
1. 整型輸入
下面先從一個簡單的例子開始:整型輸入的文本域。
JFormattedTextField intField = new JFormattedTextField(NumberFormat.getIntegerInstance( ));
NumberFormat. getIntegerInstance返回一個用當前地區采用的格式來格式化整型數值的格式器對象。在US地區,逗號是十進制數的分隔符,允許用戶輸入如1,729的數值。在卷II的國際化章節中將詳細地解釋如何選擇其他的地區。
對任何文本域,可以設置列數:
intField.setColumns(6);
可以用setValue方法設置一個默認值。這個方法有一個Object類型的參數,需要把int值包裝成Integer對象:
intField.setValue(new Integer(100));
通常,用戶在多個文本域中輸入,然后點擊一個按鈕讀取所有值。當點擊按鈕時,可以用getValue方法讀取用戶輸入的數值。這個方法將返回一個Object結果,需要將它強制類型轉換為相應的類型。如果用戶對數值進行編輯,JFormattedTextField就會返回一個Long類型的對象。然而,如果用戶沒有做任何修改,就會返回原來的Integer對象。因此,應該把返回值轉換成通用的超類Number:
Number value = (Number) intField.getValue( );
int v = value.intValue( );
在沒有考查用戶進行非法輸入后會發生什么事情之前,格式化文本域不會引起人們太大的興趣。下面就討論這個問題。
2. 失去焦點的行為
試想一下當用戶在文本域中輸入之后會發生什么情況。用戶輸入后最終決定離開這個區域,也許是通過鼠標點擊另一個組件。于是,文本域就失去焦點(lose focus)。I型光標在文本域中也不見了。鍵盤輸入將作用于另一個組件。
當格式化的文本域失去焦點時,格式器查看用戶輸入的文本字符串。如果格式器知道如何把文本字符串轉換為對象,文本就有效,否則就無效。可以用isEditValid方法檢測文本域的當前內容是否有效。
失去焦點的默認行為被稱為“提交或恢復”。如果文本字符串是有效的,它將被提交(commit)。
格式器將它轉換為對象,該對象成為當前文本域的值(就是如前所述的getValue方法返回的值)。
然后,該值被轉換為字符串,成為文本域內可見的文本字符串。例如,整型格式器認為輸入1729是有效的,設置當前值為new Long(1729),并且把它轉換為帶有逗號的字符串:1,729。
相反地,如果文本字符串無效,當前值就不會改變。文本域恢復表示原值的字符串。例如,如果用戶輸入錯誤的值(像x1),那么在文本域失去焦點時就會恢復原值。
注意:如果文本字符串以一個整型開頭,那么整型格式器就認為它是有效的。例如1729x是一個有效的字符串。它將被轉換為1729,然后格式化為字符串1,729。
可以用setFocusLostBehavior方法設置其他行為。“提交”行為和默認行為有一些微小的差別。
如果文本字符串無效,文本字符串和文本域的值都不變—它們現在不同步。“持續”行為更加保守,即使文本字符串有效,文本域和當前值也不會改變。要調用commitEdit、setValue或者setText方法保證它們同步。最后,“恢復”行為似乎沒什么用。無論何時失去焦點,用戶輸入都將被拋棄,文本字符串恢復到原值。
注意:通常,“提交或恢復”默認行為是合理的。只是存在一個潛在的問題。假設一個對話框包含一個整型值的文本域。用戶輸入字符串“1729”,前面有空格,然后點擊OK按鈕。空格導致數字無效,文本域值恢復到原值。OK按鈕的動作監視器得到文本域值并且關閉對話框。用戶并不知道他們輸入的新值沒有被接受。
在這樣的情況下,選擇“提交”行為可能更合適,并且讓OK按鈕監聽器在關閉對話框前檢測所有文本框內的值是否有效。
3. 過濾器
格式化文本域的基本功能簡單明了,在大多數情況已經夠用了。但是,可以進一步得到改進。我們可能希望完全阻止用戶輸入非數字,這里可以用文檔過濾器(document filter)達到此目的。回憶一下模型-視圖-控制器體系,控制器把輸入事件轉化為命令修改文本域的底層文檔,這就是說,文本字符串存儲在PlainDocument對象中。例如,無論何時控制器處理命令,都會將文本插入文檔中,這稱為“插入字符串”命令。被插入的字符串可以是單個的字符,也可以粘貼緩沖區的內容。文檔過濾器可以中途截取命令,并改變字符串或者取消插入。下面是過濾器的一段insertString方法代碼,它將分析要插入的字符串,并且只將數字或者“-”符號插入到文檔中。(這段代碼的處理可參見第3章解釋的輔助Unicode字符。請參看第12章的StringBuilder類。)
可以覆蓋DocumentFilter類的replace方法,在文本被選擇或替換的時候調用此方法。replace方法的實現非常簡單,請參照本節最后的程序。
現在需要安裝文檔過濾器了。可惜的是沒有一個直接的方法。需要覆蓋格式器類的getDocumentFilter方法,并傳遞一個格式器類的對象給JFormattedTextField。整型文本域使用了InternationalFormatter,它用NumberFormat.getIntegerInstance( )進行初始化。下面是安裝格式器產生想要的過濾器的程序:
注意:在JDK文檔中提到,為了避免子類化發明了DocumentFilter類。直到JDK 1.3,都是通過擴展PlainDocument類并且覆蓋insertString和replace方法來實現文本域過濾的。
現在PlainDocument類有了可插拔的過濾器,這是一個顯著的進步。如果在格式器類中有可插拔的過濾器會更好。然而,這里沒有,我們必須子類化格式器。
試一下本節最后的FormatTest程序。第三個文本域安裝了一個過濾器。只能插入數字或者負號“-”。注意,這里還可以輸入無效的字符串,如“1-2-3”。通常,不可能通過過濾器避免所有的無效字符串。例如:字符串“-”是無效的,但是過濾器沒有拒絕它,因為它是有效字符串“-1”的前綴。盡管過濾器沒能給出完全的保護,但還是可以用來避免那些明顯的無效輸入。
提示:過濾器的另一個用途是把字符串中的所有字符變成大寫。這樣的過濾器很容易編寫。在過濾器的insertString和replace方法中,把要被插入的字符串轉換成大寫,然后調用超類的方法。
4. 檢驗器
還有另外一種潛在的實用機制,它可以用來警告用戶以避免無效的輸入。可以給任何JComponent附加檢驗器(verifier)。如果組件失去焦點,就詢問檢驗器。如果檢驗器報告組件中的內容是無效的,組件馬上就會重獲焦點。因此,用戶在提供其他輸入之前,必須先修正無效的內容。
檢驗器必須擴展抽象類InputVerifier,并且定義verify方法。定義檢測格式化文本域的檢驗器是非常簡單的。JFormattedTextField類的isEditValid方法調用格式器,如果格式器可以把文本字符串轉化為對象則返回true。下面是檢驗器:
可以把它附加到任何JFormattedTextField中:
intField.setInputVerifier(new FormattedTextFieldVerifier( ));
然而,檢驗器并不是萬無一失的。如果點擊按鈕,按鈕會在無效組件重新獲得焦點之前通知它的動作監聽器。動作監聽器就會從驗證失敗的組件得到無效的結果。采用這種處理方式的原因是,用戶可能想點擊Cancel,這時不需要對無效的輸入進行修改。
在示例程序中的第4個文本域上附加了一個檢驗器。嘗試輸入一個無效的數字(如x1729)然后敲擊Tab鍵或者用鼠標點擊另外一個文本域。注意,這個文本域立刻獲得焦點。但是,如果點擊OK按鈕,動作監聽器將調用getValue報告上一個有效的數值。
5. 其他標準的格式
除了整型格式器以外,JFormattedTextField還支持幾種其他的格式器。NumberFormat類有下面幾個靜態方法:
getNumberInstance
getCurrencyInstance
getPercentInstance
這些方法產生浮點數、貨幣數值和百分數格式器。例如,可以得到用于輸入貨幣數值的文本域:
JFormattedTextField currencyField = new JFormattedTextField(NumberFormat.getCurrencyInstance( ));
編輯日期和時間需要調用DateFormat類的靜態方法之一:
getDateInstance
getTimeInstance
getDateTimeInstance
例如:
JFormattedTextField dateField = new JFormattedTextField(DateFormat.getDateInstance( ));
這個文本域使用默認(“medium”)格式編輯日期:
Feb 24, 2002
可以選擇一個“short”格式替代:
2/24/02
使用:
DateFormat.getDateInstance(DateFormat.SHORT)
注意:在默認情況下,數據格式是“不嚴格的”。也就是說,無效日期(February 31,2002)將滾動到下一個有效日期(March 3, 2002)。這種行為可能會讓用戶感到吃驚。在這種情況下,調用DateFormat對象的setLenient(false)方法。
DefaultFormatter可以格式化任何類的對象,只要該類有一個字符串類型參數的構造器和匹配的toString方法。例如,URL類有一個URL(String)構造器利用字符串構造URL,如:
URL url = new URL("http://java.sun.com");
因此,可以使用DefaultFormatter格式化URL對象。格式器針對文本域值調用toString方法來初始化文本域的文本。當文本域失去焦點時,格式器使用帶有String參數的構造器構造相同類的新對象作為當前值。如果構造器拋出了異常,編輯就是無效的。可以在示例程序中試驗一下,比如輸入一個不以“http:”前綴開頭的URL。
注意:在默認情況下,DefaultFormatter處于覆寫(overwrite)狀態。與其他格式器不同,也不很實用。可以調用setOverwriteMode (false)關閉覆寫狀態。
最后,MaskFormatter對包含一些常量和一些變量字符的固定大小的樣式很有用。例如,社會保險號(如078-05-1120)可以用
new MaskFormatter("###-##-####")格式化。#符號表示一個單個數字,表9-2顯示了可以用在掩碼格式器的符號。
可以通過調用MaskFormatter類的下列方法之一限制輸入文本域的字符:
setValidCharacters
setInvalidCharacters
例如,為了讀取用字符表示的分數(比如A+或者F),可以使用MaskFormatter formatter = new MaskFormatter("U * ");
Formatter.setValidCharacters("ABCDF+-");
但是,沒有方法能夠指定第二個字符不能是一個字母。
注意,字符串是被掩碼格式器格式化的,它和掩碼具有完全相同的長度。如果用戶在編輯過程中刪除字符,那么它們將被占位符(holer character)替代。默認的占位字符是空格,可以使用setPlaceholderCharacter方法改變它,例如:
formatter.setPlaceholderCharacter('0');
在默認情況下,掩碼格式器處于overtype狀態,直觀上是這樣,可以在示例程序中試驗一下。
同時也要注意^符號的位置在掩碼中跳過了固定字符。
掩碼格式器對于固定格式(比如社會保險號和美國電話號碼)非常有效。然而,也要注意
到在掩碼格式下不允許有任何不一致的地方。例如,國際電話號碼就不能使用掩碼格式器,因
為各個國家的電話號碼可能有不同的位數。
6. 自定義格式器
如果所有的標準格式器都不適用,就需要定義自己的格式器,做這件事情很容易。下面看一下4字節的IP地址,例如:
130.65.86.66
不能使用MaskFormatter,因為每個字節可能表示為一位、兩位或三位數字。同時,還希望在格式器中檢查每個字節的最大值為255。
為了自定義格式器,擴展DefaultFormatter類并且覆蓋其中的方法。
String valueToString(Object value)
Object stringToValue(String text)
第一個方法把值轉換為顯示在文本域中的字符串。第二個方法解析用戶輸入的文本并轉換為對象。如果有一個方法出錯,將拋出ParseException。
在示例中,把IP地址存儲在長度為4的byte[ ]數組中。valueToString方法形成一個字符串,它用句號分隔字節。注意byte值是其值在-128到127之間的符號數。為了把負數轉換為無符號整數值,需要加上256。
與之相反,如果字符串有效,stringToValue方法將解析并生成一個byte[ ]對象,如果無效,將拋出ParseException。
試一下示例中的IP地址域,如果輸入一個無效的地址,地址域就將恢復成上一個有效地址。
例9-3的程序展示了不同格式化的文本域(參見圖9-13)。點擊OK按鈕從域內得到當前值。
注意:“Swing Connection”在線新聞簡訊有一篇簡短的文章描述了一個格式器,該格式器匹配任何正則表達式。參看http://java.sun.com/products/jfc/tsc/articles/reftf/。
例9-3 FormatTest.java
javax.swing.JFormattedTextField 1.4
? JFormattedTextField(Format fmt)
使用給定的格式構造一個文本域。
? JFormattedTextField(JFormattedTextField.AbstractFormatter formatter)
使用給定的格式器構造文本域。注意DefaultFormatter和InternationalFormatter是JFormattedText
Field.AbstractFormatter的子類。
? Object getValue( )
返回文本域的當前有效值。注意,它可能和正在編輯的字符串不一致。
? void setValue(Object value)
設置給定對象的值。如果格式器不能將對象轉換為字符串,操作失敗。
? void commitEdit( )
用正在編輯的字符串設置文本域的有效值。如果格式器無法轉換該字符串,操作可能失敗。
? boolean isEditValid( )
檢測正在編輯的一個字符串是否代表了一個有效值。
? void setFocusLostBehavior(int behavior)
? int getFocusLostBehavior( )
設置或得到“失去焦點”的行為。behavior的合法值可以是JFormattedTextField類的常量
COMMIT_ OR_REVERT、REVERT、COMMIT和PERSIST。
java.text.DateFormat 1.1
? static DateFormat getDateInstance( )
? static DateFormat getDateInstance(int dateStyle)
? static DateFormat getTimeInstance( )
? static DateFormat getTimeInstance(int timeStyle)
? static DateFormat getDateTimeInstance( )
? static DateFormat getDateTimeInstance(int dateStyle, int timeStyle)
返回Date對象產生的日期、時間或日期與時間。dateStyle與timeStyle的合法值是DateFormat類的常量SHORT、MEDIUM、LONG、FULL以及DEFAULT。
javax.swing.JFormattedTextField.AbstractFormatter 1.4
? abstract String valueToString(Object value)
將一個值轉換為可編輯字符串。如果value不適合這個格式器將拋出ParseException異常。
? abstract Object stringToValue(String s)
將一個字符串轉換為值。如果s不適合這個格式拋出ParseException異常。
? DocumentFilter getDocumentFilter( )
覆蓋該方法以提供一個文檔過濾器,用來限制對文本域的輸入。返回值是null表示不需要過濾器。
javax.swing.text.DefaultFormatter 1.3
? void setOverwriteMode(boolean mode)
? boolean getOverwriteMode( )
設置或得到覆寫的方式。如果mode為true,編輯文本時用新的字符覆寫已有的字符。
javax.swing.text.DocumentFilter 1.4
? void insertString(DocumentFilter.FilterBypass bypass, int offset, String text, AttributeSet attrib)
字符串插入文檔之前調用這一方法。可以覆蓋該方法并修改字符串。也可以不調用 super.insert
String禁止插入,或者調用bypass方法修改文檔而不使用過濾器。
參數:bypass 一個對象,允許執行屏蔽過濾器的編輯命令
offset 插入文本處的偏移
text 將要插入的字符
attrib 插入文本的格式屬性
? void replace(DocumentFilter.FilterBypass bypass, int offset, int length, String text, AttributeSet attrib)
該方法在使用新字符串替換部分文檔之前調用。可以覆蓋該方法并修改字符串。也可以調用super.replace禁止替換,或者調用bypass方法修改文檔而不使用過濾器。
參數:bypass 一個對象,允許你執行一個屏蔽過濾器的編輯命令
offset 插入文本處的偏移
length 被替換部分的長度
text 將要插入的字符
attrib 插入文本的格式屬性
? void remove(DocumentFilter.FilterBypass bypass, int offset, int length)
在文檔的一部分被刪除(removed)之前調用。如果需要分析刪除的效果,調用bypass.getDocument( ) 獲得文檔。
參數:bypass 一個對象,允許你執行一個屏蔽過濾器的編輯命令
offset 被刪除部分的偏移
length 被刪除部分的長度
javax.swing.text.MaskFormatter 1.4
? MaskFormatter(String mask)
用給定的掩碼構造掩碼格式器。掩碼符號參看表9-2。
? void setValidCharacters(String characters)
? String getValidCharacters( )
設置或得到有效的編輯字符。只有給定字符串中的字符才能作為掩碼的可變部分。
? void setInvalidCharacters(String characters)
? String getInvalidCharacters( )
設置或得到無效的編輯字符。任何給定字符串中的字符都不能輸入。
? void setPlaceholderCharacter(char ch)
? char getPlaceholderCharacter( )
設置或得到占位符,在用戶沒有提供時該占位符作為掩碼的可變字符使用。默認的占位符是空格。
? void setPlaceholder(String s)
? String getPlaceholder( )
設置或得到占位符字符串。如果用戶沒有提供掩碼中所有的可變字符則使用其尾部。如果是null或比掩碼短,用占位符填充剩余的輸入。
? void setValueContainsLiteralCharacters(boolean b)
? boolean getValueContainsLiteralCharacters( )
設置或得到“包含直接量字符的值”標志。如果該標志為true,文本域中的值包含掩碼的直接量(非可變)部分。如果為false,直接量字符將被移除。默認值為true。
有時,用戶的輸入超過一行。正像前面提到的,需要使用JTextArea組件來接受這樣的輸入。當在程序中放置一個文本區組件時,用戶就可以輸入多行文本,并用ENTER鍵換行。每行都以一個'\n'結尾。如果需要將用戶的輸入分割為多個單獨的行,可以使用StringTokenizer類(參見第12章)。圖9-14顯示了一個工作的文本區。
在JTextArea組件構造器中,可以指定文本區的行數和列數。例如:
textArea = new JTextArea(8,40); //8 lines of 40 columns each
與文本域一樣,出于安全的考慮,應該把列數設置得大一些。用戶不僅僅限于輸入指定的行數和列數,當輸入過長時,文本會滾動。也可以使用setColumns方法改變列數,用setRows方法改變行數。這些數值只是首選大小—布局管理器可能會對文本區進行縮放。
如果文本區的文本超出顯示的范圍,剩下的文本就會被剪裁掉。可以使用換行來避免裁剪過長的行:
textArea.setLineWrap(true); //long lines are wrapped
換行只是視覺效果;文檔中的文本沒有改變,在文本中沒有插入'\n'字符。
在Swing中,文本區沒有滾動條。如果需要滾動條,可以把文本區中插入一個滾動窗格(scroll pane)中。
textArea =new JTextArea(8, 40);
JScrollPane scrollPane = new JScrollPane(textArea);
現在滾動窗格管理文本區的視圖。如果文本超出了文本區可以顯示的范圍,滾動條就會自動出現,并且在刪除部分文本后,當文本能夠顯示在文本區范圍內時,滾動條會再次消失。滾動是由滾動窗格內部處理的,編寫程序時無需處理滾動事件。
提示:在Swing中,為組件增加滾動條的通用機制是將組件放置在滾動窗格中。
例9-4給出了文本區演示的完整代碼。這個程序只能在文本區中修改文本。點擊“Insert”將句子插入文本末尾。點擊第二個按鈕將打開和關閉換行(它的標簽在“Wrap”和“No Wrap”之間切換)。當然,可以使用鍵盤來編輯文本區的文本。注意,可以高亮顯示部分文本并且使用CTRL+X、CTRL+C和CTRL+V鍵來剪切、拷貝和粘貼文本。(快捷鍵有特定的觀感效果。前面幾個組合鍵在Metal、Windows和Mac中的觀感效果相同。)
注意:JTextArea組件只顯示無格式的文本,沒有字體或者格式設置。如果想要顯示格式化文本(如HTML或者RTF),就需要使用JEditorPane和JTextPane類。在卷II將詳細討論這幾個類。
例9-4 TextAreaTest.java
javax.swing.JTextArea 1.2
? JTextArea(int rows, int cols)
構造一個新的文本區。
參數:rows 行數
cols 列數
? JTextArea(String text, int rows, int cols)
用一個初始文本構造一個新的文本區。
參數:text 初始文本
rows 行數
cols 列數
? void setColumns(int cols)
設置文本區應該使用的首選的列數。
參數:cols 列數
? void setRows(int rows)
設置文本區應該使用的首選的行數。
參數:rows 行數
? void append(String newText)
將給定的文本附加到文本區中已有文本的尾部。
參數:newText 要附加的文本
? void setLineWrap(boolean wrap)
打開或關閉換行。
參數:wrap 應該換行,則為true
? void setWrapStyleWord(boolean word)
如果word是true,超長的行會在字邊界處換行。如果為false,超長的行將被截斷而不考慮字邊界。
? void setTabSize(int c)
設置跳格(tab stop)為c列。注意,跳格不會轉化為空格,但是,它們能夠讓文本對齊到下一個跳格處。
參數:c 跳格的列數
javax.swing.JScrollPane 1.2
? JScrollPane(Component c)
創建一個顯示指定組件內容的滾動窗格。當組件內容超過顯示范圍時,滾動條會自動出現。
參數:c 需要滾動的組件
明天給大家分享選擇組件這一塊兒的內容~~~~~大家準時來圍觀哈~~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。