Android|Android 邮箱自动补全-MultiAutoCompleteTextView实现

因为项目需要,要写一个邮箱自动补全的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等才会匹配
注:邮箱格式为123456@XX.com,整体内容肯定不行。
【Android|Android 邮箱自动补全-MultiAutoCompleteTextView实现】下面我们又看到一个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
Android|Android 邮箱自动补全-MultiAutoCompleteTextView实现
文章图片
这里写图片描述
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
例:
  1. 输入:monoqn → 不suggest
  2. 输入:monoqn@ → 不suggest
  3. 输入:monoqn@i → monoqn@i.softbank.jp/monoqn@icloud.com
  4. 输入:monoqn@ic → monoqn@icloud.com
下面先验证规则:
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

    推荐阅读