2013-02-04 3 views
9

Ich versuche, E-Mail-IDs von verwendet Kontakte zu erhalten. Dafür benutze ich den Cursor Loader. Es gibt ein Problem Ich bekomme auch doppelte E-Mail-IDs. So entfernen Sie die E-Mail-Duplizierung Sollte ich die rohe Abfrage "SELECT DISTINCT" anstelle von CursorLoader verwenden oder gibt es eine andere Lösung?Die Verwendung von CursorLoader, um E-Mails zu erhalten, führt zur Duplizierung von E-Mails

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

Antwort

5

Ich stieß vor kurzem auf dieses Problem. Es scheint, dass der CursorLoader keine Implementierung von "DISTINCT" hat. Meine Abhilfe fügt ein paar Zeilen in die onLoadFinish Verfahren und erweitert die BaseAdapter eine Liste Parameter zu akzeptieren:

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

Hier ist meine BaseAdapter Klasse:

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

Sie sollten diese in der Lage sein, gezielt zu modifizieren für Ihre braucht.

+0

Dank mars, ich hatte diese Art von Ansatz verwendet. War auf der Suche nach einer anderen Lösung, in der ich keine Erinnerung verwenden müsste. –

+0

Ich habe etwas ähnliches getan, aber Cursor statt List verwendet. – zeeshan

0

Sie können setDistinct in Ihrem Content-Provider setzen.

+0

Die Frage bezieht sich auf CursorLoader, nicht SQL-Abfrage – Build3r

3

Inspiriert von @ Mars, habe ich eine Lösung, die keine Änderung des Adapters benötigt. Die Idee ist, die Duplikate des Cursors zu löschen; Da es keine Möglichkeit gibt, dies zu tun, erstellen wir einen neuen Cursor ohne die Duplikate.

der gesamte Code ist 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

Wenn Element keine DISPLAY_NAME enthält compareToIgnore wird einen Fehler haben, daher wäre es gut, in der WHERE-Klausel des CursorsLoaders anzugeben, dass Sie keinen Name-Wert akzeptieren, der null ist. – NVA

0

Wenn Sie über die Leistung besorgt sind und wollen nicht wieder mit Cursor spielen, um in onLoadFinished(), dann gibt es einen kleinen Hack

Ich habe folgende zwei Lösungen von SO kombiniert.

  1. select distinct value in android sqlite

  2. CursorLoader with rawQuery

Und hier ist meine Arbeitslösung:

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

Wenn Sie in diesem Fall Tabellenname sehen ist Elbe ersten 2 Fälle aber Ich habe einen Dummy Uri geschaffen, um dies zu erreichen. Ist vielleicht kein sehr guter Ansatz, funktioniert aber perfekt.

1

benutzte ich einen kleinen Hack in meinem Projekt - eine SQL-Injektion, wie folgt aus:

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

Dieser Code nur Bündelnamen und ihre IDs aus der Galerie zurück. Also, würde ich Ihren Code so umschreiben:

@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

Ich fand eine Lösung Verwendung DISTINCT Stichwort in Auswahl Array.

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