1.问题概述

设计一个通信录app

2.课程设计基本要求

(1)添加联系人

(2)查看所有联系人

(3)查看联系人的详情

(4)对联系人信息进行编辑修改、删除

(5)拨打联系人电话、发送短信

3.通信录设计需求分析

3.1可行性研究

在我们生活中,有如下四个通讯需求:

1.基础通话与通话记录

2.查看、管理联系人

3.收发短信

4.基于数据流量的社交与通讯工具

因此一个安卓通讯录app,对于手机来说是一项较为重要的功能。

3.1.1 技术可行性

随着计算机技术的发展,网络环境不断提升,各种通信工具技术也得到提高,同时,计算机软件开发越来越人性化,门槛更低,使用当前主流的JAVA语言完全可以开发出功能完善、简单易用的互联网程序,Android具备多种存储方式,对于通信录这样的轻量级数据,可以使用sharepreference或者sqlite和文件存储,技术上是可行的。

3.1.2 经济可行性

现在,计算机使用已经相当普遍,网络费用不断降低,开发通信录可以大大的方便管理联系人,同时开发此应用的周期短,Android也是开源免费,因此,此系统在经济方面是可行的。

3.1.3 操作可行性

目前是信息高速发展的时代,几乎人人都配备着手机,并且都会简单的使用手机,通信录无需复杂的操作,会用手机的人都能轻轻松松使用,因此操作上是可行的。

3.2 系统功能需求分析

通信录的主要功能是对联系人进行添加。浏览,修改和删除,同时具备快捷拨打电话。发送短信等功能。

3.2.1 系统功能模块概述

现在对此系统的功能模块进行概述。

1、功能主页模块:该模块用来为用户提供功能入口,用户可以选择添加联系人或者查看所有联系人

2、添加联系人模块:该模块下用户可以进行添加联系人。

3、查看所有联系人模块:该模块可以查看所有已添加的联系人信息。

4、联系人详情模块:该模块可以对联系人进行修改、删除或快捷拨打电话、发送信息

4.通信录概要分析

4.1通信录开发环境

本应用的开发语言是Java,所使用的IDE开发工具是Android Studio

4.2项目所需技术技术

本项目使用当前比较流行的MVC架构设计技术,利用Activity配合Fragment进行界面的切换显示,利用MMKV做数据存储读取

4.2.1 MMKV介绍

MMKV 是由腾讯开发的基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强,便于轻量级数据的存储。

4.2.2 Activity介绍

Activity是Android组件中最基本也是最为常见用的四大组件之一,Activity是一个应用程序组件,提供一个界面,用户可以用来交互为了完成某项任务。Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件。在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。

4.2.3 Fragment介绍

Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑,当前普通手机开发也会加入这个Fragment,可以把他看成一个小型的Activity,又称Activity片段,使用Fragment可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理,从而可以更加方便的在 运行过程中动态地更新Activity的用户界面。Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁。

4.3通信录应用的总体结构设计

通信录应用主要包括四大功能模块,主页菜单、添加联系人、浏览联系人、联系人详情等功能模块。总体模块如下所示。

待补充

5.通信录应用的详细设计

5.1功能主页设计

应用提供功能菜单,包括两个按钮菜单项,用来跳转到相应界面

5.1.1界面布局

包括两个按钮。用普通的线性布局包裹。

<?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="16dp">

    <Button
        android:textSize="18sp"
        android:text="添加联系人"
        android:id="@+id/addUser"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:textSize="18sp"
        android:text="查看所有联系人"
        android:id="@+id/allUser"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

点击添加按钮跳转到添加联系人的fragment:

view.findViewById(R.id.addUser).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ((MainActivity)getActivity()).jumpToAddPage();
        }
    });

实际上的跳转逻辑在Activity里:

public void jumpToAddPage() {
    AddUserFragment addUserFragment = (AddUserFragment) getSupportFragmentManager().findFragmentByTag("add");
    if (addUserFragment == null) {
        addUserFragment = new AddUserFragment();
    }
    MainFragment fragment = (MainFragment) getSupportFragmentManager().findFragmentByTag("main");
    if (fragment != null) {
        getSupportFragmentManager().beginTransaction().hide(fragment)
                .add(R.id.layout_container, addUserFragment)
                .addToBackStack(null)
                .commitAllowingStateLoss();
    } else {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.layout_container, addUserFragment)
                .addToBackStack(null)
                .commitAllowingStateLoss();
    }
}

点击查看所有联系人按钮跳转到所有联系人列表界面:

view.findViewById(R.id.allUser).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               ((MainActivity)getActivity()).jumpToAllPage();
           }
       });

实际上的跳转逻辑在Activity里:

public void jumpToAllPage() {
    AllUserFragment allUserFragment = (AllUserFragment) getSupportFragmentManager().findFragmentByTag("all");
    if (allUserFragment == null) {
        allUserFragment = new AllUserFragment();
    }
    MainFragment fragment = (MainFragment) getSupportFragmentManager().findFragmentByTag("main");
    if (fragment != null) {
        getSupportFragmentManager().beginTransaction().hide(fragment)
                .add(R.id.layout_container, allUserFragment)
                .addToBackStack(null)
                .commitAllowingStateLoss();
    } else {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.layout_container, allUserFragment)
                .addToBackStack(null)
                .commitAllowingStateLoss();
    }
}

5.2添加联系人设计

点击添加联系人后跳转到添加联系人界面,布局如下:

<?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="16dp">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:text="姓名:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <EditText
            android:maxLength="7"
            android:id="@+id/et_user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:text="电话:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <EditText
            android:maxLength="11"
            android:id="@+id/et_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <Button
        android:textSize="18sp"
        android:text="添加电话"
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:textSize="18sp"
        android:text="返回"
        android:id="@+id/back"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


</LinearLayout>

这个界面包括两个TextView和两个EditTextView还有两个Button。TextView起提示作用,EditText接收用户的输入,Button进行数据的保存和返回。点击保存的代码:

view.findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               String name = etName.getText().toString();
               String phone = etPhone.getText().toString();
               if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(phone)) {
                   CommonManager.getInstance().addUserInfo(new UserInfo(name, phone));
                   etName.setText("");
                   etPhone.setText("");
                   Toast.makeText(getActivity(),"添加成功!",Toast.LENGTH_LONG).show();
               }else {
                   Toast.makeText(getActivity(),"姓名或电话不能为空!",Toast.LENGTH_LONG).show();
               }
           }
       });

5.3查看所有联系人设计

布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/back"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="回到首页"
        android:textSize="18sp" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="姓名"
            android:textColor="@color/black"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="号码"
            android:textColor="@color/black"
            android:textSize="18sp" />
    </LinearLayout>


    <androidx.recyclerview.widget.RecyclerView
        android:layout_marginTop="8dp"
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         />


</LinearLayout>

其中最主要是用了RecyclerView进行用户信息的列表显示,RecycerView的适配器如下:

public class RvAdapter extends RecyclerView.Adapter<RvAdapter.ItemHolder> {
    private Context mContext;
    private List<UserInfo> mList;
    private ItemClickListener mItemClickListener;

    public RvAdapter(Context context, List<UserInfo> list) {
        mContext = context;
        mList = list;
    }

    public interface ItemClickListener {
        void onItemClick(UserInfo userInfo);
    }

    public void setOnItemClickListener(ItemClickListener itemClickListener) {
        mItemClickListener = itemClickListener;
    }

    @NonNull
    @Override
    public ItemHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(mContext).inflate(R.layout.rv_item, parent, false);

        return new ItemHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemHolder holder, int position) {
        UserInfo data = mList.get(position);
        holder.name.setText(data.name);
        holder.phone.setText(data.phone);
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mItemClickListener!=null){
                    mItemClickListener.onItemClick(data);
                }
            }
        });

    }

    @Override
    public int getItemCount() {
        return mList.size();
    }


    class ItemHolder extends RecyclerView.ViewHolder {
        private TextView name;
        private TextView phone;

        public ItemHolder(@NonNull View itemView) {
            super(itemView);
            name = itemView.findViewById(R.id.name);
            phone = itemView.findViewById(R.id.phone);
        }

    }
}

点击某一项,跳转到联系人详情页:

  rvAdapter.setOnItemClickListener(new RvAdapter.ItemClickListener() {
            @Override
            public void onItemClick(UserInfo userInfo) {
                ((MainActivity)getActivity()).jumpToDetailPage(userInfo);
            }
        });
真正跳转逻辑在activity里:
public void jumpToDetailPage(UserInfo userInfo) {
        UserDetailFragment userDetailFragment = (UserDetailFragment) getSupportFragmentManager().findFragmentByTag("detail");
        if (userDetailFragment == null) {
            userDetailFragment = new UserDetailFragment();
            Bundle bundle = new Bundle();
            bundle.putParcelable("userinfo",userInfo);
            userDetailFragment.setArguments(bundle);
        }
        AllUserFragment fragment = (AllUserFragment) getSupportFragmentManager().findFragmentByTag("all");
        if (fragment != null) {
            getSupportFragmentManager().beginTransaction().hide(fragment)
                    .add(R.id.layout_container, userDetailFragment)
                    .addToBackStack(null)
                    .commitAllowingStateLoss();
        } else {
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.layout_container, userDetailFragment)
                    .addToBackStack(null)
                    .commitAllowingStateLoss();
        }
    }

5.4联系人详情页设计

联系人详情页不仅要显示联系人的姓名和名称,还要能对联系人的信息进行修改、删除,也可以拨打电话和发送短信,布局如下:

<?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="16dp">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:text="姓名:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <EditText
            android:maxLength="7"
            android:id="@+id/et_user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:text="电话:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <EditText
            android:maxLength="11"
            android:id="@+id/et_phone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <LinearLayout
        android:layout_marginTop="8dp"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_weight="1"
            android:textSize="18sp"
            android:text="删除"
            android:id="@+id/delete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:layout_marginStart="16dp"
            android:layout_weight="1"
            android:textSize="18sp"
            android:text="修改"
            android:id="@+id/modify"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <EditText
        android:padding="8dp"
        android:hint="请输入信息内容"
        android:gravity="left"
        android:layout_marginTop="8dp"
        android:inputType="textMultiLine"
        android:layout_weight="1"
        android:id="@+id/et_msg"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

    <LinearLayout
        android:layout_marginTop="8dp"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_weight="1"
            android:textSize="18sp"
            android:text="拨打电话"
            android:id="@+id/call"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Button
            android:layout_marginStart="16dp"
            android:layout_weight="1"
            android:textSize="18sp"
            android:text="发送短信"
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <Button
        android:layout_marginTop="8dp"
        android:textSize="18sp"
        android:text="返回"
        android:id="@+id/back"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

有三个EditText,前两个是显示和编辑联系人的名字和号码,第三个是用来编辑发送的信息内容,删除按钮可以对信息进行删除,修改按钮对信息进行修改,拨打电话会立刻调动手机进行拨号,发送短信也会直接读取内容发送到相应号码上。删除代码如下:

  case R.id.delete:
                Toast.makeText(getActivity(), "删除成功!", Toast.LENGTH_LONG).show();
                CommonManager.getInstance().removeUserInfo(userInfo);
                getActivity().getSupportFragmentManager().popBackStack();
                break;
会显示一个删除成功的提示,并且对全局里的数据进行删除,然后返回上一页。
修改代码如下:
 case R.id.modify:
                String name = etName.getText().toString();
                String phone = etPhone.getText().toString();
                if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(phone)) {
                    userInfo.name = name;
                    userInfo.phone = phone;
                    Toast.makeText(getActivity(), "修改成功!", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(getActivity(), "姓名或电话不能为空!", Toast.LENGTH_LONG).show();
                }
                break;
把存储的数据替换为当前修改的信息,如果为空则提示并且不做修改。
拨打电话如下:
   private void callPhone() {
        String phoneNumber = etPhone.getText().toString();
        if (TextUtils.isEmpty(phoneNumber)) {
            Toast.makeText(getActivity(), "电话为空无法拨打电话!", Toast.LENGTH_LONG).show();
        } else {
            Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber));
            startActivity(intent);
        }
    }

调用系统的action进行拨号,拨号前需要申请系统权限。
发送信息如下:

private void sendSMS() {
      String content = etMsg.getText().toString().trim();
      String phone = etPhone.getText().toString().trim();
      if (!TextUtils.isEmpty(content) && !TextUtils.isEmpty(phone)) {
          SmsManager manager = SmsManager.getDefault();
          ArrayList<String> strings = manager.divideMessage(content);
          for (int i = 0; i < strings.size(); i++) {
              manager.sendTextMessage(phone, null, content, null, null);
          }
          Toast.makeText(getContext(), "发送成功", Toast.LENGTH_SHORT).show();
      } else {
          Toast.makeText(getContext(), "手机号或内容不能为空", Toast.LENGTH_SHORT).show();
          return;
      }

  }

调用系统的信息管理器发送信息,信息内容为编辑框里编辑的内容,如果内容为空则提示并且不发送

5.5数据的保存和恢复设计

在每次启动应用的时候会读取存储的数据,保存为全局数据:

String jsonStr = MMKV.defaultMMKV().decodeString("userinfolist");
     if (!TextUtils.isEmpty(jsonStr)) {
         List<UserInfo> userInfoList = CommonManager.getInstance().getUserInfoList();
         userInfoList.clear();
         try {
             Gson gson = new Gson();
             JsonArray array = JsonParser.parseString(jsonStr).getAsJsonArray();
             for (JsonElement jsonElement : array) {
                 userInfoList.add(gson.fromJson(jsonElement, UserInfo.class));
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }

用MMKV读取存储的数据,然后用Gson进行解析,放到数据集合中。
在关闭应用的时候进行数据保存:

@Override
  protected void onDestroy() {
      super.onDestroy();
      Gson gson = new Gson();
      String strJson = gson.toJson(CommonManager.getInstance().getUserInfoList());
      MMKV.defaultMMKV().encode("userinfolist", strJson);
  }

把数据集合转换json,用MMKV进行保存

6.系统的调试分析

6.1运行截图

功能主页:

添加用户:

所有联系人:

联系人详情页:

7.总结

经过这段开发,Android移动应用开发工作基本完成,相应的功能也基本实现,但系统的功能完善性还不是很好,由于本次系统开发受到我的编码能力的限制,因此本系统还存在很多方面的不足。

通过开发这个系统,我掌握了基本的过程,学习了知识的同时还掌握了开发系统的基本的相关操作,同时,对于java的操作也更加熟练了。