Android字数限制的EditText实现方案研究(经典在回贴。)

来源:互联网 发布:js session的用法 编辑:程序博客网 时间:2024/05/17 10:05

在应用开发中,有时需要实现有字数限制的EditText,首先来分析下市面上存在的类似实现方案吧,好有个感性的认识。


【方案一:腾讯微博】

每个中文字符算一个字数,每两个英文字符算一个字数,当用户输入内容时,实时显示剩余的字数,当超出字数限制时,剩余字数显示为负数,但此时用户仍然可以继续在EditText中输入内容,直到用户点击菜单中的“发送”按钮时,才会弹出对话框或者Toast显示用户输入的字数超标,如下图所示:

 

这个方案实现起来很简单,只需要给EditText设置TextWatcher监听器,然后判断输入的是中文字符还是英文字符,实时更新剩余输入字数显示即可,不需要限制EditText的输入。


【方案二:百度旅游】

中英文字符都算一个字数,当用户输入内容时,实时显示剩余的字数,当超出字数限制时,剩余字数显示为0,不会出现负数的情况,这时EditText再也不接收用户输入的任何内容了。

这个方案由于中英文都占一个字数,因此可以直接给EditText设置InputFilter.LengthFilter,这时LengthFilter会自动帮EditText限制用户输入的内容;再给EditText设置TextWatcher监听器,就可以实时更新剩余字数了。


本文综合上面两个方案,给出【方案三】,每个中文字符算一个字数,每两个英文字符算一个字数,当用户输入内容时,实时显示剩余的字数,当超出字数限制时,剩余字数显示为0,不会出现负数的情况,这时EditText再也不接收用户输入的任何内容了。

方案三可用于app需要集成第三方sns分享功能,且必须自己实现分享界面的情况。由于中英文所占的字数不一样,就不能使用LengthFilter来限制用户再EditText中输入内容(因为在用户完成内容输入之前,是不知道要给lengthFilter设置的最大值的)。因此只能在TextWatcher中做些手脚了。方案三界面如下:
 
整个功能的核心实现都在EditText的TextWatcher监听器里面的afterTextChanged回调函数中。代码如下所示:
  1. package com.hust.demo;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.text.Editable;  
  6. import android.text.TextWatcher;  
  7. import android.widget.EditText;  
  8. import android.widget.TextView;  
  9.   
  10. public class MainActivity extends Activity {  
  11.   
  12.     private EditText mEditText = null;  
  13.     private TextView mTextView = null;  
  14.   
  15.     private static final int MAX_COUNT = 140;  
  16.   
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.main);  
  21.         mEditText = (EditText) findViewById(R.id.content);  
  22.         mEditText.addTextChangedListener(mTextWatcher);  
  23.         mEditText.setSelection(mEditText.length()); // 将光标移动最后一个字符后面  
  24.           
  25.         mTextView = (TextView) findViewById(R.id.count);  
  26.         setLeftCount();  
  27.     }  
  28.   
  29.     private TextWatcher mTextWatcher = new TextWatcher() {  
  30.   
  31.         private int editStart;  
  32.   
  33.         private int editEnd;  
  34.   
  35.         public void afterTextChanged(Editable s) {  
  36.             editStart = mEditText.getSelectionStart();  
  37.             editEnd = mEditText.getSelectionEnd();  
  38.   
  39.             // 先去掉监听器,否则会出现栈溢出  
  40.             mEditText.removeTextChangedListener(mTextWatcher);  
  41.   
  42.             // 注意这里只能每次都对整个EditText的内容求长度,不能对删除的单个字符求长度  
  43.             // 因为是中英文混合,单个字符而言,calculateLength函数都会返回1  
  44.             while (calculateLength(s.toString()) > MAX_COUNT) { // 当输入字符个数超过限制的大小时,进行截断操作  
  45.                 s.delete(editStart - 1, editEnd);  
  46.                 editStart--;  
  47.                 editEnd--;  
  48.             }  
  49.             // mEditText.setText(s);将这行代码注释掉就不会出现后面所说的输入法在数字界面自动跳转回主界面的问题了,多谢@ainiyidiandian的提醒  
  50.             mEditText.setSelection(editStart);  
  51.   
  52.             // 恢复监听器  
  53.             mEditText.addTextChangedListener(mTextWatcher);  
  54.   
  55.             setLeftCount();  
  56.         }  
  57.   
  58.         public void beforeTextChanged(CharSequence s, int start, int count,  
  59.                 int after) {  
  60.   
  61.         }  
  62.   
  63.         public void onTextChanged(CharSequence s, int start, int before,  
  64.                 int count) {  
  65.   
  66.         }  
  67.   
  68.     };  
  69.   
  70.     /** 
  71.      * 计算分享内容的字数,一个汉字=两个英文字母,一个中文标点=两个英文标点 注意:该函数的不适用于对单个字符进行计算,因为单个字符四舍五入后都是1 
  72.      *  
  73.      * @param c 
  74.      * @return 
  75.      */  
  76.     private long calculateLength(CharSequence c) {  
  77.         double len = 0;  
  78.         for (int i = 0; i < c.length(); i++) {  
  79.             int tmp = (int) c.charAt(i);  
  80.             if (tmp > 0 && tmp < 127) {  
  81.                 len += 0.5;  
  82.             } else {  
  83.                 len++;  
  84.             }  
  85.         }  
  86.         return Math.round(len);  
  87.     }  
  88.   
  89.     /** 
  90.      * 刷新剩余输入字数,最大值新浪微博是140个字,人人网是200个字 
  91.      */  
  92.     private void setLeftCount() {  
  93.         mTextView.setText(String.valueOf((MAX_COUNT - getInputCount())));  
  94.     }  
  95.   
  96.     /** 
  97.      * 获取用户输入的分享内容字数 
  98.      *  
  99.      * @return 
  100.      */  
  101.     private long getInputCount() {  
  102.         return calculateLength(mEditText.getText().toString());  
  103.     }  
  104.   
  105. }  

但是上面代码存在一个bug,给EditText设置TextWatcher之后,由于afterTextChanged的代码实现会导致输入法界面刷新,从而使得每次输入字符,输入法界面都会跳转到他的主界面去,

例如我们当我们要输入数字时,首先要转到数字输入界面,正常情况下可以连续输入多个数字,数字输入完成后,界面仍然维持在数字输入界面,输入数字1前后界面对比图(正常情况):
 

而给EditText设置我们定义的Textwatcher监听器之后,在数字输入界面,每输入一个数字,输入法都会跳回主界面,需要用户再点击才能回到数字输入界面,如下图所示(引入的bug),也是输入数字1前后界面对比图:

 
如果有哪位知道怎么解决的,欢迎在评论中予以指出。

附:上面代码中对Editable作操作前,必须先将自身的监听器去使能,否则会引起EditText自身的死循环,从而导致堆栈溢出,将函数afterTextChanged中去掉监听器和添加监听器两行代码注释掉,再次运行程序,在输入内容超出字数限制时,如下代码将被执行到:
  1. while (calculateLength(s.toString()) > MAX_COUNT) { // 当输入字符个数超过限制的大小时,进行截断操作  
  2.     s.delete(editStart - 1, editEnd);  
  3.     editStart--;  
  4.     editEnd--;  
  5. }  
这时Demo出现Crash,异常信息如下:

 

Demo源码参见:http://download.csdn.net/detail/ace1985/4757626
 
http://blog.csdn.net/asce1885/article/details/8172517







更多1
10
2
主题推荐
android腾讯微博新浪微博输入法人人网
博文推荐
关于Android中利用java反射阻止...
android 实现自定义状态栏通知(S...
Android中关于JNI 的学习(三)...
利用命令行删除Android系统自带应用...
Android控件 之 GridView...
android炫酷动画柱状图表例子
Android控件 之 GridView...
Android 使用ContentPro...
查看评论
12楼 shuangfengzhongfu 刚刚发表 [回复]
这个帖子我复制了,太强大了。特别是回贴.
11楼 hongbing 2013-09-05 16:51发表 [回复]
// mEditText.setText(s);将这行代码注释掉就不会出现后面所说的输入法在数字界面自动跳转回主界面的问题了,多谢@ainiyidiandian的提醒 
mEditText.setSelection(editStart); 

这两句代码 都可以不要,不然会出现不能输入中文的问题,时时的去赋值导致的引发的错误,编辑框没有必要时时的去赋值
Re: fanszheqq 2013-09-23 14:14发表 [回复]
回复hongbingfans:你说得对,非常赞同!
10楼 hongbing 2013-09-05 16:41发表 [回复]
有个很严重的bug 不让输中文了,点击字母弹出中文立即消失,点的字母就到编辑框中去了,楼主可发现!
9楼 u011114478 2013-07-22 09:30发表 [回复]
赞一个
8楼 yyp5257 2013-01-11 16:49发表 [回复]
亲 ,正好遇到堆栈的问题哟 很好很强大
7楼 xianmengyu 2012-12-07 18:07发表 [回复]
很不错,谢谢。我自己写了个有BUG 呵呵
6楼 cq67560868 2012-11-17 21:44发表 [回复]
很好,收益匪浅、、
5楼 darwin9 2012-11-14 22:23发表 [回复]
楼主代码配色真好。能告诉我吗??我想在ECLIPSE里面用这样的颜色。
Re: ACE1985 2012-11-17 12:31发表 [回复]
回复darwin9:这个配色是CSDN自带的,如果你想使用黑色主题的,可以看下这个:http://download.csdn.net/download/liubo060807/1831716
4楼 挤不上公交车的路人甲 2012-11-14 15:32发表 [回复]
楼主的排版不错!
Re: ACE1985 2012-11-17 12:30发表 [回复]
回复gqs519:多谢哈
3楼 Chuekup 2012-11-13 12:05发表 [回复]
怎么感觉你写的这么麻烦?我在项目中直接就下面这样写的:
private void restrictEditText(final EditText editText, final int maxLength, final int hint) {
//editText.setText("");
editText.addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}

@Override
public void afterTextChanged(Editable s) {
Editable edit = editText.getText();
if (edit.length() > maxLength) {
edit.delete(edit.length() - 1, edit.length());
Toast.makeText(app, getString(hint, maxLength), Toast.LENGTH_LONG).show();
}
}

});
}
还有,在afterTextChanged中如果不remove listener怎么会出现栈溢出?你后面不是又add回去了吗? 始终都还是保持的原来的那个listener

最后你说的那个数字键盘问题,我是没遇到过。。。检查下你的xml中是不是加了什么属性
Re: ACE1985 2012-11-17 12:25发表 [回复]
回复Chuekup:必须使用while循环进行删除多余字符,因为用户并不是每次只输入一个字符,例如粘贴,可能一下就增加几百个字符,所以你用if只删掉了一个字符,不行的。还有就是关于栈溢出的,我更新了博客,你看下,也可以自己下载Demo实践一下。
Re: Chuekup 2012-11-21 11:58发表 [回复]
回复ACE1985: 哦。。。感谢提醒,我都快忘了还有有粘贴这个操作了(话说用了几年android手机还从来没用过这个功能...)
不过其实你也不需要使用循环吧,直接把
s.delete(editStart - 1, editEnd) 换成 s.delete(MAX_COUNT, s.length())不就就行了吗?
一次性删除所有超过界线的字符,不用while循环删除,这样也不需要先remove再add listener从而导致栈溢出问题
Re: ACE1985 2012-11-21 13:35发表 [回复]
回复Chuekup:不行额,限制输入的内容是在超过个数后的内容,而不是位于末尾的内容,按你的做法,在超出字数限制后,我将EditText光标移到中间任何一处,做粘贴操作,这时,位于EditText最后面的内容将被刚刚粘贴的内容替换掉。
2楼 zx012345 2012-11-13 09:53发表 [回复]
在onTextChanged中计算字数。
Re: ACE1985 2012-11-17 12:29发表 [回复]
回复zx012345:这个没什么区别啊,而且还多了一步获取Editable对象的操作
1楼 ainiyidiandian 2012-11-12 11:06发表 [回复]
while (calculateLength(s.toString()) > MAX_COUNT) { // 当输入字符个数超过限制的大小时,进行截断操作 
s.delete(editStart - 1, editEnd); 
editStart--; 
editEnd--; 
} 
// mEditText.setText(s); 
mEditText.setSelection(editStart); 

去掉mEditText.setText(s);
Re: xinayida 2013-03-13 13:24发表 [回复]
回复ainiyidiandian:int len = calculateLength(s.toString());
if (len > MAX_COUNT) {
s.delete(MAX_COUNT, len); 
} 
这样不好么?
Re: ACE1985 2012-11-12 13:25发表 [回复]
回复ainiyidiandian:原来如此,不用手动重新设置text啊
发表评论
0 0