文本备忘录(安卓应用开发)
更新:20230408
修复BUG:新建备注录时,多次点击“保存”会创建多个副本。使用默认的rowid管理数据。APK安装文件和源代码已更新。
原文
本文将构建一个文本备忘录,应用命名为InfoBox,功能包括备忘录的添加、修改、删除和查询,数据使用SQLite数据库保存,基础知识可以参考《Java与Android移动应用开发:技术、方法与实践》一书,下面将主要讨论实现代码。
首先来看应用的两个界面,如图1。
图1
左侧为主界面,包括备忘录查询功能和添加按钮。右侧为编辑界面,可以编写新的备忘录信息,也可以修改或删除已存在的备忘录信息。下面先来看主界面的创建。
主界面
主界面命名为MainActivity,其布局如下(activity_main.xml)。
XML |
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.SearchView
android:id="@+id/searchView"
android:textSize="22sp"
android:layout_weight="9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></androidx.appcompat.widget.SearchView>
<Button android:id="@+id/btnAdd"
android:text="+"
android:textSize="22sp"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"></ListView>
</LinearLayout>
|
其中,使用线性布局创建了三个组件,分别是搜索组件(searchView)、添加按钮(btnAdd)和查询结果列表(listView)。
主界面的Java代码部分如下(MainActivity.java)。
Java |
package com.caohuayu.infobox;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener, SearchView.OnQueryTextListener,
AdapterView.OnItemClickListener{
SearchView searchView = null;
Button btnAdd = null;
ListView listView=null;
//
List<String> dataId = new ArrayList<>();
List<String> dataInfo = new ArrayList<>();
private ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化数据库
SQLiteDatabase db = openOrCreateDatabase(
"infobox.db",MODE_PRIVATE,null);
String sql = "create table if not exists "+
"infobox(recid integer not null primary key,info text);";
db.execSQL(sql);
db.close();
// 初始化控件
searchView = (SearchView)findViewById(R.id.searchView);
btnAdd = (Button)findViewById(R.id.btnAdd);
listView = (ListView)findViewById(R.id.listView);
//
btnAdd.setOnClickListener(this);
listView.setOnItemClickListener(this);
//
searchView.setSubmitButtonEnabled(true);
searchView.setIconified(false);
searchView.setOnQueryTextListener(this);
// 绑定数据
adapter = new ArrayAdapter<>(
this,
android.R.layout.simple_list_item_1,
dataInfo);
listView.setAdapter(adapter);
}
@Override
protected void onStart() {
super.onStart();
//
search(searchView.getQuery().toString());
}
/* 添加按钮 */
@Override
public void onClick(View v){
// 显示
Intent intent = new Intent(MainActivity.this,EditActivity.class);
intent.putExtra("recid","0");
startActivity(intent);
}
/* 搜索 */
@Override
public boolean onQueryTextSubmit(String query){
search(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText){
search(newText);
return false;
}
// 根据关键字搜索内容
protected void search(String key){
dataId.clear();
dataInfo.clear();
//
SQLiteDatabase db = openOrCreateDatabase(
"infobox.db",MODE_PRIVATE,null);
String sql = "select recid,substr(info,1,15) as info "+
"from infobox where info like '%"+key+
"%' order by recid desc limit 100;";
Cursor cur = db.rawQuery(sql,null);
if(cur.getCount()>0){
while(cur.moveToNext()){
dataId.add(cur.getString(0));
dataInfo.add(cur.getString(1)+"...");
}
}
else{
Toast.makeText(MainActivity.this,
"没有找到相关的信息",Toast.LENGTH_SHORT).show();
}
cur.close();
db.close();
//
adapter.notifyDataSetChanged();
}
/* 响应列表 */
@Override
public void onItemClick(AdapterView<?> parent,View view,int position,long id){
Intent intent = new Intent(MainActivity.this,EditActivity.class);
intent.putExtra("recid",dataId.get((int)id));
startActivity(intent);
}
}
|
MainActivity类中自定义的对象包括:
- searchView定义为SearchView类型,表示搜索组件。
- btnAdd定义为Button类型,表示添加按钮。
- listView定义为ListView类型,表示搜索结果列表组件。
- dataId定义为List<String>类型,保存搜索结果中的记录ID。
- dataInfo定义为 List<String>类型,保存搜索结果中的文本内容。
- adapter定义为ArrayAdapter<String>类型,绑定搜索结果列表的适配器对象。
onCreate()方法中对这些对象进行初始化,并会在第一次运行时创建SQLite数据表(infobox),定义的字段包括:
- recid,记录ID,定义为自增长整数。
- info,备忘录信息,定义为文本类型。
onStart()方法中,当主界面回成为活动界面时,会根据searchView组件中的内容进行查询;点击搜索按钮或修改查询内容可以完成相同的操作。
搜索功能使用search()方法完成。方法中通过关键字查询info字段内容,返回最新的100条的备忘录信息,由dataId对象保存recid字段数据,由dataInfo对象保存info字段数据,并将dataInfo对象的数据绑定到listView组件中。
listView组件通过onItemClick()方法响应列表项的点击操作,并打开编辑界面进行修改或删除,同时向编辑界面传递记录ID数据(recid)。
onClick()方法中响应了添加按钮的操作。点击添加按钮同样打开编辑界面,并传递记录ID数据(recid)为0。
编辑界面
无论是添加新的备忘录,还是对已存在的备忘录进行编辑,都使用了编辑界面(EditActivity),其布局如下(activity_edit.xml)。
XML |
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/btnReturn"
android:text="返回"
android:textSize="22sp"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:id="@+id/btnDelete"
android:text="删除"
android:textSize="22sp"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:id="@+id/btnSave"
android:text="保存"
android:textSize="22sp"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
</LinearLayout>
<EditText android:id="@+id/txtInfo"
android:gravity="top"
android:textSize="25sp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:scrollbarStyle="outsideOverlay"></EditText>
</LinearLayout>
|
其中定义了三个按钮,分别是返回(btnReturn)、删除(btnDelete)和保存(btnSave)按钮,此外,文本编辑使用了EditView组件(txtInfo)。
编辑界面的Java代码部分如下(EditActivity.java)。
Java |
package com.caohuayu.infobox;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
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;
public class EditActivity extends AppCompatActivity
implements View.OnClickListener{
// 当前记录ID,为0时表示新记录
private long recid = 0;
//
Button btnDelete,btnSave,btnReturn;
EditText txtInfo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit);
// 确认记录ID
Intent intent = getIntent();
String s = intent.getStringExtra("recid");
if(s==null || s.length()==0) recid=0;
else recid = Long.valueOf(s);
//Toast.makeText(this,String.valueOf(recid),Toast.LENGTH_LONG).show();
// 控件初始化
btnDelete = (Button)findViewById(R.id.btnDelete);
btnSave = (Button)findViewById(R.id.btnSave);
btnReturn = (Button)findViewById(R.id.btnReturn);
txtInfo=(EditText)findViewById(R.id.txtInfo);
btnDelete.setOnClickListener(this);
btnSave.setOnClickListener(this);
btnReturn.setOnClickListener(this);
// 如果记录ID大于0则显示信息内容
if(recid>0){
SQLiteDatabase db = openOrCreateDatabase(
"infobox.db",MODE_PRIVATE,null);
String sql = "select info from infobox where recid="+recid+";";
Cursor cur = db.rawQuery(sql,null);
if(cur.getCount()>0){
cur.moveToFirst();
txtInfo.setText(cur.getString(0));
}
cur.close();
db.close();
}
}
@Override
public void onClick(View v){
int vid = v.getId();
if(vid==R.id.btnDelete){
// 删除记录
delete();
}else if(vid==R.id.btnSave){
// 保存记录
save();
}else if (vid==R.id.btnReturn){
// 返回
finish();
}
}
// 删除当前记录
private void delete(){
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
dlg.setTitle("删除信息");
dlg.setMessage("真的要删除当前信息吗?");
dlg.setCancelable(false);
// 确认操作
dlg.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
SQLiteDatabase db = openOrCreateDatabase(
"infobox.db",MODE_PRIVATE,null);
String sql = "delete from infobox where recid="+recid+";";
db.execSQL(sql);
db.close();
txtInfo.setText("");
Toast.makeText(EditActivity.this,
"信息已删除",Toast.LENGTH_SHORT).show();
finish();
}
});
// 取消操作
dlg.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
return;
}
});
//
dlg.show();
}
// 保存记录
private void save(){
String s = txtInfo.getText().toString().trim();
if(s.length()==0){
Toast.makeText(this,
"请输入内容",Toast.LENGTH_SHORT).show();
return;
}
SQLiteDatabase db = openOrCreateDatabase(
"infobox.db",MODE_PRIVATE,null);
if(recid<=0) {
// 添加记录
ContentValues values = new ContentValues();
values.put("info", s);
if (db.insert("infobox", null, values) > 0)
Toast.makeText(this,
"信息已保存", Toast.LENGTH_SHORT).show();
else
Toast.makeText(this,
"信息保存失败", Toast.LENGTH_SHORT).show();
}else{
// 更新记录
ContentValues values = new ContentValues();
values.put("info", s);
if(db.update("infobox",values,
"recid="+recid,null)>0)
Toast.makeText(this, "信息已保存",
Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, "信息保存失败",
Toast.LENGTH_SHORT).show();
}
db.close();
}
}
|
EditActivity中定义的变量和对象包括:
- recid,保存当前操作的备忘录ID,为0时表示编辑新的备忘录信息,大于0时为编辑已存在的备忘录信息。
- btnReturn、btnDelete、btnSave分别表示返回按钮、删除按钮、保存按钮。
- txtInfo表示文本编辑组件。
onCreate()方法,会读取由主界面传递的recid数据,当recid大于0时,会从数据库中读取信息并显示到txtInfo组件。
返回操作会直接调用Activity的finish()方法回到主界面。
删除操作时,会通过一个消息对话框(AlertDialog)确认,然后通过recid数据删除数据库的备忘录信息。
保存操作时,如果recid为0,会将txtInfo中的文本添加到数据库;如果recid大于0,则更新相应的备忘录数据。