使用ContentProvider对收集联系人进行读写操作+RecyclerView+ActionBar
基于前期WeChat项目,在 Fragment中实现
- RecyclerView以展开和收缩的效果显示手机联系人信息(姓名、手机号、邮箱地址) 。
- WeChat标题处右上角添加按钮,点击按钮弹出对话框,实现添加手机联系人,并在指定Fragment中显示(刷新得以显示)。
- 点击Fragment界面的ActionBar弹出对话框,实现添加手机联系人,并在当前Fragment中显示。
我选择的是在findFragment中实现相关功能。
RecyclerView展开和收缩
主要参考https://www.jianshu.com/p/c4cfe38a91ed,需要注意的是数据的切片处理和adapter onBindViewHolder对其动态效果的实现。
获取读写手机联系人的权限
AndroidManifest.xml 加上权限说明
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
findFragment.java 申请授权
int hasWriteContactsPermisson = ContextCompat.checkSelfPermission(view.getContext(), Manifest.permission.READ_CONTACTS);
if(hasWriteContactsPermisson != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS}, 1);
}
标题右上角的点击事件
MainActivity.java
btnTopAdd = (ImageButton) findViewById(R.id.btnTopAdd);
btnTopAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
// 加载result.xml界面布局代表的视图
final View resultDialog = getLayoutInflater().inflate(R.layout.top_btn_add, null); // 设置成final,否则String name = ((EditText) resultDialog.findViewById(R.id.name)).getText().toString();报EditText空指针错误
// 使用对话框来显示查询结果
AlertDialog show = new AlertDialog.Builder(MainActivity.this)
.setView(resultDialog)
.setTitle("Add your contact")
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 获取程序界面中的三个文本框的内容
String name = ((EditText) resultDialog.findViewById(R.id.name)).getText().toString();
String phone = ((EditText) resultDialog.findViewById(R.id.phone)).getText().toString();
String email = ((EditText) resultDialog.findViewById(R.id.email)).getText().toString();
// 创建一个空的ContentValues
ContentValues values = new ContentValues();
// 向RawContacts.CONTENT_URI执行一个空值插入
// 目的是获取系统返回的rawContactId
Uri rawContactUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
// 设置内容类型
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// 设置联系人名字
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
// 向联系人URI添加联系人名字
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// 设置联系人的电话号码
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
// 设置电话类型
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
// 向联系人电话号码URI添加电话号码
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
// 设置联系人的E-mail地址
values.put(ContactsContract.CommonDataKinds.Email.DATA, email);
// 设置该电子邮件的类型
values.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
// 向联系人E-mail URI添加E-mail数据
getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
Toast.makeText(v.getContext(), "下拉刷新[朋友],查看新增联系人", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("取消", null).show();
}
});
注:
- 在点击按钮弹出对话框AlertDialog后,在 setPositiveButton() 中用
new DialogInterface.OnClickListener()
来给对话框的确认按钮创建点击事件监听,其中的 context 为当前对话框的resultDailog 的环境。
- Uri 代表要操作的数据,ContentResolver通过ContentProvider提供的Uri接口获得封装的数据。如果是在Activity条件下,直接写getContentResolver()…;在非Activity条件下,如Fragment,需写作getActivity().getContentResolver()…。
*** 未能用及时刷新的方法让RecyclerView在点击对话框的确认按钮之后马上显示item(只怪自己太菜… …)
在此采用下拉刷新的方法对RecyclerView刷新,用到swiperefreshlayout:
- 在build.gradle(Module:app)的dependencies 加上如下代码:
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46'
implementation group: 'androidx.recyclerview', name: 'recyclerview', version: '1.1.0-alpha01'
- 在build.gradle(Project:…)的allprojects->repositories 加上如下代码:
maven { url "https://jitpack.io"}
tab_find.xml
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcv_expandcollapse"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:layout_margin="8dp"
android:scrollbars="none"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
findFragment.java
private SwipeRefreshLayout swipeRefreshLayout;
//...
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshLayout);
swipeRefreshLayout.setOnRefreshListener(this); // 下拉刷新
onRefresh();
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
for (int i = 0; i < mList.size(); ){// 先清空mList数据和RecyclerView的Item
mList.remove(0);
adapter.removeItem(0);
}
viewShow(); //在RecyclerView中显示所有手机联系人及其相关信息
swipeRefreshLayout.setRefreshing(false);
}
}, 500);
}
ExpandCollapseAdapter.java 加上removeItem(),参考此blog
public void removeItem(int position) {
mList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position,getItemCount());
}
ActionBar点击事件
findFragment.java
FloatingActionButton fab = view.findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) {
final View resultDialog = getActivity().getLayoutInflater().inflate(R.layout.top_btn_add, null); // 设置成final,否则String name = ((EditText) resultDialog.findViewById(R.id.name)).getText().toString();报EditText空指针错误
// 使用对话框来显示查询结果
AlertDialog show = new AlertDialog.Builder(view.getContext())
.setView(resultDialog)
.setTitle("Add your contact")
.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 获取程序界面中的三个文本框的内容
String name = ((EditText) resultDialog.findViewById(R.id.name)).getText().toString();
String phone = ((EditText) resultDialog.findViewById(R.id.phone)).getText().toString();
String email = ((EditText) resultDialog.findViewById(R.id.email)).getText().toString();
// 创建一个空的ContentValues
ContentValues values = new ContentValues();
// 向RawContacts.CONTENT_URI执行一个空值插入
// 目的是获取系统返回的rawContactId
Uri rawContactUri = getActivity().getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
// 设置内容类型
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// 设置联系人名字
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
// 向联系人URI添加联系人名字
getActivity().getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
// 设置联系人的电话号码
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
// 设置电话类型
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
// 向联系人电话号码URI添加电话号码
getActivity().getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
// 设置联系人的E-mail地址
values.put(ContactsContract.CommonDataKinds.Email.DATA, email);
// 设置该电子邮件的类型
values.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
// 向联系人E-mail URI添加E-mail数据
getActivity().getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
Toast.makeText(view.getContext(), "联系人添加成功", Toast.LENGTH_SHORT).show();
PersonData personData = new PersonData();
personData.setName(name);
personData.setPhoneNumber(phone);
personData.setEmailAddress(email);
mDataList.add(personData);
mList.add("");
adapter.notifyDataSetChanged();
}
})
.setNegativeButton("取消", null).show();
}
});
和WeChat标题的右上角按钮差不多,相对来说较容易实现RecyclerView的及时刷新,每点一次对话框的确认按钮,就会有新的item加入到RecyclerVIew的下面。因为定义的adapter在此Fragment中可以直接调用,但在MainActivity中就比较复杂,目前没有找到解决办法。
adapter.notifyDataSetChanged();
注:
mList也要进行add操作,为了增加mList的size。否则先点悬浮按钮添加联系人,后下拉刷新,会发现有item在数量和形式上不合乎逻辑。在OnRefresh()的run()中可见其原因(先清除,后添加)。
源码已上传至github