本文目录导读:

- 项目结构
- 实体类(Task.java)
- 数据库助手(DatabaseHelper.java)
- 适配器(TaskAdapter.java)
- 主活动(MainActivity.java)
- 布局文件
- build.gradle 依赖
- 功能特点
- 运行效果
我来为您提供一个完整的Java移动开发案例 —— 一个简易的待办事项应用(Todo App)。
项目结构
TodoApp/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/example/todoapp/
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── TaskAdapter.java
│ │ │ │ ├── Task.java
│ │ │ │ └── DatabaseHelper.java
│ │ │ └── res/
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── item_task.xml
│ │ │ └── values/
│ └── build.gradle
实体类(Task.java)
package com.example.todoapp;
public class Task {
private int id;
private String title;
private String description;
private boolean completed;
private String createDate;
public Task() {
}
public Task(String title, String description, boolean completed, String createDate) {
this.title = title;
this.description = description;
this.completed = completed;
this.createDate = createDate;
}
// Getters and Setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
public String getCreateDate() {
return createDate;
}
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
}
数据库助手(DatabaseHelper.java)
package com.example.todoapp;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "todo.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_TASKS = "tasks";
private static final String COLUMN_ID = "id";
private static final String COLUMN_TITLE = "title";
private static final String COLUMN_DESCRIPTION = "description";
private static final String COLUMN_COMPLETED = "completed";
private static final String COLUMN_CREATE_DATE = "create_date";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String createTable = "CREATE TABLE " + TABLE_TASKS + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_TITLE + " TEXT NOT NULL, " +
COLUMN_DESCRIPTION + " TEXT, " +
COLUMN_COMPLETED + " INTEGER DEFAULT 0, " +
COLUMN_CREATE_DATE + " TEXT)";
db.execSQL(createTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TASKS);
onCreate(db);
}
// 添加任务
public long addTask(Task task) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_TITLE, task.getTitle());
values.put(COLUMN_DESCRIPTION, task.getDescription());
values.put(COLUMN_COMPLETED, task.isCompleted() ? 1 : 0);
values.put(COLUMN_CREATE_DATE, task.getCreateDate());
long id = db.insert(TABLE_TASKS, null, values);
db.close();
return id;
}
// 获取所有任务
public List<Task> getAllTasks() {
List<Task> taskList = new ArrayList<>();
String selectQuery = "SELECT * FROM " + TABLE_TASKS + " ORDER BY " + COLUMN_CREATE_DATE + " DESC";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()) {
do {
Task task = new Task();
task.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID)));
task.setTitle(cursor.getString(cursor.getColumnIndex(COLUMN_TITLE)));
task.setDescription(cursor.getString(cursor.getColumnIndex(COLUMN_DESCRIPTION)));
task.setCompleted(cursor.getInt(cursor.getColumnIndex(COLUMN_COMPLETED)) == 1);
task.setCreateDate(cursor.getString(cursor.getColumnIndex(COLUMN_CREATE_DATE)));
taskList.add(task);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return taskList;
}
// 更新任务
public int updateTask(Task task) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_TITLE, task.getTitle());
values.put(COLUMN_DESCRIPTION, task.getDescription());
values.put(COLUMN_COMPLETED, task.isCompleted() ? 1 : 0);
return db.update(TABLE_TASKS, values, COLUMN_ID + " = ?",
new String[]{String.valueOf(task.getId())});
}
// 删除任务
public void deleteTask(int id) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_TASKS, COLUMN_ID + " = ?",
new String[]{String.valueOf(id)});
db.close();
}
}
适配器(TaskAdapter.java)
package com.example.todoapp;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder> {
private List<Task> taskList;
private Context context;
private OnTaskListener onTaskListener;
public interface OnTaskListener {
void onTaskClick(Task task);
void onTaskLongClick(Task task);
void onTaskCheckChange(Task task, boolean isChecked);
}
public TaskAdapter(List<Task> taskList, OnTaskListener listener) {
this.taskList = taskList;
this.onTaskListener = listener;
}
@NonNull
@Override
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View view = LayoutInflater.from(context)
.inflate(R.layout.item_task, parent, false);
return new TaskViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
Task task = taskList.get(position);
holder.bind(task);
}
@Override
public int getItemCount() {
return taskList.size();
}
class TaskViewHolder extends RecyclerView.ViewHolder {
TextView titleText;
TextView descText;
TextView dateText;
CheckBox completedCheckBox;
TaskViewHolder(View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.textTitle);
descText = itemView.findViewById(R.id.textDescription);
dateText = itemView.findViewById(R.id.textDate);
completedCheckBox = itemView.findViewById(R.id.checkCompleted);
itemView.setOnClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onTaskListener != null) {
onTaskListener.onTaskClick(taskList.get(position));
}
});
itemView.setOnLongClickListener(v -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onTaskListener != null) {
onTaskListener.onTaskLongClick(taskList.get(position));
return true;
}
return false;
});
completedCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION && onTaskListener != null) {
Task task = taskList.get(position);
task.setCompleted(isChecked);
onTaskListener.onTaskCheckChange(task, isChecked);
}
});
}
void bind(Task task) {
titleText.setText(task.getTitle());
descText.setText(task.getDescription());
completedCheckBox.setChecked(task.isCompleted());
// 格式化日期显示
try {
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
SimpleDateFormat outputFormat = new SimpleDateFormat("MM月dd日 HH:mm", Locale.getDefault());
Date date = inputFormat.parse(task.getCreateDate());
if (date != null) {
dateText.setText(outputFormat.format(date));
}
} catch (Exception e) {
dateText.setText(task.getCreateDate());
}
// 根据完成状态设置样式
if (task.isCompleted()) {
titleText.setAlpha(0.5f);
descText.setAlpha(0.5f);
} else {
titleText.setAlpha(1f);
descText.setAlpha(1f);
}
}
}
}
主活动(MainActivity.java)
package com.example.todoapp;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity implements TaskAdapter.OnTaskListener {
private RecyclerView recyclerView;
private TaskAdapter adapter;
private List<Task> taskList;
private DatabaseHelper databaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
databaseHelper = new DatabaseHelper(this);
taskList = new ArrayList<>();
initViews();
loadTasks();
setupRecyclerView();
}
private void initViews() {
recyclerView = findViewById(R.id.recyclerView);
FloatingActionButton fab = findViewById(R.id.fabAdd);
fab.setOnClickListener(v -> showAddTaskDialog());
}
private void setupRecyclerView() {
adapter = new TaskAdapter(taskList, this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
private void loadTasks() {
taskList.clear();
taskList.addAll(databaseHelper.getAllTasks());
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
private void showAddTaskDialog() {
LayoutInflater inflater = LayoutInflater.from(this);
View dialogView = inflater.inflate(R.layout.dialog_add_task, null);
EditText etTitle = dialogView.findViewById(R.id.etTitle);
EditText etDescription = dialogView.findViewById(R.id.etDescription);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("添加新任务")
.setView(dialogView)
.setPositiveButton("添加", (dialog, which) -> {
String title = etTitle.getText().toString().trim();
String description = etDescription.getText().toString().trim();
if (title.isEmpty()) {
Toast.makeText(this, "请输入任务标题", Toast.LENGTH_SHORT).show();
return;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
String createDate = sdf.format(new Date());
Task task = new Task(title, description, false, createDate);
long id = databaseHelper.addTask(task);
if (id > 0) {
loadTasks();
Toast.makeText(this, "任务添加成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "添加失败", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("取消", null)
.show();
}
private void showEditTaskDialog(Task task) {
LayoutInflater inflater = LayoutInflater.from(this);
View dialogView = inflater.inflate(R.layout.dialog_add_task, null);
EditText etTitle = dialogView.findViewById(R.id.etTitle);
EditText etDescription = dialogView.findViewById(R.id.etDescription);
etTitle.setText(task.getTitle());
etDescription.setText(task.getDescription());
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("编辑任务")
.setView(dialogView)
.setPositiveButton("保存", (dialog, which) -> {
String title = etTitle.getText().toString().trim();
String description = etDescription.getText().toString().trim();
if (title.isEmpty()) {
Toast.makeText(this, "请输入任务标题", Toast.LENGTH_SHORT).show();
return;
}
task.setTitle(title);
task.setDescription(description);
int result = databaseHelper.updateTask(task);
if (result > 0) {
loadTasks();
Toast.makeText(this, "任务更新成功", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("取消", null)
.show();
}
private void showDeleteDialog(Task task) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("删除任务")
.setMessage("确定要删除这个任务吗?")
.setPositiveButton("删除", (dialog, which) -> {
databaseHelper.deleteTask(task.getId());
loadTasks();
Toast.makeText(this, "任务已删除", Toast.LENGTH_SHORT).show();
})
.setNegativeButton("取消", null)
.show();
}
@Override
public void onTaskClick(Task task) {
showEditTaskDialog(task);
}
@Override
public void onTaskLongClick(Task task) {
showDeleteDialog(task);
}
@Override
public void onTaskCheckChange(Task task, boolean isChecked) {
task.setCompleted(isChecked);
databaseHelper.updateTask(task);
loadTasks();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_clear_completed) {
clearCompletedTasks();
return true;
} else if (id == R.id.action_delete_all) {
deleteAllTasks();
return true;
}
return super.onOptionsItemSelected(item);
}
private void clearCompletedTasks() {
for (Task task : new ArrayList<>(taskList)) {
if (task.isCompleted()) {
databaseHelper.deleteTask(task.getId());
}
}
loadTasks();
Toast.makeText(this, "已完成的任务已清理", Toast.LENGTH_SHORT).show();
}
private void deleteAllTasks() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("清空任务")
.setMessage("确定要删除所有任务吗?此操作不可恢复!")
.setPositiveButton("确定", (dialog, which) -> {
for (Task task : new ArrayList<>(taskList)) {
databaseHelper.deleteTask(task.getId());
}
loadTasks();
Toast.makeText(this, "所有任务已删除", Toast.LENGTH_SHORT).show();
})
.setNegativeButton("取消", null)
.show();
}
}
布局文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"
android:padding="16dp"
android:clipToPadding="false" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="添加任务"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:srcCompat="@android:drawable/ic_input_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
item_task.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardElevation="4dp"
app:cardCornerRadius="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<CheckBox
android:id="@+id/checkCompleted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginStart="12dp">
<TextView
android:id="@+id/textTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@android:color/black" />
<TextView
android:id="@+id/textDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/darker_gray"
android:maxLines="3"
android:ellipsize="end"
android:layout_marginTop="4dp" />
<TextView
android:id="@+id/textDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@color/teal_700"
android:layout_marginTop="8dp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
dialog_add_task.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"
android:padding="16dp">
<EditText
android:id="@+id/etTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="任务标题"
android:inputType="text"
android:maxLines="1"
android:textSize="16sp" />
<EditText
android:id="@+id/etDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="任务描述(可选)"
android:inputType="textMultiLine"
android:minLines="3"
android:textSize="14sp"
android:layout_marginTop="12dp" />
</LinearLayout>
menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_clear_completed"
android:title="清理已完成"/>
<item
android:id="@+id/action_delete_all"
android:title="删除全部"/>
</menu>
build.gradle 依赖
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.cardview:cardview:1.0.0'
}
功能特点
- 添加任务:通过浮动按钮添加新任务
- 编辑任务:点击任务卡片进行编辑
- 删除任务:长按任务卡片删除
- 标记完成:通过复选框标记任务完成状态
- 数据持久化:使用SQLite存储数据
- 批量操作:清理已完成、删除全部任务
运行效果
- 主界面显示任务列表
- 每个任务包含标题、描述、创建日期和完成状态
- 支持下拉刷新(可扩展)
- 支持搜索功能(可扩展)
这个案例涵盖了Android开发的核心知识点:Activity生命周期、RecyclerView适配器、SQLite数据库操作、Material Design组件使用、对话框交互等,是一个完整的学习项目。