Как настроить механизм автоматического предложения "фишек", используемый в поле получателей Gmail?


Фон

Я искал способ выглядеть и чувствовать себя похожим на поле Gmail receipients, которое позволяет автоматически заполнять элементы действительно классным способом:

Введите описание изображения здесь

Класс, который встроен в платформу 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. Как я могу узнать, какие элементы были введены (которые стали "фишками"), а также иметь возможность удалять или добавлять элементы из снаружи?

2 12

2 ответа:

Мне удалось добавить функциональность добавления получателя. Единственное, что нужно запомнить, это вызвать его только после того, как вид получил свой размер (пример того, как это сделать здесь) :

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

И, как я уже писал ранее, для получения списка contactIds, которые в настоящее время находятся внутри представления, используйте "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.

Единственное, чего мне сейчас не хватает-это хороший слушатель к самим чипам : когда чип добавляется, удаляется и заменяется. в большинстве случаев я могу обнаружить его, но не тогда, когда пользователь нажимает backspace и удаляет чип.


EDIT: также добавлен слушатель. теперь я нашел ошибку в поиске контактов. похоже, что он ищет обычные английские буквы, как если бы они были телефонными номерами.


EDIT: я решил поместить образец и библиотеку на GitHub, здесь . Надеюсь обновить его с более полезным особенности скоро.

Я был бы очень рад любому вкладу в этот код.

Эта библиотека, по-видимому, позволяет настраивать то, что она ищет, а также сопоставлять внешний вид материала. Он также, кажется, основан на библиотеке чипов Google. Я случайно нашел его, когда исследовал подобную проблему.

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