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);


相关文章
|
23天前
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
67 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
25天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
64 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
25天前
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
54 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
1月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
176 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
1月前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
54 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
5月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
344 0
安卓项目:app注册/登录界面设计
|
2月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
43 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
3月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
122 11
|
6月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
196 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
6月前
|
存储 开发工具 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`配置文件夹、平台特定代码及共享代码等。
512 2

热门文章

最新文章