我如何允许在也可以处理字符重音的android中进行搜索?

问题描述 投票:3回答:1

我已经在我的应用中实现了搜索机制,因此当我搜索名称或电子邮件时,它会显示带有匹配字符的字符串。但是,列表中有一些带重音的字符串,当我搜索与该特定重音有关的常规字符时,请说如果我有字符串“àngela”而我搜索“ angela”,则除非我使用精确的字符串“ àngela”。

我试图使它不管口音是否正常工作,如果我输入“à”,它应该显示所有包含“à”和“ a”的字符串,反之亦然。任何想法如何去做?我在网上查找了很多文章,例如:How to ignore accent in SQLite query (Android)“,也尝试了规范化器,但部分起作用,如果我搜索“ 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)

任何想法如何解决?

java android android-layout android-recyclerview android-search
1个回答
1
投票

[通常,我建议使用强度设置为CollatorCollator.PRIMARY比较包含重音和大小写不同的字符串(例如N vs né vs e)。不幸的是,Collator没有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

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

Edit:一种可能的优化是利用归类键以及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);
}
© www.soinside.com 2019 - 2024. All rights reserved.