当前位置: 代码迷 >> Android >> Android篇幅限制的EditText实现方案研究
  详细解决方案

Android篇幅限制的EditText实现方案研究

热度:24   发布时间:2016-05-01 12:54:01.0
Android字数限制的EditText实现方案研究

在应用开发中,有时需要实现有字数限制的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回调函数中。代码如下所示:
package com.hust.demo;import android.app.Activity;import android.os.Bundle;import android.text.Editable;import android.text.TextWatcher;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends Activity {	private EditText mEditText = null;	private TextView mTextView = null;	private static final int MAX_COUNT = 140;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.main);		mEditText = (EditText) findViewById(R.id.content);		mEditText.addTextChangedListener(mTextWatcher);		mEditText.setSelection(mEditText.length()); // 将光标移动最后一个字符后面				mTextView = (TextView) findViewById(R.id.count);		setLeftCount();	}	private TextWatcher mTextWatcher = new TextWatcher() {		private int editStart;		private int editEnd;		public void afterTextChanged(Editable s) {			editStart = mEditText.getSelectionStart();			editEnd = mEditText.getSelectionEnd();			// 先去掉监听器,否则会出现栈溢出			mEditText.removeTextChangedListener(mTextWatcher);			// 注意这里只能每次都对整个EditText的内容求长度,不能对删除的单个字符求长度			// 因为是中英文混合,单个字符而言,calculateLength函数都会返回1			while (calculateLength(s.toString()) > MAX_COUNT) { // 当输入字符个数超过限制的大小时,进行截断操作				s.delete(editStart - 1, editEnd);				editStart--;				editEnd--;			}			mEditText.setText(s);			mEditText.setSelection(editStart);			// 恢复监听器			mEditText.addTextChangedListener(mTextWatcher);			setLeftCount();		}		public void beforeTextChanged(CharSequence s, int start, int count,				int after) {		}		public void onTextChanged(CharSequence s, int start, int before,				int count) {		}	};	/**	 * 计算分享内容的字数,一个汉字=两个英文字母,一个中文标点=两个英文标点 注意:该函数的不适用于对单个字符进行计算,因为单个字符四舍五入后都是1	 * 	 * @param c	 * @return	 */	private long calculateLength(CharSequence c) {		double len = 0;		for (int i = 0; i < c.length(); i++) {			int tmp = (int) c.charAt(i);			if (tmp > 0 && tmp < 127) {				len += 0.5;			} else {				len++;			}		}		return Math.round(len);	}	/**	 * 刷新剩余输入字数,最大值新浪微博是140个字,人人网是200个字	 */	private void setLeftCount() {		mTextView.setText(String.valueOf((MAX_COUNT - getInputCount())));	}	/**	 * 获取用户输入的分享内容字数	 * 	 * @return	 */	private long getInputCount() {		return calculateLength(mEditText.getText().toString());	}}

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

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

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

 
如果有哪位知道怎么解决的,欢迎在评论中予以指出。
 
Demo源码参见:http://download.csdn.net/detail/ace1985/4757626







3楼Chuekup昨天 12:05
怎么感觉你写的这么麻烦?我在项目中直接就下面这样写的:n private void restrictEditText(final EditText editText, final int maxLength, final int hint) {n //editText.setText("");n editText.addTextChangedListener(new TextWatcher() {nn @Overriden public void onTextChanged(CharSequence s, int start, int before, int count) {n }nn @Overriden public void beforeTextChanged(CharSequence s, int start, int count, int after) {n }nn @Overriden public void afterTextChanged(Editable s) {n Editable edit = editText.getText();n if (edit.length() > maxLength) {n edit.delete(edit.length() - 1, edit.length());n Toast.makeText(app, getString(hint, maxLength), Toast.LENGTH_LONG).show();n }n }nn });n }n还有,在afterTextChanged中如果不remove listener怎么会出现栈溢出?你后面不是又add回去了吗? 始终都还是保持的原来的那个listenernn最后你说的那个数字键盘问题,我是没遇到过。。。检查下你的xml中是不是加了什么属性
2楼zx012345昨天 09:53
在onTextChanged中计算字数。
1楼ainiyidiandian昨天 11:06
while (calculateLength(s.toString()) > MAX_COUNT) { // 当输入字符个数超过限制的大小时,进行截断操作 n s.delete(editStart - 1, editEnd); n editStart--; n editEnd--; n } n// mEditText.setText(s); n mEditText.setSelection(editStart); nn去掉mEditText.setText(s);
Re: ACE1985昨天 13:25
回复ainiyidiandiann原来如此,不用手动重新设置text啊
  相关解决方案