AutoCompleteTextView ne pas afficher résultat même lorsque le ArrayAdapter est mis à jour

StackOverflow https://stackoverflow.com/questions/2671404

Question

Je suis en train de faire une AutoCompleteTextView (ACTV) pour afficher les résultats que je suis obtenir à partir d'une ressource réseau. J'ai mis la fin-à 2 et treshold je peux voir que la demande est déclenché lorsque j'entre les caractères.

Le résultat que je reçois est le bon. Disons que j'écris « ca », et je reçois le résultat « voiture » en tant que saisie semi-automatique. J'ai une fonction de rappel qui reçoit le résultat d'un AsyncTask et met le résultat dans le ArrayAdapter. Ensuite, j'appelle .showDropDown () sur la ACTV et un menu déroulant vide apparaît (la moitié de la taille d'un élément normal). Ensuite, si je rentre dans la dernière lettre « r » et le ACTV montre « voiture », le menu déroulant est affiché et le résultat est tout à coup dans la liste.

La même chose arrive si je suis entré deux caractères (qui renvoie un résultat valide), et supprimer la dernière lettre. Lorsque la lettre est supprimée, « voiture » apparaît comme une valeur de saisie semi-automatique.

Quelqu'un at-il eu ce problème? On dirait que l'adaptateur est rempli avec le résultat, mais le résultat ne montre pas jusqu'à ce que la prochaine action que je fais. J'ai aussi essayé de courir .notifyDataSetChanged () après avoir ajouté le résultat à l'adaptateur, mais cela ne devrait pas être nécessaire, ou?

Était-ce utile?

La solution

Sans voir votre code, il est difficile de dire ce qui pourrait se passer. Mais la première chose qui vient à l'esprit est que votre demande de réseau se passe sur un thread différent, et donc votre performFiltering() peut être un retour jeu de résultats vide prématurément. À ce moment-là, publishResults() renvoie le résultat vide, et votre menu déroulant est vide. Plus tard, votre AsyncTask obtiendra le dos de résultat, et vous ajoutez les résultats dans la liste de l'adaptateur, mais pour une raison ou une autre, il ne soit pas affiché encore.

Je pense que vous pouvez être trompé sur la nécessité d'AsyncTask bien. L'objet de filtre est déjà en train de faire quelque chose de similaire à AsyncTask: performFiltering() se fait dans un thread d'arrière-plan, et publishResults() est appelé à partir du thread d'interface utilisateur, après performFiltering () est terminée. Ainsi, vous pouvez faire votre demande de réseau directement dans performFiltering (), et définir les résultats dans les FilterResults objet, et vous ne serez pas à vous soucier de la demande de réseau étant trop lent et causer des problèmes dans votre interface utilisateur.

Une autre solution, qui est un peu plus compliqué, mais c'est ce que je fais dans mon objet de filtre (en raison de l'architecture existante qui fait des appels API en arrière-plan, en utilisant un rappel asynchrone au lieu du blocage / étape synchrone nécessaire pour performFiltering ()), est d'utiliser un objet synchronisé avec attente () / notify () pour faire le suivi inter-threads, donc l'effet est le même que faire la demande de réseau directement dans performFiltering (), mais il se passe réellement dans plusieurs fils:

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

Cependant, je pense que vous trouverez peut-être que la meilleure solution est de le faire de manière synchrone votre demande de réseau, directement dans la méthode performFiltering (). L'exemple de code ci-dessus est juste une possibilité, si vous avez déjà l'architecture en place pour asynchrone / rappel axée sur les appels API, et vous ne voulez pas changer ce comportement afin d'obtenir des résultats synchrones dans performFiltering ().

Autres conseils

Je pense que la réponse de Joe est le chemin à parcourir. Cependant, je pense que vous devriez utiliser CountDownLatch au lieu d'attente / notification.

La raison en est, avec attente / notify, vous risquez une condition de course si votre API réellement le retour super rapide avant de commencer « attente () » ... dans ce cas, prévenez aura pas d'effet et attendre () attendra indéfiniment. Avec verrou, le code ressemblera à ceci (copié de Joe et modifié):

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

Enfin, je n'ai pas assez de crédit pour poster un commentaire, sinon je l'aurais ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top