What makes this interesting is that, by default, the RatingBar has absolutely no idea what model in the ArrayAdapter it is looking at. After all, the RatingBar is just a widget, used in a row of a ListView. We need to teach the rows which model they are currently displaying, so when their checkbox is checked they know which model’s state to modify.
So, let’s see how this is done, using the activity in the FancyLists/RateList sample project at http://apress.com/. We’ll use the same basic classes as our previous demo—we’re showing a list of nonsense words, which you can then rate. In addition, words given a top rating will appear in all caps.
public class RateListDemo extends ListActivity {
TextView selection;
String[] items={"lorem", "ipsum", "dolor", "sit", "amet",
"consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae", "arcu", "aliquet", "mollis",
"etiam", "vel", "erat", "placerat", "ante",
"porttitor", "sodales", "pellentesque", "augue",
"purus"};
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
ArrayList<RowModel> list = new ArrayList<RowModel>();
for (String s : items) {
list.add(new RowModel(s));
}
setListAdapter(new CheckAdapter(this, list));
selection = (TextView)findViewById(R.id.selection);
}
private RowModel getModel(int position) {
return(((CheckAdapter)getListAdapter()).getItem(position));
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(getModel(position).toString());
}
class CheckAdapter extends ArrayAdapter<RowModel> {
Activity context;
CheckAdapter(Activity context, ArrayList<RowModel> list) {
super(context, R.layout.row, list);
this.context = context;
}
public View getView(int position, View convertView,
ViewGroup parent) {
View row = convertView;
ViewWrapper wrapper;
RatingBar rate;
if (row==null) {
LayoutInflater inflater = context.getLayoutInflater();
row = inflater.inflate(R.layout.row, null);
wrapper = new ViewWrapper(row);
row.setTag(wrapper);
rate = wrapper.getRatingBar();
RatingBar.OnRatingBarChangeListener l =
new RatingBar.OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar,
float rating, boolean fromTouch) {
Integer myPosition = (Integer)ratingBar.getTag();
RowModel model = getModel(myPosition);
model.rating = rating;
LinearLayout parent = (LinearLayout)ratingBar.getParent();
TextView label = (TextView)parent.findViewById(R.id.label);
label.setText(model.toString());
}
};
rate.setOnRatingBarChangeListener(l);
} else {
wrapper = (ViewWrapper)row.getTag();
rate = wrapper.getRatingBar();
}
RowModel model = getModel(position);
wrapper.getLabel().setText(model.toString());
rate.setTag(new Integer(position));
rate.setRating(model.rating);
return(row);
}
}
class RowModel {
String label;
float rating = 2.0f;
RowModel(String label) {
this.label = label;
}
public String toString() {
if (rating>=3.0) {
return (label.toUpperCase());
}
return(label);
}
}
}
Here is what is different between our earlier code and this activity and getView() implementation:
• While we are still using String[] items as the list of nonsense words, but rather than pouring that String array straight into an ArrayAdapter, we turn it into a list of RowModel objects. RowModel is this demo’s poor excuse for a mutable model; it holds the nonsense word plus the current checked state. In a real system, these might be objects populated from a Cursor, and the properties would have more business meaning.
• Utility methods like onListItemClick() had to be updated to reflect the change from a pure String model to use a RowModel.
• The ArrayAdapter subclass (CheckAdapter) in getView() looks to see if convertView is null. If so, we create a new row by inflating a simple layout (see the following code) and also attach a ViewWrapper (also in the following code). For the row’s RatingBar, we add an anonymous onRatingChanged() listener that looks at the row’s tag (getTag()) and converts that into an Integer, representing the position within the ArrayAdapter that this row is displaying. Using that, the checkbox can get the actual RowModel for the row and update the model based upon the new state of the rating bar. It also updates the text adjacent to the RatingBar when checked to match the rating-bar state.