I created a custom SimpleCursorAdapter from one of the only example I found.
When my ListActivity is called, newView and bindView are called for every of my DB entry, and called again for every entry. I've got a few questions:
-is the example right (if not, where can I find one)?
-if bindView call is always preceded by newView call, why doing the same in both functions?
-why is the sequence newView-bindView called twice for every item?
-why some CursorAdapter examples use getView instead of newView and bindView?
Basically, how should SimpleCursorAdapter be used, and what's wrong with my code?
Thanks
ListActivity
public class ContactSelection extends ListActivity {
private WhipemDBAdapter mDbHelper;
private FriendAdapter friendAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDbHelper = new WhipemDBAdapter(this);
mDbHelper.open();
setContentView(R.layout.contact_list);
Cursor c = mDbHelper.fetchAllFriends();
startManagingCursor(c);
String[] from = new String[] {};
int[] to = new int[] {};
this.friendAdapter = new FriendAdapter(this, R.layout.contact_row, c, from, to);
setListAdapter(this.friendAdapter);
getListView().setItemsCanFocus(false);
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
@Override
protected void onResume() {
super.onResume();
mDbHelper.open();
}
@Override
protected void onPause() {
super.onPause();
mDbHelper.close();
}
}
Custom SimpleCursorAdapter
public class FriendAdapter extends SimpleCursorAdapter implements OnClickListener {
private Context mContext;
private int mLayout;
public FriendAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.mContext = context;
this.mLayout = layout;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(mLayout, parent, false);
String name = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_NAME));
String fb_id = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_FB_ID));
TextView name_text = (TextView) v.findViewById(R.id.contact_name);
if (name_text != null) {
name_text.setText(name);
}
ImageView im = (ImageView) v.findViewById(R.id.contact_pic);
Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
if (im != null) {
im.setImageDrawable(drawable);
}
CheckBox bCheck = (CheckBox) v.findViewById(R.id.checkbox);
if (im != null) {
bCheck.setTag(fb_id);
}
if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
bCheck.setChecked(true);
bCheck.setOnClickListener(this);
return v;
}
@Override
public void bindView(View v, Context context, Cursor c) {
String name = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_NAME));
String fb_id = c.getString(c.getColumnIndex(WhipemDBAdapter.KEY_FB_ID));
TextView name_text = (TextView) v.findViewById(R.id.contact_name);
if (name_text != null) {
name_text.setText(name);
}
ImageView im = (ImageView) v.findViewById(R.id.contact_pic);
Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
if (im != null) {
im.setImageDrawable(drawable);
}
CheckBox bCheck = (CheckBox) v.findViewById(R.id.checkbox);
if (im != null) {
bCheck.setTag(fb_id);
}
ArrayList<String> dude = ((GlobalVars) mContext.getApplicationContext()).getSelectedFriendList();
if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
bCheck.setChecked(true);
bCheck.setOnClickListener(this);
}
@Override
public void onClick(View v) {
CheckBox cBox = (CheckBox) v;
String fb_id = (String) cBox.getTag();
if (cBox.isChecked()) {
if (!((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
((GlobalVars) mContext.getApplicationContext()).addSelectedFriend(fb_id);
} else {
if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
((GlobalVars) mContext.getApplicationContext()).removeSelectedFriend(fb_id);
}
}
private Drawable LoadImageFromWebOperations(String url)
{
try
{
InputStream is = (InputStream) new URL(url).getContent();
Drawable d = Drawable.createFromStream(is, "src name");
return d;
}catch (Exception e) {
System.out.println("Exc="+e);
return null;
}
}
}
Overriding the getView()
function gives you the possibility of "re-using" already inflated list items (the list items that are "scrolled out" from the current view port when you scroll your list back and forth).
By doing so you'll save a lot of memory resources and processor run time, since inflating is a quite time consuming operation. For each and every convertView
you re-use you also save GC run-time (since the garbage collector doesn't have to collect that specific list item).
You can also create a "view collection" class (the ViewHolder
class in the below example) which will hold the references for each view in your inflated list item. This way you don't have to find them each and every time you update a list item with new values (typically whan you scroll the list). findViewById()
is also a rather time consuming operation.
Also I think you can cache more variables, like the layout inflater, and the column indices. Everything to save time :-)
private final Context mContext;
private final int mLayout;
private final Cursor mCursor;
private final int mNameIndex;
private final int mIdIndex;
private final LayoutInflater mLayoutInflater;
private final class ViewHolder {
public TextView name;
public ImageView image;
public CheckBox checkBox;
}
public FriendAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.mContext = context;
this.mLayout = layout;
this.mCursor = c;
this.mNameIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_NAME);
this.mIdIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_FB_ID);
this.mLayoutInflater = LayoutInflater.from(mContext);
}
public View getView(int position, View convertView, ViewGroup parent) {
if (mCursor.moveToPosition(position)) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(mLayout, null);
viewHolder = new ViewHolder();
viewHolder.name = (TextView) convertView.findViewById(R.id.contact_name);
viewHolder.image = (ImageView) convertView.findViewById(R.id.contact_pic);
viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
String name = mCursor.getString(mNameIndex);
String fb_id = mCursor.getString(mIdIndex);
Drawable drawable = LoadImageFromWebOperations("http://graph.facebook.com/"+fb_id+"/picture");
boolean isChecked = ((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id);
viewHolder.name.setText(name);
viewHolder.image.setImageDrawable(drawable);
viewHolder.checkBox.setTag(fb_id);
viewHolder.checkBox.setChecked(isChecked);
}
return convertView;
}
The example is almost right. You don't need to do the binding in newView()
, since like you mentioned, bindView()
will be called. If you see the sequence newView/bindView called twice per item, you are probably using ListView
with its height set to wrap_content
, which is always a bad idea. Finally, newView()
and bindView()
are specific to CursorAdapter
: it implements getView()
and calls either newView()
or bindView()
for you. However, overriding getView()
is perfectly valid as well. This is how other adapters work.
Note that getView()
(and therefore bindView/newView) are only invoked for each item that will be displayed on the screen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With