sobre - Android: Problema con newView y bindView en SimpleCursorAdapter personalizado
cursoradapter vfp (2)
Creé un SimpleCursorAdapter personalizado de uno de los únicos ejemplos que encontré .
Cuando se llama a mi ListActivity, se llama a newView y bindView para cada una de las entradas de mi base de datos, y se vuelve a llamar para cada entrada. Tengo algunas preguntas:
-es el ejemplo correcto (si no, ¿dónde puedo encontrar uno)?
-si la llamada bindView siempre está precedida por la llamada newView, ¿por qué hacer lo mismo en ambas funciones?
¿Por qué se llama a la secuencia newView-bindView dos veces para cada elemento?
¿Por qué algunos ejemplos de CursorAdapter usan getView en lugar de newView y bindView?
Básicamente, ¿cómo se debe usar SimpleCursorAdapter y qué tiene de malo mi código?
Gracias
ListaActividad
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();
}
}
Personalizado 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;
}
}
}
El ejemplo es casi correcto. No es necesario realizar el enlace en newView()
, ya que, como mencionó, se bindView()
. Si ve la secuencia que se llama a newView / bindView dos veces por elemento, probablemente esté usando ListView
con su altura establecida en wrap_content
, lo que siempre es una mala idea. Finalmente, newView()
y bindView()
son específicos de CursorAdapter
: implementa getView()
y llama a newView()
o bindView()
por ti. Sin embargo, anular getView()
es perfectamente válido. Así es como funcionan otros adaptadores.
Tenga en cuenta que getView()
(y, por lo tanto, bindView / newView) solo se invocan para cada elemento que se mostrará en la pantalla.
La getView()
función getView()
le brinda la posibilidad de "reutilizar" elementos de la lista ya inflados (los elementos de la lista que están "desplazados" desde el puerto de vista actual cuando se desplaza de una lista a otra).
Al hacerlo, ahorrará una gran cantidad de recursos de memoria y tiempo de ejecución del procesador, ya que inflar es una operación que consume bastante tiempo. Para todos y cada uno de los convertView
que reutiliza, también ahorra tiempo de ejecución de GC (ya que el recolector de basura no tiene que recopilar ese elemento de lista específico).
También puede crear una clase de "colección de vistas" (la clase ViewHolder
en el siguiente ejemplo) que contendrá las referencias de cada vista en su elemento de lista inflada. De esta manera, no tiene que encontrarlos cada vez que actualice un elemento de la lista con nuevos valores (normalmente cuando se desplaza por la lista). findViewById()
también es una operación que consume bastante tiempo.
También creo que se pueden almacenar más variables, como el inflador de diseño y los índices de columna. Todo para ahorrar tiempo :-)
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;
}