因为项目需要,要写一个邮箱自动补全的EditText,刚开始考虑使用AutoCompleteTextView来实现,但是满足不到需求官方组件太low了。。。
先来介绍下AutoCompleteTextView 的使用:
Activity
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
public class MainActivity extends AppCompatActivity {
private static final String[] COUNTRIES = new String[]{
"Belgium", "France", "Italy", "Germany", "Spain"
};
private AutoCompleteTextView mAutoCompleteTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, COUNTRIES);
mAutoCompleteTextView.setAdapter(adapter);
}
}
XML
使用方法很简单,获取到组件然后设置一个弹出的Adapter就能完成一个自动提示。
但是这个组件有几个特性不是很满足我们的需求。
1.默认是第二个字母开始匹配
2.整体内容匹配模式 Belgium 我们只能输入B ,Be,Bel等才会匹配
注:邮箱格式为[email protected],整体内容肯定不行。
下面我们又看到一个MultiAutoCompleteTextView组件,来看下MultiAutoCompleteTextView能否满足我们的需求
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView;
public class MainActivity extends AppCompatActivity {
private static final String[] COUNTRIES = new String[]{
"Belgium", "France", "Italy", "Germany", "Spain"
};
private AutoCompleteTextView mAutoCompleteTextView;
private MultiAutoCompleteTextView mMultiAutoCompleteTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
mMultiAutoCompleteTextView = (MultiAutoCompleteTextView) findViewById(R.id.multiAutoCompleteTextView);
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, COUNTRIES);
mAutoCompleteTextView.setAdapter(adapter);
mMultiAutoCompleteTextView.setAdapter(adapter);
mMultiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
}
}
XML
Activity
经过查询API 我们知道 MultiAutoCompleteTextView 继承于 AutoCompleteTextView 继承于EditText
API中在MultiAutoCompleteTextView 提供了一个接口MultiAutoCompleteTextView.Tokenizer 可以用来匹配提示的子字符串。好吧,勉强能实现需求。
首先看MultiAutoCompleteTextView源码中有一个对Tokenizer接口的实现!然后换成我们的。
/**
* This simple Tokenizer can be used for lists where the items are
* separated by a comma and one or more spaces.
*/
public static class CommaTokenizer implements Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ',') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ',') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ',') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + ", ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + ", ";
}
}
}
}
根据MultiAutoCompleteTextView源码中的实现,我们知道Tokenizer的实现方式。
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.widget.MultiAutoCompleteTextView;
public class EmailAutoTokenizer implements MultiAutoCompleteTextView.Tokenizer {
@Override
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == '@') {
return i;
} else {
i++;
}
}
return len;
}
@Override
public int findTokenStart(CharSequence text, int cursor) {
int index = text.toString().indexOf("@");
if (index < 0) {
index = text.length();
}
if (index >= findTokenEnd(text, cursor)) {
index = 0;
}
return index;
}
@Override
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == '@') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text);
TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0);
return sp;
} else {
return text;
}
}
}
}
在Activity中把Tokenizer换成我们自己的看下效果 mMultiAutoCompleteTextView.setTokenizer(new EmailAutoTokenizer());
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.MultiAutoCompleteTextView;
public class MainActivity extends AppCompatActivity {
private String[] email_sufixs = new String[]{"@qq.com", "@163.com", "@126.com", "@gmail.com", "@sina.com", "@hotmail.com",
"@yahoo.cn", "@sohu.com", "@foxmail.com", "@139.com", "@yeah.net", "@vip.qq.com", "@vip.sina.com"};
private AutoCompleteTextView mAutoCompleteTextView;
private MultiAutoCompleteTextView mMultiAutoCompleteTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);
mMultiAutoCompleteTextView = (MultiAutoCompleteTextView) findViewById(R.id.multiAutoCompleteTextView);
ArrayAdapter adapter = new ArrayAdapter(this,
android.R.layout.simple_dropdown_item_1line, email_sufixs);
mAutoCompleteTextView.setAdapter(adapter);
mMultiAutoCompleteTextView.setAdapter(adapter);
mMultiAutoCompleteTextView.setTokenizer(new EmailAutoTokenizer());
}
}
看下最后的效果图,当然我们还需要对弹出框进行调整下,自己去查询API吧!提供有方法。
正当我高兴的时候,万恶的产品Dog给我加一个右边删除效果,丢一个验证规则过来!
在@之后输入字母,从输入的字母开始suggest邮箱
点击suggest的邮箱,邮箱被输入到邮箱输入栏里,光标移动到下一text box
例:
- 输入:monoqn → 不suggest
- 输入:monoqn@ → 不suggest
- 输入:monoqn@i → monoqn@i.softbank.jp/[email protected]
- 输入:monoqn@ic → [email protected]
下面先验证规则:
1.在@之后输入字母,从输入的字母开始suggest邮箱。
很简单android:completionThreshold="2" 设置输入几个字符之后显示下拉菜单,默认为2个。不用管就行
2.点击suggest的邮箱,邮箱被输入到邮箱输入栏里,光标移动到下一text box
也简单MultiAutoCompleteTextView中有OnItemClickListener事件,
email.setOnItemClickListener((parent, view1, position, id) -> password.requestFocus());
3.显示规则
看下了满足需求,666666!
最后一个右边加入删除按钮!本宝宝自定义一个就OK。
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.MultiAutoCompleteTextView;
public class CleanableMultiAutoCompleteTextView extends MultiAutoCompleteTextView {
private Drawable mRightDrawable;
public CleanableMultiAutoCompleteTextView(Context context) {
super(context);
init();
}
public CleanableMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CleanableMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
// getCompoundDrawables:
// Returns drawables for the left, top, right, and bottom borders.
Drawable[] drawables = this.getCompoundDrawables();
// get right drawable in layout.xml that is android:drawableRight
mRightDrawable = drawables[2];
setOnFocusChangeListener(new FocusChangeListenerImpl());
addTextChangedListener(new TextWatcherImpl());
setClearDrawableVisible(false);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean isClean = (event.getX() > (getWidth() - getTotalPaddingRight()))
&& (event.getX() < (getWidth() - getPaddingRight()));
if (isClean) {
setText("");
}
break;
}
return super.onTouchEvent(event);
}
private class FocusChangeListenerImpl implements OnFocusChangeListener {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
boolean isVisible = !TextUtils.isEmpty(getText());
setClearDrawableVisible(isVisible);
} else {
setClearDrawableVisible(false);
}
}
}
private class TextWatcherImpl implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
boolean isVisible = !TextUtils.isEmpty(getText());
setClearDrawableVisible(isVisible);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// no-op
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// no-op
}
}
public void setClearDrawableVisible(boolean isVisible) {
Drawable rightDrawable = isVisible ? mRightDrawable : null;
setCompoundDrawables(getCompoundDrawables()[0],
getCompoundDrawables()[1], rightDrawable,
getCompoundDrawables()[3]);
}
}
有小伙伴需求是需要把输入的值放在@前面,感谢这位小伙伴的意见,后续我会把代码更新上来。
下载地址:https://github.com/liliLearn/AutoCompleteEmail-sample