1.电话簿主要是由三个大部分组成的:
(1)拨号界面
(2)联系人界面
(3)消息界面
2.除了这三个界面外还有一个更重要的就是权限的处理:
通常在Android6.0以下是不需要通过动态添加权限的方式的,但在6.0以上权限被分为了两类,一类是普通权限(Normal Permissions)还有一类是危险权限(Dangerous Permission),普通权限还是和以前一样只需要通过在Manifes中添加就可以了,但是危险权限不仅需要在manife中添加还需要通过动态的方式来申请授权;
整个通讯录需要在Manifes中添加的权限如下:
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA"/>
一丶通话界面
效果图:
1.这个界面主要是一个RecyclerView(显示联系人用),一个自定义的数字拨号键盘以及一个FloatingActionButton;
需要实现RecyclerView的联系人列表显示需要自定义一个专用Adapter,代码如下:
RAdapter.java
public class RAdapter extends RecyclerView.Adapter<RAdapter.MyViewHolder> implements View.OnClickListener {
private List<Contacts> mDatas;
private Context mContext;
private LayoutInflater inflater;
private OnRecyclerviewItemClickListener mOnRecyclerviewItemClickListener = null;
public RAdapter(Context context, List<Contacts> datas,OnRecyclerviewItemClickListener mOnRecyclerviewItemClickListener) {
this.mContext = context;
this.mDatas = datas;
inflater = LayoutInflater.from(mContext);
this.mOnRecyclerviewItemClickListener = mOnRecyclerviewItemClickListener;
}
@Override
public int getItemCount() {
return mDatas.size();
} //填充onCreateViewHolder方法返回的holder中的控件
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
MyViewHolder holder1 = (MyViewHolder) holder;
holder1.tv_name.setText(mDatas.get(position).getName().toString());
holder1.tv_phone.setText(mDatas.get(position).getPhone());
if (mDatas.get(position).getPhoto() == null) {
holder1.tv_photo.setImageResource(R.drawable.no);
} else {
holder1.tv_photo.setImageBitmap(mDatas.get(position).getPhoto());
}
holder1.itemView.setTag(position);
} //重写onCreateViewHolder方法,返回一个自定义的ViewHolder
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.contacts, parent, false);
MyViewHolder holder = new MyViewHolder(view);
view.setOnClickListener(this);
return new MyViewHolder(view);
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv_name;
TextView tv_phone;
RoundImageView tv_photo;
public MyViewHolder(View view) {
super(view);
tv_name = (TextView) view.findViewById(R.id.Name);
tv_phone = view.findViewById(R.id.Phone);
tv_photo = view.findViewById(R.id.icon);
}
}
@Override
public void onClick(View v) {
//将监听传递给自定义接口
mOnRecyclerviewItemClickListener.onItemClickListener(v, ((int) v.getTag()));
}
public interface OnRecyclerviewItemClickListener {
void onItemClickListener(View v,int position);
}
}
其中的Contacts是我自己定义的联系人类同样附上代码如下:
Contacts.java
public class Contacts {
//联系人姓名
private String Name;
//联系人电话
private String Phone;
//联系人头像
private Bitmap photo;
//联系人邮箱
private String Email;
//联系人住址
private String Address;
public void setName(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setPhone(String phone) {
Phone = phone;
}
public String getPhone() {
return Phone;
}
public void setPhoto(Bitmap photo) {
this.photo = photo;
}
public Bitmap getPhoto() {
return photo;
}
public void setAddress(String address) {
Address = address;
}
public String getAddress() {
return Address;
}
public void setEmail(String email) {
Email = email;
}
public String getEmail() {
return Email;
}
}
接着就是RecyclerView的显示联系人了,在显示前就先要对其进行获取,在这里我是直接获取的系统联系人,所以只需要直接通过查询系统联系人数据库就可以得到所有的联系人列表,代码如下:
GetContacts.java
public class GetContacts {
static String contactId;
static String name,Phone,email,address;
public static List<Contacts> getContacts(Context context){
List<Contacts> mList = new ArrayList<>();
Uri uri = ContactsContract.Contacts.CONTENT_URI;
ContentResolver contentResolver = context.getContentResolver();
StringBuilder sb=new StringBuilder();
Cursor cursor = contentResolver.query(uri, null, null, null, null);
while (cursor.moveToNext()) {
contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
sb.append("contactId=").append(contactId).append(",Name=").append(name);
Log.e("1111111111111111111111", "getContacts: "+name);
Cursor phone = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);
while (phone.moveToNext()) {
Phone = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
sb.append(",Phone=").append(Phone);
// contacts.setNumber(Phone);
}
phone.close();
Cursor Email = contentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + "=" + contactId, null, null);
while (Email.moveToNext()) {
email = Email.getString(Email.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
// sb.append(",Email=").append(email);
}
Email.close();
Cursor Address = contentResolver.query(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI,null,ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID+"="+contactId,null,null);
while (Address.moveToNext()){
address=Address.getString(Address.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.DATA));
}
ContentResolver cr=context.getContentResolver();
Uri uri1 = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,contactId);
InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri1);
Bitmap photo= BitmapFactory.decodeStream(is);
Contacts contacts = new Contacts();
contacts.setName(name);
contacts.setPhone(Phone);
// contacts.setEmail(email);
// contacts.setAddress(address);
contacts.setPhoto(photo);
mList.add(contacts);
}
cursor.close();
return mList;
}
}
但是上面的这个方法不能实际需要,我想要达到edittext输入内容自动匹配的效果,所以改成了一下的这个方法主要还是基于系统数据库的查询
public class Search {
public static List<Contacts> searchContacts(Context context, String keyword) {
ContentResolver cr = context.getContentResolver();
List<Contacts> contactList = new ArrayList<>();
if (isPhoneNum(keyword)) {
Cursor cursorP = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.NUMBER + " like " + "'%" + keyword + "%'",
null,
null);
while (cursorP.moveToNext()) {
Map<String, String> map = new HashMap<>();
String phoneNumber = cursorP.getString(cursorP.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String photo = cursorP.getString(cursorP.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO_URI));
String contactId = cursorP.getString(cursorP.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
Cursor nameC = cr.query(ContactsContract.Contacts.CONTENT_URI, null, ContactsContract.Contacts._ID + "=" + contactId, null, null);
while (nameC.moveToNext()) {
String name = nameC.getString(nameC.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Contacts contacts = new Contacts();
contacts.setName(name);
contacts.setPhone(phoneNumber);
Uri uriNumber2Contacts = Uri.parse("content://com.android.contacts/"
+"data/phones/filter/"+ phoneNumber);
// 查询Uri,返回数据集
Cursor cursorCantacts = context.getContentResolver().query(
uriNumber2Contacts,
null,
null,
null,
null);
// 如果该联系人存在
if(cursorCantacts.getCount() >0) {
// 移动到第一条数据
cursorCantacts.moveToFirst();
// 获得该联系人的contact_id
Long contactID = cursorCantacts.getLong(cursorCantacts
.getColumnIndex("contact_id"));
// 获得contact_id的Uri
Uri uri = ContentUris.withAppendedId(
ContactsContract.Contacts.CONTENT_URI, contactID);
// 打开头像图片的InputStream
InputStream input = ContactsContract.Contacts
.openContactPhotoInputStream(context.getContentResolver(), uri);
// 从InputStream获得bitmap
Bitmap bmp_head = BitmapFactory.decodeStream(input);
byte[] contactIcon = cursorCantacts.getBlob(0);
contacts.setPhoto(bmp_head);
}
contactList.add(contacts);
}
// map.put("name", name);
// map.put("phoneNum", phoneNumber);
// map.put("photo", photo);
// contactList.add(map);
}
cursorP.close();
} else {
Cursor cursorName = cr.query(
ContactsContract.Contacts.CONTENT_URI,
null,
ContactsContract.PhoneLookup.DISPLAY_NAME + " like " + "'%" + keyword + "%'",
null,
null);
while (cursorName.moveToNext()) {
String name = cursorName.getString(
cursorName.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String contactId = cursorName.getString(cursorName.getColumnIndex(ContactsContract.Contacts._ID));
Cursor phone = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);
while (phone.moveToNext()) {
Map<String, String> map = new HashMap<>();
String phoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String photo = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO_URI));
Contacts contacts = new Contacts();
contacts.setName(name);
contacts.setPhone(phoneNumber);
Uri uriNumber2Contacts = Uri.parse("content://com.android.contacts/"
+"data/phones/filter/"+ phoneNumber);
// 查询Uri,返回数据集
Cursor cursorCantacts = context.getContentResolver().query(
uriNumber2Contacts,
null,
null,
null,
null);
// 如果该联系人存在
if(cursorCantacts.getCount() >0) {
// 移动到第一条数据
cursorCantacts.moveToFirst();
// 获得该联系人的contact_id
Long contactID = cursorCantacts.getLong(cursorCantacts
.getColumnIndex("contact_id"));
// 获得contact_id的Uri
Uri uri = ContentUris.withAppendedId(
ContactsContract.Contacts.CONTENT_URI, contactID);
// 打开头像图片的InputStream
InputStream input = ContactsContract.Contacts
.openContactPhotoInputStream(context.getContentResolver(), uri);
// 从InputStream获得bitmap
Bitmap bmp_head = BitmapFactory.decodeStream(input);
byte[] contactIcon = cursorCantacts.getBlob(0);
contacts.setPhoto(bmp_head);
}
contactList.add(contacts);
}
}
cursorName.close();
}
return contactList;
}
public static boolean isPhoneNum (String keyword){
//正则 匹配以数字或者加号开头的字符串(包括了带空格及-分割的号码
if (keyword.matches("^([0-9]|[/+]).*")) {
return true;
} else {
return false;
}
}
}
这三个主要的界面是由Fragment关联起来的,其中的三个LinearLayout指的是底部的导航栏(命名不够规范见谅);
MainActivity.java
public class MainActivity extends AppCompatActivity {
LinearLayout l1,l2,l3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
l1=findViewById(R.id.call);
View1 fragment = new View1();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager. beginTransaction();
transaction.replace(R.id.main, fragment);
transaction.commit();
Log.e("1111111111", "onClick: " );
l1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View1 fragment = new View1();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager. beginTransaction();
transaction.replace(R.id.main, fragment);
transaction.commit();
Log.e("1111111111", "onClick: " );
Toast.makeText(getApplicationContext(),"转到拨号",Toast.LENGTH_SHORT).show();
}
});
l2=findViewById(R.id.contacts);
l2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View2 fragment = new View2();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager. beginTransaction();
transaction.replace(R.id.main, fragment);
transaction.commit();
Toast.makeText(getApplicationContext(),"转到联系人",Toast.LENGTH_SHORT).show();
}
});
l3=findViewById(R.id.email);
l3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View3 fragment = new View3();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentManager. beginTransaction();
transaction.replace(R.id.main, fragment);
transaction.commit();
Toast.makeText(getApplicationContext(),"转到消息",Toast.LENGTH_SHORT).show();
}
});
}
}
主页面的实现如上图拨号界面最主要的就是拨号的这个功能了,但是这个功能需要到了拨打电话的权限,在6.0后拨打电话权限就被列入了危险权限(Dangerous Permission)中了,需要动态添加具体的操作方式可自行百度;
View1.java(拨号界面)
public class View1 extends Fragment {
EditText editText;
FrameLayout fl;
RecyclerView lv;
FloatingActionButton button;
List<Contacts> List = new ArrayList<>();
List<Contacts> List2 = new ArrayList<>();
RAdapter adapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view1, null);
editText = view.findViewById(R.id.editText);
lv = view.findViewById(R.id.lv);
adapter = new RAdapter(getActivity(), List,onRecyclerviewItemClickListener);
lv.setItemAnimator(new DefaultItemAnimator());
lv.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayoutManager.VERTICAL));
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); //设置布局管理器
lv.setLayoutManager(layoutManager); //设置为垂直布局,这也是默认的
ayoutManager.setOrientation(OrientationHelper.VERTICAL); //设置Adapter
lv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState){
case 0:
fl.setVisibility(View.GONE);
break;
}
}
});
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
List2 = Search.searchContacts(getActivity(), s.toString());
adapter = new RAdapter(getActivity(),List2,onRecyclerviewItemClickListener);
lv.setAdapter(adapter); //设置增加或删除条目的动画
Toast.makeText(getActivity(),""+List2,Toast.LENGTH_SHORT).show();
}
});
editText.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent event) {
int inType = editText.getInputType();
editText.setInputType(InputType.TYPE_NULL);
editText.onTouchEvent(event);
editText.setInputType(inType);
int len = editText.getText().toString().length();
editText.setSelection(len);
return true;
}
});
view.findViewById(R.id.n0).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "0");
}
});
view.findViewById(R.id.n1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "1");
}
});
view.findViewById(R.id.n2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "2");
}
});
view.findViewById(R.id.n3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "3");
}
});
view.findViewById(R.id.n4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "4");
}
});
view.findViewById(R.id.n5).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "5");
}
});
view.findViewById(R.id.n6).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "6");
}
});
view.findViewById(R.id.n7).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "7");
}
});
view.findViewById(R.id.n8).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "8");
}
});
view.findViewById(R.id.n9).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "9");
}
});
view.findViewById(R.id.nq).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "*");
}
});
view.findViewById(R.id.ne).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = editText.getSelectionStart();
Editable editable = editText.getText();
editable.insert(index, "#");
}
});
view.findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (editText.getText().length() > 0) {
int index = editText.getSelectionStart();
if (index >= 1) {
Editable editable = editText.getText();
editable.delete(index - 1, index);
}
}
}
});
fl = view.findViewById(R.id.eheh);
view.findViewById(R.id.key).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fl.setVisibility(View.GONE);
editText.setText("");
}
});
fl.setVisibility(View.GONE);
button = view.findViewById(R.id.floatingActionButton);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fl.setVisibility(View.VISIBLE);
}
});
view.findViewById(R.id.text_call).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Call.call(getActivity(), editText.getText().toString());
}
});
return view;
}
private RAdapter.OnRecyclerviewItemClickListener onRecyclerviewItemClickListener = new RAdapter.OnRecyclerviewItemClickListener() {
@Override
public void onItemClickListener(View v, int position) {
//这里的view就是我们点击的view position就是点击的position
if (fl.getVisibility()==View.GONE){
fl.setVisibility(View.VISIBLE);
if (editText.getText().toString().length()==0){
editText.setText(List.get(position).getPhone());
}
else{
editText.setText(List2.get(position).getPhone());
}
}else{
if (editText.getText().toString().length()==0){
editText.setText(List.get(position).getPhone());
}
else{
editText.setText(List2.get(position).getPhone());
}
}
Toast.makeText(getActivity(), " 点击了 " + position, Toast.LENGTH_SHORT).show();
}
};
@Override
public void onResume() {
super.onResume();
List = Search.searchContacts(getActivity(),edittext.getText().toString());
adapter = new RAdapter(getActivity(), List,onRecyclerviewItemClickListener);
lv.setAdapter(adapter);
}
}
当有了权限后就只需要一个拨打电话的方法了,这个方法就只是调了系统的拨打电话功能,来达到效果;
Call.java
public class Call {
public static void call(Context context ,String number) {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + number);
intent.setData(data);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
context.startActivity(intent);
}
}
拨号界面差不多就是这样了
剩余两个界面将会继续发出;