131
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);

Class: MyClass

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

I have created row.xml in layouts, but don't know how to show both reason and long_val in the ListView using ArrayAdapter.

ahsteele
  • 26,243
  • 28
  • 134
  • 248
Sumit M Asok
  • 2,950
  • 7
  • 29
  • 38

5 Answers5

157

Implement custom adapter for your class:

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

For those not very familiar with the Android framework, this is explained in better detail here: https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView.

ivandov
  • 619
  • 8
  • 14
Nikola Smiljanić
  • 26,745
  • 6
  • 48
  • 60
  • BUt MyClass item = item.get(position); through error, that method get(int) is not in Class MyClass. – Sumit M Asok Feb 16 '10 at 02:30
  • 2
    Sorry, MyClass item = items.get(position) solves the error, mismatches variables by me. – Sumit M Asok Feb 16 '10 at 02:38
  • in getView the var position is always between 1-7 for me (dataset of 55 items) am I missing something? – Blundell May 15 '11 at 00:33
  • Did you find a solution? position of getView always is between 1 and 7 on my ArrayAdapter and thus I don't find a way to position within the ArrayList correctly ... – Harald Wilhelm Jun 24 '11 at 16:54
  • 1
    I've removed items field from above code, because it can easily lead to errors - for example if someone change internal list of items using adapter.clear() or adapter.add(...) – prostynick Jul 03 '13 at 08:31
  • `@Override public void onItemClick(AdapterView> parent, View view, int position, long id) { }` I was wondering how you retrieve the holder values when it's clicked? Thanks – Nizzy Nov 20 '13 at 06:40
  • 1
    Am I correct in assuming `textViewResourceId` isn't used above? – gerrytan Dec 09 '13 at 12:52
  • Why do you use (and return) a temporary variable `view` instead of the `convertView` itself? Does it make any practical difference, or is it just for clarity / convention etc.? – Konrad Morawski Jan 09 '14 at 22:49
  • 1
    @gerrytan Yes you are. – Nikola Smiljanić Jan 10 '14 at 03:27
  • @KonradMorawski There might have been a cast in the assignment in the previous version of the code. There shouldn't be any difference. – Nikola Smiljanić Jan 10 '14 at 03:30
  • @NikolaSmiljanić I followed your example, but `getView` is never called, although after changing the data, I already call `notifyDataSetChanged`, and that piece of code is running on uiThread already. Any idea? thanks. – EyeQ Tech May 16 '14 at 18:46
  • 9
    where did viewHolder come from? – Gerry Apr 19 '15 at 22:38
  • @Gerry It is declared in the beginning. It is a common pattern in Android adapters and is used to store pointers to the constituent parts of the recycled views. That way you gain performance by not having to call `findViewById` everytime you want to access the child views. – Gonzalo Jul 21 '15 at 02:05
  • 1
    Also consider overriding `getDropDownView` which calls a private method that is the equivalent of `getView` so that the `toString` methods picks up your own implementation. – Patrick.SE Oct 17 '15 at 00:23
  • Why would I need to pass the view id to the class constructor if I have to use it again when creating the view? – Daniel Möller Feb 06 '16 at 23:11
  • A great reference here is https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView – ivandov Oct 19 '16 at 22:49
  • 2
    What is R.layout.listview_association? Is that your custom layout? Can I replace it with android.R.layout.simple_list_item_1? – Daniel Viglione Apr 19 '17 at 23:11
80

You could just add a toString() method to MyClass, per http://developer.android.com/reference/android/widget/ArrayAdapter.html:

However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be displayed for the item in the list.

class MyClass {

 @Override
 public String toString() {
  return "Hello, world.";
 }
}
  • 20
    Although the simplest solution, this is (IMHO) not a very good idea. Three reasons: If you ever need localization, you will have to refactor. This only works if you have 1 adapter, if you have 2 different adapters for `MyClass`, you wil have to refactor. Finally, it's generally a bad idea to tie presentation logic to your models. Models should not be aware of how they are presented to the user. – fernandohur Jun 18 '14 at 23:19
  • 2
    to address some of @fernandohur concerns you can simply create a dedicated view class for the ArrayAdapter... so a model class of type `MyClass` would require you to add a dedicated class called `MyClassView` which wraps the underlying model (and provides the `toString( )` implementation – wal Nov 07 '16 at 01:57
  • This answer was from 2011, and I agree with @fernandohur -- there are better ways of handling this now. –  Aug 07 '17 at 23:07
  • i think this is a good answer because, in a lot of times each class have an atribute than represent the name of the object. – Jhon Fredy Trujillo Ortega Mar 06 '18 at 20:44
  • This should be the accepted answer. Overriding an adapter just for binding the printed name is bad coding. – L.Grillo Dec 04 '18 at 16:59
23

I think this is the best approach. Using generic ArrayAdapter class and extends your own Object adapter is as simple as follows:

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

and here your adapter (example):

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

}

and finally, how to initialize it:

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

I've created a Gist with TextView layout gravity customizable ArrayAdapter:

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

cesards
  • 15,882
  • 11
  • 70
  • 65
  • 1
    I really liked your code a LOT, but it took me a while to figure out, why it was crashing with android.content.res.Resources$NotFoundException: Resource ID #0x0. You have in your generic adapter call on super(..,0,..), where 0 is actually a layout resource, which does not exist, so it must be changed to something else.. Also, your gist is 404 – Ev0oD Oct 13 '15 at 19:37
  • In the end I ended up changing a lot - layout of the dropdown menu was different than the layout of shown item, the drawText was only called on the spinner header, not on its items on clicked.. – Ev0oD Oct 13 '15 at 20:22
  • This should be valid as generic adapter. Could you solve the problem? – cesards Oct 15 '15 at 08:22
  • Yeah, I made it work. I realized you provide a solution for something a little different then what I wanted, but I was able to use your code as a starting point and make amendments in a way that it did for me what I wanted. Thanks for this piece of code. – Ev0oD Oct 15 '15 at 08:24
  • This should be provided by default. – Goddard Jul 01 '19 at 15:26
6

Subclass the ArrayAdapter and override the method getView() to return your own view that contains the contents that you want to display.

Klarth
  • 2,035
  • 14
  • 26
5

Here's a quick and dirty example of how to use an ArrayAdapter if you don't want to bother yourself with extending the mother class:

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
  • 4,437
  • 2
  • 22
  • 14