2010-08-18 9 views
5

Acabo de encontrar la siguiente situación. Tengo una aplicación para Android con un escenario que supongo que puede suceder en varias aplicaciones. Se trata de etiquetar/etiquetar/categorizar, llámalo como quieras. Básicamente tengo las siguientes relaciones en la DB SQLitePropuestas de optimización para ListView con datos de varias "tablas"

--------     --------------    --------- 
| Tags |    | DeviceTags |   | Devices | 
|--------|    |--------------|   |---------| 
| ID  | 1 ------ * | ID   | * ------ 1 | ID  | 
| NAME |    | TAGS_ID  |   | NAME | 
--------    | DEVICE_ID |   | ...  | 
          --------------    --------- 

Todo está expuesto en un ContentProvider que he escrito. Hasta ahora todo está bien.

En la parte de IU, tengo una actividad de lista que muestra todos los dispositivos almacenados (de la tabla Dispositivos) y para personalizar un poco más la IU, creé elementos de fila personalizados mostrando una imagen pequeña en el frente según el tipo de dispositivo etc.

Lo que me gustaría lograr ahora es mostrar también las etiquetas asociadas en esa lista para cada dispositivo. Ahora aquí viene mi problema. Para la lista de dispositivos simples creé una ResourceCursorAdapter personalizada donde hice la información de acuerdo en el método Bindview

@Override 
public void bindView(final View view, final Context context, final Cursor cursor) { 
    final int objectId = cursor.getInt(cursor.getColumnIndex(Devices._ID)); 

    TextView deviceName = (TextView) view.findViewById(R.id.deviceName); 
    deviceName.setText(...); //set it from the cursor 
    ... 
    TextView associatedTagsView = (TextView)...; 
    associatedTagsView.setText(...); //<<<???? This would need a call to a different table 
    ... 
} 

Como se puede ver, por ser capaz de saber qué tipo de etiquetas de mi dispositivo ha asociado, necesitaría para consultar DeviceTags. Así que lo hice:

@Override 
public void bindView(final View view, final Context context, final Cursor cursor) { 
    ... 
    TextView associatedTagsView = (TextView)view.findViewById(R.id.deviceTags); 
    String tagsString = retrieveTagsString(view.getContext().getContentResolver(), objectId); 
    ... 
} 

private String retrieveTagsString(ContentResolver contentResolver, int objectId) { 
    Cursor tagForDeviceCursor = contentResolver.query(DroidSenseProviderMetaData.TABLE_JOINS.TAG_DEVICETAG,...); 
    if(tagForDeviceCursor != null && tagForDeviceCursor.moveToFirst()){ 
     StringBuffer result = new StringBuffer(); 

     boolean isFirst = true; 
     do{ 
      if(!isFirst) 
       result.append(", "); 
      else 
       isFirst = false; 

      result.append(retrieve name from cursor column...); 
     }while(tagForDeviceCursor.moveToNext()); 

     return result.toString(); 
    }  
    return null; 
} 

He probado esto y que en realidad funciona bien, pero para ser honesto, no me siento bien haciendo esto. De alguna manera me parece extraño ...

¿Hay alguna solución mejor sobre cómo solucionar esto?

// Edición:

Tras los comentarios de CommonsWare aquí un poco de una aclaración. Soy extraño acerca de hacer la segunda consulta al DB dentro del CursorAdapter, básicamente esto daría lugar a una consulta por fila y me temo que esto tendrá un gran impacto en mi rendimiento (aún tengo que probarlo en un dispositivo real con una cantidad sustancial de datos para ver cuánto afecta esto).

Mi pregunta es, por tanto, de si hay algunas estrategias sobre cómo evitar esto dado mi modelo de datos o si tengo que básicamente "en vivo" con eso :)

Respuesta

0

Probé mi solución (propuesta en mi pregunta) con aproximadamente 100 entradas. Todavía era útil, pero el desplazamiento no era tan sencillo como cabría esperar. Como CommonsWare ya señaló, lo mejor es adaptar el modelo de datos o, al menos, agregarlo apropiadamente. al final, el cursor contiene todos los datos necesarios para mostrarse en la IU.

No quería cambiar por completo el modelo de datos subyacente. Lo que hice fue agregar los datos de una manera, s.t. el cursor devuelto por el ContentProvider contiene los datos de los dispositivos, incluyendo una cadena ya preparado que representa nombres las etiquetas asociadas:

| ID | NAME    | ... | LABELSSTRING | 
------------------------------------------------ 
| 1 | "My Device name" | ... | "Work, Friend" | 
| 1 | "Some other"  | ... |    | 
| 1 | "Yet another" | ... | "Work"   | 

Esto más o menos resuelve el problema, porque no hay necesidad de volver a consultar en Bindview del CursorAdapter (..) para cada dispositivo, que por cierto. es bastante malo Esta solución fue posible mediante el uso de group_concat(X, Y) function de SQLite.

3

usted se quejó en Twitter que no estabas' t recibiendo cualquier comentario. No estoy terriblemente sorprendido, porque mientras que su pregunta tiene un nivel ejemplar de detalles, adolece de dos defectos principales:

  1. su pregunta, al final, es vaga
  2. Su pregunta se basa en su propia interpretación de lo que "esto" es, a lo que no tenemos acceso privado

Por lo tanto, no tengo una imagen clara de lo que usted piensa que es "raro". do...while() loops? :-)

Voy a salir en una extremidad y supongo que su preocupación se trata de hacer una segunda consulta y una ronda de concatenación de cadenas para cada fila en su ListView. Todo depende de si usted piensa que "una lista de etiquetas delimitada por comas" es parte del modelo de datos o parte de la presentación. Si es parte de la presentación, no veo cómo puedes evitar lo que estás haciendo aquí.

Si realmente cree que es parte de su modelo de datos, ajuste su ContentProvider para suministrar la lista de etiquetas delimitada por comas como parte de lo que sea que esté consultando en primer lugar (no se muestra).

En cualquier caso, el rendimiento es un problema probable. Hacer una consulta por fila, particularmente en el momento del desplazamiento, es probable que sea mala. Después de todo, Romain Guy enfatiza en repetidas ocasiones que necesita reciclar filas en su adaptador, y tendría que imaginar que una consulta de base de datos es un poquito más cara que inflar un diseño. Lamentablemente, fue con un modelo de datos que enfatizó el espacio a lo largo del tiempo, por lo que tendrá que pagarle al gaitero en algún lugar de la línea.

+0

Lo siento por mi pregunta algo vaga. No lo noté y lo arreglé ahora. Pero coincidió perfectamente con mis problemas (+1 por eso) :) El rendimiento es exactamente lo que necesita. Pero dado el requisito de que 1 dispositivo tenga múltiples etiquetas, tendré que ir con el diseño de DB que se muestra arriba ... Por supuesto, lo que podría hacer es "fusionar" DeviceTags y Tags de alguna manera, pero supongo que esto sería bastante feo. ... Tal vez sería mejor unirme a todo en el nivel de elemento de ContentProvider Ya tengo los datos listos ... Lo intentaré si veo que la solución actual tiene un gran impacto en el rendimiento. – Juri

+0

También trataría de evitar la segunda consulta en el adaptador. Simplemente cree su proveedor de contenidos de forma que todos los datos necesarios en la lista estén presentes en el momento en que se muestran al usuario. – Janusz

+0

@Juri: "Pero dado el requisito de que 1 dispositivo tenga varias etiquetas, tendré que ir con el diseño de base de datos que se muestra arriba" - no, no es así. Solo necesita eso si también va a los datos del lado de la etiqueta, y con la frecuencia suficiente para que valga la pena. Su alternativa es desnormalizar los datos, teniendo la lista de etiquetas delimitada por comas como una sola columna en la tabla del dispositivo. – CommonsWare

Cuestiones relacionadas