2011-03-03 9 views
16

Creé un SimpleCursorAdapter personalizado a partir de uno de the only example I found.Android: Problema con newView y bindView en CustomCursorAdapter personalizado

Cuando se llama a mi ListActivity, se invoca newView y bindView para cada una de las entradas de mi base de datos, y se vuelven a llamar para cada entrada. Tengo algunas preguntas:

-es el ejemplo correcto (si no, ¿dónde puedo encontrar uno)?

-si la llamada bindView siempre va precedida de la llamada a newView, ¿por qué hacer lo mismo en ambas funciones?

-por qué es la secuencia newView-bindView llamada 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é hay de malo en mi código?

Gracias


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

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

} 

Respuesta

21

Anular la función getView() le da la posibilidad de "reutilizar" elementos de lista ya inflados (los elementos de lista que se "desplazan" desde el puerto de vista actual cuando desplaza su lista hacia adelante y hacia atrás).

Al hacerlo, ahorrará una gran cantidad de recursos de memoria y tiempo de ejecución del procesador, ya que inflar es una operación bastante lenta. Para cada convertView que reutilice, también ahorra el tiempo de ejecución del GC (ya que el recolector de basura no tiene que recopilar ese elemento específico de la lista).

También puede crear una clase "ver colección" (la clase ViewHolder en el ejemplo siguiente) que contendrá las referencias para cada vista en su elemento de lista inflado. De esta forma, no tiene que encontrarlos todas y cada una de las veces que actualice un elemento de la lista con nuevos valores (normalmente cuando recorre la lista). findViewById() es también una operación bastante lenta.

También creo que puede almacenar en caché 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; 
} 
+0

Esto es muy útil. Gracias por tu tiempo. Ahora estoy investigando por qué se llama a getVBiew dos veces para cada elemento en mi base de datos. – jul

+2

Excelente respuesta, gracias. Pero, ¿qué deberíamos hacer si queremos agregar un OnClick Listener al ImageView? Agregué un método onClick (Ver v) en mi adaptador personalizado, pero no funcionó. ¿Algún consejo? – Antonis

+0

No estoy seguro de cómo agregar un oyente de clic a una vista específica en el elemento de la lista. Ni siquiera estoy seguro de si es una buena idea. Supongo que de alguna manera necesitaría "desactivar" el oyente de clic de la lista, o al menos consumir el evento onClick antes de que llegue a la lista, y crear una instancia de su propio onClickListener (crearlo en la Actividad) y pasarlo a su adaptador de lista personalizado (tal vez en el constructor), que a su vez lo asignaría a todas sus imágenes. Sin embargo, no he hecho esa maniobra antes, y no tengo idea de cuáles son las advertencias malvadas que acechan en la oscuridad :-) – dbm

13

El ejemplo es casi correcto. No necesita hacer el enlace en newView(), ya que, como mencionó, se llamará al bindView(). Si ve la secuencia newView/bindView llamada dos veces por artículo, probablemente esté usando ListView con su altura establecida en wrap_content, lo cual siempre es una mala idea. Finalmente, newView() y bindView() son específicos de CursorAdapter: implementa getView() y llama a newView() o para usted. Sin embargo, anular getView() es perfectamente válido también. Así es como otros adaptadores funcionan.

Tenga en cuenta que getView() (y por lo tanto bindView/newView) solo se invocan para cada elemento que se mostrará en la pantalla.

+1

Muchas gracias por su aclaración. La altura de ListView no está configurada para el pensamiento wrap_content. – jul

+0

Acabo de encontrar el video de su charla "Haga que su interfaz de usuario de Android sea rápida y eficiente". ¡Desearía haberlo encontrado antes! – jul

+0

Entonces, mientras uso newView BindView, ¿todavía tengo que buscar getView? Además, estoy usando el patrón Etiqueta en lugar del patrón del titular. Aún obtengo valores duplicados. – Skynet

Cuestiones relacionadas