Мое текущее приложение для Android позволяет пользователям удаленно искать контент.
например Пользователю предоставляется EditText
, который принимает его строки поиска и запускает удаленный вызов API, который возвращает результаты, соответствующие введенному тексту.
В худшем случае я просто добавляю TextWatcher
и запускаю вызов API каждый раз, когда вызывается onTextChanged
. Это можно улучшить, заставив пользователя вводить не менее N символов для поиска перед выполнением первого вызова API.
«Идеальное» решение будет иметь следующие особенности:
Как только пользователь начинает вводить строку(и) поиска
Периодически (каждые M миллисекунд) использовать всю введенную строку (строки). Запускайте вызов API каждый раз, когда истекает период, а текущий ввод пользователя отличается от предыдущего ввода пользователя.
[Возможно ли иметь динамический тайм-аут, связанный с длиной введенного текста? например, пока текст «короткий», размер ответа API будет большим, и для возврата и анализа потребуется больше времени; По мере того, как текст поиска становится длиннее, размер ответа API будет уменьшаться вместе с «в процессе» и временем синтаксического анализа]
Когда пользователь перезапустит ввод текста в поле EditText, перезапустите периодическое потребление текста.
Всякий раз, когда пользователь нажимает клавишу ENTER, инициируется «последний» вызов API и прекращается отслеживание пользовательского ввода в поле EditText.
Установите минимальную длину текста, который пользователь должен ввести, прежде чем будет запущен вызов API, но объедините эту минимальную длину с переопределяющим значением тайм-аута, чтобы, когда пользователь хочет найти «короткую» текстовую строку, он мог это сделать.
Я уверен, что RxJava и/или RxBindings могут поддерживать вышеуказанные требования, однако до сих пор мне не удалось реализовать работоспособное решение.
Мои попытки включают
private PublishSubject<String> publishSubject;
publishSubject = PublishSubject.create();
publishSubject.filter(text -> text.length() > 2)
.debounce(300, TimeUnit.MILLISECONDS)
.toFlowable(BackpressureStrategy.LATEST)
.subscribe(new Consumer<String>() {
@Override
public void accept(final String s) throws Exception {
Log.d(TAG, "accept() called with: s = [" + s + "]");
}
});
mEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
}
@Override
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
publishSubject.onNext(s.toString());
}
@Override
public void afterTextChanged(final Editable s) {
}
});
И это с RxBinding
RxTextView.textChanges(mEditText)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe(new Consumer<CharSequence>(){
@Override
public void accept(final CharSequence charSequence) throws Exception {
Log.d(TAG, "accept() called with: charSequence = [" + charSequence + "]");
}
});
Ни один из них не дает мне условный фильтр, который сочетает в себе введенную длину текста и значение тайм-аута.
Я также заменил debounce на ThrottleLast и образец, ни один из которых не дал требуемого решения.
Можно ли достичь требуемой функциональности?
ДИНАМИЧЕСКИЙ ТАЙМ-АУТ
Приемлемое решение справится со следующими тремя сценариями
я). Пользователь хочет найти любое слово, начинающееся с «P».
ii). Пользователь хочет найти любое слово, начинающееся с «Пневмо».
III). Пользователь желает выполнить поиск по слову «пневмоноультрамикроскопический силиковулканокониоз».
Во всех трех сценариях, как только пользователь введет букву «P», я покажу счетчик прогресса (однако в этот момент вызов API выполняться не будет). Я хотел бы сбалансировать необходимость предоставления пользователю отзывов о поиске в адаптивном пользовательском интерфейсе и «бесполезных» вызовов API по сети.
Если бы я мог положиться на то, что пользователь вводит текст поиска, а затем нажимает клавишу «Готово» (или «Ввод»), я мог бы немедленно инициировать последний вызов API.
Первый сценарий
Поскольку текст, введенный пользователем, имеет короткую длину (например, 1 символ), мое значение тайм-аута будет максимальным. Это дает пользователю возможность вводить дополнительные символы и экономит «бесполезные вызовы API».
Поскольку пользователь хочет искать только букву «P», по истечении максимального времени ожидания я выполню вызов API и отобразлю результаты. Этот сценарий дает пользователю наихудший пользовательский опыт, поскольку ему приходится ждать истечения срока действия моего динамического тайм-аута, а затем ждать возврата и отображения ответа Large API. Они не увидят промежуточных результатов поиска.
Сценарий 2
Этот сценарий объединяет сценарий один, поскольку я понятия не имею, что пользователь будет искать (или конечную длину строки поиска), если он наберет все 6 символов «быстро», я могу выполнить один вызов API, однако чем медленнее они вводят 6 символы увеличат вероятность выполнения напрасных вызовов API.
Этот сценарий дает пользователю улучшенный пользовательский опыт, поскольку ему нужно дождаться истечения моего динамического тайм-аута, однако у него есть шанс увидеть промежуточные результаты поиска. Ответы API будут меньше, чем в первом сценарии.
Сценарий третий
Этот сценарий сочетает в себе сценарий один и два, поскольку я понятия не имею, что пользователь будет искать (или конечную длину строки поиска), если он наберет все 45 символов «быстро», я могу выполнить один вызов API (возможно!), однако чем медленнее они набирают 45 символов, тем выше вероятность выполнения бесполезных вызовов API.
Я не привязан к какой-либо технологии, которая обеспечивает желаемое решение. Я считаю, что Rx — лучший подход, который я определил до сих пор.