AutoCompleteTextView适配器,“隐藏”的适配器?

5
我有3个AutoCompleteTextViews,我想在它们上面注册2个String[]适配器。目前,我正在这样做:
atw_from.setAdapter(new ArrayAdapter(ctx, android.r.layout.simple_dropdown_item_1line, stages_adapter));

假设我的用户想要输入“Középmező”,他开始输入“Közé”,然后会被提供选择Középmező,到这里还很简单。但是如果用户太懒以至于不想打重音符号(很多人都是这样的),那么他只会输入Kozepmezo,然后他就不会得到任何建议,因为在我的String[]中没有Kozepmezo。我想要的是,如果他输入“Koze”,他应该被提供Középmező,这样即使他不使用重音符号,他也总是可以获得实际带有重音符号的单词。
目前,我有一个相当愚蠢的解决方案,我有一个大小为原始[]两倍的String[],前一半包含带重音符号的单词,后一半包含去除重音符号的版本。所以现在,如果他输入Közé,他将被提供Középmező,如果他输入Koze,他将被提供Kozepmezo。它有效是因为服务器可以处理这两个版本,但它看起来很愚蠢,我想解决它。
据我所知,我应该制作一个完全自定义的适配器。这是最好的方法吗?或者SDK中是否包含任何解决方案?如果我应该制作自定义适配器,有人可以指点我正确的方向吗?:)
编辑:添加了我的答案,应该适用于每个人,感谢其他答案,它引导我走向了正确的方向!
3个回答

1

我会选择自定义适配器,其中您可以提供自己的过滤函数来匹配带重音和去重音符号的标记。

一个实现这一点的示例可以在这里找到。基本上,您需要在performFiltering中实现实际的过滤 - 我假设您已经有了一种方法来去除查询中的重音符号,因为您当前正在使用去重音版本填充String[]。您将想要将带重音符号和不带重音符号的查询与数组中的条目进行比较(您将希望使用带重音符号和不带重音符号)。最终,您应该至少有以下四个测试:

accented(query) -> accented(entry)
accented(query) -> deaccented(entry)
deaccented(query) -> accented(entry)
deaccented(query) -> deaccented(entry)

通过即时去重音,您仅需要提供带有重音的单词 String[],而过滤逻辑(在您的适配器中)将负责匹配(去除)重音后的单词。

编辑:如下是我正在进行的项目中的示例实现,正如所讨论的那样。

一些指针:

  1. CustomArrayAdapter 主要是一个简化常见任务的包装类;例如与行包装器/视图持有者的交互。基本上,它只需要一个构造函数和实现 updateRow(这显然将从超类的 getView 方法调用)。
  2. CustomRowWrapper 应该非常直观。
  3. ArrayUtilArrayUtil.FilterFuction 负责实际的过滤。简单地说,这些作为代替构建所有符合某些条件的项的新列表的 for 循环的功能。

public class CARMedicationSuggestionAdapter extends CustomArrayAdapter<CARMedicationInfo, RowWrapper> {

    private List<CARMedicationInfo> mMedications;
    private Filter mFilter;

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * constructor
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    public CARMedicationSuggestionAdapter(Context context, List<CARMedicationInfo> objects) {
        super(RowWrapper.class, context, R.layout.medication_suggestion_item_layout, objects);
        // keep copy of all items for lookups 
        mMedications = new ArrayList<CARMedicationInfo>(objects);
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * update row
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    @Override protected void updateRow(RowWrapper wrapper, CARMedicationInfo item) {
        wrapper.getNameTextView().setText(item.toString());
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * get filter
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    @Override public Filter getFilter() {
        // return if already created
        if (mFilter != null) return mFilter;
        mFilter = new Filter() {
            @Override protected void publishResults(CharSequence constraint, FilterResults results) {
                @SuppressWarnings("unchecked") List<CARMedicationInfo> filtered = (List<CARMedicationInfo>) results.values;
                if (results == null || results.count == 0) return;
                // clear out current suggestions and add all new ones
                clear(); 
                addAll(filtered);
            }

            @Override protected FilterResults performFiltering(final CharSequence constraint) {
                // return empty results for 'null' constraint
                if (constraint == null) return new FilterResults();
                // get all medications that contain the constraint in drug name, trade name or whose string representation start with the constraint
                List<CARMedicationInfo> suggestions = ArrayUtil.filter(mMedications, new ArrayUtil.FilterFunction<CARMedicationInfo>() {
                    @Override public boolean filter(CARMedicationInfo item) {
                        String query = constraint.toString().toLowerCase().trim();
                        return  item.mMedicationDrugName.toLowerCase().contains(query) || 
                                item.mMedicationTradeName.toLowerCase().contains(query) ||
                                item.toString().toLowerCase().startsWith(query); 
                    }
                });
                // set results and size
                FilterResults filterResults = new FilterResults();
                filterResults.values = suggestions;
                filterResults.count = suggestions.size();
                return filterResults;
            }
        };
        return mFilter;
    }

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * row wrapper
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

    static class RowWrapper extends CustomRowWrapper {

        private ImageView mIconImageView;
        private TextView mNameTextView;

        public RowWrapper(View row) {
            super(row);
        }

        public ImageView getIconImageView() {
            if (mIconImageView == null) mIconImageView = (ImageView) mRow.findViewById(R.id.icon_imageview);
            return mIconImageView;
        }

        public TextView getNameTextView() {
            if (mNameTextView == null) mNameTextView = (TextView) mRow.findViewById(R.id.name_textview);
            return mNameTextView;
        }

    }

}

这个听起来确实很不错,我今晚会在我的开发者笔记本上试一下,谢谢!如果它有效,我当然会接受它作为答案 :) - hundeva
我之前已经向你授予了赏金,并且它将保持不变,但我将在一分钟内发布一个单独的答案。 - hundeva

1
好的,经过很多麻烦的折腾,这是我最终做的事情。这绝对不是一个好的实践方法,我可能做错了,但至少现在它完美地工作了。
只需复制BaseAdapter的源代码,然后复制ArrayAdapter的源代码。现在看看私有内部类ArrayFilter,特别是performFiltering方法。修改(而不是覆盖!)它,尽可能多地修改,在我的情况下,我添加了很多.replace("x","y")的东西,用于去重音部分。
无论我尝试什么其他方法,都会产生不可预测的强制关闭(很多,而且完全随机),或者我根本无法做到,因为太多的方法/变量是私有的,而不是受保护的。我必须说,谷歌应该重新审视这些代码...
注意:你不需要复制粘贴BaseAdapter的代码,因为它是一个公共抽象类,但是嘿,这不是很多代码,这样一切都清晰可见。
干杯

很遗憾听到你之前没有能够通过我的解决方案解决问题。如果你(仍然)有兴趣,我可以发布我几周前编写的解决方案?我们使用内部库来简化常见任务,但过滤部分应该相对不变。 - MH.
我一直对更好的解决方案感兴趣! :) 如果您不介意,我想看一下。无论如何,您的答案指引了我解决问题的方向,所以非常感谢您。此外,我正在考虑完全重写这个ArrayAdapter的某些部分,因为通常情况下它可能是稳定的,但对于我的情况来说,它不需要一些额外的数组,从而降低过滤所需的内存/ CPU时间。 - hundeva
好的,我在之前的回答中添加了一些我目前在一个项目中使用的代码。 对你来说最有趣的部分应该是重写的getFilter方法。希望这对你有意义。 :) - MH.
不错 :) 我会在我开发电脑上检查是否可以在我的情况下实现。 - hundeva

0

很抱歉,这不是我正在寻找的:/ 无论如何,感谢您的回答。 - hundeva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接