接着上面的实现,逐步拓展几个功能。
比如实现查看通讯录,以及直接通过点击联系人条目实现拨号。
实际上功能往往伴随着组件的熟练掌握和使用。当掌握所有组件的使用的时候大概就可以随心所欲玩出花来。
对于拿到通讯录并展示主要涉及到的技术就是——ListView组件的使用(学完RecyclerView也可以完成这个功能,而且好像更常用耶,但是目前这个简单点,暂时学习使用)。
看了上一次的基本才能理解这一次在干啥,上一次文章直通车:Android开发——实现一个拨号器(一)_Biangbangbing的博客-CSDN博客
功能描述
在主页面点击联系人按钮,跳转到通讯录页面。
通讯录页面显示若干联系人条目,一个条目的内容包括:联系人姓名,电话号(还可以自行设计增添其他内容)。
工程结构
增加一个联系人页面,一对应的活动类;另外还要增加一个条目的布局设计,以及游标类,联系人类。
设计&coding
1.最开始要记得设计好在主页面,点击联系人按键之后,跳转到通讯录这个页面。
2.再看通讯录页面的布局设计activity_contact.xml
- 先设计整体的布局为线性布局。设置宽,高,以及垂直排布。
- 线性布局内部包含一个文字title——TextView,设置id,宽,高,文字,文字大小,文字颜色,左右边距等等。(注意:padding是内边距,margin是外边距)
- 文字框下面,线性布局内第二个组件是:ListView,存放一个个联系人条目。设计id,宽高边距等。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Contacts"
android:textSize="24sp"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:textColor="@color/black">
</TextView>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
>
</ListView>
</LinearLayout>
3.看ListView的一个条目应该张什么样。activity_list_item.xml(这个没有设计相应的活动,以逆袭需要在layout文件下手动建立)
- 上来就是一个线性布局,设计内容也是id,宽高,边距,还有垂直/水平分布。(我选的垂直)
- 然后线性布局内部两个文本框textview,一个放姓名,一个放联系方式。他们的text标签先随便填,到时候会被取出的实际数据覆盖掉。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="name"
android:textSize="24sp"
android:textColor="@color/black"></TextView>
<TextView
android:id="@+id/telno"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="telno"
android:textSize="20sp"
android:textColor="@color/black"></TextView>
</LinearLayout>
4.接着来看活动类如何设计——如何拿到实际的通讯录信息,并且将数据填充到listview的每一个条目里。
- Contactor.java 联系人(类/结构体)
- 属性包括姓名和电话号,都是String。
- 分别包含构造函数、以及两个属性的set,get方法。
package com.example.bydialer.listView;
public class Contactor {
private String name;
private String telno;
public Contactor(String name, String telno) {
this.name = name;
this.telno = telno;
}
public void setName(String name) {
this.name = name;
}
public void setTelno(String telno) {
this.telno = telno;
}
public String getName() {
return name;
}
public String getTelno() {
return telno;
}
}
- MyListAdapter.java 控制listview显示情况的一个类
- 设计传进去的数据list
- list的条目个数
- 如何让一条数据对应显示到list的一个条目里
这个地方视频没有讲解的很清楚,有一篇博文写的很清晰:挂大佬文章——>BaseAdapter使用教程及方法详解 - 简书
package com.example.bydialer.listView;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.bydialer.R;
import java.util.ArrayList;
public class MyListAdapter extends BaseAdapter {
private Context context;
private LayoutInflater layoutInflater;
private ContentResolver contentResolver;
private ArrayList<Contactor> list;
public MyListAdapter(Context context,ArrayList<Contactor> list){
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
static class ViewHolder{
public TextView name,telno;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = this.layoutInflater.inflate(R.layout.activity_list_item,null);
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.telno = (TextView) convertView.findViewById(R.id.telno);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
//给控件赋值
holder.name.setText(list.get(position).getName());
holder.telno.setText(list.get(position).getTelno());
return convertView;
}
}
- ContactActivity.java 联系人页面,负责得到联系人数据并且传给MyListAdapter
- 设计储存所有联系人的联系人列表ArrayList<Contactor> list,内容访问者ContentResolover
- 通过getContentResolver方法获取内容访问者,通过内容访问者拿到条目游标,遍历游标,取出联系人姓名,电话号,储存在ArrayList<Contactor> list。
- 设计listview的Adapter管理器,并且传入刚刚获取额联系人信息list。
- 设计点击列表条目之后给目标联系人拨打电话的方法,获取到条目的电话号并拨打这个号码。
package com.example.bydialer;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.bydialer.listView.Contactor;
import com.example.bydialer.listView.MyListAdapter;
import java.util.ArrayList;
public class ContactActivity extends AppCompatActivity {
private ContentResolver contentResolver;
private ArrayList<Contactor> list = new ArrayList<>();
private ListView mlv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contact);
mlv = findViewById(R.id.lv);
//获取内容访问者
this.contentResolver = getContentResolver();
//拿到所有通讯录的姓名
Uri uri=Uri.parse("content://com.android.contacts/raw_contacts");
Cursor cursor=contentResolver.query(uri,null,null,null,null);
while(cursor.moveToNext()){
@SuppressLint("Range") String _id=cursor.getString(cursor.getColumnIndex("_id")); //拿到id
@SuppressLint("Range") String display_name=cursor.getString(cursor.getColumnIndex("display_name")); //拿到姓名
Log.i("test",""+_id+"\t\t\t"+display_name);
Uri uriData=Uri.parse("content://com.android.contacts/raw_contacts/"+_id+"/data"); //根据id拿到这个对象的所有信息
Cursor contactsInfo=contentResolver.query(uriData,null,null,null,null);
while (contactsInfo.moveToNext()){
@SuppressLint("Range") String mimetype=contactsInfo.getString(contactsInfo.getColumnIndex("mimetype"));
@SuppressLint("Range") String data1=contactsInfo.getString(contactsInfo.getColumnIndex("data1"));
if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
list.add(new Contactor(display_name,data1));
Log.i("test",""+mimetype+"\t\t\t"+data1);
}
}
}
mlv.setAdapter(new MyListAdapter(ContactActivity.this,this.list));
mlv.setOnItemClickListener( new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView tv = (TextView) findViewById(R.id.telno);
String str = tv.getText().toString();
System.out.println("str:"+str);
if ((str !=null) && (!str.trim().equals(""))){
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+str));
startActivity(intent);
}
}
});
}
}
这样基本功能就设计完了,测试跑一下
测试验证
小tips&问题待解决(提出的时候然后顺手。。解决了)
- 一开始获取联系人信息放在了MyListAdapter 里面,然后获取的时候巨卡无比,要等很久才会显示出来信息,执行的时间都是一次,应该是顺序不对,而且要通过listview获得信息访问者。拿到信息然后游标直接遍历list比较合乎常理。
- 有一个bug在于我点击联系人想要拨打电话的时候,会发现点击的人和最终拨打出去的号不一致,感觉游标的相对位置大概获取的有问题,检测点击条目之后得到的条目号码在运行台输出是和最终拨打的情况是一致的。
=>然后就发现自己的问题,原本去找电话号码的时候没有加view.,是直接findViewById,这 样找到的应该是随机一个。view.findViewById才是从list的当前点击的这个条目view中找 电话号的TextView这一项,修改之后就可以准确给点击的条目拨打电话了。
ps:图片中修改的是ContactActivity.java 点击条目拨打的方法,最上面给的时候还没有修改。
上次文章的链接也还没改呀。
上次也挂出来的项目链接:GitHub - Biangbangbing/ByDial
其他功能有待学习更多的组件以及叠buff。欢迎各位大佬提出建议!