Android——一个神奇的通讯录APP

简介: 联系人总览效果图SQLite数据库建表CRUD增删改查滚动条联系人分组数据库数据与滚动条绑定添加联系人效果图添加紧急电话联系人数据源添加新增编辑与查看联系人查看效果图大图姓氏数据收发发收编辑及删除删除拨号申请权限隐式Intent


联系人总览


效果图

image.png


SQLite数据库

建表

使用常量定义需要使用的行,方便后期更改;简洁,方便

public class Helper extends SQLiteOpenHelper {
    /**数据库名称*/
    public static final String DataBase = "MailList.db";
    public static final SQLiteDatabase.CursorFactory factory = null;
    public static int version = 1;
    /**表名*/
    public static final String TableName = "Contacts";
    /**行名*/
    public static final String Row_LastName = "LastName";
    public static final String Row_FirstName = "FirstName";
    public static final String Row_PinYin = "PinYin";
    public static final String Row_Phone = "PhoneNumber";
    public static final String Row_School = "School";
    public static final String Row_Address = "Address";
    public static final String Row_Other = "Other";
    public Helper(@Nullable Context context) {
        super( context, DataBase, factory, version );
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table " + TableName + "( "+Row_Phone+" varchar(20) primary key, "+Row_LastName+" varchar(20),"+Row_FirstName+" varchar(20),"+Row_PinYin+" varchar(20), "+Row_School+" varchar(20),"+Row_Address+" varchar(20),"+Row_Other+" varchar(20));";
        db.execSQL( sql );
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists "+TableName);
        onCreate(db);
    }
}

CRUD

将SQLite数据库的CRUD操作封装为一个Dao类,便于管理


public void Insert(News news){
        DB = helper.getReadableDatabase();
        if (DB.isOpen())
        {
            ContentValues values = new ContentValues();
            values.put( Helper.Row_Phone,news.getPhoneNumber());
            values.put( Helper.Row_LastName,news.getLastName());
            values.put( Helper.Row_FirstName,news.getFirstName());
            values.put(Helper.Row_PinYin,news.getPinYin());
            values.put( Helper.Row_School,news.getSchool());
            values.put( Helper.Row_Address,news.getAddress());
            values.put(Helper.Row_Other,news.getOther());
            long RowId = DB.insert(Helper.TableName,null,values);
            if(RowId == -1)
                Log.i(TAG, "数据插入失败!");
            else
                Log.i(TAG, "数据插入成功!"+RowId);
        }
    }


列出根据某个字段删掉某行和删掉表中所有数据两种方法

public void Delete(String Phone){
        DB = helper.getReadableDatabase();
        if (DB.isOpen()){
            String whereClause = "PhoneNumber = ?";
            String[] whereArgs = {Phone + ""};
            int count = DB.delete(Helper.TableName, whereClause, whereArgs);
            if (count > 0)
                Log.i(TAG, "删除了: " + count + "行");
            else
                Log.i(TAG, "数据未删除!");
            DB.close(); // 数据库关闭
        }
    }
    public void DeleteAll(){
        DB = helper.getReadableDatabase();
        if (DB.isOpen()){
            String whereClause = null;
            String[] whereArgs = null;
            int count = DB.delete(Helper.TableName, whereClause, whereArgs);
            if (count > 0)
                Log.i(TAG, "删除了: " + count + "行");
            else
                Log.i(TAG, "数据未删除!");
            DB.close(); // 数据库关闭
        }
    }


以PhoneNumber为索引改该行的数据元素

public void Update(String PhoneNumber,String School,String Addr,String Other){
        DB = helper.getReadableDatabase();
        if (DB.isOpen()){
            ContentValues values = new ContentValues();
            values.put( Helper.Row_School,School);
            values.put( Helper.Row_Address,Addr);
            values.put(Helper.Row_Other,Other);
            int count = DB.update(Helper.TableName,values,"PhoneNumber = ?",new String[]{PhoneNumber});
            if (count > 0){
                Log.d(TAG,"update password row="+count);
            }else {
                Log.d(TAG,"update fail");
            }
            DB.close();
        }
    }


获取表中所有数据

public List<News> QueryAll() {
        DB = helper.getReadableDatabase();  // 获得一个只读的数据库对象
        if(DB.isOpen()) {
            String[] columns = {Helper.Row_Phone, Helper.Row_LastName,Helper.Row_FirstName,Helper.Row_PinYin,Helper.Row_School,Helper.Row_Address,Helper.Row_Other};  // 需要的列
            String selection = null;    // 选择条件, 给null查询所有
            String[] selectionArgs = null;  // 选择条件的参数, 会把选择条件中的? 替换成数据中的值
            String groupBy = null;  // 分组语句  group by name
            String having = null;   // 过滤语句
            String orderBy = null;  // 排序
            Cursor cursor = DB.query(Helper.TableName, columns, selection, selectionArgs, groupBy, having, orderBy);
            String last;
            String first;
            String pinyin;
            String phone;
            String school;
            String addr;
            String other;
            if(cursor != null && cursor.getCount() > 0) {
                List<News> news = new ArrayList<News>();
                while(cursor.moveToNext()) {
                    last = cursor.getString(cursor.getColumnIndex( Helper.Row_LastName ));
                    first = cursor.getString(cursor.getColumnIndex( Helper.Row_FirstName ));
                    phone = cursor.getString(cursor.getColumnIndex( Helper.Row_Phone));
                    pinyin = cursor.getString(cursor.getColumnIndex( Helper.Row_PinYin));
                    school = cursor.getString(cursor.getColumnIndex(Helper.Row_School));
                    addr = cursor.getString(cursor.getColumnIndex( Helper.Row_Address ));
                    other = cursor.getString( cursor.getColumnIndex( Helper.Row_Other ) );
                    news.add(new News(last,first,pinyin,phone,school,addr,other));
                }
                DB.close();
                return news;
            }
            if (cursor != null) {
                cursor.close();
            }
            DB.close();
        }
        return null;
    }


联系人分组

private void InitContacts(){
        newsList = dao.QueryAll();
        RemoveAll();
        String curGroup = "0";
        ArrayList<String> indexList = new ArrayList<>();
        if (newsList != null){
            for (int i = 0; i <newsList.size() ; i++) {
                ContactsInfo info = new ContactsInfo(newsList.get(i).getLastName()+newsList.get(i).getFirstName(),newsList.get(i).getPinYin(),newsList.get(i).getPhoneNumber(),false,false);
                if (!info.getGroup().equals(curGroup)) {
                    // group 信息与保存的不一致, 那么就是该 group 的第一个
                    info.setIsFirstInGroup(true);
                    // 同时将该 group 信息添加到索引中
                    indexList.add(info.getGroup());
                    // 它的上一个城市就是上一个 group 的最后一个
                    if (i > 0) {
                        contactsInfoList.get(contactsInfoList.size() - 1).setIsLastInGroup(true);
                    }
                    curGroup = info.getGroup();
                }
                contactsInfoList.add(info);
            }
        }
    }


数据库数据与滚动条绑定

 SideBar.setOnTouchLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {
            @Override
            public void onTouchingLetterChanged(String s) {
                int position = Adapter.getPositionForSection(s.charAt(0));
                if (position != -1) {
                    manager.scrollToPositionWithOffset(position, 0);
                }
            }
        });


添加联系人



效果图

image.png


添加紧急电话联系人

一开始添加紧急联系人的好处有两点:

  1. 救命稻草,时期必有急用!!!
  2. 防止数据库为空,避免尚未添加联系人时RecyclerView为空


数据源

public class Emergency {
   //lastname firstname 拼音 tel
    public static final String[][] PoliceTel = {
            {"公安","报警电话","gongan","110"},
           {"火警","报警电话","huojing","119"},
           {"急救中心","电话","jijiu","120"},
           {"交通事故","报警电话","jiaotong","122"}};
}


添加

//    添加紧急报警电话
    private void AddEmergency(){
        try {
            newsList = dao.QueryAll();
        }catch (NullPointerException e){
            for (int i = 0; i < 4; i++) {
                dao.Insert(new News(Emergency.PoliceTel[i][0],Emergency.PoliceTel[i][1],Emergency.PoliceTel[i][2],Emergency.PoliceTel[i][3],"-","-","-"));
            }
        }
    }


新增

对用户输入的数据进行一下判断,然后对数据库进行插入操作。返回主界面后,对适配器进行一个刷新即可显示

private void AddContacts(){
        String lastname = LastName.getText().toString();
        String firstname = FirstName.getText().toString();
        String pinyin = ChatExchange.getInstance().getPinyin(lastname+firstname);
        String phone = PhoneNumber.getText().toString();
        String school = School.getText().toString();
        String addr = Address.getText().toString();
        String other = Other.getText().toString();
        if (TextUtils.isEmpty(lastname)){
            Tips.setText("姓氏不能为空");
            Error.show();
            return;
        }
        if (TextUtils.isEmpty(firstname)){
            Tips.setText("名字不能为空");
            Error.show();
            return;
        }
        if (TextUtils.isEmpty(phone)){
            Tips.setText("手机号码不能为空");
            Error.show();
            return;
        }
        if (newsList != null){
            for (int i = 0; i <newsList.size() ; i++) {
                if (phone.equals(newsList.get(i).getPhoneNumber())){
                    Tips.setText("该手机号码已被绑定");
                    return;
                }
            }
        }
        News news = new News(lastname,firstname,pinyin,phone,school == null ? " ":school,addr == null ? " ":addr,other == null ? " ":other);
        dao.Insert(news);
        Tips.setText("插入成功");
        Error.show();
    }


编辑与查看联系人



查看

效果图

image.png


大图姓氏

姓氏背景是一个渐变色xml文件

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <gradient android:startColor="#BDB8B8" android:endColor="#E97878"/>
</shape>

姓氏通过截取LastName字段的第一个元素,大部分LastName为一个字,单避免复姓以及乱输入等情况,就截取第一个字符。姓名就结合LastName和FirstName

 LastName.setText(newsList.get(i).getLastName().substring(0,1));
Name.setText(newsList.get(i).getLastName()+newsList.get(i).getFirstName());


数据收发

在联系人总览界面跳转联系人详情界面时,对RecyclerView子项进行点击事件监听,并将此子项的手机号码一并传输

  private void Listener(){
        Adapter.setOnclick(new AllManAdapter.OnClick() {
            @Override
            public void OnClickListener(View view, int Position) {
                Intent intent = new Intent(ContactsActivity.this,DetailsActivity.class);
                String Phone = contactsInfoList.get(Position).getCityID();
                intent.putExtra("PhoneNumber",Phone);
                startActivity(intent);
            }
        });
    }


从主界面获取intent携带的数据,并以此为索引,遍历数据库,获取与它相关行的所有数据

PhoneNumber = getIntent().getStringExtra("PhoneNumber");
        if (!TextUtils.isEmpty(PhoneNumber)){
            Phone.setText(PhoneNumber);
        }
try{
            newsList = dao.QueryAll();
        }catch (NullPointerException e){
            e.printStackTrace();
        }
        if (newsList != null){
            for (int i = 0; i <newsList.size() ; i++) {
                if (PhoneNumber.equals(newsList.get(i).getPhoneNumber())){
                    Address.setText(newsList.get(i).getAddress());
                    School.setText(newsList.get(i).getSchool());
                    Note.setText(newsList.get(i).getOther());
                    LastName.setText(newsList.get(i).getLastName().substring(0,1));
                    Name.setText(newsList.get(i).getLastName()+newsList.get(i).getFirstName());
                }
            }
        }


编辑及删除

image.png

#### 编辑 点击编辑按钮之后,对一些EditText进行解封,以便用户可以进行编辑 ```

public void OtherThings(View view){
    count++;
    if (count %2 == 0){
        //编辑状态
        UnLock();
        EditMessage.setText("完成");
    }else {
        //正常状态
        Lock();
        UpdateSQL();
        EditMessage.setText("编辑");
    }
}

最后将用户修改的数据以手机号码为媒介对数据库进行更新操作

 private void UpdateSQL(){
        String addr = Address.getText().toString();
        String school = School.getText().toString();
        String node = Note.getText().toString();
        dao.Update(PhoneNumber,school,addr,node);
    }


删除

直接以手机号码为媒介对数据库该行进行删除操作即可

dao.Delete(PhoneNumber);


拨号

申请权限

静态申请

<uses-permission android:name="android.permission.CALL_PHONE" />

动态申请

 if (ActivityCompat.checkSelfPermission(DetailsActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(DetailsActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
                    startActivity(intent);
                    return;
                }


隐式Intent

Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + PhoneNumber));
startActivity(intent);


相关文章
|
30天前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
110 0
安卓项目:app注册/登录界面设计
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
104 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
2月前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
107 2
|
2月前
|
XML Android开发 数据格式
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
在全球化背景下,实现Android应用的国际化与本地化至关重要。本文以一款旅游指南App为例,详细介绍如何通过资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗等步骤,完成多语言支持和本地化调整。通过邀请用户测试并收集反馈,确保应用能无缝融入不同市场,提升用户体验与满意度。
81 3
|
29天前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
155 0
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
67 10
|
2月前
|
XML 数据库 Android开发
10分钟手把手教你用Android手撸一个简易的个人记账App
该文章提供了使用Android Studio从零开始创建一个简单的个人记账应用的详细步骤,包括项目搭建、界面设计、数据库处理及各功能模块的实现方法。
|
3月前
|
API Android开发
Android P 性能优化:创建APP进程白名单,杀死白名单之外的进程
本文介绍了在Android P系统中通过创建应用进程白名单并杀死白名单之外的进程来优化性能的方法,包括设置权限、获取运行中的APP列表、配置白名单以及在应用启动时杀死非白名单进程的代码实现。
60 1
|
3月前
|
IDE Java 开发工具
探索安卓开发之旅:打造你的第一款App
【8月更文挑战第24天】在这篇文章中,我们将一起踏上激动人心的安卓开发之旅。不论你是编程新手还是希望扩展技能的老手,本文将为你提供一份详尽指南,帮助你理解安卓开发的基础知识并实现你的第一个应用程序。从搭建开发环境到编写“Hello World”,每一步都将用浅显易懂的语言进行解释。那么,让我们开始吧!
|
3月前
|
开发工具 Android开发