Cómo usar ArrayAdapter <myClass>

129
ArrayList<MyClass> myList = new ArrayList<MyClass>();

ListView listView = (ListView) findViewById(R.id.list);

ArrayAdapter<MyClass> adapter = new ArrayAdapter<MyClass>(this, R.layout.row,
    to, myList.);
listView.setAdapter(adapter);

Clase: MyClass

class MyClass {
    public String reason;
    public long long_val;
}

He creado row.xml en diseños, pero no sé cómo mostrar el motivo y long_val en ListView usando ArrayAdapter.

Sumit M Asok
fuente

Respuestas:

155

Implemente un adaptador personalizado para su clase:

public class MyClassAdapter extends ArrayAdapter<MyClass> {

    private static class ViewHolder {
        private TextView itemView;
    }

    public MyClassAdapter(Context context, int textViewResourceId, ArrayList<MyClass> items) {
        super(context, textViewResourceId, items);
    }

    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(this.getContext())
            .inflate(R.layout.listview_association, parent, false);

            viewHolder = new ViewHolder();
            viewHolder.itemView = (TextView) convertView.findViewById(R.id.ItemView);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        MyClass item = getItem(position);
        if (item!= null) {
            // My layout has only one TextView
                // do whatever you want with your string and long
            viewHolder.itemView.setText(String.format("%s %d", item.reason, item.long_val));
        }

        return convertView;
    }
}

Para aquellos que no están muy familiarizados con el marco de Android, esto se explica con más detalle aquí: https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView .

Nikola Smiljanić
fuente
2
Lo sentimos, MyClass item = items.get (position) resuelve el error, no corresponde las variables por mí.
Sumit M Asok
1
Eliminé el campo de elementos del código anterior, porque puede provocar errores fácilmente, por ejemplo, si alguien cambia la lista interna de elementos usando adapter.clear () o adapter.add (...)
prostynick
1
¿Estoy en lo correcto al suponer textViewResourceIdque no se usa arriba?
gerrytan
8
¿De dónde vino viewHolder?
Gerry
2
¿Qué es R.layout.listview_association? ¿Es ese tu diseño personalizado? ¿Puedo reemplazarlo con android.R.layout.simple_list_item_1?
Donato
76

Simplemente puede agregar un toString()método a MyClass, según http://developer.android.com/reference/android/widget/ArrayAdapter.html :

Sin embargo, se hace referencia a TextView, se rellenará con toString () de cada objeto en la matriz. Puede agregar listas o matrices de objetos personalizados. Anule el método toString () de sus objetos para determinar qué texto se mostrará para el elemento en la lista.

class MyClass {

 @Override
 public String toString() {
  return "Hello, world.";
 }
}

fuente
20
Aunque la solución más simple, esta (en mi humilde opinión) no es una muy buena idea. Tres razones: si alguna vez necesita localización, tendrá que refactorizar. Esto solo funciona si tiene 1 adaptador, si tiene 2 adaptadores diferentes MyClass, tendrá que refactorizar. Finalmente, generalmente es una mala idea vincular la lógica de presentación a sus modelos. Los modelos no deben saber cómo se presentan al usuario.
fernandohur
2
para abordar algunas de las preocupaciones de @fernandohur, simplemente puede crear una clase de vista dedicada para ArrayAdapter ... por lo que una clase de tipo de modelo MyClassrequeriría que agregue una clase dedicada llamada MyClassViewque envuelva el modelo subyacente (y proporcione la toString( )implementación
wal
Esta respuesta fue de 2011, y estoy de acuerdo con @fernandohur: hay mejores formas de manejar esto ahora.
Creo que esta es una buena respuesta porque, en muchas ocasiones, cada clase tiene un atributo que representa el nombre del objeto.
Jhon Fredy Trujillo Ortega
Esta debería ser la respuesta aceptada. Anular un adaptador solo para enlazar el nombre impreso es una codificación incorrecta.
L.Grillo
22

Creo que este es el mejor enfoque. Usar la clase ArrayAdapter genérica y extender su propio adaptador de objetos es tan simple como sigue:

public abstract class GenericArrayAdapter<T> extends ArrayAdapter<T> {

  // Vars
  private LayoutInflater mInflater;

  public GenericArrayAdapter(Context context, ArrayList<T> objects) {
    super(context, 0, objects);
    init(context);
  }

  // Headers
  public abstract void drawText(TextView textView, T object);

  private void init(Context context) {
    this.mInflater = LayoutInflater.from(context);
  }

  @Override public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder vh;
    if (convertView == null) {
      convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
      vh = new ViewHolder(convertView);
      convertView.setTag(vh);
    } else {
      vh = (ViewHolder) convertView.getTag();
    }

    drawText(vh.textView, getItem(position));

    return convertView;
  }

  static class ViewHolder {

    TextView textView;

    private ViewHolder(View rootView) {
      textView = (TextView) rootView.findViewById(android.R.id.text1);
    }
  }
}

y aquí tu adaptador (ejemplo):

public class SizeArrayAdapter extends GenericArrayAdapter<Size> {

  public SizeArrayAdapter(Context context, ArrayList<Size> objects) {
    super(context, objects);
  }

  @Override public void drawText(TextView textView, Size object) {
    textView.setText(object.getName());
  }

}

y finalmente, cómo inicializarlo:

ArrayList<Size> sizes = getArguments().getParcelableArrayList(Constants.ARG_PRODUCT_SIZES);
SizeArrayAdapter sizeArrayAdapter = new SizeArrayAdapter(getActivity(), sizes);
listView.setAdapter(sizeArrayAdapter);

Creé un Gist con ArrayAdapter personalizable por gravedad de diseño TextView:

https://gist.github.com/m3n0R/8822803

cesards
fuente
1
Realmente me gustó mucho su código, pero me llevó un tiempo descubrir por qué se estaba bloqueando con android.content.res.Resources $ NotFoundException: ID de recurso # 0x0. Tiene en su adaptador genérico la llamada en super (.., 0, ..), donde 0 es en realidad un recurso de diseño, que no existe, por lo que debe cambiarse a otra cosa ... Además, su esencia es 404
Ev0oD
Al final terminé cambiando mucho: el diseño del menú desplegable era diferente al diseño del elemento mostrado, el texto de dibujo solo se llamaba en el encabezado de la ruleta, no en sus elementos al hacer clic ..
Ev0oD
Esto debería ser válido como adaptador genérico. ¿Podrías resolver el problema?
cesards
Sí, lo hice funcionar. Me di cuenta de que proporciona una solución para algo un poco diferente de lo que quería, pero pude usar su código como punto de partida y hacer modificaciones de una manera que hizo lo que quería para mí. Gracias por este fragmento de código.
Ev0oD
Esto se debe proporcionar por defecto.
Goddard
6

Subclase el ArrayAdapter y anule el método getView () para devolver su propia vista que contiene el contenido que desea mostrar.

Klarth
fuente
5

Aquí hay un ejemplo rápido y sucio de cómo usar un ArrayAdapter si no quiere molestarse en extender la clase madre:

class MyClass extends Activity {
    private ArrayAdapter<String> mAdapter = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mAdapter = new ArrayAdapter<String>(getApplicationContext(),
            android.R.layout.simple_dropdown_item_1line, android.R.id.text1);

        final ListView list = (ListView) findViewById(R.id.list);
        list.setAdapter(mAdapter);

        //Add Some Items in your list:
        for (int i = 1; i <= 10; i++) {
            mAdapter.add("Item " + i);
        }

        // And if you want selection feedback:
        list.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                //Do whatever you want with the selected item
                Log.d(TAG, mAdapter.getItem(position) + " has been selected!");
            }
        });
    }
}
Francois Dermu
fuente