Android学习指南Android知识Android技术知识

Android入门之手机通讯录开发

2016-12-13  本文已影响3708人  站在冰箱上的姑娘

临近期末,目前在进行Android课程设计,对于我这个压根不怎么学习的孩子来说,刚开始的完全是懵逼的,后来花两天时间把书认真的看了一遍,然后结合网上的的一些经验,也算是勉强把这个手机通讯录做出来了。不过不能直接在自己的手机上运行,因为自己手机上的通讯录权限早已被更改。晚饭前才把课程设计的报告也已经写完了。根据自己的课程设计报告总结下开发的过程以及自己的心得~

From  爱笑的姑娘

先附上源码:手机通讯录开发MyContacts


更:老师说我界面做的太丑,于是我又改了下界面,顺便解决了调用相机拍照的问题~~界面的图片就没有重新加载到此文了,感兴趣的可以看源代码~~


一.需求分析

手机通讯录是现代安卓手机里面最基本的功能,而且是安卓APP开发的一个经典案例,对于学习巩固Android应用技术有很大的帮助。对于当代智能手机,至少应该具备的的一项功能就是要能够存取联系人,并进行拨打电话,发送短信等功能,该APP就是为了实现该功能设计的。

二.数据库

智能设备的联系人存储在SQLite数据库之中,名称为Contacts2,查看路径为DDMS/FileExplorer/data/data/com.android.provider.contacts/database/contacts2。可以利用SQLiteManager打开该数据库。数据库Contacts2里面有很多的表,其中最主要的是4张表:

From   爱笑的姑娘

1.Contact表

_id :表的 ID,关键字。 display_name:联系人名称

photo_id:头像的 ID,如果头像未设置,该字段就为空

last_time_contacted: 最后的通话时间

lookup :是一个持久化的储存 因为用户可能会改名字,但不能改 lookup 值

2.Data表

raw_contact_id:通过 raw_contact_id 可以找到 raw_contact 表中相对的数据

data1 到 data15 这里保存着联系人的相关信息、联系人名称、联系人电 话号码、 电子邮件、备注等。

mimetype_id:

vnd.android.cursor.item/photo -> 4 该行 data15 代表照片

vnd.android.cursor.item/phone_v2 -> 5 该行 data1 代表电话号码

vnd. android.cursor.item/name -> 6 说明该行 data1 代表联系人名称

3.Phone_lookup表

data_id : 通过 data_id 可以找到 data 表中相对的数据。

raw_contact_id : 通过 raw_contact_id 可以找到 raw_contact_表中相对的数据。 normalized_number: 将每个电话号码逆序排列

4.Raw_contacts表

contact_id:引用 contacts 表的_id 字段

account_name:指明该联系人是从哪个账户上同步下载下来的。

account_type:邮件帐户的类型

sync1:说明该联系人信息是与哪个网址进行交互(同步联系人信息)

三.设计过程

1)内容设计

设计一个手机通讯录,能够对其进行增删改查操作,并且能够调用系统的拨打电话,发送短信功能。

1.主界面MainActivity:主界面顶部有一个搜索框,输入姓名,点击搜索按钮弹出选择对话框。可以对被搜索的联系人进行打电话,发短信,删除等操作,被搜索的该联系人如果不存在数据库中的话,则不会弹出该选择对话框;主界面中间是 用ListView和适配器将存在于数据库中的联系人以条目的形式显示出来,在每  个条目中显示该联系人的头像,姓名,电话号码。在每个条目的右边有两个 Image

Button分别用来给该联系人打电话和发短信。同时点击该条目进入联系人修改界面EditAcvity,长按该条目弹出对话框,对该条目的联系人进行删除操作;在主界面的底部还有两个按钮:拨号主界面:点击该按钮进入该界面可以直接输入电话号码进行拨号。添加:点击“添加”按钮可以进入添加联系人EditActivity界面。

2.添加联系人界面EditActvity:该界面上端是一个ImageView,点击该图片按钮会弹出一个对话框可以进行头像选择操作:调用系统相机或者从系统图库里选取一张照片作为该联系人的头像;头像下面是三个EditText,分别用联系人姓名,电话,邮箱信息的输入;该界面的底端是两个按钮:添加:点击该按钮将上述信息存入数据库,然后返回主界面并将信息显示在主界面。取消:点击该按钮返回主界面。

3.修改联系人界面ChangeActvity:该界面和添加联系人界面相似,但是当进入该界面时,显示的是需要修改的联系人信息,可以对这些信息进行修改。点击“修改”按钮时修改数据库中的信息,然后返回主界面并将修改后的信息显示在主界面,点击“取消”按钮则返回主界面。

2)方法设计

主界面MainActivity:

1.搜索功能:输入姓名,并根据姓名搜索数据库中的相关信息。首先用一个EditText,,输入该需要查询的联系人姓名,找到该EditText对应的id,然后用getText().toString()方法得到输入的值。在EditText的旁边有一个搜索按钮,为该按钮设置一个监听事件,点击该按钮,根据EditText中得到的姓名进行查询。如果该联系人在数据库中存在则利用Builder builder = new AlertDialog.Builder(MainActivity.this)弹出一个对话框,在该对话框中可以对选中的联系人进行打电话,发短信,删除等操作。其中打电话和发短息都是调用模拟器系统的功能,删除操作的话,是根据姓名返回该联系人的ID,然后根据该ID对联系人进行删除操作。

2.联系人显示功能:对数据库中的联系人信息进行显示。首先定义一个游标phoneCursor用来访问数据库,得到数据库中的联系人数据。然后定义一个适配器MyListAdapter继承BaseAdapter对得到数据进行绘制,这样才能将数据库的信息以适配器中的布局item一条一条的显示出来。

对显示的联系人进行操作:对该条目采取匿名内部类的方式设置了一个监听器setOnItemClickListener,点击某个联系人的时候根据该联系人条目的id获取到该联系人的相关信息,并启用一个Intent存储这些信息,然后将该信息传递到联系人信息修改界面ChangeActivity,并经该信息在修改界面进行显示。对该条目还采取匿名内部类的方式设置了一个监听器setOnItemLongClickLis

tener,长按该条目时,获取该条目的id和姓名,然后利用Builder builder = newAlertDialog.Builder(MainActivity.this)弹出一个对话框,选择确定删除或者取消删除该联系人操作,若点击确定,则根据长按该条目得到的姓名访问数据库,根据姓名查找到数据库中的ID,然后根据ID删除该联系人,使用startActivity(newIntent(MainActivity.this,MainActivity.class))刷新当前界面。

3.拨号主界面功能:由于用户可能想要拨打一些并不存于在通讯录中间的号码,这是就需要用药拨号主界面。对“拨号主界面”按钮用匿名内部类的方式设置了一个监听器,点击该按钮的时候,调用系统的拨打电话功能,并进入系统的拨号主界面,输入电话号码,进行拨号。

4.添加联系人功能:对“拨号主界面”按钮用匿名内部类的方式设置了一个监听器,点击该按钮的时候,调用Intent进入添加联系人界面EditActivity。

联系人添加界面EditAcitivity:

1.选择头像功能:点击头像所在的ImageButton,然后利用Builder builder = newAlertDialog.Builder()弹出一个一个对话框:“拍照”“选择系统图片”若选择“拍照”,则调用系统相机进行拍照,由于相机拍摄的照片太大,因而要调用系统工具对图片进行剪切,然后在该界面调用一个Intent返回该图片并在ImageView中显示。若选择“选择系统图片”则进入系统的图库进行头像选择,并且在该界面调用一个Inten返回该图片并在ImageView中显示。

2.姓名电话邮箱输入功能:使用三个EditText,分别用findViewById分别得到这三个EditText的ID,然后用getText().toString()函数便能得到在EditText中输入的值。

3.添加功能:对于“添加”按钮利用匿名内部类设置一个监听器,当点击该按钮的时候,首先向 RawContacts.CONTENT_URI 执行一个空值插入,获取系统返回的数据表中rawContactId,然后将输入的头像,姓名,电话,邮箱等信息依次存入数据库系统之中,一个一个的值存进去,这样有助于存取。然后调用一个Intent返回主界面,主界面会显示新增的联系人信息。

4.取消功能:对于“取消”按钮利用匿名内部类设置一个监听器,当点击该按钮的时候,调用一个Intent返回主界面,联系人的信息没有变化。

联系人修改界面ChangeActvity:该页面的功能和联系人添加界面EditAcitiVity的功能相似,不同的是:

1.信息获取功能:利用一个Intent获取在主界面的条目点击事件中利用Intent传过来的该需要修改的联系人的相关信息,然后利用.setText()函数对传递过来的信息在相应的EditText和ImageButton进行显示(图片信息是用.setImageBitmap(photo)函数进行显示)。

2.更新信息功能:对于“修改”按钮利用匿名内部类设置一个监听器,当点击该按钮的时候,首先删除在数据库中删除该联系人原来的信息,然后将更新后的联系人信息插入数据库之中,这样便实现了对联系人的修改功能(删除和插入功能和前面所说的原理是的一样的)然后调用一个Intent返回主界面,主界面会显示修改过的联系人信息。

3)页面展示

主界面MainActivity

From 爱笑的姑娘

搜索联系人弹出的界面

From 爱笑的姑娘

拨号主界面

From 爱笑的姑娘

长按删除联系人弹出的界面

From 爱笑的姑娘

添加联系人界面EditActvity

From 爱笑的姑娘

修改联系人界面ChangeActvity

From 爱笑的姑娘

拨号界面

From 爱笑的姑娘

短信发送界面

From 爱笑的姑娘

4)具体代码实现

搜索功能:

1.设置监听器对“搜索”按钮的点击事件进行监听,产生相应的操作

Button button2=(Button)findViewById(R.id.button2);

button2.setOnClickListener(new OnClickListener(){

public void onClick(View v){

name = (EditText)findViewById(R.id.editText1);

String  m= name.getText().toString();

testQueryandOperate(m);

}

});

2.testQueryandOperate(m)函数的主要代码

//使用ContentResolver查找联系人数据

Cursor cursor = getContentResolver().query(ContactsContract.Contacts

.CONTENT_URI, null, null, null, null);

//遍历查询结果,找到所需号码

while (cursor.moveToNext()) {

//获取联系人ID

String contactId = cursor.getString(cursor.getColumnIndex

(ContactsContract.Contacts._ID));

//获取联系人的名字

String contactName = cursor.getString(cursor.getColumnIndex

(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));

if (name.equals(contactName)) {

//使用ContentResolver查找联系人的电话号码

Cursor phone = getContentResolver().query      (ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,                                 ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);

if (phone.moveToNext()) {

             Final String phoneNumber=phone.getString(phone.getColumnIn

             dex(ContactsContract.CommonDataKinds.Phone.NUMBER);}

}

}

根据该函数得到的该联系人的电话号码和姓名,利用Builder可以弹出一个对话框对该联系人进行打电话,发短信,删除等操作。

联系人显示功能:

在主界面的Oncreate()函数中调用getPhoneContacts()函数得到手机通讯录中的联系人信息,然后用继承了BaseAdapter的适配器对联系人的数据信息进行适配。

1.得到手机通讯录联系人信息

getPhoneContacts();

2.对数据进行适配

mListView =(ListView)findViewById(R.id.lv);

myAdapter = new MyListAdapter(this);

mListView.setAdapter(myAdapter);

3.getPhoneContacts()函数的主要代码

// 获取手机联系人

ContentResolver resolver = mContext.getContentResolver();

Cursor phoneCursor = resolver.query(Phone.CONTENT_URI,

PHONES_PROJECTION,null,null,null);

if (phoneCursor != null) {

while (phoneCursor.moveToNext()) {

//得到手机号码

String phoneNumber = phoneCursor.getString

(PHONES_NUMBER_INDEX);

//当手机号码为空的或者为空字段 跳过当前循环

if (TextUtils.isEmpty(phoneNumber))

continue;

//得到联系人名称

String contactName = phoneCursor.getString

(PHONES_DISPLAY_NAME_INDEX);

//得到联系人ID

Long contactid = phoneCursor.getLong(PHONES_CONTACT_ID_INDEX);

//得到联系人头像ID

Long photoid = phoneCursor.getLong(PHONES_PHOTO_ID_INDEX);

//得到联系人头像Bitamp

Bitmap contactPhoto = null;

//photoid 大于0 表示联系人有头像 如果没有给此人设置一个默认的头像

if(photoid > 0 ) {

Uri uri =ContentUris.withAppendedId(ContactsContract.

Contacts.CONTENT_URI,contactid);

InputStream input = ContactsContract.Contacts.openContact

PhotoInputStream(resolver, uri);

contactPhoto = BitmapFactory.decodeStream(input);

}else {

contactPhoto = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

}

mContactsName.add(contactName);

mContactsNumber.add(phoneNumber);

mContactsPhonto.add(contactPhoto);

}

phoneCursor.close();

}

4.对数据进行适配的主要代码

public View getView(final int position, View convertView, ViewGroup parent) {

if (convertView == null) {

convertView = LayoutInflater.from(mContext).inflate(R.layout.item, null);

}//拨打电话图标和发送短信图标则直接在布局中加载出来了

message = (ImageButton) convertView.findViewById(R.id.imageButton2);

//绘制联系人名称

title.setText(mContactsName.get(position));

//绘制联系人号码

text.setText(mContactsNumber.get(position));

//绘制联系人头像

iamge.setImageBitmap(mContactsPhonto.get(position));

});

在此时使用的ConvertView这个参数用于之前加载好的布局进行缓存,以便之后可以进行重用,对布局界面起到了了一定的优化效果。

5.对于该号码调用系统拨号功能

Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:"+ mContactsNumber.get(position)));

startActivity(intent);

6.对于得到的该号码调用系统发短信功能

Uri a=Uri.parse("smsto:"+phoneNumber);

Intent intent = new Intent(Intent.ACTION_SENDTO, a);

intent.putExtra("sms_body", message);

startActivity(intent);

在显示的主界面当中,每一个条目的右侧都有拨打电话和发送短信按钮,所以这两个图标所对应的功能也要在适配器中进行适配。点击该按钮的时候,首先要得到点击按钮的相应联系人的电话,然后对该号码进行打电话,发短信的相关操作,因此,这两个按钮也要设置相关的监听事件。

拨号主界面功能:

对于拨号主界面使用内部类按钮设置一个监听器,然后在Onclick()函数中使用Intent打开系统的拨号界面,关键代码如下两行:

Intent intent= new Intent("android.intent.action.DIAL");

startActivity(intent);

添加联系人功能:

1.添加联系人的函数testInsert()

//得到从界面输入的联系人信息

bt_name=(EditText)findViewById(R.id.name);

bt_number=(EditText)findViewById(R.id.number);

bt_email=(EditText)findViewById(R.id.email);

bt_image=(ImageView)findViewById(R.id.imageView1);

String name=bt_name.getText().toString();

String number=bt_number.getText().toString();

String email=bt_email.getText().toString();

bt_image.setDrawingCacheEnabled(true);

Bitmap image= Bitmap.createBitmap(bt_image.getDrawingCache());

bt_image.setDrawingCacheEnabled(false);

ContentValues values = new ContentValues();

Uri rawContactUri = this.getBaseContext().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, Photo.CONTENT_ITEM_TYPE);

ByteArrayOutputStream array = new ByteArrayOutputStream();

image.compress(Bitmap.CompressFormat.JPEG, 100, array);

values.put(Photo.PHOTO, array.toByteArray());         this.getBaseContext().getContentResolver().insert( android.provider.Con tactsContract.Data.CONTENT_URI, values);

//往 data 表入姓名数据

values.clear();

values.put(Data.RAW_CONTACT_ID, rawContactId);

values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);

values.put(StructuredName.GIVEN_NAME, name); this.getBaseContext().getContentResolver().insert( android.provider.Con tactsContract.Data.CONTENT_URI, values);

//往 data 表入电话数据

values.clear();

values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ ID, rawContactId);

values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);

values.put(Phone.NUMBER, number);

values.put(Phone.TYPE, Phone.TYPE_MOBILE); this.getBaseContext().getContentResolver().insert(android.provider.Cont actsContract.Data.CONTENT_URI, values);

//往 data 表入 Email 数据

values.clear();

values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ ID, rawContactId);

values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);

values.put(Email.DATA,email);

values.put(Email.TYPE, Email.TYPE_WORK);        this.getBaseContext().getContentResolver().insert(android.provider.Cont actsContract.Data.CONTENT_URI, values);

}

2.利用相机拍摄照片并在界面显示

// 指定开启系统相机的Action

Intent intent = new Intent(Intent.ACTION_PICK, null);              intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent, 1);

//选择联系人头像并在页面显示

if(requestCode == 0&& resultCode == RESULT_OK){

Bundle extras = data.getExtras();

Bitmap photo= (Bitmap) extras.get("data");

doCropPhoto(photo);

bt_image.setImageBitmap(photo);

}

//doCropPhoto()函数

protected void doCropPhoto(Bitmap data){

Intent intent = getCropImageIntent(data);

startActivityForResult(intent, 0);

}

//getCropImageIntent()函数

public static Intent getCropImageIntent(Bitmap data) {

Intent intent = new Intent("com.android.camera.action.CROP");

intent.setType("image/*");

intent.putExtra("data", data);

intent.putExtra("crop", "true");

intent.putExtra("aspectX", 1);

intent.putExtra("aspectY", 1);

intent.putExtra("outputX", 64);

intent.putExtra("outputY", 64);

intent.putExtra("return-data", true);

return intent;

}

3.从图库选择头像并在界面显示

//打开系统的图库

Intent intent2 = new Intent(Intent.ACTION_PICK, null);              intent2.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(intent2, 1);

//选择联系人头像并在页面显示

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

// TODO Auto-generated method stub

bt_image= (ImageView)findViewById(R.id.imageView1);

if (requestCode == 1 && resultCode == RESULT_OK) {

if (data != null) {

bt_image.setImageURI(data.getData());

}

}

super.onActivityResult(requestCode, resultCode, data);

}

修改联系人功能:

1.获取信息并显示在界面中

Intent intent=getIntent();

final String name = intent.getStringExtra("name");

String number = intent.getStringExtra("number");

String email = intent.getStringExtra("email");

Bitmap photo = intent.getParcelableExtra("photo");

bt_name = (EditText)findViewById(R.id.name);

bt_number = (EditText)findViewById(R.id.number);

bt_email= (EditText)findViewById(R.id.email);

bt_image=(ImageView)findViewById(R.id.imageView1);

bt_name.setText(name);

bt_number.setText(number);

bt_email.setText(email);

bt_image.setImageBitmap(photo);

2.对联系人的信息进行修改

testDelete(name);

testInsert();

Intent intent2=new Intent(mContext,MainActivity.class);

startActivity(intent2);

权限问题:

1.读取数据库操作要在Mainifeast.xml中添加如下权限:

<uses-permission android:name="android.permission.READ_CONTACTS"/>

<uses-permission android:name="android.permission.WRITE_CONTACTS"/>

2.  读取打电话,发短信操作要在Mainifeast.xml中添加如下权限:

<uses-permission android:name="android.permission.CALL_PHONE"/>

<uses-permission android:name="android.permission.SEND_SMS" />

四.心得体会

1) 遇到的问题以及解决办法

1.setOnItemClickListener点击事件点击无效

原因:当布局文件中存在Button或者ImageButtond等控件时,点击事件的焦点会由这些控件获得,所以点击Item无效。

解决办法:在ImageButton或Button控件中添加android:focusable="false"属性,并将android:descendantFocusability="blocksDescendants"属性添加给ImageButton或者Button控件。

2.符号没有错误却报错Synax error on token’’’

原因:在内部类的Onclick()方法中不可以直接创建函数。

解决办法:在该内部类外面定义该函数,然后在内部类的Onclick()方法中调用该函数即可。

3.android.content.ActivityNotFoundException:.....

原因:组件Activity没有在AndroidMainifeast.xml中注册。

解决方法:在AndroidMainifeast.xml注册该组件android:name="com.itcast.

mycontacts.MainActivity"

注意:名字一定要完全正确,一开始我就因为名字少打了个字母,然后导致一直报错。

4.DDMS/File Explorer/mnt/sdcard目录打不开

原因:该模拟器没有为SD卡分配内存

解决:重新建一个模拟器,在SD card/size选项中分配一定内存(256M)

5.头像在ListView中显示为黑色的背景

原因:图片存储在数据库中的格式不对。当调用bitmap.compress(Com

pressFormat.JPEG, 100, fos)保存图片时发现图片背景为黑色。

解决:改成用png保存就可以了,bitmap.compress(CompressForma.PNG, 100, fos)

2) 小结

通过这次课程设计,我对本学期所学的知识点有了更进一步的了解和认识。UI布局,Activity组件以及如何在Activity中使用Intent,用SQLite对数据进行存储,如何对ListView进行适配,如何用ContentResolver获取系统数据库中的数据等等,对于这些知识,我得到了进一步的巩固。在这次课程设计当中,我最大的收获是在遇到问题的时候,我变得越来越冷静,能够重新理清思路,再一个个排除可能出错的地方,最后找到错误。

END

上一篇下一篇

猜你喜欢

热点阅读