1. 程式人生 > >【Android】設定EditText為僅輸入數字且最多隻能有兩位數字

【Android】設定EditText為僅輸入數字且最多隻能有兩位數字

需求很簡單,就是要設定一個EditText僅能輸入數字且輸入的數字中小數部分最多可以有兩位。

第一步,很簡單,在XML檔案中,將EditText的inputType設定成NumberDecimal,多餘的屬性我就不寫出來,只寫出主要的部分:

<EditText
            。。。
            android:inputType="numberDecimal"
            。。。
 />

第二部,程式碼中修改EditText 的addTextChangedListener 方法,同樣的先上程式碼,再來解釋:

EditText.addTextChangedListener(new TextWatcher() {
	        //    Tips: 
		//    1. onTextChanged和beforTextChanged傳入的引數s其實是當前EditText的文字內容,而不是當前輸入的內容
		//    2. 如果在任意一個方法中呼叫了設定當前EditText文字的方法,setText(),實際都觸發了一遍這3個函式,
		//       所以要有判斷條件,在if體內去setText,而且就需要手動設定游標的位置,不然每次游標都會到最開始的位置
		//    3. onTextChanged中,before=0: 增加;before=1: 點選刪除按鍵
			
		private int count_decimal_points_ = 0;  // 標識當前是不是已經有小數點了
		private int selection_start_;  			// 監聽游標的位置
		private StringBuffer str_buf_;			// 快取當前的string,用以修改內容
		@Override
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
			str_buf_ = new StringBuffer(s.toString().trim());
			// 先判斷輸入的第一位不能是小數點
			if (before == 0 && s.length() == 1 && s.charAt(start) == '.') {
				recharge_money.setText("");
			} else if (before == 0 && count_decimal_points_ == 1) {
			// 在判斷如果當前是增加,並且已經有小數點了,就要判斷輸入是否合法;如果是減少不做任何判斷
				// 注意在if語句中都是在else體內呼叫了設定游標監聽位的方法,因為在呼叫setText之後會出現巢狀的情況
				// 非合法的輸入包括: 1. 輸入的依舊是小數點,2.小數點後位數已經達到兩位了
				if (s.charAt(start) == '.' ||  (start - str_buf_.indexOf(".") > 2) ) {
					str_buf_.deleteCharAt(start);
					recharge_money.setText(str_buf_);
				} else {
					selection_start_ = str_buf_.length();		// 設定游標的位置為結尾
				}
			} else {
				selection_start_ = str_buf_.length();		// 設定游標的位置為結尾	
			}
		}
		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
			if (s.toString().contains(".")) {
				count_decimal_points_ = 1;
			} else {
				count_decimal_points_ = 0;	// 因為可能存在如果是刪除的話,把小數點刪除的情況
			}
		}
		@Override
		public void afterTextChanged(Editable s) {
			// 重置游標位置
			recharge_money.setSelection(selection_start_);
			
			if (s != null) {
				try {
					// TODO Your things
				} catch (NumberFormatException e) {
					e.printStackTrace();
				}
			}
		}
		});


其實我們看到在listener中是new 了一個TextWatcher類,在TextWatcher類中,其實有3個方法,beforeTextChanged、onTextChanged和afterTextChanged。 從它們的名字中我們也不難看出分別是當text發生變化的時候,要做的處理。

在類中,我們聲明瞭3個變數,變數的作用我在註釋裡面也添加了,這裡就不囉嗦了。注意的是之所以用StringBuffer來代替String是因為StringBuffer的效率更高,至於為什麼,自行Google。

看了一下我的程式碼,覺得我該說的東西在註釋裡面都已經寫出來了。這裡需要特別注意的是,如果在你的類中,你呼叫了EditText的setText方法,那麼就會立即觸發TextWatch類,所以,如果你不加if條件判斷,而是直接setText,那麼就會出現死迴圈最終記憶體洩露而崩潰。所以一定要注意,比如你發現使用者的輸入不合法,要把剛輸入的內容刪除掉,重新set一下當前EditText的內容,那麼一定是在if迴圈語句中setText。

還有一點也是和這個相關的,就是我開始的時候,在onTextChanged方法中,把設定游標位置的語句放在了if迴圈體之外,因為我覺得每次不管你是不是滿足條件,都要執行一遍重置游標的操作,就出現了bug,後來是打log的時候才發現了,這裡我把程式碼中的log內容省去了。

具體情況我來給大家解釋一遍,我們先來看一下我最初的程式碼:

<pre name="code" class="java">public void onTextChanged(CharSequence s, int start, int before,
					int count) {
				str_buf_ = new StringBuffer(s.toString().trim());
				// 先判斷輸入的第一位不能是小數點
				if (before == 0 && s.length() == 1 && s.charAt(start) == '.') {
					LogUtil.logMsg("輸入錯誤","第一位不能是小數點");
					recharge_money.setText("");
				} else if (before == 0 && count_decimal_points_ == 1) {
					// 在判斷如果當前是增加,並且已經有小數點了,就要判斷輸入是否合法;如果是減少不做任何判斷
					// 注意在if語句中都是在else體內呼叫了設定游標監聽位的方法,因為在呼叫setText之後會出現巢狀的情況
					// 非合法的輸入包括: 1. 輸入的依舊是小數點,2.小數點後位數已經達到兩位了
					if (s.charAt(start) == '.' ||  (start - str_buf_.indexOf(".") > 2) ) {
						str_buf_.deleteCharAt(start);
						EditText.setText(str_buf_);
					}
				}
selection_start_ = str_buf_.length();		// 設定游標的位置為結尾	
			}




重點就在最後一行,我在任何時候都會設定游標的位置。我們來看bug是如何產生的:

加入這時候已經有一串合法的數字123.45寫在了EditText中,這個時候游標的位置是6,假設這個時候我們又輸入了一個數字6,那麼在TextWatch類中,selection_start_實際上變成了7,因為在這個方法中,CharSequence s,也就是str_buf_的長度是7,也就是123.456,然後我們發現已經有了兩位小數了,手動設定EditText的setText方法,把它改成了123.45,前面我們說過了,這裡會有一個巢狀呼叫,相當於遞迴了一下,注意我們的程式在afterTextChanged中要重置游標位置的,bug就出在這裡。

我們來畫一下流程吧:

123.456 onTextChanged --> 123.45 onTextChanged --> 123.45 afterTextChanged --> 123.456 afterTextChanged

我省去了beforeTextChanged的過程,但是可以看到,我們在迴圈體的外面是要呼叫123.456的afterTextChanged方法的,這個方法裡面的selection_start_也就是游標位置是7,但是其實,這個時候text的內容已經變成了123.45,那這個text只有6位,你要把游標設定在7,那一定會崩潰的。

所以,因為有巢狀的關係,我們一定要謹慎地處理每一步。