2010-03-01 12 views
11

Tengo un ArrayAdapter (myAdapter) conectado a un componente AutoCompleteTextView (textView).
Una vez que el usuario presione un carácter, me gustaría completar la lista desplegable de AutoCompleteTextView con los elementos que contienen este carácter.
Recupero los elementos usando AsyncTask (que usa un servicio web).Android, la función add() de ArrayAdapter no funciona

Llamo a myAdapter.add (artículo) pero la lista desplegable está vacía.
Agregué una llamada myAdapter.getCount() después de cada adición y muestra cero cada vez. Llamar notifyDataSetChanged() no ayudó.
Incluso traté de agregar objetos String simples en lugar de mis objetos personalizados, fue en vano.
¿Qué estoy haciendo mal?

Edit: He cambiado el código como miette se sugiere a continuación, pero aún así fue en vano.
Generalmente, lo que hago es después de que el texto se cambia en mi vista de texto completa automática, llamo a una nueva AsyncTask y le paso el texto ingresado y un Manejador (vea afterTextChanged()). La tarea recupera objetos relevantes para el texto y una vez hecho esto se llama a handleMessage(). En handleMessage() intento llenar los objetos del adaptador. Pero aún la lista desplegable del adaptador termina vacía.

Aquí está mi código:

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

Muchas gracias, Rob

Respuesta

0

¿Dónde se llama addItemsToAdapter()? ¿Puede mostrarnos cómo ha intentado agregar cadenas simples a su adaptador?

Editar: de los comentarios del ejemplo de código útil:

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

Aquí es lo que hice: He definido una Handler y la puso en mi AsyncTask (llamado AppTask) params así: new AppTask().execute(new AppTask.Payload(Consts.taskType.SEARCH_STOCK, \t \t \t \t new Object[] {text, handler}, this)); Luego, en handleMessage de mi Handler() Llamé addItemsToAdapter . En cuanto a las cadenas, he declarado esta matriz: privada estática final Cadena [] COUNTRIES = new String [] { "Bélgica", "Francia", "Italia", "Alemania", "España" }; y en lugar de llamar a: myAdapter.add (tmpItem); Llamé; myAdapter.add (PAÍSES [i]) e incluso PAÍSES [0]. ¡Gracias por su ayuda! – Rob

+0

¿Hay alguna manera de publicar el código completo? – Rob

+0

Puede cargar un archivo zip de proyecto minimalista en algún lado o editar su pregunta anterior y agregar el código allí ... – WarrenFaith

2

Crear un adaptador de matriz con un vector o matriz como:

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

Por inicializar su ArrayAdapter, se hará escuchar a la matriz de objetos. No agregue elementos al adaptador ni borre el adaptador, haga sus adiciones en una matriz de "objetos" y también límpiela. Después de los cambios en esta matriz llamar

adapter.notifyDataSetChanged(); 

Más específicamente

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:)"); 

Esto debe hacerse después de cargar YourDataList, he comprobado el código, ¿está seguro guía llama addStockItemsToAdapter() antes de buscar su adaptador está vacía o no ? También debe verificar si el vector de acciones tiene algún elemento.

+0

Hice lo que me sugirió, pero todavía no funciona. Tengo un controlador cuyo handleMessage() se invoca una vez que llega la estructura de datos. En handleMessage() invoco una función que itera a través de la estructura de datos y agrega elementos a los objetos T [] del adaptador y luego llama a adapter.notifyDataSetChanged(). Después de todo este adapter.getCount() todavía devuelve cero. ¿Qué demonios podría ser? – Rob

+0

Hola de nuevo, estoy cambiando mi respuesta para usted. He probado lo que quieres hacer y funciona para mí. – miette

+0

Hola miette, te agradezco que estudies esto. Estás sugiriendo lo mismo que Warren y de hecho esto resolvió el problema. Por cierto, me doy cuenta de que llamar a notifyDataSetChanged() ahora es redundante ya que la nueva instrucción "ArrayAdapter" contiene la fuente de datos actualizada. Pensé que podría tener mi fuente de datos (YourDataList) vacía al crear el adaptador, agregarle elementos en el camino y llamar a notifyDataSetChanged() cuando termine de agregar, pero parece que no funciona, ¿o sí? De todos modos, muy obligado mi hombre. – Rob

12

Tuve exactamente el mismo problema. Después de examinar el código fuente ArrayAdapter y AutoCompleteTextView, descubrí que el problema era, en resumen, que:

  • la lista objeto original se almacena en ArrayAdapter.mObjects.
  • Sin embargo, AutoCompleteTextView habilita el filtrado ArrayAdapter, lo que significa que los objetos nuevos se agregan a ArrayAdapter.mOriginalValues, mientras que mObjects contiene los objetos filtrados.
  • ArrayAdapter.getCount() siempre devuelve el tamaño de mObjects.

Mi solución era anular ArrayAdapter.getFilter() para devolver un filtro que no filtra. De esta forma, es null y mObjects se usa en su lugar en todos los casos.

Código de ejemplo:

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

USTED SIR IMPRESIONANTE !!! – Anderson

+0

en otros casos, el filtrado es lo que realmente necesita y puede usarlo tal como se explica [aquí] (http://android.foxykeep.com/dev/how-to-add-autocompletion-to-an-edittext) en el Ejemplo de servicio web. – vfede

Cuestiones relacionadas