-
2021-05-29 16:56:34
项目目录
一、项目概述
本次项目主要实现了对联系人信息(姓名和电话)的增加、删除、修改和查询功能,在删除和修改用户前,可以通过姓名搜索到指定联系人的信息,方便用户操作。通过SQLiteDatabase来存储数据,SQLite数据库和其他的SQL数据库不同, 我们并不需要在手机上另外安装一个数据库软件,Android系统已经集成了这个数据库,功能非常强大,而且使用也很方便快捷。我们下面会详细介绍SQLite数据库。
二、开发环境
开发环境还是老样子哈,Android Studio版本3.6.1及以上都可以顺利运行。
三、详细设计
1、主界面的搭建
最外层布局采用线性布局LinearLayout。orientation方向选择的也是vertical垂直排列。下面先是放置了一个小的LinearLayout,里面先放一个文本框来显示标题,再放一个图片框,显示图标。然后不断调整内外边距,找到最合适的位置。
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="通讯录" android:textSize="40sp" android:textStyle="bold" android:textColor="@color/DarkCyan" android:layout_margin="20dp" android:gravity="center"/> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/contact" android:layout_marginBottom="10dp" android:layout_gravity="center"/>
下面是四个操作按钮,id分别以它们的功能命名,其实四个按钮除了id和text内容不同,其他都是一样的,背景background选择的是写好的btn_selector,在其中设置了按钮的外边框和弧度,而且按钮按下时会有相应的颜色变化。
<Button android:id="@+id/main_add" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="添加联系人" android:gravity="center" android:textColor="@color/Honeydew" android:background="@drawable/btn_selector" android:textSize="20sp" android:layout_margin="10dp"/> <Button android:id="@+id/main_delete" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="删除联系人" android:gravity="center" android:textColor="@color/Honeydew" android:background="@drawable/btn_selector" android:textSize="20sp" android:layout_margin="10dp"/> <Button android:id="@+id/main_update" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="修改联系人" android:gravity="center" android:textColor="@color/Honeydew" android:background="@drawable/btn_selector" android:textSize="20sp" android:layout_margin="10dp"/> <Button android:id="@+id/main_query" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="查询联系人" android:gravity="center" android:textColor="@color/Honeydew" android:background="@drawable/btn_selector" android:textSize="20sp" android:layout_margin="10dp"/>
我们看下主界面的示意图:
2、SQLite数据库
2.1、SQLite是什么?
SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用, 不仅支持标准SQL语法,还遵循ACID(数据库事务)原则,无需账号,使用起来非常方便!Android内置的SQLite是SQLite 3版本的。
2.2、为什么要用SQLite?
前面我们学习了使用文件与SharedPreference来保存数据,但是在很多情况下,文件并不一定是有效的,如多线程并发访问是相关的;app要处理可能变化的复杂数据结构等等。比如银行的存钱与取钱!使用前两者就会显得很无力或者繁琐,数据库的出现可以解决这种问题,而Android又给我们提供了这样一个轻量级的SQLite,那肯定要好好用啊。
2.3、SQLite有什么特点?
SQLite支持五种数据类型:NULL,INTEGER,REAL(浮点数),TEXT(字符串文本)和BLOB(二进制对象) 虽然只有五种,但是对于varchar,char等其他数据类型都是可以保存的;因为SQLite有个最大的特点:
你可以将各种数据类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。
比如你可以在Integer类型的字段中存放String字符串,当然除了声明为主键INTEGER PRIMARY KEY的字段只能够存储64位整数。
另外,SQLite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息。如下面语句会忽略 name字段的类型信息:CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))
SQlite通过文件来保存数据库,一个文件就是一个数据库,数据库中又包含多个表格,表格里又有 多条记录,每个记录由多个字段构成,每个字段有对应的值,每个值我们可以指定类型,也可以不指定 类型(主键除外) 3、SQLite相关的类
3.1、SQLiteOpenHelper
抽象类,我们通过继承该类,然后重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库。
3.2、SQLiteDatabase
数据库访问类:我们可以通过该类的对象来对数据库做一些增删改查的操作。最核心的类!
3.3、Cursor
游标,有点类似于JDBC里的resultset,结果集!可以简单理解为指向数据库中某 一个记录的指针。
4、创建数据库与版本管理
Android给我们提供了SQLiteOpenHelper的两个方法:onCreate( )与onUpgrade( )来实现
onCreate(database):首次使用软件时生成数据库表。
onUpgrade(database,oldVersion,newVersion):在数据库的版本发生变化时会被调用, 一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的。class MyHelper extends SQLiteOpenHelper{ public MyHelper(Context context){ super(context,"dzh.db",null,1);//数据库文件夹名为dzh.db } //数据库第一次创建时被调用 @Override public void onCreate(SQLiteDatabase db){ db.execSQL("CREATE TABLE person(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20),phone VARCHAR(20))"); } //软件版本号发生改变时调用 @Override public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){ }
上述代码第一次启动应用,我们会创建这个dzh.db的文件,并且会执行onCreate()里的方法, 创建一个person的表,它有两个字段:主键personId和name字段;接着如果我们修改db的版本号,那么下次启动就会调用onUpgrade()里的方法,这里我们没有去填写方法,感兴趣的小伙伴可以试一下!这就是创建数据库的模板代码!
5、实现增删改查
本项目使用Android给我们提供的操作数据库的一些API方法,先将它们集成在ContactsDao中,然后在增删改查四个Activity中,直接调用对应方法即可。下面教大家如何使用数据库进行增删改查操作:
- 首先打开数据库,想要访问数据库,第一步肯定先打开。
//打开数据库 public void open() throws SQLException { myDBOpenHelper=new MyDBOpenHelper(context); try { sqLiteDatabase=myDBOpenHelper.getWritableDatabase(); }catch (SQLException e){ sqLiteDatabase=myDBOpenHelper.getReadableDatabase(); } }
- 有开就有关,在操作完数据库后记得关闭数据库。
//关闭数据库 public void close(){ if(sqLiteDatabase!=null){ sqLiteDatabase.close(); sqLiteDatabase=null; } }
- 添加数据,想要添加数据就是通过values的键值对进行插入数据。
//添加联系人 public long addContacts(Contacts c){ //先创建集合对象 ContentValues values=new ContentValues(); //插入键值对 values.put("name",c.name); values.put("phone",c.phone); return sqLiteDatabase.insert("contacts",null,values); }
- 删除数据,肯定是根据主键来删除对应的记录,用的是where约束。
//删除联系人 public int deleteContacts(Contacts c){ return sqLiteDatabase.delete("contacts","name=?",new String[]{String.valueOf(c.name)}); }
- 修改数据,也是通过where约束找到对应键的记录,然后修改它的其他非键属性。
//修改联系人 public int updateContacts(Contacts c){ ContentValues value=new ContentValues(); value.put("phone",c.phone); return sqLiteDatabase.update("contacts",value,"name=?",new String[]{String.valueOf(c.name)}); }
- 查询数据,使用cursor游标,从第一行遍历到最后一行,在遍历每一行记录中,获得到每个字段的值,这样就获取到所有对象的数据了。
//根据姓名查找联系人 public Contacts getContacts(String name){ Cursor cursor=sqLiteDatabase.query("contacts",null,"name=?",new String[]{name},null,null,null); Contacts contacts=new Contacts(); while(cursor.moveToNext()){ contacts.name=cursor.getString(cursor.getColumnIndex("name")); contacts.phone=cursor.getString(cursor.getColumnIndex("phone")); } return contacts; }
四、项目效果
通讯录的运行演示视频如下:
Android Studio实现一个简单的通讯录
五、项目总结
本次通讯录项目主要考验学生对于SQLite数据库的使用,还是比较基础的,刚接触Android不久的同学只要动手打一遍就掌握了。这些数据库知识点在今后的Android项目中会经常使用,因此希望大家能够熟练掌握SQLite的使用,方便后续开发项目。
六、源码下载
点击右侧链接,即可下载源码:👉Android Studio实现通讯录项目👈
🍉还可以关注我的公众号《萌新加油站》,🍍后台回复:通讯录
🚀这有你错过的精彩内容
这凡尘到底有什么可留恋的?
原来,都是这些小欢喜啊。
它们在我的生命里唱着歌,跳着舞。
活着,也就成了一件特别让人不舍的事情。
2016.3.12 17:34 YXN更多相关内容 -
Android studio 通讯录开发
2019-01-22 12:15:39使用Android开发 简单 强悍 适合初学者,代码具有登录界面,注册界面,添加通讯录联系人,修改联系人,删除联系人,查询联系人等多个功能,简单使用十分适合初学者练习 参考使用 内有apk 可直接在手机上运行 -
Android Studio实现通讯录
2021-05-30 11:12:11本次通讯录项目主要实现了联系人信息(姓名和电话)的增加、删除、修改和查询功能。主要用到SQLiteDatabase来存储数据,SQLite数据库和其他的SQL数据库不同, 我们并不需要在手机上另外安装一个数据库软件,Android... -
Android studio—读取通讯录
2020-12-19 16:57:06用于与博客匹配的资源,简单学习Android 中读取通讯录的操作,方便大家进行学习和思考。仅用于学习参考,谢谢 -
Android Studio 做的小型通讯录
2020-03-28 15:07:52运用Android Studio练习的一个通讯录App,其中存储数据用的是SQLite数据库,能够实现联系人信息的增、删、改、查。 并且还能实现打电话功能和发短信功能。 -
Android Studio实现通讯录项目,优秀课设,简单易上手,小白必看!
2022-02-02 23:39:10本次项目主要实现了对联系人信息(姓名和电话)的增加、删除、修改和查询功能,在删除和修改用户前,可以通过姓名搜索到指定联系人的信息,方便用户操作。使用SQLiteDatabase数据库来存储数据,界面简洁优雅,美观... -
Android Studio简易通讯录界面.rar
2019-11-20 16:40:021、获取手机通讯录的信息;2、手机通讯录的数据封装;3、手机通讯录的信息的UI适配;4、对ListView的优化。 -
AndroidStudio安卓课设-简易通讯录
2020-07-02 15:23:55基于SQLite开发的简易通讯录 共两个页面:启动页+主页面 主页面实现增删改查、读取系统联系人、拨号等功能 非常非常非常简陋,所有功能都在主页面实现,没有封装类 -
android Studio通讯录
2020-03-29 12:05:45帮别人做的期末考试作业· 1.大纲 2.目录结构 3.显示 4.源码展开全文 -
Android studio 安卓通讯录开发
2019-01-22 11:55:57程序是使用 Android studio 开发的简单通讯录 适合刚入门的Android程序员,有对通讯录的增删改查功能,有登录界面 和验证密码功能 有数据库的简单练习 -
android实战 绿豆通讯录(android studio源程序工程)
2020-01-19 14:52:47包括Android 移动开发基础案例教程课本中第五章实战演练——绿豆通讯录的源代码,可用Android studio运行,适合初学者学习。 -
基于Android Studio开发的安卓的通讯录管理app
2022-05-24 16:20:41功能包含:新增联系人、编辑联系人、删除联系人、拨打电话、发送短信等相关操作。 资源包含源码:1、apk安装包 2、演示视频 3、 基本安装环境、4、运行文档 5、以及源代码 -
基于Android电话通讯录
2018-03-16 10:10:39使用Android Studio平台,自定义适配器,利用SQLite的数据库技术,实现在手机电话通讯录。功能有:查看所有联系人,添加联系人,修改联系人信息,删除联系人。 -
android实现通讯录源码
2021-03-26 13:00:33用Java实现的简单通讯录源码,运行环境:Eclipse,SQLite开发软件:Eclipse-helios、JDK1.6、Android SDK 、ADT10.0 -
自己做的通讯录,android studio版本的,demo性质,适合入门
2018-03-13 10:15:05自己闲暇时间做的安卓通讯录,是用android studio 搭建的,直接可以导入使用,适合入门的新手使用,本人也是初入者,所以大家一起学习 -
Android Studio 获取手机联系人项目main文件
2022-03-14 12:45:02Android Studio 获取手机联系人并进行,发短信,打电话,增删“改”操作 -
android 简易通讯录(sqlite示例)
2018-12-31 15:00:31android 简易通讯录,使用sqlite数据库,开发环境idea。 -
Android 通讯录 Android contact源代码
2021-03-16 20:27:13摘要:Java源码,Android源码,通讯录 Android contact通讯录实例,比较不错的例子,来自书中的完整实例,Android手机读取联系人信息、显示、保存、编辑联系人,多卡同时读取联系人等,涉及要点:删除指定数据列、URI... -
Android Studio实现发短信功能
2020-08-30 05:32:56主要介绍了Android Studio实现发短信功能,非常不错,具有参考借鉴价值,需要的朋友可以参考下 -
Android Studio 实验四:手机通讯录(ListView的应用)
2022-04-25 15:27:44Android Studio 实验四:手机通讯录(ListView的应用)目录
activity_main.xml
<?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"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
FruitAdapter
(虽然是通讯录,但是因为书上是拿水果举的例子,所以文件的命名也就照搬了)在包内创建一个Java类并命名为FruitAdapter,输入代码如下:
public class FruitAdapter extends ArrayAdapter<Fruit> { private int resouceId; public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) { super(context, textViewResourceId, objects); resouceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resouceId,parent,false); } else { view = convertView; } ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); fruitImage.setImageResource(fruit.getImageId()); fruitName.setText(fruit.getName()); return view; } }
Fruit类
在包里创建Java类并命名为Fruit,代码如下:
public class Fruit { private String name; private int imageId; public Fruit(String name, int imageId) { this.name = name; this.imageId = imageId; } public String getName() { return name; } public int getImageId() { return imageId; } }
fruit_item.xml
创建layout文件并命名为fruit_item,代码如下:
<?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="wrap_content"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp"/> </LinearLayout>
Activity
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits();//初始化水果数据 FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item,fruitList); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); } //对图片进行加载,图片放在mian/res/drawable里面 private void initFruits() { for(int i=0; i < 3; i++) { Fruit apple = new Fruit("周杰伦",R.drawable.image04); fruitList.add(apple); Fruit banana = new Fruit("哆啦",R.drawable.image02); fruitList.add(banana); Fruit orange = new Fruit("天皇",R.drawable.image03); fruitList.add(orange); } } }
运行截图
项目文件
-
android简易通讯录
2012-10-21 00:43:06android简易通讯录,适合入门新手.本程序适合初学者学习项目开发技巧. -
Android Studio中的手机通讯录开发
2021-01-18 16:40:53Android Studio中的手机通讯录,包含功能(按首字母排序,动态添加) 第一次写博客,也刚踏入工作,想着把自己在项目中遇到的问题,以及自己在工作中所做的项目记录下来,方便以后自己查找知识,一开始没有接触过...Android Studio中的手机通讯录,包含功能(按首字母排序,动态添加)
第一次写博客,也刚踏入工作,想着把自己在项目中遇到的问题,以及自己在工作中所做的项目记录下来,方便以后自己查找知识,一开始没有接触过Android,为了找工作,自学了5个月的Java,到公司实习的主要负责任务是安卓开发。
第一个做的任务就是做的手机通讯录,虽然做出来了,但是还是大多数借鉴网络上的,自己再做了一点修改。
下面是自己做的效果图,供大家参考,写的不好,大家就随便看看,希望对借鉴的人有点参考的意义。
上面几幅图形是运行成功候的图像,接下来就是代码部分。
MainActivity 主页面功能代码
package com.example.lastmodel; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.ImageButton; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private ImageButton addUser; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); addUser = findViewById(R.id.addUser); addUser.setOnClickListener(this); } @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this,AddUserActivity.class)); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" tools:context=".MainActivity"> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="联系人" android:layout_centerHorizontal="true" android:textSize="30dp"/> <ImageButton android:id="@+id/addUser" android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:background="@drawable/add"/> </RelativeLayout>
AddUserActivity 添加联系人页面功能
package com.example.lastmodel; import android.content.ContentValues; import android.content.Intent; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; /** * @author: hanxuan * @date: 2020/12/2 */ public class AddUserActivity extends AppCompatActivity implements View.OnClickListener { private MyDatabaseHelper dbHelper; private EditText username,telnumber; private Button ok_add; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_user); dbHelper = new MyDatabaseHelper(this,"Use.db",null,1); username = findViewById(R.id.add_user_name); telnumber = findViewById(R.id.add_user_telnumber); ok_add = findViewById(R.id.ok_add); ok_add.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.ok_add:{ String name = username.getText().toString(); String tel_number = telnumber.getText().toString(); if (name == null||tel_number == null){ Toast.makeText(this,"用户名和电话不能为空",Toast.LENGTH_SHORT).show(); }else { SQLiteDatabase db = dbHelper.getReadableDatabase(); ContentValues values = new ContentValues(); values.put("name",name); values.put("telnumber",tel_number); db.insert("Use",null,values); Toast.makeText(this,"添加成功",Toast.LENGTH_SHORT).show(); startActivity(new Intent(AddUserActivity.this,UserListViewActivity.class)); this.finish(); db.close(); } } } } }
activity_add_user.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" tools:context=".AddUserActivity"> <EditText android:id="@+id/add_user_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:hint="姓名"/> <EditText android:id="@+id/add_user_telnumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_below="@+id/add_user_name" android:hint="电话号码"/> <Button android:id="@+id/ok_add" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/add_user_telnumber" android:text="添加"/> </RelativeLayout>
UserListViewActivity 展示姓名的页面功能
package com.example.lastmodel; import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.Window; import android.widget.ListView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author: hanxuan * @date: 2020/12/2 */ public class UserListViewActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; private RecyclerView mRecyclerView; private List<SortModel> SourceDateList; private SideBar sideBar; private TextView dialog; private SortAdapter adapter; private ClearEditText mClearEditText; private PinyinComparator pinyinComparator; LinearLayoutManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_user_list_view); dbHelper = new MyDatabaseHelper(this,"User4.db",null,1); initViews(); initUser(); mRecyclerView = findViewById(R.id.recyclerView); SourceDateList = filledData(SourceDateList); } private void initViews() { pinyinComparator = new PinyinComparator(); sideBar = findViewById(R.id.sideBar); dialog = findViewById(R.id.dialog); sideBar.setTextView(dialog); sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() { @Override public void onTouchingLetterChanged(String s) { //该字母首次出现的位置 int position = adapter.getPositionForSection(s.charAt(0)); if (position != -1) { manager.scrollToPositionWithOffset(position, 0); } } }); mClearEditText = findViewById(R.id.filter_edit); mClearEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { filterData(s.toString()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); } public List<SortModel> initUser(){ SourceDateList = new ArrayList<>(); SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query("Use",null,null,null,null,null,null); if (cursor.moveToFirst()){ do{ String name = cursor.getString(cursor.getColumnIndex("name")); SortModel data = new SortModel(name); //SourceDateList.add(data); SourceDateList.add(data); }while (cursor.moveToNext()); } cursor.close(); return SourceDateList; } private List<SortModel> filledData(List<SortModel> list) { List<SortModel> mSortList = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { SortModel sortModel = list.get(i); //sortModel.setName(list.get(i)); //汉字转换成拼音 String pinyin = PinyinUtils.getPingYin(list.get(i).getName()); String sortString = pinyin.substring(0, 1).toUpperCase(); // 正则表达式,判断首字母是否是英文字母 if (sortString.matches("[A-Z]")) { sortModel.setLetters(sortString.toUpperCase()); } else { sortModel.setLetters("#"); } mSortList.add(sortModel); } return mSortList; } private void filterData(String filterStr) { List<SortModel> filterDateList = new ArrayList<>(); if (TextUtils.isEmpty(filterStr)) { filterDateList = SourceDateList; } else { filterDateList.clear(); for (SortModel sortModel : SourceDateList) { String name = sortModel.getName(); if (name.indexOf(filterStr.toString()) != -1 || PinyinUtils.getFirstSpell(name).startsWith(filterStr.toString()) //不区分大小写 || PinyinUtils.getFirstSpell(name).toLowerCase().startsWith(filterStr.toString()) || PinyinUtils.getFirstSpell(name).toUpperCase().startsWith(filterStr.toString()) ) { filterDateList.add(sortModel); } } } // 根据a-z进行排序 Collections.sort(filterDateList, pinyinComparator); adapter.updateList(filterDateList); } }
activity_user_list_view.xml
<?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:focusable="true" android:focusableInTouchMode="true"> <com.example.lastmodel.ClearEditText android:id="@+id/filter_edit" android:layout_width="match_parent" android:layout_height="38dp" android:layout_marginLeft="12dp" android:layout_marginTop="10dp" android:layout_marginRight="12dp" android:layout_marginBottom="5dp" android:background="@drawable/shape" android:drawableLeft="@drawable/drawable" android:hint="搜索联系人" android:maxLines="1" android:paddingLeft="8dp" android:textSize="17dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" > <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/dialog" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:background="#A9A9A9" android:gravity="center" android:textColor=" #000000" android:textSize="30dp" android:visibility="invisible" /> <com.example.lastmodel.SideBar android:id="@+id/sideBar" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" /> </RelativeLayout> </LinearLayout>
以上的代码就是界面以及部分功能实现的代码,接下来的代码就是辅助类。
CharPortraitView.java
该类主要的功能是获得名字的首汉字,以及名字前面的圆形和随机分布圆形的颜色
package com.example.lastmodel; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import androidx.appcompat.widget.AppCompatTextView; import androidx.core.widget.TextViewCompat; import java.util.Random; /** * @author: hanxuan * @date: 2020/12/2 */ public class CharPortraitView extends AppCompatTextView { private boolean isRandom = false; private Random random; private int mBackColor; private Context mContext; private String[] colors; private boolean mHead=true; private String mContent; public CharPortraitView(Context context) { this(context, null); } public CharPortraitView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(attrs); build(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = View.MeasureSpec.getSize(widthMeasureSpec); int height = View.MeasureSpec.getSize(heightMeasureSpec); int minSize = Math.min(width, height); setMeasuredDimension(minSize, minSize); } private void init(AttributeSet attrs) { // 初始化随机数 random = new Random(); // 获取参数 TypedArray array = mContext.obtainStyledAttributes(attrs, R.styleable.CharPortraitView); // 获取是否随机背景 isRandom = array.getBoolean(R.styleable.CharPortraitView_random, false); // 获取背景颜色 mBackColor = array.getColor(R.styleable.CharPortraitView_back_color, Color.BLUE); array.recycle(); } //设置 private void build() { if (!isRandom) { mBackColor = getRandomColor(); //产生随机颜色 } // 设置居中 setGravity(Gravity.CENTER); // 设置自适应文字大小 TextViewCompat.setAutoSizeTextTypeWithDefaults(this, TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM); // 设置背景颜色 setBackgroundResource(R.drawable.shape_drawable); GradientDrawable drawable = (GradientDrawable) getBackground(); drawable.setColor(mBackColor); setBackgroundDrawable(drawable); if (mContent==null){ return; } if (mHead){ setText(mContent.substring(0, 1));//取首字符显示 }else { setText(mContent.substring(mContent.length()-1, mContent.length()));//取末尾字符显示 } } /** * 设置文本内容 *@param mHead true 第一个字符 false 最后一个字符 *@return */ public CharPortraitView setHead(boolean mHead) { this.mHead=mHead; build(); return this; } //设置文本内容 public CharPortraitView setContent(String str) { this.mContent=str; build(); return this; } //设置背景颜色 public CharPortraitView setBackColor(int backColor) { mBackColor = backColor; isRandom = false; build(); return this; } //设置是否开启随机颜色 public CharPortraitView setRandom(boolean isRandom) { this.isRandom = isRandom; build(); return this; } /** * 设置随机背景颜色数组 * @param colors * @return */ public CharPortraitView setBackColor(String[] colors){ this.colors=colors; build(); return this; } /** * 获取随机背景颜色 * @return */ private int getRandomColor() { String[] colorArray; if (colors!=null&&colors.length>0){ colorArray=colors; }else { colorArray=mContext.getResources().getStringArray(R.array.color); } int value = random.nextInt(colorArray.length); return Color.parseColor(colorArray[value]); } }
ClearEditText.java
该类主要是实现清除搜索栏的内容
package com.example.lastmodel; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.View.OnFocusChangeListener; import android.view.animation.Animation; import android.view.animation.CycleInterpolator; import android.view.animation.TranslateAnimation; import android.widget.EditText; /** * @author: hanxuan * @date: 2020/12/2 */ public class ClearEditText extends androidx.appcompat.widget.AppCompatEditText implements OnFocusChangeListener, TextWatcher { private Drawable mClearDrawable; public ClearEditText(Context context) { this(context, null); } public ClearEditText(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.editTextStyle); } public ClearEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mClearDrawable = getCompoundDrawables()[2]; if (mClearDrawable == null) { mClearDrawable = getResources().getDrawable(R.drawable.guanbifanhuishanchu); } mClearDrawable.setBounds(-17, 5, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); setClearIconVisible(false); setOnFocusChangeListener(this); addTextChangedListener(this); } @Override public boolean onTouchEvent(MotionEvent event) { if (getCompoundDrawables()[2] != null) { if (event.getAction() == MotionEvent.ACTION_UP) { boolean touchable = event.getX() > (getWidth() - getPaddingRight() - mClearDrawable.getIntrinsicWidth()) && (event.getX() < ((getWidth() - getPaddingRight()))); if (touchable) { this.setText(""); } } } return super.onTouchEvent(event); } @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { setClearIconVisible(getText().length() > 0); } else { setClearIconVisible(false); } } protected void setClearIconVisible(boolean visible) { Drawable right = visible ? mClearDrawable : null; setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]); } @Override public void onTextChanged(CharSequence s, int start, int count, int after) { setClearIconVisible(s.length() > 0); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } public void setShakeAnimation(){ this.setAnimation(shakeAnimation(5)); } public static Animation shakeAnimation(int counts){ Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); translateAnimation.setInterpolator(new CycleInterpolator(counts)); translateAnimation.setDuration(1000); return translateAnimation; } }
MyDatabaseHelper.java
数据库的创建
package com.example.lastmodel; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.widget.Toast; import androidx.annotation.Nullable; /** * @author: hanxuan * @date: 2020/12/2 */ public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String Create_Table_User = "create table Use (" + "name text," + "telnumber text)"; private Context mContext; public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(Create_Table_User); //Toast.makeText(mContext,"创建成功", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
PinyinComparator.java
package com.example.lastmodel; import java.util.Comparator; public class PinyinComparator implements Comparator<SortModel> { public int compare(SortModel o1, SortModel o2) { if (o1.getLetters().equals("@") || o2.getLetters().equals("#")) { return -1; } else if (o1.getLetters().equals("#") || o2.getLetters().equals("@")) { return 1; } else { return o1.getLetters().compareTo(o2.getLetters()); } } }
PinyinUtils.java
package com.example.lastmodel; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; /** * @author: hanxuan * @date: 2020/12/2 */ public class PinyinUtils { /** * 获取拼音 * * @param inputString * @return */ public static String getPingYin(String inputString) { HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.LOWERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); format.setVCharType(HanyuPinyinVCharType.WITH_V); char[] input = inputString.trim().toCharArray(); String output = ""; try { for (char curChar : input) { if (Character.toString(curChar).matches("[\\u4E00-\\u9FA5]+")) { /*至少匹配一个汉字的写法,这两个unicode值正好时Unicode表中的汉字的头和尾 * []代表里面的值出现一个就可以, 后边的"+"代表至少出现1次,合起来即至少匹配一个汉字*/ String[] temp = PinyinHelper.toHanyuPinyinStringArray(curChar, format); output += temp[0]; } else output += Character.toString(curChar); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } return output; } /** * 获取第一个字的拼音首字母 * @param chinese * @return */ public static String getFirstSpell(String chinese) { StringBuffer pinYinBF = new StringBuffer(); char[] arr = chinese.toCharArray(); HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (char curChar : arr) { if (curChar > 128) { try { String[] temp = PinyinHelper.toHanyuPinyinStringArray(curChar, defaultFormat); if (temp != null) { pinYinBF.append(temp[0].charAt(0)); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } } else { pinYinBF.append(curChar); } } return pinYinBF.toString().replaceAll("\\W", "").trim(); } }
SideBar.java
右边侧滑栏的实现
package com.example.lastmodel; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; /** * @author: hanxuan * @date: 2020/12/2 */ public class SideBar extends View { // 触摸事件 private OnTouchingLetterChangedListener onTouchingLetterChangedListener; public static String[] b = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z","#"}; private int choose = -1; private Paint paint = new Paint(); private TextView mTextDialog; /** * 为SideBar设置显示字母的TextView * @param textDialog */ public void setTextView(TextView textDialog) { this.mTextDialog = textDialog; } public SideBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SideBar(Context context, AttributeSet attrs) { super(context, attrs); } public SideBar(Context context) { super(context); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); int height = getHeight(); int width = getWidth(); int singleHeight = height / b.length; for (int i = 0; i < b.length; i++) { paint.setColor(Color.rgb(105, 105, 105)); // paint.setColor(Color.WHITE); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setAntiAlias(true); paint.setTextSize(35); if (i == choose) { paint.setColor(Color.parseColor("#3399ff")); paint.setFakeBoldText(true); } float xPos = width / 2 - paint.measureText(b[i]) / 2; float yPos = singleHeight * i + singleHeight; canvas.drawText(b[i], xPos, yPos, paint); paint.reset(); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float y = event.getY(); final int oldChoose = choose; final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; final int c = (int) (y / getHeight() * b.length); switch (action) { case MotionEvent.ACTION_UP: setBackground(new ColorDrawable(0x00000000)); choose = -1; invalidate(); if (mTextDialog != null) { mTextDialog.setVisibility(View.INVISIBLE); } break; default: setBackgroundResource(R.drawable.sidebar_background); if (oldChoose != c) { if (c >= 0 && c < b.length) { if (listener != null) { listener.onTouchingLetterChanged(b[c]); } if (mTextDialog != null) { mTextDialog.setText(b[c]); mTextDialog.setVisibility(View.VISIBLE); } choose = c; invalidate(); } } break; } return true; } public void setOnTouchingLetterChangedListener( OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } public interface OnTouchingLetterChangedListener { void onTouchingLetterChanged(String s); } }
SortAdapter.java
首字母排序功能
package com.example.lastmodel; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; import androidx.recyclerview.widget.RecyclerView; import java.util.List; /** * @author: hanxuan * @date: 2020/12/2 */ public class SortAdapter extends RecyclerView.Adapter<SortAdapter.ViewHolder> { private LayoutInflater mInflater; private List<SortModel> mData; private Context mContext; public SortAdapter(Context context, List<SortModel> data) { mInflater = LayoutInflater.from(context); mData = data; this.mContext = context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = mInflater.inflate(R.layout.item, parent,false); ViewHolder viewHolder = new ViewHolder(view); viewHolder.tvTag = view.findViewById(R.id.tag); viewHolder.tvName = view.findViewById(R.id.name); return viewHolder; } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { int section = getSectionForPosition(position); //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现 if (position == getPositionForSection(section)) { holder.tvTag.setVisibility(View.VISIBLE); holder.tvTag.setText(mData.get(position).getLetters()); holder.portrait.setContent(mData.get(position).getName()).setHead(true); } else { holder.tvTag.setVisibility(View.GONE); holder.portrait.setContent(mData.get(position).getName()).setHead(true); } if (mOnItemClickListener != null) { holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOnItemClickListener.onItemClick(holder.itemView, position); } }); } holder.tvName.setText(this.mData.get(position).getName()); holder.tvName.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(mContext, mData.get(position).getName(),Toast.LENGTH_SHORT).show(); } }); } @Override public int getItemCount() { return mData.size(); } public interface OnItemClickListener { void onItemClick(View view, int position); } private OnItemClickListener mOnItemClickListener; public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { this.mOnItemClickListener = mOnItemClickListener; } public static class ViewHolder extends RecyclerView.ViewHolder { TextView tvTag, tvName; private CharPortraitView portrait; public ViewHolder(View itemView) { super(itemView); tvName = itemView.findViewById(R.id.name); portrait = itemView.findViewById(R.id.user_img); } } /** * 提供给Activity刷新数据 * @param list */ public void updateList(List<SortModel> list){ this.mData = list; notifyDataSetChanged(); } public Object getItem(int position) { return mData.get(position); } /** * 根据ListView的当前位置获取分类的首字母的char ascii值 */ public int getSectionForPosition(int position) { return mData.get(position).getLetters().charAt(0); } /** * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getItemCount(); i++) { String sortStr = mData.get(i).getLetters(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; } }
item.xml
<?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="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tag" android:layout_width="match_parent" android:layout_height="40dp" android:background="#ffffff" android:gravity="center_vertical" android:paddingLeft="15dp" android:text="A" android:textColor="#000000" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp"> <!--<ImageView android:id="@+id/user_img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" />--> <com.example.lastmodel.CharPortraitView android:id="@+id/user_img" android:layout_width="40dp" android:layout_height="40dp" android:textColor="#ffffff" android:layout_centerVertical="true" android:padding="5dp"/> <TextView android:id="@+id/name" android:layout_width="match_parent" android:layout_height="60dp" android:layout_marginLeft="20dp" android:layout_centerVertical="true" android:layout_toRightOf="@id/user_img" android:gravity="center_vertical" android:textSize="20sp" android:textColor="#000000" /> <View android:id="@+id/view_lv_item_line" android:layout_width="320dp" android:layout_height="0.05dp" android:layout_below="@+id/name" android:layout_alignParentLeft="true" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:layout_marginEnd="30dp" android:layout_marginRight="30dp" android:background="#C0C0C0" android:paddingBottom="15dp" /> </RelativeLayout> </LinearLayout>
SortModel.java
实体类
package com.example.lastmodel; public class SortModel { private String telnumber; private String name; private String letters;//显示拼音的首字母 public SortModel(String name) { this.name = name; } public SortModel(String telnumber, String name) { this.telnumber = telnumber; this.name = name; } public SortModel() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLetters() { return letters; } public void setLetters(String letters) { this.letters = letters; } @Override public String toString() { return "SortModel{" + "name='" + name + '\'' + ", letters='" + letters + '\'' + '}'; } }
接下来就是资源类的文件了
drawable下的
shape.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="20dp"/> <stroke android:width="1dp" android:color="#bcb8b8"/> <size android:width="320dp" /> <solid android:color="#F5F5F5" /> </shape>
shape_drawable.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="@android:color/holo_red_light"/> </shape>
sidebar_background.xml
<?xml version="1.0" encoding="utf-8"?> <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#ffffff"/> <corners android:topLeftRadius="8dp" android:bottomLeftRadius="8dp"/> </shape>
values下的xml文件
arrys.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="color"> <item>#FFBBFF</item> <item>#A8A8A8</item> <item>#EEC591</item> <item>#D8BFD8</item> <item>#CAE1FF</item> <item>#BC8F8F</item> <item>#CDB79E</item> <item>#EEE8AA</item> <item>#C5C1AA</item> <item>#C1CDC1</item> </string-array> </resources>
attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CharPortraitView"> <attr name="back_color" format="reference"/> <attr name="random" format="boolean"/> </declare-styleable> </resources>
OK,以上就是全部的代码了,有些图片资源我没有附上去,那不太重要,可以自己找几张。仅供大家参考,若有侵权,请联系本人立即删除。
-
Android 完整的通讯录项目源码.rar
2019-06-19 20:09:03本资源是初学者用于练手的作品可以供初学者参考 -
Android案例:通讯录。利用SQLite+ListView,实现通讯录增删查改
2020-07-27 17:50:29利用SQLite数据库 + ListView开发技术,实现了一个简易的通讯录系统,其功能模块包括通讯录信息的增加、删除、修改、查询等。 -
Android Studio ListView数据适配器实现通讯录设计(头像加名称)
2022-02-12 07:20:50Android StudioAndroid Studio ListView数据适配器实现通讯录设计(头像加名称) 一、ui设计 二、自定义适配器BaseAdapter 三、绑定绑定数据适配器 -
Android 仿通讯录A-Z侧边索引查询 LetterListView androidstudio
2016-05-25 16:11:36Android 仿通讯录A-Z侧边索引查询 LetterListView androidstudio