美文网首页
Android 邮箱自动补全-MultiAutoComplete

Android 邮箱自动补全-MultiAutoComplete

作者: liliLearn | 来源:发表于2017-09-30 11:16 被阅读0次

    因为项目需要,要写一个邮箱自动补全的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<String> adapter = new ArrayAdapter(this,
                    android.R.layout.simple_dropdown_item_1line, COUNTRIES);
            mAutoCompleteTextView.setAdapter(adapter);
        }
    
    }
    

    XML

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="AutoCompleteTextView实现"
            android:textColor="@android:color/black" />
    
        <AutoCompleteTextView
            android:id="@+id/autoCompleteTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:completionThreshold="1"/>
    </LinearLayout>
    
    

    使用方法很简单,获取到组件然后设置一个弹出的Adapter就能完成一个自动提示。

    这里写图片描述

    但是这个组件有几个特性不是很满足我们的需求。
    1.默认是第二个字母开始匹配
    2.整体内容匹配模式 Belgium 我们只能输入B ,Be,Bel等才会匹配
    注:邮箱格式为123456@XX.com,整体内容肯定不行。

    下面我们又看到一个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<String> adapter = new ArrayAdapter(this,
                    android.R.layout.simple_dropdown_item_1line, COUNTRIES);
    
            mAutoCompleteTextView.setAdapter(adapter);
    
            mMultiAutoCompleteTextView.setAdapter(adapter);
            mMultiAutoCompleteTextView.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
        }
    
    }
    

    XML

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="AutoCompleteTextView实现"
            android:textColor="@android:color/black" />
    
        <AutoCompleteTextView
            android:id="@+id/autoCompleteTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:completionThreshold="1" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="MultiAutoCompleteTextView实现"
            android:textColor="@android:color/black" />
    
        <MultiAutoCompleteTextView
            android:id="@+id/multiAutoCompleteTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:completionThreshold="1" />
    </LinearLayout>
    
    
    这里写图片描述
    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<String> 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

    相关文章

      网友评论

          本文标题:Android 邮箱自动补全-MultiAutoComplete

          本文链接:https://www.haomeiwen.com/subject/hkugextx.html