如何在EditText中实现带空格的单词自动补全,满足代码编辑器开发需求
MultiAutoCompleteTextView默认使用
CommaTokenizer作为分词器,仅支持以逗号作为单词分隔符,默认逻辑会把空格视为分词边界,因此无法适配带空格的补全项场景,可通过以下方案实现需求:
第一步:自定义
Tokenizer替换默认的CommaTokenizer
实现MultiAutoCompleteTextView.Tokenizer接口,自定义终止符规则,不要把空格纳入终止符范围,让分词逻辑可以识别包含空格的前缀字符串,参考实现代码:public class CodeTokenizer implements MultiAutoCompleteTextView.Tokenizer { // 自定义代码场景的终止触发字符,排除空格 private static final char[] TERMINATORS = {';', '(', ')', '{', '}', '\n', '\t', '=', ','}; @Override public int findTokenStart(CharSequence text, int cursor) { int i = cursor; // 向前查找直到遇到终止符或文本开头,保留中间所有字符(包含空格)作为匹配前缀 while (i > 0) { char c = text.charAt(i - 1); if (isTerminator(c)) break; i--; } // 可按需删除以下行,保留前缀前导空格 while (i < cursor && Character.isWhitespace(text.charAt(i))) i++; return i; } @Override public int findTokenEnd(CharSequence text, int cursor) { int i = cursor; int len = text.length(); while (i < len) { if (isTerminator(text.charAt(i))) return i; i++; } return len; } @Override public CharSequence terminateToken(CharSequence text) { // 补全后追加空格,可按需修改为其他后缀或直接返回原文本 return text + " "; } private boolean isTerminator(char c) { for (char t : TERMINATORS) { if (c == t) return true; } return false; } }第二步:绑定自定义分词器到控件
初始化MultiAutoCompleteTextView时调用setTokenizer方法替换默认分词器:multiAutoCompleteTextView.setTokenizer(new CodeTokenizer());
可同步设置补全触发阈值,比如输入1个字符就触发补全:multiAutoCompleteTextView.setThreshold(1);第三步:重写Adapter的过滤逻辑
默认ArrayAdapter的过滤规则不支持带空格的字符串匹配,需要自定义适配器重写Filter逻辑,匹配时保留空格判定,参考实现代码:public class CodeAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable { private final List<String> allSuggestions; private final List<String> filteredSuggestions = new ArrayList<>(); public CodeAutoCompleteAdapter(Context context, int resource, List<String> suggestions) { super(context, resource); this.allSuggestions = new ArrayList<>(suggestions); } @Override public int getCount() { return filteredSuggestions.size(); } @Override public String getItem(int position) { return filteredSuggestions.get(position); } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); filteredSuggestions.clear(); if (constraint == null || constraint.length() == 0) { filteredSuggestions.addAll(allSuggestions); } else { String pattern = constraint.toString().toLowerCase().trim(); for (String item : allSuggestions) { // 可按需修改为包含匹配、模糊匹配等规则 if (item.toLowerCase().startsWith(pattern)) { filteredSuggestions.add(item); } } } results.values = filteredSuggestions; results.count = filteredSuggestions.size(); return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { notifyDataSetChanged(); } }; } }进阶优化方案
如果需要实现类似ACODE的高度自定义补全效果,可放弃使用MultiAutoCompleteTextView,直接给普通EditText添加TextWatcher监听输入变化,手动计算光标前的匹配前缀,通过自定义PopupWindow展示补全候选项。该方案适配性更强,可自由扩展不同编程语言的补全规则、代码高亮、候选项分类、参数提示等功能。
内容的提问来源于stack exchange,提问作者Pratima Dash

