El uso de CursorLoader para obtener correos electrónicos provoca la duplicación de correos electrónicos
Estoy tratando de obtener ids de correo electrónico de contactos utiliza. Para eso estoy utilizando Cursor Loader. Hay un problema que estoy recibiendo identificadores de correo electrónico duplicados también. Cómo eliminar duplicidad de correo electrónico. ¿Debo utilizar la consulta en bruto "SELECT DISTINCT" en lugar de utilizar CursorLoader o hay alguna otra solución?
@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); }
- SherlockFragmentActivity con múltiples ListFragments y batallas con cursor SQLite
- Android 3.0 - ¿Cuáles son las ventajas de usar instancias de LoaderManager exactamente?
- cursor loader no se actualiza en mi pestaña de desplazamiento hasta que reinicie la aplicación
- Rellenar widgets desde un cursor en onLoadFinished ()
- Android eclipse startManagingCursor Deprecated pero quiere soportar versiones anteriores de la API?
- ¿Cuál es la ventaja de los cargadores sobre Asynctask en Android?
- Filtro ListView con CursorLoader Y FilterQueryProvider?
- NullPointerException en onLoaderFinished usando SimpleCursorAdapter
- Confundido sobre CursorLoaders y usarlos con un SQLiteOpenHelper personalizado
- Error en LoaderManager al utilizar la biblioteca de soporte
- Android Historial de navegación dejando fuera sólo algunos sitios
- Filtro ListView con CursorLoader y CursorAdapter personalizado
- Datos fuera de sincronización entre un CursorLoader personalizado y un CursorAdapter respaldando un ListView
Recientemente me encontré con este problema. Parece que el CursorLoader no tiene una implementación de "DISTINCT". Mi solución añade algunas líneas al método onLoadFinish y extiende el BaseAdapter para aceptar un parámetro List:
@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); }
Aquí está mi clase 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(); }
Usted debe ser capaz de modificar esto específicamente para sus necesidades.
Inspirado por @mars, tengo una solución que no necesita una modificación del adaptador. La idea es eliminar los duplicados del cursor; Como no hay manera de hacerlo, creamos un cursor nuevo sin los duplicados.
Todo el código está en 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); }
He utilizado un pequeño hack en mi proyecto – una inyección de SQL, así:
@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); }
Este código devuelve sólo nombres de bundle y sus ID de Galería. Así que, reescribiría tu código así:
@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); }
Puede poner setDistinct en su proveedor de contenido.
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setDistinct(true);
Si estás preocupado por el rendimiento y no quieres jugar con el cursor de nuevo en onLoadFinished (), entonces hay un pequeño hack
Me combiné siguiendo dos soluciones de SO.
-
Seleccionar un valor distinto en android sqlite
-
CursorLoader con rawQuery
Y aquí está mi solución de trabajo:
@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; }
Si ves en este caso el nombre de la tabla es el mismo es primeros 2 casos, pero he creado un maniquí Uri para lograr esto. No puede ser un enfoque muy bueno, pero funciona perfectamente.
Encontré una solución Utilizar la palabra clave DISTINCT
en la matriz de selección.
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, "DISTINCT" + ContactsContract.CommonDataKinds.Email.DATA};
- Pasa la referencia de objeto dentro de Intent sin implementar Serializable o Parcelable
- Cargar una imagen de Android a Amazon S3?