NotePad记事本——Android 笔记应用开发
Android 笔记应用开发背景
在移动办公与学习场景日益普及的当下,便捷的笔记工具成为刚需。传统记事本功能单一,难以满足用户分类管理、快速查询、数据备份等多元化需求。基于此,本次以 NotePad 开源项目为基础,开展 Android 笔记应用扩展开发,通过新增时间戳、查询功能及个性化拓展,打造更贴合用户使用习惯、功能全面且体验优良的笔记工具,夯实移动开发技术实践。
一、核心功能实现:满足基础使用需求
根据实验要求,我首先完成了三项核心功能的开发,确保应用的基础实用性。
(一)笔记查询功能开发
为了让用户能快速找到所需笔记,我实现了基于标题和内容的模糊查询功能:
在 NoteList 界面顶部添加搜索框组件,设置搜索按钮和输入监听;
优化数据访问层,增加查询方法,支持根据输入关键词对笔记标题和内容进行模糊匹配;
处理搜索结果的展示逻辑,当用户输入关键词并点击搜索后,列表会实时刷新显示匹配的笔记;
支持搜索框为空时自动显示所有笔记,提升用户体验。
(二)UI 美化优化
原应用的 UI 风格较为简洁,缺乏个性化设计,我从以下几个方面进行了美化:
主题定制:为应用设计了不同颜色主题,用户可根据使用场景切换;
背景优化:允许用户为记事本选择自定义背景,提供多种纯色背景和简约图案背景供选择,也支持设置透明度,避免背景影响文字阅读;
编辑器优化:调整笔记编辑界面的字体大小、行间距,增加字体颜色选择功能,支持粗体、斜体等基础格式设置,提升编辑体验;
列表项优化:为笔记列表项添加圆角效果和轻微阴影,hover 时显示高亮状态,让界面更具层次感。
二、主要功能模块
在完成核心功能后,我额外实现了四项实用的扩展功能,让应用更具竞争力。
- 笔记管理
- 创建笔记
- 编辑笔记
- 删除笔记
- 复制笔记
- 搜索笔记
- 分类管理
- 用户界面
- 列表视图
- 编辑视图
- 背景颜色切换
- 数据持久化
- SQLite 数据库存储
- ContentProvider 数据访问
- 自动保存机制
- 高级功能
- Live Folder 支持
- 剪贴板集成
- Intent 过滤器
- 替代操作(Alternative Actions)
三、项目结构分析
- NotesList.java
功能: 笔记列表主界面Activity
主要职责:
- 显示所有笔记的列表
- 支持搜索功能
- 提供创建、编辑、删除笔记的入口
- 支持背景颜色切换
/**
* 自定义CursorAdapter,用于在列表项中显示笔记信息并设置操作按钮。
*
*
该类扩展了SimpleCursorAdapter,添加了以下功能:
*
*- 自定义视图绑定:显示标题、内容预览、时间戳和缩略图
*- 编辑按钮:快速编辑笔记
*- 删除按钮:快速删除笔记(带确认对话框)
*- 搜索模式适配:在搜索模式下隐藏操作按钮
*
*
*
视图元素:
*
*- 标题(text1):笔记标题
*- 内容预览(text_content_preview):笔记内容的前80个字符
*- 时间戳(text_timestamp):修改日期(可选显示)
*- 缩略图(thumbnail):根据内容类型显示的图标
*- 编辑按钮(btn_edit):打开编辑界面
*- 删除按钮(btn_delete):删除笔记
*
*
* @see android.widget.SimpleCursorAdapter
*/
private class NotesCursorAdapter extends SimpleCursorAdapter {
public NotesCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
// Convert cursor to Note entity
Cursor cursor = (Cursor) getItem(position);
Note note = Note.fromCursor(cursor);
if (note != null) {
// Set title using entity getter
TextView textTitle = (TextView) view.findViewById(android.R.id.text1);
if (textTitle != null && note.getTitle() != null) {
textTitle.setText(note.getTitle());
}
// Set content preview using entity getter
TextView textContentPreview = (TextView) view.findViewById(R.id.text_content_preview);
if (textContentPreview != null) {
String noteContent = note.getNote();
if (noteContent != null && noteContent.length() > 0) {
// Remove line breaks and limit length for preview
String preview = noteContent.replaceAll("\\n", " ").trim();
if (preview.length() > 100) {
preview = preview.substring(0, 100) + "...";
}
textContentPreview.setText(preview);
textContentPreview.setVisibility(View.VISIBLE);
} else {
textContentPreview.setVisibility(View.GONE);
}
}
// Set timestamp using entity getter
TextView textTimestamp = (TextView) view.findViewById(R.id.text_timestamp);
if (textTimestamp != null) {
boolean isSearchMode = mCurrentFilter != null && mCurrentFilter.length() > 0;
if (isSearchMode || !mShowTimestamp) {
textTimestamp.setVisibility(View.GONE);
} else {
textTimestamp.setVisibility(View.VISIBLE);
// Only show modified date
long modified = note.getModified();
if (modified > 0) {
DateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日", java.util.Locale.CHINESE);
String dateStr = dateFormat.format(new Date(modified));
textTimestamp.setText(dateStr);
}
}
}
// Setup edit and delete buttons
long noteId = cursor.getLong(0);
if (noteId >= 0) {
Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), noteId);
boolean isSearchMode = mCurrentFilter != null && mCurrentFilter.length() > 0;
ImageButton btnEdit = (ImageButton) view.findViewById(R.id.btn_edit);
ImageButton btnDelete = (ImageButton) view.findViewById(R.id.btn_delete);
if (isSearchMode) {
if (btnEdit != null) btnEdit.setVisibility(View.GONE);
if (btnDelete != null) btnDelete.setVisibility(View.GONE);
} else {
if (btnEdit != null) {
btnEdit.setVisibility(View.VISIBLE);
btnEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(Intent.ACTION_EDIT, noteUri));
}
});
}
if (btnDelete != null) {
btnDelete.setVisibility(View.VISIBLE);
btnDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(NotesList.this)
.setTitle(R.string.menu_delete)
.setMessage(getString(R.string.confirm_delete_message))
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getContentResolver().delete(noteUri, null, null);
Cursor newCursor = queryNotes(mCurrentFilter);
mAdapter.swapCursor(newCursor);
mCursor = newCursor;
updateListTitle();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
});
}
}
}
}
return view;
}
}
- NoteEditor.java
功能: 笔记编辑Activity
主要职责:
- 创建新笔记
- 编辑现有笔记
- 从剪贴板粘贴创建笔记
- 支持背景颜色自定义
- 自动保存功能
/**
* 自定义EditText视图,在每行文本之间绘制行线。
*
*
该类扩展了标准的EditText,添加了类似笔记本的行线效果,
* 使文本编辑体验更接近真实的纸质笔记本。
*
*
特性:
*
*- 自动绘制行线
*- 支持主题感知的行线颜色
*- 行线位置与文本基线对齐
*
*
*
使用方式:在布局XML中直接使用此类作为EditText的替代。
*
* @see android.widget.EditText
*/
public static class LinedEditText extends EditText {
private Rect mRect;
private Paint mPaint;
// This constructor is used by LayoutInflater
public LinedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// Creates a Rect and a Paint object, and sets the style and color of the Paint object.
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(1.0f);
// Use theme-aware color for lines
// Default to a subtle gray that works in both light and dark themes
int lineColor = 0x40000000; // Semi-transparent black (works in light theme)
try {
// Try to get theme color
android.content.res.TypedArray a = context.obtainStyledAttributes(
new int[]{android.R.attr.textColorSecondary});
int textColorSecondary = a.getColor(0, 0x40000000);
a.recycle();
// Use a lighter version for lines
lineColor = (textColorSecondary & 0x00FFFFFF) | 0x40000000;
} catch (Exception e) {
// Fallback to default
}
mPaint.setColor(lineColor);
}
- NotePad.java
功能: 数据库契约类(Contract Class)
主要职责:
- 定义数据库表结构常量
- NotePadProvider.java
功能: 数据访问层
主要职责:
- 管理 SQLite 数据库
- 提供增删改查操作
- NotePadSettings.java
功能: 应用设置Activity
主要职责:
- 背景颜色设置
- 时间戳显示开关
- 默认排序方式设置
/**
- NotePad设置Activity,提供应用配置选项。
-
允许用户配置以下选项:
-
- 主题选择:浅色主题或深色主题
- 时间戳显示:控制是否在列表中显示笔记的修改时间
- 默认排序:按时间或按标题排序
-
使用PreferenceActivity和SharedPreferences来管理用户设置。
- 主题变更会立即应用到整个应用。
*/ import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;public class NotePadSettings extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {public static final String PREF_THEME = "pref_theme"; public static final String PREF_THEME_LIGHT = "light"; public static final String PREF_THEME_DARK = "dark"; @Override protected void onCreate(Bundle savedInstanceState) { // Apply theme before super.onCreate applyTheme(); super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); // Register preference change listener PreferenceManager.getDefaultSharedPreferences(this) .registerOnSharedPreferenceChangeListener(this); } @Override protected void onDestroy() { super.onDestroy(); PreferenceManager.getDefaultSharedPreferences(this) .unregisterOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (PREF_THEME.equals(key)) { // Theme changed, recreate activity to apply new theme recreate(); } } /** * Apply theme based on user preference. */ private void applyTheme() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String theme = prefs.getString(PREF_THEME, PREF_THEME_LIGHT); if (PREF_THEME_DARK.equals(theme)) { setTheme(android.R.style.Theme_Holo); } else { setTheme(android.R.style.Theme_Holo_Light); } }}
- TitleEditor.java
功能: 标题编辑Activity
主要职责:
- 提供独立的标题编辑界面
- NotesLiveFolder.java
功能: Live Folder 创建Activity
主要职责:
- 创建 Android Live Folder
- Note.java
功能: 笔记实体类
主要职责:
- 封装笔记数据
- 提供从 Cursor 创建 Note 对象的工厂方法
关键属性:
- id: 笔记ID
- title: 标题
- note: 内容
- created: 创建时间
- modified: 修改时间
- category: 分类
public class Note {
private long id;
private String title;
private String note;
private long created;
private long modified;
private String category;public Note() { } public Note(long id, String title, String note, long created, long modified, String category) { this.id = id; this.title = title; this.note = note; this.created = created; this.modified = modified; this.category = category; }数据库结构:notes 表
| 列名 | 类型 | 说明 |
| ID | INTEGER PRIMARY KEY | 主键,自增 |
| title | TEXT | 笔记标题 |
| note | TEXT | 笔记内容 |
| created | INTEGER | 创建时间戳 |
| modified | INTEGER | 修改时间戳 |
| category | TEXT | 分类 |四、技术栈
- 开发语言: Java
- 最低 SDK: Android API Level
- 数据库: SQLite
- 数据访问: ContentProvider
- UI 框架: Android SDK
- 构建工具: Gradle
五、开发总结与未来规划
通过这次 NotePad 应用的扩展开发,我不仅巩固了 Android 基础开发知识,还学会了如何从用户需求出发设计功能,提升应用的实用性和用户体验。在开发过程中,我也遇到了不少问题,比如数据库升级时的数据兼容、云备份的同步逻辑等,通过查阅官方文档、请教同学和调试代码,最终都顺利解决了这些问题。未来,我计划继续完善这款应用,添加更多实用功能,比如:
支持多类型笔记,如图片、语音、视频等多媒体内容的存储;
实现 OCR 扫描功能,支持将纸质文档扫描为电子笔记;
增加语音搜索功能,用户可通过语音指令快速查找笔记;
优化云同步功能,支持多设备实时同步笔记数据。
作者:黄哲轩
原文链接:NotePad记事本——Android 笔记应用开发- TitleEditor.java
423

被折叠的 条评论
为什么被折叠?



