android通讯录开发

阅读更多
这周也空闲,所以继续花时间看下关于android内部应用的一些东西。比如通讯录,这个东西比自己想的还要麻烦些,通讯录可以和相关的账号关联起来。比如user1,对应有test1,test2,帐户user2对应有联系人test3,test4。因此先得添加帐户,然而模拟器测试的时候发现帐号还得联网绑定才行,结果试了下联不上,晕!
于是试着用程序的方式添加帐号,这个也不容易,在网上找了很久的资料,有本android应用开发揭密有段代码勉强达到效果了,但是有些原理还是不太明白。再结合了下sdk本身的例子contactmanager的代码,把帐号和联系人的功能合在了一起。因此这个功能主要有四个功能:
1.查询帐号
2.增加帐号
3.增加联系人
4.查询联系人

大的功能就是这四个,如果像我这等入行不久的人来看的话,值得细究的东西很多,一个一个的讲的话篇幅会很长,因此挑部分重点的讲下吧!
一、查询帐号部分:

/*查询某一种类型的帐号*/
AccountManager _am = AccountManager.get(this);
Account[] accounts = _am.getAccountsByType(getString(R.string.ACCOUNT_TYPE));

查询所有类型帐号:
AuthenticatorDescription[] accountTypes = AccountManager.get(this).getAuthenticatorTypes();
for (int i = 0; i < a.length; i++) {
     String systemAccountType = a[i].type;
     AuthenticatorDescription ad = getAuthenticatorDescription(systemAccountType,
                    accountTypes);
     AccountData data = new AccountData(a[i].name, ad);
     mAccounts.add(data);
 }

上面这段代码不但能查询所有帐号,还包括每种帐号类型的描述。其中contactmanager有个显示帐号的下拉菜单spinner,如图:
android通讯录开发_第1张图片

这个东西实现起来有点麻烦。首先添加帐号时,得把帐号相关的基本信息包括图片都包括进去,有个叫authenticator.xml的文件:
 

上面是对帐号图标及label、类型的描述。要达到上面显示的效果,得自定义spinner的外观,代码如下:
private class AccountAdapter extends ArrayAdapter {
        public AccountAdapter(Context context, ArrayList accountData) {
            super(context, android.R.layout.simple_spinner_item, accountData);//simple_spinner_item为系统自带
            setDropDownViewResource(R.layout.account_entry);
        }

        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            // Inflate a view template
            if (convertView == null) {
                LayoutInflater layoutInflater = getLayoutInflater();
                convertView = layoutInflater.inflate(R.layout.account_entry, parent, false);
            }
            TextView firstAccountLine = (TextView) convertView.findViewById(R.id.firstAccountLine);
            TextView secondAccountLine = (TextView) convertView.findViewById(R.id.secondAccountLine);
            ImageView accountIcon = (ImageView) convertView.findViewById(R.id.accountIcon);

            // Populate template
            AccountData data = getItem(position);
            firstAccountLine.setText(data.getName());
            secondAccountLine.setText(data.getTypeLabel());
            Drawable icon = data.getIcon();
            if (icon == null) {
                icon = getResources().getDrawable(android.R.drawable.ic_menu_search);//系统图标
            }
            accountIcon.setImageDrawable(icon);
            return convertView;
        }
    }

这个类主要实现了自定义spinner的作用,这引用了外部的布局文件R.layout.account_entry。这个文件相对来说易懂,就不说了。

二、添加帐号。帐号创建这个本人一点不熟悉,代码也是完全参考别人的,代码勉强能看懂。主要是创建一个Service类去掉用继承自AbstractAccountAuthenticator的类,再由继承自AbstractAccountAuthenticator的类去调用继承直AccountAuthenticatorActivity的界面处理类。具体的代码也不难,数据是保存在databases内的account表里面,可到数据库内去查询。

三、增加联系人。这个功能由ContactAdder类完成,具体实现不是比较麻烦,保存动作由ContentResolver类解决,但实现方式有所不同,可分为一次性批量增加与挨个增加。
批量增加代码:
protected void createContactEntry() {
        // Get values from UI
        String name = mContactNameEditText.getText().toString();
        String phone = mContactPhoneEditText.getText().toString();
        String email = mContactEmailEditText.getText().toString();
        int phoneType = mContactPhoneTypes.get(mContactPhoneTypeSpinner.getSelectedItemPosition());
        int emailType = mContactEmailTypes.get(mContactEmailTypeSpinner.getSelectedItemPosition());;

        //批量插入
        ArrayList ops = new ArrayList();
        ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName())
                .build());
        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE,
                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)//姓名类型
                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
                .build());
        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE,
                		ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)//电话类型
                .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
                .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType)
                .build());
        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                .withValue(ContactsContract.Data.MIMETYPE,
                        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)//邮箱类型
                .withValue(ContactsContract.CommonDataKinds.Email.DATA, email)
                .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType)
                .build());

        // Ask the Contact provider to create a new contact
        Log.i(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
                mSelectedAccount.getType() + ")");
        Log.i(TAG,"Creating contact: " + name);
        try {
            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
        } catch (Exception e) {
            // Display warning
            Context ctx = getApplicationContext();
            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exceptoin encoutered while inserting contact: " + e);
        }
    }

即通过包装ContentProviderOperation进入list,然后调用applyBatch方法,至于这个applyBatch这个方法到底又做了些什么事呢?本人虽然也下了个android的源码包,但是有些源码没看见,所以细究不了。不过查看数据库的数据发现,至少向contacts、raw_contacts、data三张表增加过数据。在上面的代码中MIMETYPE比较重要,上面的代码中会向data添加三条数据,只是MIMETYPE内容不一样。还会向raw_contacts添加一条数据,同时也会向contacts里面增加一条数据。具体详情可参阅数据库。

单条依次插入数据代码:
public void createContactInsert(){
    	String name = mContactNameEditText.getText().toString();
        String phone = mContactPhoneEditText.getText().toString();
        String email = mContactEmailEditText.getText().toString();
        int phoneType = mContactPhoneTypes.get(mContactPhoneTypeSpinner.getSelectedItemPosition());
        int emailType = mContactEmailTypes.get(mContactEmailTypeSpinner.getSelectedItemPosition());;
        
        ContentValues values = new ContentValues();
        values.put(RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
        values.put(RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
        Uri rawContactUri = this.getContentResolver().insert(RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(rawContactUri);
        
        //往data表入姓名数据
        values.clear();
        values.put(Data.RAW_CONTACT_ID, rawContactId);
        values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
        values.put(StructuredName.DISPLAY_NAME, name);
        this.getContentResolver().insert(Data.CONTENT_URI, values);
        
        //往data表入电话数据
        values.clear();
        values.put(Data.RAW_CONTACT_ID, rawContactId);
        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
        values.put(Phone.NUMBER, phone);
        values.put(Phone.TYPE, phoneType);
        this.getContentResolver().insert(Data.CONTENT_URI, values);

        //往data表入Email数据
        values.clear();
        values.put(Data.RAW_CONTACT_ID, rawContactId);
        values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
        values.put(Email.DATA, email);
        values.put(Email.TYPE, emailType);
        this.getContentResolver().insert(Data.CONTENT_URI, values);
    }

上面的代码看起来似乎比批量添加更加明白,至少知道insert是做什么的。虽然作用都是一样。

四、查询联系人。主要代码如下:
private Cursor getContacts(){
        // Run query
        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        String[] projection = new String[] {
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME
        };
        String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" +
                (mShowInvisible ? "0" : "1") + "'";
        String[] selectionArgs = null;
        //sort ordering based on localized preferences(sqlite排序用法:根据本地化设置对字符串进行比较排序)
        String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";

        return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
    }

初次接触时上面这段代码很不好理解,尤其是根本不了解数据库结构的情况下,而且还有那么未知的字段是怎么来的,URI这些又是怎么来的。其实这个URI就是刚才插入时使用的那个URI,projection表示查询的字段,selection是指查询的参数名,selectonArgs表示参数值,sortOrder表示排序,而那个排序"COLLATE LOCALIZED"是sqlite特有的东西,指排序规则。

上面基本上把本人认为难以理解的部分提及了下,讲得并不是很仔细,因为这个工作量确实不少,基本上花了本人三四天的时候来研究才到这种程度。现在之所以把它写下来,也不是说本人已经参考透了,只是怕再这样搞下去会不了了知,所以趁现在还点印象,把它记录下来。具体工程代码见附件。
  • android通讯录开发_第2张图片
  • 大小: 14.3 KB
  • contactmanager.zip (155.7 KB)
  • 下载次数: 284
  • 查看图片附件

你可能感兴趣的:(android通讯录开发)