Android 使用CursorLoader获取电子邮件会导致电子邮件重复

Android 使用CursorLoader获取电子邮件会导致电子邮件重复,android,android-contacts,android-cursorloader,Android,Android Contacts,Android Cursorloader,我正在尝试获取联系人的电子邮件ID。为此,我使用游标加载器。有一个问题,我得到重复的电子邮件ID也。如何消除电子邮件的重复性。我应该使用原始查询“SELECTDISTINCT”而不是使用CursorLoader还是有其他解决方案 @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { String[] projection = new String[] { ContactsContract

我正在尝试获取联系人的电子邮件ID。为此,我使用游标加载器。有一个问题,我得到重复的电子邮件ID也。如何消除电子邮件的重复性。我应该使用原始查询“SELECTDISTINCT”而不是使用CursorLoader还是有其他解决方案

@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);
}
@覆盖
公共加载器onCreateLoader(int arg0,Bundle arg1){
字符串[]投影=新字符串[]{ContactsContract.Contacts.\u ID,ContactsContract.Contacts.DISPLAY\u NAME,ContactsContract.CommonDataTypes.Email.DATA};
字符串排序器=Contacts contract.Contacts.DISPLAY_NAME+“整理本地化ASC”;
字符串选择=Contacts contract.Contacts.IN_VISIBLE_GROUP+“='1'和“+Email.DATA+”不为空且“+Email.DATA+”!=\”;
//只显示可见的联系人
字符串[]selectionArgs=null;
返回新的游标装入器(此,ContactsContract.CommonDataTypes.Email.CONTENT\u URI,投影,选择,SelectionAgs,sortOrder);
}

我最近遇到了这个问题。游标加载程序似乎没有“DISTINCT”的实现。我的解决方法是向onLoadFinish方法添加几行代码,并扩展BaseAdapter以接受列表参数:

@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);
}
@覆盖
公共加载器onCreateLoader(int-id,Bundle-args){
字符串投影[]={
CommonDataTypes.Phone.\u ID,
CommonDataTypes.Phone.DISPLAY\u名称,
};      
String select=“((“+CommonDataTypes.Phone.DISPLAY_NAME+”NOTNULL)和“+CommonDataTypes.Phone.HAS_Phone_NUMBER+”>0)”;
字符串排序=CommonDataTypes.Phone.DISPLAY_NAME+“ASC”;
游标装入器装入器=新游标装入器(
McContext,
CommonDataTypes.Phone.CONTENT\u URI,
投影,
选择,
无效的
分类
);  
返回装载机;
}
@凌驾
public void onLoadFinished(加载器,光标){
List displayNames=new ArrayList();
cursor.moveToFirst();
而(!cursor.isAfterLast()){
字符串名称=cursor.getString(cursor.getColumnIndex(commonDataTypes.Phone.DISPLAY_name));
如果(!displayNames.contains(name))
displayNames.add(名称);
cursor.moveToNext();
}
mAdapter.swapCursor(显示名称);
}
下面是我的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();
}
公共类适配器AddContacts扩展BaseAdapter{
private List mData=new ArrayList();
私有上下文;
公共适配器地址联系人(上下文、列表显示名称){
mData=显示名称;
mContext=上下文;
}   
@凌驾
public int getCount(){
如果(mData!=null)
返回mData.size();
其他的
返回0;
}
@凌驾
公共对象getItem(int-pos){
返回mData.get(pos);
}
@凌驾
公共长getItemId(int id){
返回id;
}
@凌驾
公共视图getView(int pos、视图转换视图、视图组父视图){
LayoutFlater充气机=LayoutFlater.from(mContext);
视图=充气机。充气(右布局。输入\添加\联系人,父项,错误);
字符串数据=mData.get(pos);
TextView textName=(TextView)view.findViewById(R.id.my\u contacts\u add\u display\u name);
textName.setText(数据);
textName.setTag(数据);
返回视图;
}   
公共无效交换(列表显示名称){
mData=显示名称;
this.notifyDataSetChanged();
}
您应该能够根据自己的需要对其进行专门的修改。

您可以在内容提供商中输入内容

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

受@mars的启发,我有了一个不需要修改适配器的解决方案。想法是删除光标的重复项;因为没有办法,我们创建了一个没有重复项的新光标

所有代码都在
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);
    }
@覆盖
public void onLoadFinished(加载器,光标){
MatrixCursor newCursor=新MatrixCursor(投影);//与loader中使用的投影相同
if(cursor.moveToFirst()){
字符串lastName=“”;
做{
if(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataTypes.Phone.DISPLAY_NAME)).CompareTIgnoreCase(lastName)!=0){
newCursor.addRow(新对象[]{cursor.getString(0)、cursor.getString(1)、cursor.getString(2)…});//匹配原始的游标字段
lastName=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataTypes.Phone.DISPLAY_NAME));
}
}while(cursor.moveToNext());
}
mContactsAdapter.swapCursor(newCursor);
}

如果您担心性能,不想在onLoadFinished()中再次使用游标,那么有一个小问题

我结合了来自SO的以下两种解决方案

  • 以下是我的工作解决方案:

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

    如果您在本例中看到,表名相同是前两种情况,但我创建了一个虚拟Uri来实现这一点。这可能不是一个很好的方法,但效果很好。

    我在我的项目中使用了一个小技巧-SQL注入,如下所示:

    @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);
    }
    
    @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);
    }
    
    @覆盖
    公共加载器onCreateLoader(int-id,Bundle-args){
    返回新游标装入器(
    这
    MediaStore.Images.Media.EXTERNAL\u CONTENT\u URI,
    新字符串[]{
    “独特”+MediaStore.Images.Media.BUCKET_ID,
    MediaStore.Images.Media.BUCKET\u DISPLAY\u NAME},
    空,空,空);
    }
    
    此代码仅从Gallery返回捆绑包名称及其ID。 所以,我会像这样重写你的代码:

    @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);
    }
    
    @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);
    }
    
    @覆盖
    公共加载器onCreateLoader(int arg0,Bundle arg1){
    字符串[]投影=新字符串[]{
    “不同”+联系人合同联系人。\u ID,
    Contacts contract.Contacts.DISPLAY\u NAME,
    ContactsContract.CommonDataTypes.Email.DATA};
    字符串排序器=Contacts contract.Contacts.DISPLAY_NAME+“整理本地化ASC”;
    字符串选择=Contacts contract.Contacts.IN_VI