当前位置: 首页 > 知识库问答 >
问题:

我如何允许在Android系统中搜索字符口音?

韦绍晖
2023-03-14

我在我的应用程序中实现了一个搜索机制,这样当我搜索一个名字或电子邮件时,它会显示匹配字符的字符串。然而,我的列表中有一些重音字符串,当我搜索与特定重音相关的常规字符时,比如说,如果我有字符串“angela ”,我搜索“Angela ”,它不会显示该字符串,除非我搜索精确的字符串“Angela”。

我试图让它工作,不管口音与否,比如说,如果我输入“à”,它应该显示包含“à”和“a”的所有字符串,反之亦然。你知道怎么做吗?我在网上查了一堆文章,例如:如何在SQLite查询(Android)中忽略重音”,并尝试了normalizer,但它部分工作,如果我搜索“a”,它也会用普通字母显示重音字母,但如果我用重音字母搜索,它不会显示任何内容。

这是我的过滤器代码:

 @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence charSequence) {
                String charString = charSequence.toString();
                if (charString.isEmpty()) {
                    mSearchGuestListResponseListFiltered = mSearchGuestListResponseList;
                } else {
                    List<RegisterGuestList.Guest> filteredList = new ArrayList<>();
                    for (RegisterGuestList.Guest row : mSearchGuestListResponseList) {

                        // name match condition. this might differ depending on your requirement
                        // here we are looking for name or phone number match
                        String firstName = row.getGuestFirstName().toLowerCase();
                        String lastName = row.getGuestLastName().toLowerCase();
                        String name = firstName + " " +lastName;
                        String email = row.getGuestEmail().toLowerCase();
                        if ( name.trim().contains(charString.toLowerCase().trim()) ||
                                email.trim().contains(charString.toLowerCase().trim())){
                            filteredList.add(row);
                            searchText = charString.toLowerCase();
                        }
                    }

                    mSearchGuestListResponseListFiltered = filteredList;
                }

                FilterResults filterResults = new FilterResults();
                filterResults.values = mSearchGuestListResponseListFiltered;
                return filterResults;
            }

            @Override
            protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
                mSearchGuestListResponseListFiltered = (ArrayList<RegisterGuestList.Guest>) filterResults.values;
                notifyDataSetChanged();
            }
        };
    }

如果有人感兴趣,下面是整个适配器类:https://pastebin.com/VxsWWMiS以下是相应的活动视图:

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                mSearchGuestListAdapter.getFilter().filter(query);

                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                mSearchGuestListAdapter.getFilter().filter(newText);
                mSearchGuestListAdapter.notifyDataSetChanged();
                mSearchGuestListAdapter.setFilter(newText);

                if(mSearchGuestListAdapter.getItemCount() == 0){


                    String sourceString = "No match found for <b>" + newText + "</b> ";
                    mNoMatchTextView.setText(Html.fromHtml(sourceString));
                } else {
                    mEmptyRelativeLayout.setVisibility(View.GONE);
                    mRecyclerView.setVisibility(View.VISIBLE);
                }
                return false;
            }
        });

如有必要,欢迎分享任何细节。此外,我在搜索时随机获得了indexoutofboundexception onBind()方法(使用recyclerview查看列表):

java.lang.IndexOutOfBoundsException: Index: 7, Size: 0
        at java.util.ArrayList.get(ArrayList.java:437)

你知道怎么做吗?

共有1个答案

花永昌
2023-03-14

一般来说,我建议使用强度设置为< code>Collator的< code>Collator。PRIMARY比较包含重音和不同大小写的字符串(例如,< code>N与< code>n以及< code>é与< code>e)。不幸的是,< code>Collator没有< code>contains()函数。

所以我们要自己做。

private static boolean contains(String source, String target) {
    if (target.length() > source.length()) {
        return false;
    }

    Collator collator = Collator.getInstance();
    collator.setStrength(Collator.PRIMARY);

    int end = source.length() - target.length() + 1;

    for (int i = 0; i < end; i++) {
        String sourceSubstring = source.substring(i, i + target.length());

        if (collator.compare(sourceSubstring, target) == 0) {
            return true;
        }
    }

    return false;
}

这将循环访问源字符串,并检查与搜索目标具有相同长度的每个子字符串是否等于搜索目标(就整理器而言)。

例如,假设我们的源字符串是“This is a Tèst”,并且我们正在搜索单词“test”。此方法将循环访问每个四个字母的子字符串:

This
his 
is i
s is
 is 
is a
s a 
 a T
a Tè
 Tès
Tèst

一旦找到匹配项,就会返回 true。由于强度设置为 Collator.PRIMARY,因此排序规则器认为 “Tèst”“test” 相等,因此我们的方法返回 true

很有可能对这种方法进行更多优化,但这应该是一个合理的起点。

编辑:一种可能的优化是利用归类键以及RuleBasedCollatorRuleBasedCollationKey实现的已知细节(假设您的项目中有Google的Guava):

private static boolean containsBytes(String source, String target) {
    Collator collator = Collator.getInstance();
    collator.setStrength(Collator.PRIMARY);

    byte[] sourceBytes = dropLastFour(collator.getCollationKey(source).toByteArray());
    byte[] targetBytes = dropLastFour(collator.getCollationKey(target).toByteArray());

    return Bytes.indexOf(sourceBytes, targetBytes) >= 0;
}

private static byte[] dropLastFour(byte[] in) {
    return Arrays.copyOf(in, in.length - 4);
}

这要脆弱得多(可能不适用于所有语言环境),但在我的测试中,它的速度要快2到10倍。

编辑:要支持突出显示,应将 contains() 转换为 indexOf(),然后使用该信息:

private static int indexOf(String source, String target) {
    if (target.length() > source.length()) {
        return -1;
    }

    Collator collator = Collator.getInstance();
    collator.setStrength(Collator.PRIMARY);

    int end = source.length() - target.length() + 1;

    for (int i = 0; i < end; i++) {
        String sourceSubstring = source.substring(i, i + target.length());

        if (collator.compare(sourceSubstring, target) == 0) {
            return i;
        }
    }

    return -1;
}

然后你可以这样应用它:

String guestWholeName = guest.getGuestFirstName() + " " + guest.getGuestLastName();
int wholeNameIndex = indexOf(guestWholeName, searchText);

if (wholeNameIndex > -1) {
    Timber.d("guest name first : guest.getGuestFirstName() %s", guest.getGuestFirstName());
    Timber.d("guest name last : guest.getGuestLastName() %s", guest.getGuestLastName());

    int endPos = wholeNameIndex + searchText.length();

    Spannable spannable = new SpannableString(guestWholeName);
    Typeface firstNameFont = Typeface.createFromAsset(context.getAssets(), "fonts/Graphik-Semibold.otf");
    spannable.setSpan(new CustomTypefaceSpan("", firstNameFont), wholeNameIndex, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    Objects.requireNonNull(guestName).setText(spannable);
} else {
    Objects.requireNonNull(guestName).setText(guestWholeName);
}
 类似资料:
  • 我刚开始使用hibernate lucene搜索。从几天以来,我一直致力于搜索关键字与特殊字符。我正在使用MultiFieldQueryParser进行精确短语匹配以及布尔搜索。但在这个过程中,我无法得到搜索关键字的结果,如“有1年以上的经验”,如果我没有在搜索关键字周围添加任何引号,那么我就得到了结果。所以我在执行lucene查询时观察到的是,它正在转义特殊符号(+)。我正在使用Standard

  • 问题内容: 在Android Rooms持久性库中,我如何将以下SQL语句编写为@Query? 该语法无效,我在文档中找不到任何有关此语法的信息。 问题答案: 您可以使用SQLite字符串串联来串联。

  • 问题内容: 我正在处理长度为25的DNA序列(请参见下面的示例)。我有一个230,000的列表,需要查找整个基因组中的每个序列(弓形虫寄生虫)。我不确定基因组有多大,但是比230,000个序列长得多。 我需要查找25个字符的每个序列,例如()。 基因组被格式化为连续字符串,即() 我不在乎它在哪里或被发现多少次,无论它是否存在。 我认为这很简单- 但是我也要找到一个在任何位置(只有一个位置)被定义

  • 问题内容: 我想要一个自动完成功能。我对数据类型的属性有简短的描述性字符串。我在redis中有一个ID列表,其中包含按创建日期排序的数据类型,并且使用ID来设置和获取该数据类型的属性,如redis类型文档中所述。我不使用哈希表。在此设置下,获取与输入到自动完成输入框中的内容相匹配的字符串集的最佳方法是什么?遍历所有id并检查我要搜索的属性- 对于每次击键,这似乎是错误的方法。 编辑: 除了下面的答

  • 我还没有为索引创建任何显式设置。谢了。

  • 在Android Rooms持久性库中,我将如何编写以下SQL语句作为@Query? 这个语法是无效的,我在留档中找不到任何关于它的内容。