2013-02-04 9 views
9

Sto cercando di ottenere gli ID e-mail dei contatti di utilizzo. Per quello sto usando Cursor Loader. C'è un problema che sto ottenendo anche ID e-mail duplicati. Come rimuovere la duplicazione della posta elettronica. Dovrei usare la query raw "SELECT DISTINCT" invece di usare CursorLoader o c'è qualche altra soluzione?Utilizzo di CursorLoader per ottenere e-mail causa la duplicazione delle e-mail

@Override 
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { 
    String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA}; 
    String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; 
    String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ; 

    //showing only visible contacts 
    String[] selectionArgs = null; 
    return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder); 
} 

risposta

5

Recentemente mi sono imbattuto in questo problema. Sembra che CursorLoader non abbia un'implementazione di "DISTINCT". La mia soluzione aggiunge poche righe al metodo onLoadFinish e si estende il BaseAdapter di accettare un parametro di listino:

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    String projection[] = { 
      CommonDataKinds.Phone._ID, 
      CommonDataKinds.Phone.DISPLAY_NAME, 
    };  
    String select = "((" + CommonDataKinds.Phone.DISPLAY_NAME + " NOTNULL) and " + CommonDataKinds.Phone.HAS_PHONE_NUMBER + " > 0)"; 
    String sort = CommonDataKinds.Phone.DISPLAY_NAME + " ASC"; 

    CursorLoader loader = new CursorLoader(
      mContext, 
      CommonDataKinds.Phone.CONTENT_URI, 
      projection, 
      select, 
      null, 
      sort 
      ); 

    return loader; 
} 

@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 
    List<String> displayNames = new ArrayList<String>(); 
    cursor.moveToFirst(); 

    while(!cursor.isAfterLast()){ 
     String name = cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME)); 

     if(!displayNames.contains(name)) 
      displayNames.add(name); 

     cursor.moveToNext(); 
    } 

    mAdapter.swapCursor(displayNames); 
} 

Ecco la mia classe BaseAdapter:

public class AdapterAddContacts extends BaseAdapter{ 
private List<String> mData = new ArrayList<String>(); 
private Context mContext; 

public AdapterAddContacts(Context context,List<String> displayNames){ 
    mData = displayNames; 
    mContext = context; 
} 

@Override 
public int getCount() { 
    if(mData != null) 
     return mData.size(); 
    else 
     return 0; 
} 

@Override 
public Object getItem(int pos) { 
    return mData.get(pos); 
} 

@Override 
public long getItemId(int id) { 
    return id; 
} 

@Override 
public View getView(int pos, View convertView, ViewGroup parent) { 
    LayoutInflater inflater = LayoutInflater.from(mContext); 
    View view = inflater.inflate(R.layout.entry_add_contacts,parent,false); 

    String data = mData.get(pos);       

    TextView textName = (TextView)view.findViewById(R.id.my_contacts_add_display_name); 
    textName.setText(data); 
    textName.setTag(data);   

    return view; 
} 

public void swapCursor(List<String> displayNames){ 
    mData = displayNames; 
    this.notifyDataSetChanged(); 
} 

si dovrebbe essere in grado di modificare questo specificamente per la vostra esigenze.

+0

Grazie Marte, avevo usato questo tipo di approccio. Stavo cercando qualche altra soluzione in cui non avrei dovuto usare la memoria. –

+0

Ho fatto qualcosa di simile ma ho usato il cursore al posto di Elenco. – zeeshan

0

È possibile inserire setDistinct nel proprio fornitore di contenuti.

@Override 
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
    ... 
    final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 
    qb.setDistinct(true); 
+0

La domanda riguarda CursorLoader, non la query SQL – Build3r

3

Ispirato da @mars, ho una soluzione che non richiede una modifica dell'adattatore. L'idea è di eliminare i duplicati del cursore; poiché non c'è modo di farlo, creiamo un nuovo cursore senza i duplicati.

Tutto il codice è in onLoadFinished:

@Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { 

     MatrixCursor newCursor = new MatrixCursor(PROJECTION); // Same projection used in loader 
     if (cursor.moveToFirst()) { 
      String lastName = ""; 
      do { 
       if (cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).compareToIgnoreCase(lastName) != 0) { 

        newCursor.addRow(new Object[]{cursor.getString(0), cursor.getString(1), cursor.getString(2) ...}); // match the original cursor fields 
        lastName =cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); 
       } 
      } while (cursor.moveToNext()); 
     } 
     mContactsAdapter.swapCursor(newCursor); 
    } 
+0

Se l'articolo non contiene un DISPLAY_NAME compareToIgnore avrà errore quindi sarebbe opportuno specificare nella clausola WHERE del cursore che non si accetta alcun valore Nome che è null. – NVA

0

Se siete preoccupati per le prestazioni e non si vuole giocare con il cursore di nuovo in onLoadFinished(), poi c'è un piccolo trucco

Ho combinato le seguenti due soluzioni di SO.

  1. select distinct value in android sqlite

  2. CursorLoader with rawQuery

Ed ecco la mia soluzione di lavoro:

@Override 
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
    String tableName; 

    /* 
    * Choose the table to query and a sort order based on the code returned 
    * for the incoming URI. 
    */ 
    switch (uriMatcher.match(uri)) { 
     case NOTIFICATION: 
      tableName = NOTIFICATIONS_TABLE_NAME; 

      break; 

     case NOTIFICATION_TIMESTAMP: 

      Cursor cursor = db.query(true, NOTIFICATIONS_TABLE_NAME, projection, selection, selectionArgs, TIMESTAMP, null, sortOrder, null); 
      cursor.setNotificationUri(getContext().getContentResolver(), uri); 

      return cursor; 

     case DOWNLOAD: 
      tableName = DOWNLOADS_TABLE; 
      break; 
     default: 
      throw new IllegalArgumentException("Unknown URI " + uri); 
    } 


    if (selection != null) { 
     selection = selection + "=?"; 
    } 

    Cursor cursor = db.query(tableName, projection, selection, selectionArgs, null, null, sortOrder); 


    // Tell the cursor what uri to watch, so it knows when its source data 
    // changes 
    cursor.setNotificationUri(getContext().getContentResolver(), uri); 
    return cursor; 
} 

Se si vede in questo nome caso della tabella è lo stesso è il primo 2 casi, ma Ho creato un Uri fittizio per raggiungere questo obiettivo. Potrebbe non essere un ottimo approccio ma funziona perfettamente.

1

ho usato un piccolo trucco nel mio progetto - un'iniezione SQL, come quella:

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    return new CursorLoader(
      this, 
      MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
      new String[] { 
       "DISTINCT "+ MediaStore.Images.Media.BUCKET_ID, 
       MediaStore.Images.Media.BUCKET_DISPLAY_NAME}, 
      null, null, null); 
} 

Questo codice restituisce solo i nomi di bundle ei relativi ID dalla Galleria. Così, mi piacerebbe riscrivere il codice così:

@Override 
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { 
    String[] projection = new String[] { 
     "DISTINCT " + ContactsContract.Contacts._ID, 
     ContactsContract.Contacts.DISPLAY_NAME, 
     ContactsContract.CommonDataKinds.Email.DATA}; 
    String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; 
    String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ; 

    //showing only visible contacts 
    String[] selectionArgs = null; 
    return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder); 
} 
0

ho trovato una soluzione Usa DISTINCT parola chiave nella scelta Array.

String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, "DISTINCT" + ContactsContract.CommonDataKinds.Email.DATA};