EditText最大字符數限制
一、寄語:
EditText輸入最大字符個數限制實質上即事最大字節數限制,此代碼通過比較輸入的字符的字節數,然后一次性截取掉超過字節數限制的部分,不用單獨區分中文還是英文。此代碼不處理特殊字符(暫無此需求),網上很多例子都是對edittext最大字符個數(輸入的字符數)限制進行編碼實現,例如:同是6個中文和英文,他們占用的字節是不一樣的,既然有限制肯定是對存儲空間的限制,即文件名太長無法保存等,所以考慮字符串的字節數才是最主要的,而不是字符個數。同時,此例子中對超出最大字節數的字符進行一次性截取掉,而非網上大多數進行的每次刪除一個,這樣刪除掉超出限制的字節數字符后就只會再觸發一次afterTextChanged方法,便於處理。
思路真的非常重要,希望大家多看思路、明白思路。
轉載請注明出處:http://www.cnblogs.com/wangqx/p/6096272.html
有任何疑問請留言,內容有不對的地方或者還可以優化,或者園友有更好的實現方式,歡迎探討與指正 ,QQ群238696947
二、需求描述:
對EditText字符個數限制,當用戶輸入字符串超長度過指定值時,不允許輸入,並給出提示。不允許輸入其實是用戶輸入了,代碼中自動剪切掉超出的部分,並不是不允許輸入,因為用戶不輸入,是沒有辦法判斷當前的字符數是否超出限制的,只有用戶輸入超出了限制,才可以判斷輸入的字符超出了限制,進一步處理,剪切掉超出的部分,故給人的感覺是超出最大限制了不允許輸入。
三、思路描述:
以在字符串中間插入字符串(插入后將會超出限制)的情況來歸納總結(其他情況如:在字符串前面插入,或者在字符串末尾插入,均可視為此種情況的特殊情況),思路很重要
1、獲取改變前的字符串4、即下圖中4部分
2、獲取要插入的字符串、即下圖中3部分
3、依次將3中的字符串拼接到4后面,當字符串字節數超出限制時,將最后一次沒有超出限制可以拼接的字符串的個數N記錄下來(之所以記錄個數而不是字節數,是為了截取,不可能把一個漢字分開,例如例子中一個漢字為3個字節,最大限制20個字節,就只能顯示6個漢字,扔下2個字節就剩下吧,故截取的時候不能按字節截取,按字符個數截取)。此拼接只為暫時計算在字節數限制內總共可以容納的字符串個數,
對應代碼中if語句
4、將下圖3部分中超出限制的字符串刪除,對應代碼中s.delete操作
四、原理圖解及效果圖
其中、1為字符串改變前光標前的部分"你好",2為光標后的部分"美女",4為改變前的字符串"你好美女",3為要插入的字符串"我們都是",5為插入后沒有處理的字符串"你好我們都是美女","我們都是"字符串將要插入在"你好"和"美女"之間,6/7/8/9分別對應"我"、"們"、"都"、"是" 這四個字,在拼接判斷還可以插入多少字符串時用到
原理圖解:
效果圖:
效果圖:搜狗輸入法、魅族手機
log圖示:
五、代碼實現
MainActivity.java
代碼及添加的log只考慮插入字符的情況(包括復制黏貼),不考慮刪除情況,因為刪除不會有字符數限制的問題,沒有實際意義(不考慮刪除字符同時降低了難度)。
1 package com.example.textwatcher; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.text.Editable; 6 import android.text.TextWatcher; 7 import android.util.Log; 8 import android.widget.EditText; 9 import android.widget.TextView; 10 import android.widget.Toast; 11 12 public class MainActivity extends Activity implements TextWatcher{ 13 14 private static final boolean Debug = true; 15 private static final String TAG = MainActivity.class.getName(); 16 private static final int MAX_LENGTH = 20;//最大字節數,不用區分是中文還是英文,字節數一定,可以動態插入字符個數(如:還可插入1個漢字,若是英文則還可以插入3個英文字母<手機顯示一個漢字要3個字節,一個字母1個字節>),一般此值為255, 2^8 -1 17 private EditText edittext; 18 private TextView textview; 19 private String temp = null;//臨時變量 20 private String name = null;//改變后的字符串 21 private String frontString = null;//frontString是字符串 1 22 private String middleString = null;//字符串3 23 private String behindString = null;//字符串2 24 private int editStart = 0; 25 private int editEnd = 0; 26 private int startPos = 0;//光標的位置,即1/2之間的位置 27 private int endPos = 0;//改變文字后光標的位置 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 edittext = (EditText) findViewById(R.id.et); 33 textview = (TextView) findViewById(R.id.tv); 34 edittext.addTextChangedListener(this); 35 } 36 37 @Override 38 public void beforeTextChanged(CharSequence s, int start, int count, 39 int after) { 40 // TODO Auto-generated method stub 41 startPos = start;//在字符串改變前保存光標的位置,即1、2之間的位置 42 temp = s.toString();//保留字符串改變前的值 1+2、即4 43 frontString = s.subSequence(0, startPos).toString();//frontString是字符串 1 44 if(Debug){ 45 Log.i(TAG, "beforeTextChanged temp="+temp); 46 Log.i(TAG, "beforeTextChanged temp.getBytes().length="+temp.getBytes().length); 47 Log.i(TAG, "frontString="+frontString); 48 } 49 50 } 51 52 @Override 53 public void onTextChanged(CharSequence s, int start, int before, int count) { 54 // TODO Auto-generated method stub 55 56 } 57 58 @Override 59 public void afterTextChanged(Editable s) { 60 // TODO Auto-generated method stub 61 //此count和 beforeTextChanged方法中的count沒有任何聯系 62 int count = 0;//定義為局部變量,防止上次的值對其有影響(小編剛開始定義為了全局變量,結果引發的錯誤排查了很久) 63 name = s.toString().trim();//主要是為了獲取name的字節長度,為了調試用 、即5 64 editEnd = edittext.getSelectionEnd(); 65 behindString = name.substring(editEnd, name.length());//behindString是字符串2 66 middleString = name.substring(startPos, editEnd);//middleString是字符串3 67 StringBuilder sb = new StringBuilder(temp);//根據改變前的字符串構鍵sb 68 if(name.getBytes().length > MAX_LENGTH && middleString != null && middleString != ""){ 69 if(Debug){ 70 Log.i(TAG, "*************afterTextChanged 插入后字符串字節長度超出限制 開始處理**************"); 71 Log.i(TAG, "afterTextChanged 插入后的完整字符串字節長度 name.getBytes().length="+name.getBytes().length); 72 Log.i(TAG, "afterTextChanged 插入前的完整字符串 temp="+temp); 73 Log.i(TAG, "afterTextChanged 插入時,光標前的字符串 frontString="+frontString); 74 Log.i(TAG, "afterTextChanged 需要插入的字符串 middleString="+middleString); 75 Log.i(TAG, "afterTextChanged 插入時,光標后的字符串 behindString="+behindString); 76 Log.i(TAG, "afterTextChanged 需要插入的字符串字符個數 middleString.length()="+middleString.length()); 77 Log.i(TAG, "afterTextChanged 需要插入的字符串字節數 middleString.getBytes().length="+middleString.getBytes().length); 78 } 79 for(int i = 0;i < middleString.length();i++){ 80 if(Debug){ 81 Log.i(TAG, "需要拼接的第"+i+"個字符 middleString.subSequence(i, i+1)="+middleString.subSequence(i, i+1)); 82 Log.i(TAG, "需要拼接的第"+i+"個字符字節長度 middleString.subSequence(i, i+1).toString().getBytes().length="+middleString.subSequence(i, i+1).toString().getBytes().length); 83 } 84 //依次將3中的字符串6、7、8、9拼接上去直到拼接后的字符串字節數大於最大值為止,將最多可以拼接的數賦值給count 85 //例如本例子中,小編在自己手機上打印到的每個漢字占3個字節(不是應該2個字節嗎,很納悶),"你好美女"共12字節,所以還可以拼接2個漢字 86 //即一共可以顯示6個字,18個字節。最大是20個字節,如果是7個字的話,21個字節,超出范圍了。故此時count為2 87 //在給count賦值時,一定要把count寫在if語句里面,剛開始小編把count的賦值寫在了for循環里面,if語句的下面了,最后發現當if語句不成立時 88 //count也有值,浪費了好久才找出問題 89 if(sb.append(middleString.subSequence(i, i+1)).toString().getBytes().length < MAX_LENGTH){//后面有改動,需要將 "<" 修改為 "<=",如果此處需要使用"<"號,請將68行處的">"改為">=" 90 count = i + 1; 91 if(Debug){ 92 Log.i(TAG, "i="+i); 93 Log.i(TAG, "拼接第"+i+"個字符后的字符串 sb.toString()="+sb.toString()); 94 Log.i(TAG, "拼接第"+i+"個字符后的字符串字節數 sb.toString().getBytes().length="+sb.toString().getBytes().length); 95 Log.i(TAG, "需要拼接的字符串 middleString="+middleString+" 已經拼接:"+count+"個 字符"+middleString.subSequence(0, i+1)); 96 } 97 }else{ 98 if(Debug){ 99 Log.d(TAG, "原字符串:"+temp); 100 Log.d(TAG, "原字符串字節數:"+temp.getBytes().length); 101 Log.d(TAG, "需要插入的字符串:"+middleString); 102 Log.d(TAG, "middleString:"+middleString+",一共可以拼接 "+count+" 個字符:"+ middleString.subSequence(0, count)+" 。可以插入的字符字節數:"+middleString.subSequence(0, count).toString().getBytes().length); 103 } 104 break;//如果if語句不成立,則直接跳出for循環 105 } 106 } 107 temp = new StringBuilder(frontString).append(middleString.substring(0, count)).append(behindString).toString(); 108 ////delete掉3中字符串不可以拼接的部分 109 //此時s字符串為 "你好我們都是美女" 110 //startPos + count 是1+3中可以拼接的字符串,即從"們"和"都"之間的位置開始,去掉"都是"這兩個字符串 111 //startPos值為2,count值為2,startPos + middleString.length()即為"是"后面的位置,刪除"都是" 112 if(Debug){ 113 Log.i(TAG, "before s.delete temp="+temp); 114 Log.i(TAG, "要刪除的字符:"+s.subSequence(startPos + count, startPos + middleString.length()).toString()); 115 } 116 Toast.makeText(this, "已超過最大字節數限制", Toast.LENGTH_SHORT).show(); 117 s.delete(startPos + count, startPos + middleString.length()); 118 if(Debug){ 119 Log.i(TAG, "afters s.delete temp="+temp); 120 Log.i(TAG, "before setText###########"); 121 } 122 //當執行s.delete時,字符串發生變變,會再次執行beforeTextChanged、afterTextChanged這些方法,只有執行完afterTextChanged方法后 123 //才會執行s.delete后面的方法,即textview.setText("1 ...); 124 //再一次執行afterTextChanged方法時,由於字符串長度已經沒有超長,故會執行else中的方法,然后打印11 name= ,打印完后 125 //會繼續執行s.delete下面的after setText###########,,然后再次打印11 name= 126 //textview.setText("1、內容:"+temp+" 字符數:"+temp.length()+" 字節數:"+temp.getBytes().length); 127 textview.setText(s.toString().trim()); 128 if(Debug){ 129 Log.i(TAG, "after setText###########"); 130 } 131 132 }else{ 133 textview.setText("2、內容:"+name+" 字符數:"+name.length()+" 字節數:"+name.getBytes().length); 134 } 135 if(Debug){ 136 Log.i(TAG, "11 name="+name); 137 } 138 } 139 }
activity_main.xml文件,只有兩個空間,一個textview用來顯示當前edittext中的內容(處理后,未超出限制的內容),editext用來輸入字符,漢字,英文都可以
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context="${relativePackage}.${activityClass}" > 6 7 <TextView 8 android:id="@+id/tv" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:text="@string/hello_world" /> 12 <EditText 13 android:id="@+id/et" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:layout_below="@id/tv"/> 17 </RelativeLayout>
六、代碼bug修復:
bug描述:
你好我們美女,中間插入"1234我來了",只能插入1,單獨插入英文字母時時可以再插入2個英文字母
原因分析:
輸入的字節數最大為20個字節,應該包括20字節。
代碼修改:
MainActivity.java代碼第89行出應為
if(sb.append(middleString.subSequence(i, i+1)).toString().getBytes().length <= MAX_LENGTH)
整理后的代碼,去掉了log(建議編寫代碼時一定要加log、加注釋)
1 package com.example.textwatcher; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.text.Editable; 6 import android.text.TextWatcher; 7 import android.util.Log; 8 import android.widget.EditText; 9 import android.widget.TextView; 10 import android.widget.Toast; 11 12 public class MainActivity extends Activity implements TextWatcher{ 13 14 private static final int MAX_LENGTH = 20;//最大字節數,不用區分是中文還是英文,字節數一定,可以動態插入字符個數(如:還可插入1個漢字,若是英文則還可以插入3個英文字母<手機顯示一個漢字要3個字節,一個字母1個字節>),一般此值為255, 2^8 -1 15 private EditText edittext; 16 private TextView textview; 17 private String temp = null;//臨時變量 18 private String name = null;//改變后的字符串 19 private String frontString = null;//frontString是字符串 1 20 private String middleString = null;//字符串3 21 private String behindString = null;//字符串2 22 private int editStart = 0; 23 private int editEnd = 0; 24 private int startPos = 0;//光標的位置,即1/2之間的位置 25 private int endPos = 0;//改變文字后光標的位置 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 super.onCreate(savedInstanceState); 29 setContentView(R.layout.activity_main); 30 edittext = (EditText) findViewById(R.id.et); 31 textview = (TextView) findViewById(R.id.tv); 32 edittext.addTextChangedListener(this); 33 } 34 35 @Override 36 public void beforeTextChanged(CharSequence s, int start, int count, 37 int after) { 38 // TODO Auto-generated method stub 39 startPos = start;//在字符串改變前保存光標的位置,即1、2之間的位置 40 temp = s.toString();//保留字符串改變前的值 1+2、即4 41 frontString = s.subSequence(0, startPos).toString();//frontString是字符串 1 42 } 43 44 @Override 45 public void onTextChanged(CharSequence s, int start, int before, int count) { 46 // TODO Auto-generated method stub 47 48 } 49 50 @Override 51 public void afterTextChanged(Editable s) { 52 // TODO Auto-generated method stub 53 //此count和 beforeTextChanged方法中的count沒有任何聯系 54 int count = 0;//定義為局部變量,防止上次的值對其有影響(小編剛開始定義為了全局變量,結果引發的錯誤排查了很久) 55 name = s.toString().trim();//主要是為了獲取name的字節長度,為了調試用 、即5 56 editEnd = edittext.getSelectionEnd(); 57 behindString = name.substring(editEnd, name.length());//behindString是字符串2 58 middleString = name.substring(startPos, editEnd);//middleString是字符串3 59 StringBuilder sb = new StringBuilder(temp);//根據改變前的字符串構鍵sb 60 if(name.getBytes().length > MAX_LENGTH && middleString != null && middleString != ""){ 61 for(int i = 0;i < middleString.length();i++){ 62 if(sb.append(middleString.subSequence(i, i+1)).toString().getBytes().length <= MAX_LENGTH){ 63 count = i + 1; 64 }else{ 65 break;//如果if語句不成立,則直接跳出for循環 66 } 67 } 68 temp = new StringBuilder(frontString).append(middleString.substring(0, count)).append(behindString).toString(); 69 Toast.makeText(this, "已超過最大字節數限制", Toast.LENGTH_SHORT).show(); 70 s.delete(startPos + count, startPos + middleString.length()); 71 textview.setText(s.toString().trim()); 72 }else{ 73 textview.setText("2、內容:"+name+" 字符數:"+name.length()+" 字節數:"+name.getBytes().length); 74 }75 } 76 }
七、有任何疑問請留言,歡迎探討與指正 ,QQ群238696947
轉載請注明出處:http://www.cnblogs.com/wangqx/p/6096272.html