实验二 通讯录APP实验
一、目的:1、熟悉Android数据存储和访问;2、掌握SQLite数据库的使用方法;3、掌握ContentResolver的基本用法;4、学会创建自己的内容提供器。
二、内容:设计与实现通讯录APP,具有以下功能:1、实现一个通讯录APP,展示联系人列表(不少于5个联系人,联系人信息包括:姓名,电话,邮箱);2、能对通讯录进行增删改查;3、创建自己的内容提供器,确定哪部分数据可以被外部程序访问;4、新建一个应用程序,使用ContentResolver读取通讯录里的联系人。
三、设计与实现
界面设计思路:
主界面:打开app时,直接显示通讯录中的联系人,主界面有一个新建联系人按钮,可以添加联系人,用listview显示联系人,点击后可以对其中的联系人进行修改,删除等操作。使用相对布局是为了Listview能在中间显示,容易控制如图:
image.png
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom">
<TextView
android:id="@+id/tvtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="23sp"
android:text="联系人列表"
android:gravity="center"
android:layout_marginTop="10dp"/>
<ListView
android:id="@+id/lv_people"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:divider="@android:color/darker_gray"
android:layout_below="@+id/tvtitle"
android:layout_above="@+id/btn_new"
android:dividerHeight="1dp" />
<Button
android:id="@+id/btn_new"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="新建联系人"/>
</RelativeLayout>
添加联系人界面:使用3个editview读取用户的输入,啷个按钮表示添加和取消。界面如图
image.png
实现代码如下
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#000"
android:textSize="25sp"
android:text="新建联系人"/>
<EditText
android:layout_marginTop="10dp"
android:id="@+id/edt_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="姓名"/>
<EditText
android:layout_marginTop="10dp"
android:id="@+id/edt_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="电话"/>
<EditText
android:layout_marginTop="10dp"
android:id="@+id/edt_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="邮箱"/>
<Button
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_add"
android:text="添加"
android:textSize="19sp"/>
<Button
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_cancel"
android:text="取消"
android:textSize="19sp"/>
</LinearLayout>
删除修改的界面:
由于修改的时候不允许修改姓名,所以把姓名的控件改为TextView,其他的为EditView,为了对其,使用3*2的网格布局,用于显示,又可以修改。如图:
image.png
实现代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.myapplication.UpdateActivity"
android:orientation="vertical"
android:layout_margin="10dp">
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:rowCount="3"
android:columnCount="2">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:text="姓名:"/>
<TextView
android:id="@+id/tvuname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="号码:"
android:textSize="22sp"/>
<EditText
android:id="@+id/edtunumber"
android:layout_width="wrap_content"
android:textSize="22sp"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:text="邮箱:"/>
<EditText
android:id="@+id/edtuemail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"/>
</GridLayout>
<Button
android:id="@+id/btn_del"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="删除联系人"
android:layout_marginTop="10dp"/>
<Button
android:id="@+id/btn_saveup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存更改"
android:textSize="20sp" />
</LinearLayout>
业务逻辑思路:
主界面显示联系人,点击新建按钮跳转到新建联系人界面,点击listView进入删除和修改的界面。主界面负责从数据库中读取联系人的信息并显示。
首先通过ContactsdbHelper类来初始化数据库,创建联系人表,该类内容如下:
package com.example.myapplication;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
/**
* Created by 永恒之蓝 on 2018/5/19.
*/
public class ContactsdbHelper extends SQLiteOpenHelper {
public static String CONTACTSDB="create table Contacts("
+"name text,"
+"number text,"
+"email text)";
private Context mContext;
public ContactsdbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext=context;
}
public ContactsdbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
mContext=context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CONTACTSDB);
Toast.makeText(mContext, "Create Succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
主界面逻辑实现代码如下:想实现的效果是当新建联系人成功后会回到当前主界面,主界面会显示刚刚创建的联系人。此时需要重写onResume()方法,在该方法中实现该界面显示的更新。代码如下:
package com.example.myapplication;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener {
private ContactsdbHelper contactsdbHelper;
Button btnNew;
ListView mlvPeople;
ArrayAdapter<String> adapter;
List<String> contactlists=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().hide();
contactsdbHelper=new ContactsdbHelper(this,"Contacts.db",null,2);
initViews();
}
private void readContacts() {
SQLiteDatabase db=contactsdbHelper.getWritableDatabase();
Cursor cursor=null;
try{
cursor =db.query("Contacts",null,null,null,null,null,null);
if(cursor.moveToFirst()){
do{
String sName=cursor.getString(cursor.getColumnIndex("name"));
String sNumber=cursor.getString(cursor.getColumnIndex("number"));
String sEmail=cursor.getString(cursor.getColumnIndex("email"));
contactlists.add("姓名:"+sName+"\t\t电话:"+sNumber+"\n邮箱:"+sEmail);
} while(cursor.moveToNext());
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
}
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactlists);
mlvPeople.setAdapter(adapter);
}
//初始化控件
public void initViews()
{
btnNew=findViewById(R.id.btn_new);
btnNew.setOnClickListener(this);
mlvPeople=findViewById(R.id.lv_people);
mlvPeople.setOnItemClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_new:
Intent intent=new Intent(this,AddContacts.class);
startActivity(intent);
break;
}
}
@Override
public void onResume() {
super.onResume();
//清空list列表
contactlists.clear();
//读取更新后的数据库
readContacts();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent=new Intent(this,UpdateActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("pos", position);
intent.putExtras(bundle);
startActivity(intent);
}
}
新建联系人界面:
可以输入联系人信息,其中姓名和电话是必填的,邮箱可以不填,点击添加按钮保存输入的信息,并结束当前activity,点击取消也结束该活动,返回主activity。实现代码如下:
package com.example.myapplication;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
/**
* Created by 永恒之蓝 on 2018/5/19.
*/
public class AddContacts extends AppCompatActivity implements View.OnClickListener {
Button btnAdd,btnCancel;
EditText edtName,edtNumber,edtEmail;
ContactsdbHelper dbHelper;
SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_addcontacts);
getSupportActionBar().hide();
initViews();
}
public void initViews(){
btnAdd=findViewById(R.id.btn_add);
btnAdd.setOnClickListener(this);
btnCancel=findViewById(R.id.btn_cancel);
btnCancel.setOnClickListener(this);
edtName=findViewById(R.id.edt_name);
edtNumber=findViewById(R.id.edt_number);
edtEmail=findViewById(R.id.edt_email);
dbHelper=new ContactsdbHelper(this,"Contacts.db",null,2);
db=dbHelper.getWritableDatabase();
}
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btn_add:
if(!isEmpty()){
ContentValues values=new ContentValues();
values.put("name",edtName.getText().toString());
values.put("number",edtNumber.getText().toString());
values.put("email",edtEmail.getText().toString());
db.insert("Contacts",null,values);
values.clear();
finish();
}
else {
Toast.makeText(this,"必须填写姓名和邮箱!",Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_cancel:
finish();
break;
}
}
//判断输入是否为空。
public boolean isEmpty(){
if(edtName.getText().toString().equals("")||edtNumber.getText().toString().equals("")){
return true;
}
else
{
return false;
}
}
}
修改和删除联系人:通过点击主界面的listview进入,可以修改电话号码,邮箱信息,可以删除一个联系人,当删除或保存时会结束当前活动,返回上一个活动。
package com.example.myapplication;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class UpdateActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tvuname;
private EditText edtunumber;
private EditText edtuemail;
private Button btnDel;
private Button btnSaveup;
int pos;
private ContactsdbHelper contactsdbHelper;
private void initViews() {
tvuname = findViewById(R.id.tvuname);
edtunumber = findViewById(R.id.edtunumber);
edtuemail = findViewById(R.id.edtuemail);
btnDel = findViewById(R.id.btn_del);
btnDel.setOnClickListener(this);
btnSaveup = findViewById(R.id.btn_saveup);
btnSaveup.setOnClickListener(this);
pos= (int)getIntent().getSerializableExtra("pos");
contactsdbHelper=new ContactsdbHelper(this,"Contacts.db",null,2);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.updatecontacts);
initViews();
initContacts();
}
private void initContacts() {
SQLiteDatabase db=contactsdbHelper.getWritableDatabase();
Cursor cursor=null;
try{
cursor =db.query("Contacts",null,null,null,null,null,null);
if(cursor.moveToPosition(pos)){
String sName=cursor.getString(cursor.getColumnIndex("name"));
String sNumber=cursor.getString(cursor.getColumnIndex("number"));
String sEmail=cursor.getString(cursor.getColumnIndex("email"));
tvuname.setText(sName);
edtunumber.setText(sNumber);
edtuemail.setText(sEmail);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(cursor!=null){
cursor.close();
}
}
}
@Override
public void onClick(View v) {
SQLiteDatabase db=contactsdbHelper.getWritableDatabase();
switch (v.getId()){
case R.id.btn_del:
db.delete("Contacts","name=?",new String[]{tvuname.getText().toString()});
finish();
break;
case R.id.btn_saveup:
ContentValues values=new ContentValues();
values.put("number",edtunumber.getText().toString());
values.put("email",edtuemail.getText().toString());
db.update("Contacts",values,"name=?",new String[]{tvuname.getText().toString()});
finish();
break;
}
}
}
至此,已经实现了第一和第二个实验要求。
实现自己的内容提供器:将内容都设为可让外部读取,方便进行测试。
package com.example.myapplication;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by 永恒之蓝 on 2018/5/22.
*/
public class MyProvider extends ContentProvider {
public static final int CONTACTS_DIR=0;
public static final int CONTACTS_ITEM=1;
private ContactsdbHelper dbHelper;
public static final String AUTHORITY="com.example.myapplication";
private static UriMatcher uriMatcher;
static {
uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI( AUTHORITY,"Contacts",CONTACTS_DIR);
uriMatcher.addURI( AUTHORITY,"Contacts/#",CONTACTS_ITEM);
}
@Override
public boolean onCreate() {
dbHelper=new ContactsdbHelper(getContext(),"Contacts.db",null,2);
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
SQLiteDatabase db=dbHelper.getReadableDatabase();
Cursor cursor=null;
switch (uriMatcher.match(uri))
{
case CONTACTS_DIR:
cursor=db.query("Contacts",projection,selection,selectionArgs,null,null,sortOrder);
break;
case CONTACTS_ITEM:
String nameId=uri.getPathSegments().get(1);
cursor=db.query("Contacts",projection,"name=?",new String[]{nameId},null,null,sortOrder);
break;
default:
break;
}
return cursor;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (uriMatcher.match(uri))
{
case CONTACTS_DIR:
return "vnd.android.cursor.dir/vnd.com.example.myapplication.Contacts";
case CONTACTS_ITEM:
return "vnd.android.cursor.item/vnd.com.example.myapplication.Contacts";
default:
break;
}
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
SQLiteDatabase db=dbHelper.getWritableDatabase();
Uri uriRetrun=null;
switch (uriMatcher.match(uri))
{
case CONTACTS_DIR:
case CONTACTS_ITEM:
long newContactsId=db.insert("Contacts",null,values);
uriRetrun=Uri.parse("content://"+AUTHORITY+"/Contacts/"+newContactsId);
break;
default:
break;
}
return uriRetrun;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase db =dbHelper.getWritableDatabase();
int deleteRows=0;
switch (uriMatcher.match(uri)){
case CONTACTS_DIR:
deleteRows=db.delete("Contacts",selection,selectionArgs);
break;
case CONTACTS_ITEM:
String nameId=uri.getPathSegments().get(1);
deleteRows=db.delete("Contacts","name=?",new String[]{nameId});
break;
default:
break;
}
return deleteRows;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
SQLiteDatabase db =dbHelper.getWritableDatabase();
int updateRows=0;
switch (uriMatcher.match(uri)){
case CONTACTS_DIR:
updateRows=db.update("Contacts",values,selection,selectionArgs);
break;
case CONTACTS_ITEM:
String nameId=uri.getPathSegments().get(1);
updateRows=db.update("Contacts",values,"name=?",new String[]{nameId});
break;
default:
break;
}
return updateRows;
}
}
新建一个项目,用来读取以上建立的数据库。界面布局和主界面差不多,通过listview显示。
读取数据库代码如下:
package com.example.providertest;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
ListView lvPeople;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().hide();
lvPeople=findViewById(R.id.lv_people);
ArrayAdapter<String> adapter;
List<String> contactlists=new ArrayList<>();
Uri uri=Uri.parse("content://com.example.myapplication/Contacts");
Cursor cursor=getContentResolver().query(uri,null,null,null,null);
if (cursor!=null){
while(cursor.moveToNext()){
String name=cursor.getString(cursor.getColumnIndex("name"));
String number=cursor.getString(cursor.getColumnIndex("number"));
String email=cursor.getString(cursor.getColumnIndex("email"));
contactlists.add("姓名:"+name+"\t\t电话:"+number+"\n邮箱:"+email);
}
cursor.close();
}
adapter=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,contactlists);
lvPeople.setAdapter(adapter);
}
}
实验结果如下(运行结果可在上面界面代码部分的图片看见),可以发现可以读取该数据库信息:
image.png
四、遇到的问题
主界面的新建联系人按钮会挡住
listView的下半部分,会挡住最后一个联系人的信息。解决方法:
使用相对布局,使
ListView位置控制在button上方。关键代码如下:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom">
<TextView
android:id="@+id/tvtitle"
…>
<ListView
android:id="@+id/lv_people"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:divider="@android:color/darker_gray"
android:layout_below="@+id/tvtitle"
android:layout_above="@+id/btn_new"
android:dividerHeight="1dp" />
<Button
android:id="@+id/btn_new"
…>
android:text="新建联系人"/>
</RelativeLayout>
在修改通讯录界面,如何获取用户点击的联系人信息?解决方法是监听用户点击的是第几个ListViewItem,把位置传给修改的活动,该活动在从数据库中读取该联系人信息并显示。
关键代码如下:
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent=new Intent(this,UpdateActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("pos", position);
intent.putExtras(bundle);
startActivity(intent);
}
当返回主活动时由于会重新读取更新后的数据库,会显示重复相同的联系人。解决方法是清空list的数据,在重新读取:
@Override
public void onResume() {
super.onResume();
//清空list列表
contactlists.clear();
//读取更新后的数据库
readContacts();
}












网友评论