How to customize the “chips” auto-suggesting mechanism, as used on Gmail's recipients field?

寵の児 提交于 2019-12-03 16:46:27

问题


Background

I've searched for a way to have a similar look & feel to the Gmail receipients field, which allows auto-filling of items in a really cool way:

The class that is built into the Android framework and is responsible for this is called "MultiAutoCompleteTextView" .

The problem

the MultiAutoCompleteTextView is quite basic, yet it doesn't hold enough samples, tutorials and libraries to get to know how to customize it like on Gmail and the likes.

I would like to know how to customize it to handle any kind of data, and that I will have full control over it (for example adding, deleting and getting the items that it has auto-completed).

What I've tried

I've found the next possible ways to achieve it:

  1. use a third library like splitwise-TokenAutoComplete. the downside: it's very buggy and doesn't work well on some devices.
  2. create my own way (as shown here). the downside: will take a long time and I will probably need to handle the same problems as of the library.
  3. use the code of Google (found here). The downside: it's really not customizable.

I've decided to use #3 (Google's chips library).

Currently the code for getting the list of contacts used on Google's library:

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;
}

It works fine, but I'm having difficulties adding items and deleting them.

I think that getting the items is used by calling "getContactIds" , but about modifying the items within the chips, that's very problematic to find.

For example, I've tried to add a similar function to "submitItemAtPosition" , which seems to add a new entity found from the adapter. It does add, but the display-name of the contact isn't shown on the chip itself.

The question

After a lot of thoughts, I decided to use Google's code.

Sadly, as I've written, the view and its classes are very tight to the usage of it.

  1. How can I de-couple the view and make it much more customizable? How can I make it use any type of data instead of just what Google has done?

  2. How do I get which items were entered (that became "chips"), and also be able to remove or add items from outside?


回答1:


I've succeeded adding the functionality of adding a recipient. The only thing to remember is to call it only after the view got its size (example of how to do it here) :

/** 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);
}

And, for deletion:

/** 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);
}

and as I've written before, for getting the list of contactIds that are currently inside the view, use "getContactIds()" . Another alternative is:

/** 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;
}

Maybe I should post the code on Github.

The only thing I miss now is a good listener to the chips themselves : when a chip is added, removed and replaced. for most of the cases I can detect it, but not when the user presses backspace and removes a chip.


EDIT: also added the listener. now I've found a bug in searching of contacts. it seems to search the normal English letters as if they were phone numbers.


EDIT: I've decided to put a sample and a library on GitHub, here . Hope to update it with more useful features soon.

I would really be happy for any contribution to the code.




回答2:


This library seems to allow you to configure what it searches for while also matching the Material Design look. It also seems to be based off of Google's chip library. I happened to find it when investigating a similar problem.

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



来源:https://stackoverflow.com/questions/22835888/how-to-customize-the-chips-auto-suggesting-mechanism-as-used-on-gmails-recip

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!