欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android通讯录开发

程序员文章站 2022-07-15 11:22:12
...
这周也空闲,所以继续花时间看下关于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通讯录开发
            
    
    博客分类: android

这个东西实现起来有点麻烦。首先添加帐号时,得把帐号相关的基本信息包括图片都包括进去,有个叫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特有的东西,指排序规则。

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