APP介绍
一个普通的通讯录app。有展示联系人的主页面、联系人的详情页、联系人的新增/编辑页面。该app没有读取系统的联系人而是单独使用SQLite数据库来存储app的数据。涉及到的知识点有:SQLite数据库、SharedPreferences文件、Activity的生命周期、Activity的跳转和传值、自定义列表控件的设计和实现、监听器、对话框、JAVA基础知识等。
![]()
界面设计
主要有三个页面:app主页、详情页、编辑页。
- app主页,使用Listview列表显示联系人(头像、姓名、备注),底部有新增联系人按钮
- 详情页,展示联系人详细信息。
- 编辑页,编辑联系人信息。
![]()
代码实现
导入图片资源
![]()
偷懒了,没有把控件样式放到styles.xml文件中😄。
页面
app主页
布局xml
页面设计(layout文件夹内的xml文件)如下:
![]()
ListView控件+底部按钮。(这里使用ImageView来充当按钮,按钮也可以使用Button控件使用background属性设置图片)
ListView控件的id为homelist,按钮控件的id为addbtn。(实际上该按钮有新增与删除的功能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
<?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=".View.MainActivity">
<ListView android:id="@+id/homelist" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="60dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="60dp" android:background="#fff" android:layout_alignParentBottom="true"> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:src="@drawable/add" android:id="@+id/addbtn" android:layout_centerInParent="true"/> </RelativeLayout> </RelativeLayout>
|
java
该activity页面的java代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
package com.example.addresslist.View; ... public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemLongClickListener, AdapterView.OnItemClickListener{ AL_info_dao al_info_dao; AL_tel_dao al_tel_dao; HomeList_Apdater apdater; ListView listview; ImageView btn; String btnType="add";
@Override protected void onRestart() { ... }
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { ... }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
listview = (ListView)findViewById(R.id.homelist); btn =(ImageView)findViewById(R.id.addbtn); btn.setOnClickListener(this); listview.setOnItemLongClickListener(this); listview.setOnItemClickListener(this); if (al_info_dao == null){ al_info_dao = new AL_info_dao(this); } if (al_tel_dao == null){ al_tel_dao = new AL_tel_dao(this); } ShowDate(Constant.templateItem); }
public void ShowDate(String type){ ... }
@Override public void onClick(View view) { ... }
@Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { ... }
@Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { ... } }
|
ShowDate
加载数据到页面(app主页)。根据实参type判断显示数据使用哪个自定义列表文件(有两个布局文件homelist和homelist_check,普通展示数据和删除联系人时展示数据,区别在于后者有一个单选框)。
类Constant是自定义的一个公共类,用来存放经常使用的数据(文本),避免某个地方不小心打错单词的情况。
关于如何将数据渲染到ListView控件中可以回顾一下这篇博客。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public void ShowDate(String type){ switch (type){ case Constant.templateItem: apdater=new HomeList_Apdater(this,R.layout.homelist,al_info_dao.queryAll()); btn.setImageResource(R.drawable.add); btnType="add"; break; case Constant.templateItemCheck: apdater=new HomeList_Apdater(this,R.layout.homelist_check,al_info_dao.queryAll()); btn.setImageResource(R.drawable.del); btnType="del"; break; } listview.setAdapter(apdater); }
|
onItemClick
Listview中子控件(某一联系人)的点击事件触发的方法。判断当前主页显示的模式(普通模式、删除联系人模式),普通模式(下方的按钮为新增联系人)时跳转到联系人的详情页。并且将该联系人示例addressList_info传递过去。
1 2 3 4 5 6 7 8 9
|
@Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { if (btnType.equals("add")){ AddressList_info addressList_info = (AddressList_info)apdater.getItem(i); Intent intent = new Intent(MainActivity.this, Details.class); intent.putExtra("info", addressList_info); startActivity(intent); } }
|
onItemLongClick
Listview中子控件(某一联系人)的长按事件触发的方法。
1 2 3 4 5 6 7 8 9
|
@Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) { ShowDate(Constant.templateItemCheck); SharedPreferences.Editor edit = getSharedPreferences(Constant.spxmlName_aL_Ids, Context.MODE_PRIVATE).edit(); edit.clear(); edit.commit(); return true; }
|
onClick
底部按钮点击事件触发的方法。主页显示数据的模式有普通模式和删除联系人模式,普通模式下方的按钮为新增联系人,删除联系人模式下方的按钮为删除联系人。
新增联系人,则跳转到编辑页。当从编辑页返回该页面时,执行onActivityResult方法将数据保存到SQLite数据库中。
删除联系人,根据保存在SharedPreferences文件中的id删除相应的联系人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
@Override public void onClick(View view) { if (btnType.equals("add")){ Intent intent = new Intent(MainActivity.this, AddRecord.class); startActivityForResult(intent,1); }
if (btnType.equals("del")){ SharedPreferences sp = getSharedPreferences(Constant.spxmlName_aL_Ids, Context.MODE_PRIVATE); final String ids = sp.getString(Constant.spxmlName_aL_key,""); if (ids.length()>=1){ AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("提示!"); builder.setMessage("确定要删掉联系人吗?"); builder.setPositiveButton("确认", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { al_info_dao.delete(ids); al_tel_dao.deleteAll(ids); ShowDate(Constant.templateItem); } }); builder.setNegativeButton("取消",null); builder.show(); }else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("提示!"); builder.setMessage("你还没有选择要删除的联系人呢!"); builder.setPositiveButton("不删了", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { ShowDate(Constant.templateItem); } }); builder.setNegativeButton("继续选择",null); builder.show(); } } }
|
onActivityResult
将从编辑页返回的数据保存到SQLite数据库中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1 && resultCode == 2){ Bundle bundle = data.getExtras(); if (bundle != null){ String name = bundle.getString(Constant.beanField_name); String telephone1 = bundle.getString(Constant.beanField_telephone+"1"); String telephone2 = bundle.getString(Constant.beanField_telephone+"2"); String telephone3 = bundle.getString(Constant.beanField_telephone+"3"); String remarks = bundle.getString(Constant.beanField_remarks); if (name.length()>0){ AddressList_info addressList_info = new AddressList_info(name,remarks); if (al_info_dao.queryId(addressList_info) == -1){ boolean status = al_info_dao.insert(addressList_info); Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show(); if (status == true){ AddressList_tel addressList_tel = new AddressList_tel(al_info_dao.queryId(addressList_info),telephone1); if (telephone1.length()>0){ al_tel_dao.insert(addressList_tel); } if (telephone2.length()>0){ addressList_tel.setTelephone(telephone2); al_tel_dao.insert(addressList_tel); } if (telephone3.length()>0){ addressList_tel.setTelephone(telephone3); al_tel_dao.insert(addressList_tel); } ShowDate(Constant.templateItem); } }else{ Toast.makeText(this, "保存失败,存在相同名字", Toast.LENGTH_SHORT).show(); } }else { Toast.makeText(this, "保存失败,没有设置名字", Toast.LENGTH_SHORT).show(); } } } if (requestCode==1&&resultCode==3){ Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show(); } }
|
onRestart
进入主页时重新加载页面数据。在回调函数onRestart中调用方法ShowDate重新加载页面数据。
1 2 3 4 5
|
@Override protected void onRestart() { super.onRestart(); ShowDate(Constant.templateItem); }
|
详情页
编辑页
SQLite数据库
SP文件
SharedPreferences文件,临时保存数据。
Activity
Activity的生命周期
onRestart、onCreate等
Activity的跳转和传值
Activity之间数据传递。
自定义列表控件
自定义列表控件的设计和实现
事件监听器
点击监听器、ListView中子控件的点击/长按监听器(冲突的解决)
ListView需要注意设置:
1
|
android:descendantFocusability="blocksDescendants"
|
对话框
普通对话框、带有确认/取消按钮的对话框、加载对话框、时间选择对话框、带输入框的对话框等等。
源码