Android简单通讯录的实现

原创 2015年11月20日 22:53:33

源码下载

效果图

效果图3


已经实现的内容 

  1. 通讯录的增删改查 
  2. 通讯录的拨号和发送短信 
  3. 字母导航栏的实现 
  4. 悬浮标题栏的实现 

(一)通讯录的增删改查功能

实现通讯录的增删改查需要先了解一下一些知识: 

  • ContentProvider和ContentResolver 
  • Contact Provider的组织结构 
  • Contacts、RawContacts和Data类 

ContentProvider作为四大组件之一,主要作用是对外共享数据,而ContentResolver则是用来和ContentProvider进行对话的。

Contact的组织结构如下图所示:

可以在ddms中的文件查看器的data/data/com.android.provider.contact/database/目录下导出通讯的数据库文件查看表的详细内容,

而Contacts、RawContacts和Data类分别对应Contact数据库的三张表contacts、raw_contacts和data。

/**
	 * 获取联系人信息
	 * 
	 * @param context
	 * @return 联系人列表
	 */
	public static List<ContactBean> getContacts(Context context) {
		List<ContactBean> contacts = new ArrayList<ContactBean>();
		ContentResolver resolver = context.getContentResolver();
		Cursor cRawContacts = resolver.query(RawContacts.CONTENT_URI, new String[] { RawContacts._ID }, null, null,
				null);
		while (cRawContacts.moveToNext()) {
			ContactBean contact = new ContactBean();
			long rawContactId = cRawContacts.getLong(cRawContacts.getColumnIndex(RawContacts._ID));
			contact.setRawContactId(rawContactId);
			Cursor dataCursor = resolver.query(Data.CONTENT_URI, null, // null
																		// 表示取出所有字段
					Data.RAW_CONTACT_ID + "=?", new String[] { String.valueOf(rawContactId) }, null);
			while (dataCursor.moveToNext()) {
				String data1 = dataCursor.getString(dataCursor.getColumnIndex(Data.DATA1));
				String mimeType = dataCursor.getString(dataCursor.getColumnIndex(Data.MIMETYPE));
				if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
					contact.setName(data1);
				} else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
					contact.setPhone(data1);
				}
			}
			contacts.add(contact);
			dataCursor.close();// 使用完之后需要关闭
		}
		cRawContacts.close();
		return contacts;
	}

	/**
	 * 添加联系人信息
	 */
	public static void addContact(Context context, ContactBean contact) {
		/*
		 * 1.封装成ContentValues 2.插入
		 */
		ContentResolver resolver = context.getContentResolver();
		ContentValues values = new ContentValues();
		Uri uri = resolver.insert(RawContacts.CONTENT_URI, values);
		System.out.println(uri);
		long rawContactId = ContentUris.parseId(uri);

		ContentValues value1 = new ContentValues();
		value1.put(Data.RAW_CONTACT_ID, rawContactId);
		value1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
		value1.put(Phone.NUMBER, contact.getPhone());
		resolver.insert(Data.CONTENT_URI, value1);

		ContentValues value2 = new ContentValues();
		value2.put(Data.RAW_CONTACT_ID, rawContactId);
		value2.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
		value2.put(StructuredName.DISPLAY_NAME, contact.getName());
		resolver.insert(Data.CONTENT_URI, value2);
	}

	/**
	 * 更新联系人信息
	 */
	public static void updateContact(Context context, ContactBean contact) {
		ContentResolver resolver = context.getContentResolver();
		ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
		ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
				.withSelection(Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
						new String[] { String.valueOf(contact.getRawContactId()), StructuredName.CONTENT_ITEM_TYPE })
				.withValue(StructuredName.DISPLAY_NAME, contact.getName()).build());
		ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
				.withSelection(Data.RAW_CONTACT_ID + " = ? AND " + Data.MIMETYPE + "= ?",
						new String[] { String.valueOf(contact.getRawContactId()), Phone.CONTENT_ITEM_TYPE })
				.withValue(Phone.NUMBER, contact.getPhone()).build());

		try {
			resolver.applyBatch(ContactsContract.AUTHORITY, ops);
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (OperationApplicationException e) {
			e.printStackTrace();
		}
	}

	public static void deleteContact(Context context, ContactBean contact) {
		ContentResolver resolver = context.getContentResolver();
		//这里需要删除RawContacts表中的记录
		resolver.delete(RawContacts.CONTENT_URI, RawContacts._ID + "=?",
				new String[] { String.valueOf(contact.getRawContactId())});
	}
(二)拨号和发送短信的实现

使用隐式Intent的方式:

//拨打电话
Intent intentCall = new Intent();
intentCall.setAction(Intent.ACTION_CALL);
intentCall.setData(Uri.parse("tel:" + contact.getPhone()));
startActivity(intentCall);


//发送短信
Intent intentMessage = new Intent();
intentMessage.setAction(Intent.ACTION_SENDTO);
intentMessage.setData(Uri.parse("smsto://" + contact.getPhone()));
startActivity(intentMessage);


(三)字母导航栏的实现

可以使用组合控件的方式实现,同时复写onTouchEvent(event)方法,实现触碰事件,并设置监听器处理触碰时的事件

public class LetterBar extends LinearLayout{
	private OnLetterSelectedListener mListener;

	public LetterBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public LetterBar(Context context) {
		super(context);
		init(context);
	}
	
	private void init(Context context) {
		setBackgroundColor(Color.GRAY);
		setOrientation(VERTICAL);
		for(int i = 0; i < 26;i++) {
			TextView text = new TextView(context);
			LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 0, 1);
			text.setLayoutParams(params);
			text.setText((char)('A'+i)+"");
			text.setTextColor(Color.WHITE);
			addView(text);
		}
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch(event.getAction()) {
		case MotionEvent.ACTION_DOWN:
		case MotionEvent.ACTION_MOVE:
			float y = event.getY();
			int size = getHeight() / getChildCount();
			int index = (int)(y / size);
			TextView tv = (TextView) getChildAt(index);
			if(tv != null && mListener != null) {
				mListener.onLetterSelecterd(tv.getText().toString());
			}
			break;
		case MotionEvent.ACTION_UP:
			if(mListener != null) {
				mListener.onLetterSelecterd("");
			}
			break;
		default:break;
		}
		
		return true;
	}
	
	public void setOnLetterSelectedListener(OnLetterSelectedListener listener) {
		mListener = listener;
	}
	
	public interface OnLetterSelectedListener {
		void onLetterSelecterd(String str);
	}

}
(四)悬浮标题栏的实现

实现悬浮标题栏较为复杂,这里使用到两个他人写好的工具类,PinYinUtils和PinnedSectionListView文件,PinYinUtils类的作用是将汉字转化为拼音,PinnedSectionListView是一个自定义View,替代ListView实现悬浮效果。

public class ContactAdapter extends BaseAdapter implements PinnedSectionListAdapter {
	public static final int VIEW_TYPE_CONTACT = 0;
	public static final int VIEW_TYPE_LETTER = 1;
	private Context context;
	private List<ContactBean> contacts;

	private List<Object> datas;//存储字母和联系人数据
	private Map<String, Integer> letterPositions;//存储字母和位置

	private void initList() {
		datas = new ArrayList<Object>();
		letterPositions = new HashMap<String, Integer>();
		Collections.sort(contacts, new Comparator<ContactBean>() {//排序

			@Override
			public int compare(ContactBean lhs, ContactBean rhs) {
				System.out.println(lhs.getName().toUpperCase());
				String lhsName = PinYinUtils.trans2PinYin(lhs.getName()).toUpperCase();
				String rhsName = PinYinUtils.trans2PinYin(rhs.getName()).toUpperCase();
				return lhsName.compareTo(rhsName);
			}
		});
		for (int i = 0; i < contacts.size(); i++) {
			ContactBean contact = contacts.get(i);
			String firstLetter = getFirstLetter(contact.getName());
			if (!letterPositions.containsKey(firstLetter)) {
				letterPositions.put(firstLetter, datas.size());
				datas.add(firstLetter);//添加字母
			}
			datas.add(contact);//添加联系人

		}
	}

	private String getFirstLetter(String name) {//获取姓名的首字母
		String firstLetter = "";
		char c = PinYinUtils.trans2PinYin(name).toUpperCase().charAt(0);
		if (c >= 'A' && c <= 'Z') {
			firstLetter = String.valueOf(c);
		}
		return firstLetter;
	}

	public ContactAdapter(Context context, List<ContactBean> contacts) {
		this.context = context;
		this.contacts = contacts;
		initList();
	}

	public void updateAdapter() {
		initList();
		notifyDataSetChanged();
	}

	@Override
	public int getViewTypeCount() {
		return 2;//VIEW_TYPE_CONTACT和VIEW_TYPE_LETTER两种类型
	}

	@Override
	public int getItemViewType(int position) {//根据数据类型决定视图类型
		return datas.get(position) instanceof ContactBean ? VIEW_TYPE_CONTACT : VIEW_TYPE_LETTER;
	}

	public int getLetterPosition(String letter) {//获取字母的位置,与字母导航栏配合使用,在listener中调用
		Integer positoin = letterPositions.get(letter);
		return positoin == null ? -1 : positoin;
	}

	@Override
	public int getCount() {
		return datas.size();//一定是字母和联系人数据的总和
	}

	@Override
	public Object getItem(int position) {
		return datas.get(position);//可能是String和ContactBean两种类型
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		int itemViewType = getItemViewType(position);//根据当前的position返回需要显示的视图
		return itemViewType == VIEW_TYPE_CONTACT ? getContactView(position, convertView)
				: getLetterView(position, convertView);
	}

	private View getLetterView(int position, View convertView) {//返回字母视图
		ViewHolder viewHolder;
		if(convertView ==null) {
			convertView = View.inflate(context, R.layout.item_letter_view, null);
			viewHolder = new ViewHolder();
			viewHolder.letter = (TextView) convertView.findViewById(R.id.tv_letter);
			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		String letter = (String) getItem(position);
		viewHolder.letter.setText(letter);
		return convertView;
	}

	private View getContactView(int position, View convertView) {//返回联系人视图
		ViewHolder viewHolder;
		if (convertView == null) {
			convertView = View.inflate(context, R.layout.item_contact_view, null);
			viewHolder = new ViewHolder();
			viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
			viewHolder.phone = (TextView) convertView.findViewById(R.id.tv_phone);
			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		ContactBean contact = (ContactBean) datas.get(position);
		viewHolder.name.setText(contact.getName());
		viewHolder.phone.setText(contact.getPhone());
		return convertView;
	}

	class ViewHolder {
		TextView letter;
		TextView name;
		TextView phone;
	}

	@Override
	public boolean isItemViewTypePinned(int viewType) {//如果是字母视图表示是悬浮标题栏

		return viewType == VIEW_TYPE_LETTER;
	}

}

源码下载





Android SQLite 如何制作一个简易通讯录(附上代码)

实习阶段,今天没什么事可做,就写个通讯录玩玩,供大家参考一下具体实现方法和步骤,希望对大家有用。 首先作为通讯录总得有数据吧,那数据怎么来,又该如何显示呢,我用SQLite存储读写数据的。 先给大...
  • sinat_28566169
  • sinat_28566169
  • 2015年07月31日 15:00
  • 2601

Android开发之一个简单的通讯录实现(源码)

通讯录就是一个ListView,我们需要通过数据库和ContentProvider来活动通讯录的数据,当然,我们应该提供选中后编辑的功能。 很简单的一个通讯略Demo,所以,直接上代码,需要的看一下...
  • MiniMicall
  • MiniMicall
  • 2014年10月07日 16:10
  • 5429

Python初学者笔记(4)-简单的通讯录

要求: 编写一个简单的通讯录 1、通讯录包含至少包含姓名、电话号码、电子邮箱;2、通讯录的信息能够保存在本地磁盘;3、通讯录查找特定人员的信息;4、通讯录能够修改特定人员的信息;5、通讯录...
  • u013014254
  • u013014254
  • 2017年01月04日 16:16
  • 140

Android简单通讯录的开发

本次开发分为4步: 1、获取手机通讯录的信息; 2、手机通讯录的数据封装; 3、手机通讯录的信息的UI适配; 4、对ListView的优化。 GetNumber.java: 用来获取手机通讯录。下面...
  • qq_25585701
  • qq_25585701
  • 2016年07月07日 16:02
  • 3101

Android从零开始之一步一步教你实现联系人功能(一)

在最近的项目中有这样的一个需求,就是要实现类似联系人的列表,包含模糊查询、按照A到Z拼音首字母分组排序、和收藏功能。参考了一下网上的例子,我觉得还是自己亲自操刀来实现所有的功能。今天带领大家先实现联系...
  • Poison_H
  • Poison_H
  • 2016年07月10日 14:52
  • 1740

一个简单通讯录的c#设计尝试

(1)主界面设计 通讯录的功能主要分为添加、查找、删除与修改这四个功能,因为查找和添加是最常用的功能,所以我把这两个功能单独列出来放到主界面上,把修改和删除合并成为在功能管理通讯录里,另外加上退出通...
  • ckpckp
  • ckpckp
  • 2014年07月10日 12:41
  • 1778

Android制作的一个通讯录

最近一两天在工作之余利用Android的SQLiteDatabase写了一个通讯录。先来分析下几个关键的地方: 一、Activity之间的数据传递,在这次写通讯录中还是使用Bundle来传递数据,只是...
  • zhangxiong0797
  • zhangxiong0797
  • 2013年08月09日 10:31
  • 1705

Android通讯录开发之实现删除功能

Android通讯录开发之实现删除功能 2014年1月15日 无论是Android开发或者是其他移动平台的开发,ListView肯定是一个大咖,那么对ListView的操作肯定是不会少的,上一篇博...
  • wwj_748
  • wwj_748
  • 2014年01月15日 10:40
  • 9839

Java源码——一个简单的数据库应用程序(通讯录)

无数据,不应用。现实生活中的很多系统都离不开数据库,而数据的增删改查则是最基本的功能。 这几天实现了以下个人在技术上的突破: 1. Java程序直连Oracle数据库,而之前的玩法是: a. 直...
  • hpdlzu80100
  • hpdlzu80100
  • 2017年04月20日 00:17
  • 1798

Java 通讯录管理系统 简易版(hashmap)

通讯录管理系统(java)            本程序使用java编写,主要用到了Myhashmap类(使用了自己编写的Mypair),读者可以换成Hashmap,这样就可以运行了,整体非常简单。...
  • qq845579063
  • qq845579063
  • 2016年04月29日 16:50
  • 1259
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android简单通讯录的实现
举报原因:
原因补充:

(最多只允许输入30个字)