Wow, I thought I wrote something about this some time ago but I guess I didn't. Ran into this issue again when writing my Diablo II Runewords app.
The ListView isn't exactly the most intuitive thing to pick up and use. It's some clever work, but the whole thing is also a mess of views, adapters and layouts. A by-product of over engineering, probably second worst case after Activities.
Over engineering - you only realise when it's too late.
Overview
A listview needs an "adapter" to hold and understand how to render the information.
Each item you see in a listview is actually another "view". This view is generated and populated by the adapter.
So, you use Listview.setAdapter() to specify which adapter you want to use.
ListView lv = (ListView) findViewById(R.id.lvRunewords);
lv.setAdapter(new RunewordListAdaptor(currentActivity, parser.runewords));
Item Layout
Instead of creating the layout by hand, we just use the layout editor. In this example, create a file called "runeword.xml" and position all the TextViews accordingly.
The Adapter
Ok, it's a lot to digest at the moment but I'll run through it.
public class RunewordListAdaptor extends ArrayAdapter<Runeword> {
public RunewordListAdaptor(Context context, List<Runeword> objects) {
// R.layout.runeword is the layout used for each item.
// R.id.tvRunewordName is the TextView used to display the item text
super(context, R.layout.runeword, R.id.tvRunewordName, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Runeword rw = this.getItem(position);
View v;
// Android reuses views where possible.
if (convertView != null) {
v = convertView;
}
else {
// Need to create a new for the new item.
LinearLayout layout = new LinearLayout(parent.getContext());
LayoutInflater layoutInflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.runeword, layout);
v = layout;
}
// Fill in data onto the layout
((TextView) v.findViewById(R.id.tvRunewordName)).setText(rw.name);
((TextView) v.findViewById(R.id.tvStones)).setText(Rune.listToString(rw.stones));
((TextView) v.findViewById(R.id.tvNotes)).setText(rw.notes);
((TextView) v.findViewById(R.id.tvSockets)).setText(String.valueOf(rw.sockets));
return v;
}
}
In the constructor I just called the base class ArrayAdapter constructor, giving it the name of the layout we want to use (runeword.xml) and the id of the target TextView containing the item label (R.id.tvRunewordName).
In getView(), sometimes convertView will be given to us. This is Android's way to saving us the computational time of recreating the layout for each item.
If it needs to be created, simply inflate the layout into a view we can use.
The rest of the code is just spent filling in data from the Runeword object.
Return the view when done.
It's clunky, seems inefficient but it works!
Built-in Adapters
There are a few built-in adapters you can use and in this instance I used the ArrayAdapter. To see what works best for you, see the ListAdapter documentation.