AutoCompleteTextView não exibindo resultado mesmo quando o ArrayAdapter é atualizado
-
28-09-2019 - |
Pergunta
Estou tentando obter um AutoCompleteTextView (ACTV) para exibir resultados que estou obtendo de um recurso de rede. Definei o acaso de conclusão para 2 e posso ver que a solicitação foi disparada quando entro nos caracteres.
O resultado que estou obtendo é o correto. Digamos que eu escrevo "CA" e recebo o resultado "carro" como uma conclusão automática. Eu tenho uma função de retorno de chamada que recebe o resultado de uma asyncTask e coloca o resultado no Arrayadapter. Então eu chamo .ShowDropdown () no ACTV e um suspensão vazio é mostrado (metade do tamanho de um elemento normal). Então, se eu entrar na última letra "R" e o ACTV mostra "carro", o menu suspenso é mostrado e o resultado estará de repente na lista.
O mesmo acontece se eu inseri dois caracteres (que retornam um resultado válido) e remove a última letra. Quando a letra é removida, "carro" é mostrado como um valor de conclusão automática.
Alguém já teve esse problema? Parece que o adaptador está preenchido com o resultado, mas o resultado não aparece até a próxima ação que eu faço. Eu também tentei executar .NotifyDataSchanGed () depois de adicionar o resultado ao adaptador, mas isso não deve ser necessário, ou?
Solução
Sem ver seu código, é difícil dizer o que poderia estar acontecendo. Mas a primeira coisa que vem à mente é que sua solicitação de rede está acontecendo em um tópico diferente e, portanto, seu performFiltering()
pode estar retornando um conjunto de resultados vazios prematuramente. Nesse ponto, publishResults()
está retornando o resultado vazio e seu menu suspenso está vazio. Mais tarde, sua assínceta receberá seu resultado de volta e você adicionará os resultados à lista do adaptador, mas, por um motivo ou outro, ele ainda não será exibido.
Eu acho que você pode estar enganado com a necessidade de assíncrogem. O objeto de filtro já está fazendo algo semelhante à assíncena: performFiltering()
é feito em um tópico de fundo e publishResults()
é chamado do thread da interface do usuário, depois que o desempenho do desempenho () é concluído. Assim, você pode fazer sua solicitação de rede diretamente no PerformFiltering () e definir os resultados no objeto FilterResults, e não precisará se preocupar com a solicitação de rede ser muito lenta e causar problemas na sua interface do usuário.
Uma solução alternativa, que é um pouco mais complicada, mas é o que estou fazendo no meu objeto de filtro (devido à arquitetura existente que chama a API em segundo plano, usando um retorno de chamada assíncrono em vez da etapa de bloqueio/síncrono, conforme necessário para o PerformFiltering ( )), é usar um objeto sincronizado com espera ()/notify () para fazer monitoramento de thread cruzado, portanto, o efeito é o mesmo que fazer a solicitação de rede diretamente no PerformFiltering (), mas está realmente acontecendo em vários threads:
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
synchronized APIResult waitForAPI(CharSequence constraint) {
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// wait() is a Java IPC technique that will block execution until another
// thread calls the same object's notify() method.
wait();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// notify() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
notify();
}
}
No entanto, acho que você pode achar que a solução mais fácil é apenas fazer sua solicitação de rede de forma síncrona, diretamente no método do performafiltering (). O exemplo de código acima é apenas uma possibilidade, se você já possui a arquitetura para chamadas de API assíncronas/controladas por retorno de chamada, e não deseja alterar esse comportamento para obter resultados síncronos no PerformFiltering ().
Outras dicas
Eu acho que a resposta de Joe é o caminho a percorrer. No entanto, acho que você deveria usar Countdownlatch em vez de esperar/notificar.
O motivo é que, com espera/notificar, você corre o risco de uma condição de corrida se sua API realmente retornar super rápido antes de começar "aguarde ()" ... Nesse caso, o notificar não terá efeito e espere () aguardará indefinidamente . Com a trava, o código ficará assim (copiado de Joe e modificado):
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
CountDownLatch latch;
synchronized APIResult waitForAPI(CharSequence constraint) {
latch = new CountDownLatch(1);
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// Will wait till the count is 0...
// If the count is already 0, it'll return immediately.
latch.await();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// countDown() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
latch.countDown();
}
}
Por fim, não tenho crédito suficiente para postar um comentário, caso contrário, eu teria ...