zoukankan      html  css  js  c++  java
  • 自定义的AutoComplTextView

    借鉴:https://github.com/andyxialm/KMPAutoCompleteTextView

    但这个工程有个bug就是输入框删除为空的时候,会保留最后一个字符串的过滤数据集显示,我在下面修复了这个bug

    KMPAutoComplTextView.java
    public class KMPAutoComplTextView extends AppCompatAutoCompleteTextView {
    
        private static final int DEFAULT_HIGHLIGHT       = Color.parseColor("#FF4081");
        private static final int DEFAULT_TEXTCOLOR       = Color.parseColor("#80000000");
        private static final int DEFAULT_TEXT_PIXEL_SIZE = 40;
    
        private float mTextSize;
        private boolean mIsIgnoreCase;
        private KMPAdapter mAdapter;
    
        private ColorStateList mHighLightColor, mTextColor;
        private List<KMPPopupTextBean> mSourceDatas, mTempDatas;
        private OnPopupItemClickListener mListener;
    
        public KMPAutoComplTextView(Context context) {
            this(context, null);
        }
    
        public KMPAutoComplTextView(Context context, AttributeSet attrs) {
            this(context, attrs, android.R.attr.autoCompleteTextViewStyle);
        }
    
        public KMPAutoComplTextView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context, attrs);
        }
    
        private void init(Context context, AttributeSet attrs) {
            if (attrs != null) {
                final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KMPAutoComplTextView);
                mTextColor = a.getColorStateList(R.styleable.KMPAutoComplTextView_completionTextColor);
                mHighLightColor = a.getColorStateList(R.styleable.KMPAutoComplTextView_completionHighlightColor);
                mTextSize = a.getDimensionPixelSize(R.styleable.KMPAutoComplTextView_completionTextSize, DEFAULT_TEXT_PIXEL_SIZE);
                mIsIgnoreCase = a.getBoolean(R.styleable.KMPAutoComplTextView_completionIgnoreCase, false);
                a.recycle();
            }
            initListener();
        }
    
        private void initListener() {
            addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    onInputTextChanged(s.toString());
                }
            });
    
        }
    
        private void onInputTextChanged(String input) {
            matchResult(input);
    
            if (mAdapter.mList.size() == 0) {
                KMPAutoComplTextView.this.dismissDropDown();
                return;
            }
            mAdapter.notifyDataSetChanged();
    
            if (!KMPAutoComplTextView.this.isPopupShowing() || mAdapter.mList.size() > 0) {
                showDropDown();
            }
    
        }
    
        public void show(){
            if (!KMPAutoComplTextView.this.isPopupShowing() || mAdapter.mList.size() > 0) {
                showDropDown();
            }
        }
    
        /**
         * 设置数据集
         *
         * @param strings
         */
        public void setDatas(final List<String> strings) {
            mAdapter = new KMPAdapter(getContext(), getResultDatas(strings));
            setAdapter(mAdapter);
        }
    
        public void setOnPopupItemClickListener(OnPopupItemClickListener listener) {
            mListener = listener;
            this.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    if (mListener == null) {
                        return;
                    }
                    mListener.onPopupItemClick(KMPAutoComplTextView.this.getText().toString());
                }
            });
    
        }
    
        private void matchResult(String input) {
            List<KMPPopupTextBean> datas = mSourceDatas;
    //        if (TextUtils.isEmpty(input) || datas == null || datas.size() == 0) {
    //            return;
    //        }
            if (datas == null || datas.size() == 0) {
                return;
            }
            List<KMPPopupTextBean> newDatas = new ArrayList<KMPPopupTextBean>();
            List<String> newDataStrings = new ArrayList<String>();
            if(TextUtils.isEmpty(input)){
                for (KMPPopupTextBean resultBean : datas) {
                    KMPPopupTextBean bean = new KMPPopupTextBean(resultBean.mTarget);
                    newDatas.add(bean);
                    newDataStrings.add(resultBean.mTarget);
                }
            }else {
                for (KMPPopupTextBean resultBean : datas) {
                    int matchIndex = matchString(resultBean.mTarget, input, mIsIgnoreCase);
                    if (-1 != matchIndex) {
                        KMPPopupTextBean bean = new KMPPopupTextBean(resultBean.mTarget, matchIndex, matchIndex + input.length());
                        newDatas.add(bean);
                        newDataStrings.add(resultBean.mTarget);
                    }
                }
            }
    
            mTempDatas = new ArrayList<KMPPopupTextBean>();
            mTempDatas.clear();
            mTempDatas.addAll(newDatas);
    
            mAdapter.mList.clear();
            mAdapter.mList.addAll(newDataStrings);
        }
    
    
        private List<String> getResultDatas(List<String> strings) {
            if (strings == null || strings.size() == 0) {
                return null;
            }
    
            List<KMPPopupTextBean> list = new ArrayList<KMPPopupTextBean>();
            for (String target : strings) {
                list.add(new KMPPopupTextBean(target));
            }
    
            mSourceDatas = new ArrayList<KMPPopupTextBean>();
            mSourceDatas.addAll(list);
    
    //        mTempDatas = new ArrayList<KMPPopupTextBean>();
    //        mTempDatas.clear();
    //        mTempDatas.addAll(list);
            return strings;
        }
    
        public void setMatchIgnoreCase(boolean ignoreCase) {
            mIsIgnoreCase = ignoreCase;
        }
    
        public boolean getMatchIgnoreCase() {
            return mIsIgnoreCase;
        }
    
        class KMPAdapter extends BaseAdapter implements Filterable {
            private List<String> mList;
            private Context mContext;
            private MyFilter mFilter;
    
            public KMPAdapter(Context context, List<String> list) {
                mContext = context;
                mList = new ArrayList<String>();
                mList.addAll(list);
            }
    
            @Override
            public int getCount() {
                return mList == null ? 0 : mList.size();
            }
    
            @Override
            public Object getItem(int position) {
                return mList == null ? null : mList.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                ViewHolder holder = null;
                if (convertView == null) {
                    holder = new ViewHolder();
                    TextView tv = new TextView(mContext);
                    int paddingX = DisplayUtil.dip2px(getContext(), 10.0f);
                    int paddingY = DisplayUtil.dip2px(getContext(), 5.0f);
                    tv.setPadding(paddingX, paddingY, paddingX, paddingY);
    
                    holder.tv = tv;
                    convertView = tv;
                    convertView.setTag(holder);
                } else {
                    holder = (ViewHolder) convertView.getTag();
                }
    
                KMPPopupTextBean bean = mTempDatas == null?mSourceDatas.get(position):mTempDatas.get(position);
                SpannableString ss = new SpannableString(bean.mTarget);
                holder.tv.setTextColor(mTextColor == null ? DEFAULT_TEXTCOLOR : mTextColor.getDefaultColor());
                holder.tv.setTextSize(mTextSize == 0 ? DEFAULT_TEXT_PIXEL_SIZE : DisplayUtil.px2sp(getContext(), mTextSize));
    
                // Change Highlight Color
                if (-1 != bean.mStartIndex) {
                    ss.setSpan(new ForegroundColorSpan(mHighLightColor == null ? DEFAULT_HIGHLIGHT : mHighLightColor.getDefaultColor()),
                            bean.mStartIndex, bean.mEndIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                    holder.tv.setText(ss);
                } else {
                    holder.tv.setText(bean.mTarget);
                }
    
                return convertView;
            }
    
            @Override
            public Filter getFilter() {
                if (mFilter == null) {
                    mFilter = new MyFilter();
                }
                return mFilter;
            }
    
            private class ViewHolder {
                TextView tv;
            }
    
            private class MyFilter extends Filter {
    
                @Override
                protected FilterResults performFiltering(CharSequence constraint) {
                    FilterResults results = new FilterResults();
                    if (mList == null) {
                        mList = new ArrayList<String>();
                    }
                    results.values = mList;
                    results.count = mList.size();
                    return results;
                }
    
                @Override
                protected void publishResults(CharSequence constraint, FilterResults results) {
                    if (results.count > 0) {
                        notifyDataSetChanged();
                    } else {
                        notifyDataSetInvalidated();
                    }
                }
            }
        }
    
        public interface OnPopupItemClickListener {
            void onPopupItemClick(CharSequence charSequence);
        }
    
        /**
         * 获得字符串的next函数值
         *
         * @param mode 字符串
         * @return next函数值
         */
        private static int[] next(char[] mode) {
            int[] next = new int[mode.length];
            next[0] = -1;
            int i = 0;
            int j = -1;
            while (i < mode.length - 1) {
                if (j == -1 || mode[i] == mode[j]) {
                    i ++;
                    j ++;
                    if (mode[i] != mode[j]) {
                        next[i] = j;
                    } else {
                        next[i] = next[j];
                    }
                } else {
                    j = next[j];
                }
            }
            return next;
        }
    
        /**
         * KMP匹配字符串
         *
         * @param source       主串
         * @param modeStr      模式串
         * @param isIgnoreCase 是否忽略大小写
         * @return 若匹配成功,返回下标,否则返回-1
         */
        public int matchString(CharSequence source, CharSequence modeStr, boolean isIgnoreCase) {
            char[] modeArr = modeStr.toString().toCharArray();
            char[] sourceArr = source.toString().toCharArray();
            int[] next = next(modeArr);
            int i = 0;
            int j = 0;
            while (i <= sourceArr.length - 1 && j <= modeArr.length - 1) {
                if (isIgnoreCase) {
                    if (j == -1 || sourceArr[i] == modeArr[j] || String.valueOf(sourceArr[i]).equalsIgnoreCase(String.valueOf(modeArr[j]))) {
                        i ++;
                        j ++;
                    } else {
                        j = next[j];
                    }
                } else {
                    if (j == -1 || sourceArr[i] == modeArr[j]) {
                        i ++;
                        j ++;
                    } else {
                        j = next[j];
                    }
                }
            }
            if (j < modeArr.length) {
                return -1;
            } else
                return i - modeArr.length; // 返回模式串在主串中的头下标
        }
    
    }
    KMPPopupTextBean.java
    public class KMPPopupTextBean implements Serializable {
        public String mTarget;
        public int mStartIndex = -1;
        public int mEndIndex = -1;
    
        public KMPPopupTextBean(String target) {
            this.mTarget = target;
        }
    
        public KMPPopupTextBean(String target, int startIndex) {
            this.mTarget = target;
            this.mStartIndex = startIndex;
            if (-1 != startIndex) {
                this.mEndIndex = startIndex + target.length();
            }
        }
    
        public KMPPopupTextBean(String target, int startIndex, int endIndex) {
            this.mTarget = target;
            this.mStartIndex = startIndex;
            this.mEndIndex = endIndex;
        }
    }

    attrs.xml

    <declare-styleable name="KMPAutoComplTextView">
            <attr name="completionHighlightColor" format="reference|color" />
            <attr name="completionTextColor" format="reference|color" />
            <attr name="completionTextSize" format="dimension" />
            <attr name="completionIgnoreCase" format="boolean" />
        </declare-styleable>

    使用方法:

    List<String> data = new ArrayList<String>();
            data.add("Red roses for wedding");
            data.add("Bouquet with red roses");
            data.add("Si4ngle red rose flower");
            data.add("Bouq5uet with red roses");
            data.add("Sin4gle red rose flower");
            data.add("Bou1quet with red roses");
            data.add("Sing0le red rose flower");
            data.add("Bouq7uet with red roses");
            data.add("Sing9le red rose flower");
            data.add("Bou68quet with red roses");
            data.add("S7ingle red rose flower");
            data.add("Bo0uquet with red roses");
            data.add("Sin11gle red rose flower");
            data.add("Bouq22uet with red roses");
            data.add("Sin33gle red rose flower");
            data.add("Bouquet with red roses");
            data.add("Si77ngle red rose flower");
            data.add("Bouq88uet with red roses");
            data.add("Si89ngle red rose flower");
    
            KMPAutoComplTextView complTextView = (KMPAutoComplTextView) findViewById(R.id.tvAutoCompl);
            //设置输入一个字就自动提示,默认是两个
            complTextView.setThreshold(1);
            complTextView.setDatas(data);
            complTextView.setOnPopupItemClickListener(new KMPAutoComplTextView.OnPopupItemClickListener() {
                @Override
                public void onPopupItemClick(CharSequence charSequence) {
                    Toast.makeText(HomeActivity.this, charSequence.toString(), Toast.LENGTH_SHORT).show();
                }
            });
            complTextView.setOnClickListener(v -> complTextView.show());
            mBinding.btnTest.setOnClickListener(v -> complTextView.show());

     扩展:

    Android控件之带清空按钮(功能)的AutoCompleteTextView自动提示

  • 相关阅读:
    Xcode ARC,非ARC混搭
    Xcode GData库解析XML
    NSThread
    自定义UITableViewCell中的button实现视图切换
    UITableView
    iOS事件响应链
    结构体和NSData相互转换
    UIView的transform属性
    javascript垃圾回收机制
    ios8 滚动事件解放了
  • 原文地址:https://www.cnblogs.com/woaixingxing/p/12660569.html
Copyright © 2011-2022 走看看