如何定制“芯片”自动建议机制,就像Gmail的收件人字段中使用的那样?

12

背景

我一直在寻找一种类似于 Gmail 收件人栏的外观和感觉,它可以以非常酷的方式自动填充项目:

enter image description here

负责此功能的Android框架中内置的类名为“MultiAutoCompleteTextView”。

问题

MultiAutoCompleteTextView相当基础,但是缺乏足够的示例、教程和库来了解如何像Gmail等应用程序一样自定义它。

我想知道如何自定义它以处理任何类型的数据,并且可以完全控制它(例如添加、删除和获取其自动完成的项)。

我尝试过的方法

我发现了以下可能的方法来实现它:

  1. 使用第三方库,例如splitwise-TokenAutoComplete。缺点:非常容易出错,在某些设备上无法正常工作。
  2. 创建自己的方法(如此处所示)。缺点:需要很长时间,而且我可能需要处理与库相同的问题。
  3. 使用Google的代码(在此处找到)。缺点:它真的不能定制。

我决定使用#3(Google的芯片库)。

目前用于获取Google库中使用的联系人列表的代码:

public List<RecipientEntry> doQuery() {
    final Cursor cursor = mContentResolver.query(mQuery.getContentUri(), mQuery.getProjection(), null, null, null);
    final LinkedHashMap<Long, List<RecipientEntry>> entryMap = new LinkedHashMap<Long, List<RecipientEntry>>();
    final List<RecipientEntry> nonAggregatedEntries = new ArrayList<RecipientEntry>();
    final Set<String> existingDestinations = new HashSet<String>();
    while (cursor.moveToNext())
        putOneEntry(new TemporaryEntry(cursor, false /* isGalContact */), true, entryMap, nonAggregatedEntries,
                existingDestinations);
    cursor.close();
    final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
    {
        for (final Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
            final List<RecipientEntry> entryList = mapEntry.getValue();
            for (final RecipientEntry recipientEntry : entryList)
                entries.add(recipientEntry);
        }
        for (final RecipientEntry entry : nonAggregatedEntries)
            entries.add(entry);
    }
    return entries;
}

它可以正常工作,但我在添加和删除项目方面遇到了困难。

我认为调用“getContactIds”来获取项目,但是关于在芯片内修改项目,这非常难以找到。

例如,我尝试添加类似于“submitItemAtPosition”的函数,它似乎会添加从适配器中找到的新实体。 它确实添加了,但联系人的显示名称未显示在芯片本身上。

问题

经过多次思考,我决定使用Google的代码。

不幸的是,如我所写,视图及其类与其使用方式非常紧密相关。

  1. 如何解耦视图并使其更加可定制? 如何使用任何类型的数据而不仅仅是Google所做的?

  2. 如何获取输入的哪些项目(变成了“芯片”),并能够从外部添加或删除项目?


@pskink 目前我使用谷歌提供的库,并尝试自定义它,而不是从头开始创建所有内容。也许我会去看看,但我认为我没有时间处理除了我已经做的事情之外的其他事情。 - android developer
@androiddeveloper 祝你好运... - pskink
@pskink 谢谢你,也许等我完成后,我会把它放在GitHub上。 - android developer
@pskink,我已经成功修改了它。希望很快能在Github上发布。目前,我已经放出了我的答案。可惜我已经为此设置了赏金,因为我认为这可能需要我几天时间。 - android developer
@androiddeveloper 最后一个问题(“abc”)与T9搜索有关 - 我有一个联系人,电话号码以数字6开头,输入“m”,“n”或“o”会选择该联系人。 - pskink
显示剩余8条评论
2个回答

5

我已经成功地添加了添加收件人的功能。唯一需要记住的是,仅在视图获得其大小后调用它(如何实现的示例在这里):

/** adds a recipient to the view. note that it should be called when the view has determined its size */
public void addRecipient(final RecipientEntry entry) {
    if (entry == null)
        return;
    clearComposingText();

    final int end = getSelectionEnd();
    final int start = mTokenizer.findTokenStart(getText(), end);

    final Editable editable = getText();
    QwertyKeyListener.markAsReplaced(editable, start, end, "");
    final CharSequence chip = createChip(entry, false);
    if (chip != null && start >= 0 && end >= 0) {
        editable.replace(start, end, chip);
    }
    sanitizeBetween();
}

private void submitItemAtPosition(final int position) {
    final RecipientEntry entry = createValidatedEntry(getAdapter().getItem(position));
    if (entry == null)
        return;
    addRecipient(entry);
}

还有,关于删除:

/** removes a chip of a recipient from the view */
public void removeRecipient(final RecipientEntry entry) {
    final DrawableRecipientChip[] chips = getSpannable().getSpans(0, getText().length(),
            DrawableRecipientChip.class);
    final List<DrawableRecipientChip> chipsToRemove = new ArrayList<DrawableRecipientChip>();
    for (final DrawableRecipientChip chip : chips)
        if (chip.getDataId() == entry.getDataId())
            chipsToRemove.add(chip);
    for (final DrawableRecipientChip chip : chipsToRemove)
        removeChip(chip);
}

正如我之前所写的那样,要获取当前视图中存在的联系人id列表,请使用“getContactIds()”。另一种选择是:

/** returns a collection of all of the chips' items. key is the contact id, and the value is the recipient itself */
public Map<Long, RecipientEntry> getChosenRecipients() {
    final Map<Long, RecipientEntry> result = new HashMap<Long, RecipientEntry>();
    final DrawableRecipientChip[] chips = getSortedRecipients();
    if (chips != null)
        for (final DrawableRecipientChip chip : chips) {
            // if(result.)
            final long contactId = chip.getContactId();
            if (!result.containsKey(contactId))
                result.put(contactId, chip.getEntry());
        }
    return result;
}

也许我应该在Github上发布代码。 现在唯一缺少的是一个好的监听器,用于检测芯片的添加、删除和替换。对于大多数情况,我可以检测到,但当用户按退格键并删除芯片时,我无法检测到。 编辑: 我还添加了监听器。现在我发现了一个在搜索联系人时出现的错误。它似乎将普通的英文字母作为电话号码进行搜索。 编辑: 我决定在GitHub上放置一个示例和库,这里。希望很快更新更多有用的功能。 我会非常高兴接受任何对代码的贡献。

1
这个库似乎允许您配置搜索的内容,并匹配Material Design外观。它似乎也是基于谷歌的芯片库。当我调查类似问题时,偶然发现了它。

https://github.com/klinker41/android-chips


@GauravPolekar 我之前遇到了一些问题,所以花了一些时间更新了更现代的代码。然而,那里面有一些错误,所以我没有完成它。Google发布了他们的消息应用程序的开源版本作为AOSP 6.0代码的一部分。你可以在下面找到它。也许你可以使用它来改进你现在拥有的东西。https://github.com/CyanogenMod/android_packages_apps_Messaging/tree/cm-13.0/src/com/android/messaging/ui/contact - blunden

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