android通讯录开发
程序员文章站
2022-07-15 11:22:12
...
这周也空闲,所以继续花时间看下关于android内部应用的一些东西。比如通讯录,这个东西比自己想的还要麻烦些,通讯录可以和相关的账号关联起来。比如user1,对应有test1,test2,帐户user2对应有联系人test3,test4。因此先得添加帐户,然而模拟器测试的时候发现帐号还得联网绑定才行,结果试了下联不上,晕!
于是试着用程序的方式添加帐号,这个也不容易,在网上找了很久的资料,有本android应用开发揭密有段代码勉强达到效果了,但是有些原理还是不太明白。再结合了下sdk本身的例子contactmanager的代码,把帐号和联系人的功能合在了一起。因此这个功能主要有四个功能:
1.查询帐号
2.增加帐号
3.增加联系人
4.查询联系人
大的功能就是这四个,如果像我这等入行不久的人来看的话,值得细究的东西很多,一个一个的讲的话篇幅会很长,因此挑部分重点的讲下吧!
一、查询帐号部分:
查询所有类型帐号:
上面这段代码不但能查询所有帐号,还包括每种帐号类型的描述。其中contactmanager有个显示帐号的下拉菜单spinner,如图:
这个东西实现起来有点麻烦。首先添加帐号时,得把帐号相关的基本信息包括图片都包括进去,有个叫authenticator.xml的文件:
上面是对帐号图标及label、类型的描述。要达到上面显示的效果,得自定义spinner的外观,代码如下:
这个类主要实现了自定义spinner的作用,这引用了外部的布局文件R.layout.account_entry。这个文件相对来说易懂,就不说了。
二、添加帐号。帐号创建这个本人一点不熟悉,代码也是完全参考别人的,代码勉强能看懂。主要是创建一个Service类去掉用继承自AbstractAccountAuthenticator的类,再由继承自AbstractAccountAuthenticator的类去调用继承直AccountAuthenticatorActivity的界面处理类。具体的代码也不难,数据是保存在databases内的account表里面,可到数据库内去查询。
三、增加联系人。这个功能由ContactAdder类完成,具体实现不是比较麻烦,保存动作由ContentResolver类解决,但实现方式有所不同,可分为一次性批量增加与挨个增加。
批量增加代码:
即通过包装ContentProviderOperation进入list,然后调用applyBatch方法,至于这个applyBatch这个方法到底又做了些什么事呢?本人虽然也下了个android的源码包,但是有些源码没看见,所以细究不了。不过查看数据库的数据发现,至少向contacts、raw_contacts、data三张表增加过数据。在上面的代码中MIMETYPE比较重要,上面的代码中会向data添加三条数据,只是MIMETYPE内容不一样。还会向raw_contacts添加一条数据,同时也会向contacts里面增加一条数据。具体详情可参阅数据库。
单条依次插入数据代码:
上面的代码看起来似乎比批量添加更加明白,至少知道insert是做什么的。虽然作用都是一样。
四、查询联系人。主要代码如下:
初次接触时上面这段代码很不好理解,尤其是根本不了解数据库结构的情况下,而且还有那么未知的字段是怎么来的,URI这些又是怎么来的。其实这个URI就是刚才插入时使用的那个URI,projection表示查询的字段,selection是指查询的参数名,selectonArgs表示参数值,sortOrder表示排序,而那个排序"COLLATE LOCALIZED"是sqlite特有的东西,指排序规则。
上面基本上把本人认为难以理解的部分提及了下,讲得并不是很仔细,因为这个工作量确实不少,基本上花了本人三四天的时候来研究才到这种程度。现在之所以把它写下来,也不是说本人已经参考透了,只是怕再这样搞下去会不了了知,所以趁现在还点印象,把它记录下来。具体工程代码见附件。
于是试着用程序的方式添加帐号,这个也不容易,在网上找了很久的资料,有本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,如图:
这个东西实现起来有点麻烦。首先添加帐号时,得把帐号相关的基本信息包括图片都包括进去,有个叫authenticator.xml的文件:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.yarin.AccountType" android:icon="@drawable/ic_launcher" android:smallIcon="@drawable/ic_launcher" android:label="@string/ACCOUNT_LABEL" />
上面是对帐号图标及label、类型的描述。要达到上面显示的效果,得自定义spinner的外观,代码如下:
private class AccountAdapter extends ArrayAdapter<AccountData> { public AccountAdapter(Context context, ArrayList<AccountData> 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<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 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特有的东西,指排序规则。
上面基本上把本人认为难以理解的部分提及了下,讲得并不是很仔细,因为这个工作量确实不少,基本上花了本人三四天的时候来研究才到这种程度。现在之所以把它写下来,也不是说本人已经参考透了,只是怕再这样搞下去会不了了知,所以趁现在还点印象,把它记录下来。具体工程代码见附件。