2010-03-01 3 views
11

J'ai un ArrayAdapter (myAdapter) attaché à un composant AutoCompleteTextView (textView).
Une fois que l'utilisateur appuie sur un caractère, je voudrais remplir la liste déroulante de AutoCompleteTextView avec les éléments contenant ce caractère.
Je récupère les éléments en utilisant AsyncTask (qui utilise un service Web).Android, la fonction add() de ArrayAdapter ne fonctionne pas

J'appelle myAdapter.add (item) mais la liste déroulante est vide.
J'ai ajouté un appel à myAdapter.getCount() après chaque ajout et il affiche zéro à chaque fois. L'appel de notifyDataSetChanged() n'a pas aidé.
J'ai même essayé d'ajouter des objets String simples au lieu de mes objets personnalisés, en vain.
Qu'est-ce que je fais de mal?

Modifier: J'ai changé le code en miette suggéré ci-dessous mais toujours en vain.
Généralement, ce que je fais est après que le texte est changé dans ma vue de texte automatique complète, j'appelle un nouveau AsyncTask et lui passe le texte entré et un gestionnaire (voir afterTextChanged()). La tâche récupère les objets pertinents au texte et, une fois fait, le handleMessage() du Handler est appelé. Dans handleMessage(), j'essaie de remplir les objets de l'adaptateur. Mais la liste déroulante de l'adaptateur reste vide.

Voici mon code:

public class AddStockView extends Activity 
     implements OnClickListener, OnItemClickListener, TextWatcher { 

    ArrayAdapter<Stock> adapter; 
    AutoCompleteTextView textView; 
    Vector<Stock> stocks; 
    public AddStockView() { 
     // TODO Auto-generated constructor stub 
     stocks = new Vector<Stock>(); 
    } 

    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     requestWindowFeature(Window.FEATURE_NO_TITLE); 
     setContentView(R.layout.add_stock_view); 

     findViewById(R.id.abort_button).setOnClickListener(this); 

     adapter = new ArrayAdapter<Stock>(this, 
     android.R.layout.simple_dropdown_item_1line, stocks); 
     //adapter.setNotifyOnChange(true); 
     textView = (AutoCompleteTextView) 
     findViewById(R.id.search_edit_text); 
     textView.setAdapter(adapter); 
     textView.setOnItemClickListener(this); 
     textView.addTextChangedListener(this); 

    } 
    @Override 
    public void onClick(View v) { 
     // TODO Auto-generated method stub 
     switch (v.getId()) 
     { 
     case R.id.abort_button: 
     finish(); 
     break; 
     case R.id.search_edit_text: 

     break; 
     } 
    } 
    @Override 
    public void onItemClick(AdapterView<?> parent, View v, 
          int position, long id) { 
     // TODO Auto-generated method stub 
     Stock stockToAdd = (Stock)parent.getAdapter().getItem(position); 
     //TODO: Add the above stock to user's stocks and close this screen 
     finish(); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     super.onCreateOptionsMenu(menu); 
     getMenuInflater().inflate(R.layout.menu, menu); 

     CategoryMenu.getInstance().populateMenu(menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     CategoryMenu.getInstance().menuItemSelected(item, this); 
     return false; 
    } 

    @Override 
    public boolean onPrepareOptionsMenu(Menu menu) { 
     return true; 
    } 
    @Override 
    public void afterTextChanged(Editable text) { 
     // TODO Auto-generated method stub 
     if (text.toString().equals("")) 
     return; 
     new AppTask().execute(new AppTask.Payload(Consts.taskType.SEARCH_STOCK, 
              new Object[] {text, handler}, this)); 

    } 
    @Override 
    public void beforeTextChanged(CharSequence a0, int a1, int a2, int a3) { 
     // TODO Auto-generated method stub 
    } 
    @Override 
    public void onTextChanged(CharSequence a0, int a1, int a2, int a3) { 
     // TODO Auto-generated method stub 
    } 
    private void addStockItemsToAdapter(Vector<Object> dataItems) 
    { 
     for (int i = 0; i <dataItems.size(); i++) 
     { 
     Stock stk = (Stock)dataItems.elementAt(i); 
     stocks.add(stk); 
     } 
    } 

    public void populateAdapter() 
    { 
     addStockItemsToAdapter(ContentReader.getInstance.getDataItems());  
     adapter.notifyDataSetChanged(); 
     int size = adapter.getCount(); // size == 0 STILL!!!! 
     textView.showDropDown(); 
    } 
    final Handler handler = new Handler() { 
     public void handleMessage(Message msg) { 
     populateAdapter(); 
     } 
    }; 
} 

Merci beaucoup, Rob

Répondre

0

Où appelez-vous addItemsToAdapter()? Pouvez-vous nous montrer comment vous avez essayé d'ajouter des chaînes simples à votre adaptateur?

Edit: sur les commentaires du exemple de code utiles:

adapter = new ArrayAdapter<Stock>(this, android.R.layout.simple_dropdown_item_1line, stocks); 
adapter.notifyDataSetChanged(); 
textView.setAdapter(adapter); 
+0

Voici ce que je l'ai fait: je définissais un gestionnaire et le mettre dans mon AsyncTask (appelé AppTask) params comme ceci: new AppTask().execute(new AppTask.Payload(Consts.taskType.SEARCH_STOCK, \t \t \t \t new Object[] {text, handler}, this)); Puis, dans handleMessage de mon Handler() J'ai appelé addItemsToAdapter . Comme pour les chaînes, j'ai déclaré ce tableau: privé static final String [] COUNTRIES = new String [] { "Belgique", "France", "Italie", "Allemagne", "Espagne" }; et au lieu d'appeler: myAdapter.add (tmpItem); J'ai appelé; myAdapter.add (COUNTRIES [i]) et même COUNTRIES [0]. Merci pour votre aide! – Rob

+0

Y at-il un moyen que je puisse publier l'intégralité du code? – Rob

+0

Vous pouvez télécharger un zip projet minimaliste quelque part ou modifier votre question ci-dessus et ajouter le code là-bas ... – WarrenFaith

2

Créer un adaptateur tableau avec un vecteur ou un tableau comme:

ArrayAdapter(Context context, int textViewResourceId, T[] objects) 

En initialisant votre arrayadapter, vous faire écouter au tableau d'objets. N'ajoutez pas d'élément à l'adaptateur ou effacez l'adaptateur, faites vos ajouts dans le tableau "objets" et effacez-le également. Une fois les modifications sur ce tableau appel

adapter.notifyDataSetChanged(); 

Plus précisément

ArrayAdapter<YourContentType> yourAdapter = new ArrayAdapter<YourContentType> (this,R.id.OneOfYourTextViews,YourDataList); 

yourAdapter.notifyDataSetChanged();  
aTextView.setText(yourAdapter.isEmpty() ? "List is empty" : "I have too many objects:)"); 

Cela devrait être fait après le chargement YourDataList, j'ai vérifié votre code, êtes-vous sûr appels gestionnaire addStockItemsToAdapter() avant de regarder votre carte est vide ou non ? Vous devriez également vérifier si les stocks vectoriels contiennent des éléments.

+0

J'ai fait comme vous l'avez suggéré mais cela ne fonctionne toujours pas. J'ai un gestionnaire dont handleMessage() est appelé une fois que la structure de données arrive. Dans handleMessage() j'appelle une fonction qui itère à travers la structure de données et ajoute des éléments aux objets T [] de l'adaptateur, puis appelle adaptateur.notifyDataSetChanged(). Après tout cela, adapter.getCount() renvoie toujours zéro. Que diable pourrait-il être? – Rob

+0

Salut à nouveau, je change ma réponse pour vous. J'ai testé ce que tu veux faire et ça marche pour moi. – miette

+0

Salut miette, je vous remercie de regarder dans ce domaine. Vous suggérez la même chose que Warren et en effet cela a résolu le problème. Btw, je me rends compte qu'appeler notifyDataSetChanged() est redondant maintenant puisque l'instruction "new ArrayAdapter" contient la source de données mise à jour. J'ai pensé que je pourrais avoir ma source de données (YourDataList) vide lors de la création de l'adaptateur, ajouter des éléments en cours de route et appeler notifyDataSetChanged() lorsque l'ajout, mais il semble que cela ne fonctionne pas, n'est-ce pas? Quoi qu'il en soit, beaucoup obligé mon homme. – Rob

12

J'ai eu exactement le même problème. Après avoir examiné le code source ArrayAdapter et AutoCompleteTextView, j'ai découvert que le problème était, en bref, que:

  • la liste des objets d'origine est stocké dans ArrayAdapter.mObjects.
  • Toutefois, AutoCompleteTextView permet le filtrage de ArrayAdapter, ce qui signifie que les nouveaux objets sont ajoutés à ArrayAdapter.mOriginalValues, tandis que mObjects contient les objets filtrés.
  • ArrayAdapter.getCount() renvoie toujours la taille de mObjects.

Ma solution consistait à remplacer ArrayAdapter.getFilter() pour renvoyer un filtre sans filtrage. De cette façon, est null et mObjects est utilisé à la place dans tous les cas.

Exemple de code:

public class MyAdapter extends ArrayAdapter<String> { 
    NoFilter noFilter; 
    /* 
    ... 
    */ 

    /** 
    * Override ArrayAdapter.getFilter() to return our own filtering. 
    */ 
    public Filter getFilter() { 
     if (noFilter == null) { 
      noFilter = new NoFilter(); 
     } 
     return noFilter; 
    } 

    /** 
    * Class which does not perform any filtering. 
    * Filtering is already done by the web service when asking for the list, 
    * so there is no need to do any more as well. 
    * This way, ArrayAdapter.mOriginalValues is not used when calling e.g. 
    * ArrayAdapter.add(), but instead ArrayAdapter.mObjects is updated directly 
    * and methods like getCount() return the expected result. 
    */ 
    private class NoFilter extends Filter { 
     protected FilterResults performFiltering(CharSequence prefix) { 
      return new FilterResults(); 
     } 

     protected void publishResults(CharSequence constraint, 
             FilterResults results) { 
      // Do nothing 
     } 
    } 
} 
+0

vous SIR AWESOME !!! – Anderson

+0

dans d'autres cas le filtrage est ce dont vous avez vraiment besoin et vous pouvez l'utiliser comme expliqué [ici] (http://android.foxykeep.com/dev/how-to-add-autocompletion-to-an-edittext) dans le Exemple de service Web. – vfede

Questions connexes