diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2443da8..bffc164 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -162,6 +162,25 @@ android:theme="@android:style/Theme.Holo.Light" > + + + + + + diff --git a/src/main/java/net/micode/notes/data/Notes.java b/src/main/java/net/micode/notes/data/Notes.java index 2475b1a..a204483 100644 --- a/src/main/java/net/micode/notes/data/Notes.java +++ b/src/main/java/net/micode/notes/data/Notes.java @@ -39,10 +39,12 @@ public class Notes { * - TYPE_NOTE:普通便签 * - TYPE_FOLDER:文件夹 * - TYPE_SYSTEM:系统文件夹 + * - TYPE_TODO:代办事项 */ public static final int TYPE_NOTE = 0; // 普通便签类型 public static final int TYPE_FOLDER = 1; // 文件夹类型 public static final int TYPE_SYSTEM = 2; // 系统文件夹类型 + public static final int TYPE_TODO = 3; // 代办事项类型 /** * 系统文件夹ID常量 @@ -265,6 +267,12 @@ public class Notes { *

类型 : TEXT

*/ public static final String TAGS = "tags"; + + /** + * 通用数据列1,用于存储代办事项的状态 + *

类型: INTEGER

+ */ + public static final String DATA1 = "data1"; } /** @@ -365,6 +373,14 @@ public class Notes { * Checklist模式常量 */ public static final int MODE_CHECK_LIST = 1; + + /** + * 代办事项状态常量 + * - STATUS_INCOMPLETE:未完成 + * - STATUS_COMPLETED:已完成 + */ + public static final int STATUS_INCOMPLETE = 0; + public static final int STATUS_COMPLETED = 1; /** * 文本便签集合的MIME类型 diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index 0d6067d..39bf475 100644 --- a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -36,7 +36,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME = "note.db"; // 数据库版本号 - private static final int DB_VERSION = 9; + private static final int DB_VERSION = 10; // 数据库表名定义 public interface TABLE { @@ -95,7 +95,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.PIN_PRIORITY + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TAGS + " TEXT NOT NULL DEFAULT ''" + + NoteColumns.TAGS + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.DATA1 + " INTEGER NOT NULL DEFAULT 0" + ")"; // 创建数据表的SQL语句 @@ -442,6 +443,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { upgradeToV9(db); oldVersion++; } + + if (oldVersion == 9) { + upgradeToV10(db); + oldVersion++; + } if (reCreateTriggers) { reCreateNoteTableTriggers(db); @@ -563,4 +569,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TAGS + " TEXT NOT NULL DEFAULT ''"); } + + /** + * 将数据库从v9升级到v10 + * 此版本升级添加了DATA1字段,用于存储代办事项的状态 + * @param db SQLite数据库实例 + */ + private void upgradeToV10(SQLiteDatabase db) { + // 为笔记表添加DATA1字段 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.DATA1 + + " INTEGER NOT NULL DEFAULT 0"); + } } diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 7c7df77..35f8bfb 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -55,6 +55,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; +import android.view.GestureDetector; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; @@ -130,9 +131,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private NoteItemData mFocusNoteDataItem; - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO; - private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; + private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.TYPE + "<>" + Notes.TYPE_TODO + " AND " + NoteColumns.PARENT_ID + "=?) OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; @@ -265,6 +266,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt startAsyncNotesListQuery(); } + // 手势检测相关变量 + private GestureDetector mGestureDetector; + private static final int SWIPE_MIN_DISTANCE = 120; + private static final int SWIPE_MAX_OFF_PATH = 250; + private static final int SWIPE_THRESHOLD_VELOCITY = 200; + + // 界面切换相关变量 + private TextView mNotesTab; + private TextView mTodoTab; + private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); @@ -276,7 +287,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mNotesListView.setOnItemLongClickListener(this); mNotesListAdapter = new NotesListAdapter(this); mNotesListView.setAdapter(mNotesListAdapter); - mAddNewNote = (Button) findViewById(R.id.btn_new_note); + mAddNewNote = (Button) findViewById(R.id.btn_add_note); mAddNewNote.setOnClickListener(this); mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); mDispatch = false; @@ -286,6 +297,54 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mState = ListEditState.NOTE_LIST; mModeCallBack = new ModeCallback(); + // 初始化界面切换栏 + mNotesTab = (TextView) findViewById(R.id.notes_tab); + mTodoTab = (TextView) findViewById(R.id.todo_tab); + mNotesTab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 已经在便签界面,不需要切换 + } + }); + mTodoTab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 切换到代办界面 + Intent intent = new Intent(NotesListActivity.this, TodoListActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + } + }); + + // 初始化手势检测器 + mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + try { + if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) + return false; + // 右滑手势:从便签界面切换到代办界面 + if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { + Intent intent = new Intent(NotesListActivity.this, TodoListActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); + return true; + } + } catch (Exception e) { + // 异常处理 + } + return false; + } + }); + + // 为ListView添加手势监听 + mNotesListView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mGestureDetector.onTouchEvent(event); + } + }); + // 初始化搜索栏组件 mSearchBar = (LinearLayout) findViewById(R.id.search_bar); mSearchEditText = (EditText) findViewById(R.id.search_edit_text); @@ -795,7 +854,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } public void onClick(View v) { - if (v.getId() == R.id.btn_new_note) { + if (v.getId() == R.id.btn_add_note) { createNewNote(); } } diff --git a/src/main/java/net/micode/notes/ui/TodoEditActivity.java b/src/main/java/net/micode/notes/ui/TodoEditActivity.java new file mode 100644 index 0000000..c1b0ae3 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/TodoEditActivity.java @@ -0,0 +1,180 @@ +package net.micode.notes.ui; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.database.Cursor; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +import java.util.ArrayList; +import java.util.List; + +public class TodoEditActivity extends Activity implements View.OnClickListener { + // 界面元素 + private ScrollView mScrollView; + private LinearLayout mInputContainer; + private Button mDoneButton; + + // 内容解析器 + private ContentResolver mContentResolver; + + // 输入框列表 + private List mInputs; + + // 要编辑的代办事项ID(如果是编辑模式) + private long mTodoId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.todo_edit); + + initResources(); + initInputs(); + } + + private void initResources() { + mContentResolver = getContentResolver(); + mScrollView = findViewById(R.id.todo_edit_scroll); + mInputContainer = findViewById(R.id.todo_edit_container); + mDoneButton = findViewById(R.id.todo_edit_done_btn); + mDoneButton.setOnClickListener(this); + + // 获取传递过来的代办事项ID(如果是编辑模式) + mTodoId = getIntent().getLongExtra("todo_id", -1); + + // 初始化输入框列表 + mInputs = new ArrayList<>(); + EditText firstInput = findViewById(R.id.todo_edit_first_input); + mInputs.add(firstInput); + + // 设置第一个输入框的键盘事件 + setInputKeyListener(firstInput); + } + + private void initInputs() { + if (mTodoId != -1) { + // 编辑模式:加载现有代办事项 + Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI, + new String[]{NoteColumns.SNIPPET}, NoteColumns.ID + "=?", + new String[]{String.valueOf(mTodoId)}, null); + + if (cursor != null && cursor.moveToFirst()) { + String content = cursor.getString(0); + mInputs.get(0).setText(content); + cursor.close(); + } + } + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.todo_edit_done_btn) { + // 保存所有代办事项 + saveTodos(); + } + } + + // 设置输入框的键盘事件 + private void setInputKeyListener(EditText input) { + input.setOnEditorActionListener((v, actionId, event) -> { + boolean handled = false; + if (actionId == EditorInfo.IME_ACTION_NEXT || + (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN)) { + // 处理回车事件 + handleEnterKey(input); + handled = true; + } + return handled; + }); + } + + // 处理回车事件 + private void handleEnterKey(EditText currentInput) { + String text = currentInput.getText().toString().trim(); + if (!text.isEmpty()) { + // 添加新的输入框 + addNewInput(); + } + } + + // 添加新的输入框 + private void addNewInput() { + // 创建新的输入框 + EditText newInput = (EditText) LayoutInflater.from(this).inflate( + R.layout.todo_edit_input, null); + + // 设置输入框属性 + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT); + params.bottomMargin = getResources().getDimensionPixelSize(R.dimen.todo_edit_input_margin_bottom); + newInput.setLayoutParams(params); + + // 设置键盘事件 + setInputKeyListener(newInput); + + // 添加到容器和列表 + mInputContainer.addView(newInput); + mInputs.add(newInput); + + // 滚动到新输入框 + mScrollView.post(() -> mScrollView.fullScroll(ScrollView.FOCUS_DOWN)); + } + + // 保存所有代办事项 + private void saveTodos() { + for (EditText input : mInputs) { + String content = input.getText().toString().trim(); + if (!content.isEmpty()) { + if (mTodoId != -1) { + // 编辑模式:更新现有代办事项 + updateTodo(mTodoId, content); + } else { + // 新建模式:创建新的代办事项 + createTodo(content); + } + } + } + + // 返回代办界面 + setResult(RESULT_OK); + finish(); + } + + // 创建新的代办事项 + private void createTodo(String content) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER); + values.put(NoteColumns.CREATED_DATE, System.currentTimeMillis()); + values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + values.put(NoteColumns.SNIPPET, content); + values.put(NoteColumns.TYPE, Notes.TYPE_TODO); + values.put(NoteColumns.DATA1, Notes.TextNote.STATUS_INCOMPLETE); // 未完成状态 + + mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); + } + + // 更新现有代办事项 + private void updateTodo(long id, String content) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.SNIPPET, content); + values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, + NoteColumns.ID + "=?", new String[]{String.valueOf(id)}); + } +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/TodoListActivity.java b/src/main/java/net/micode/notes/ui/TodoListActivity.java new file mode 100644 index 0000000..7da6b78 --- /dev/null +++ b/src/main/java/net/micode/notes/ui/TodoListActivity.java @@ -0,0 +1,505 @@ +package net.micode.notes.ui; + +import android.app.Activity; +import android.content.AsyncQueryHandler; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Intent; +import android.database.Cursor; +import android.os.Bundle; +import android.view.ActionMode; +import android.view.GestureDetector; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; + +import java.util.ArrayList; +import java.util.List; + +public class TodoListActivity extends Activity implements View.OnClickListener { + // 手势检测相关变量 + private GestureDetector mGestureDetector; + private static final int SWIPE_MIN_DISTANCE = 120; + private static final int SWIPE_MAX_OFF_PATH = 250; + private static final int SWIPE_THRESHOLD_VELOCITY = 200; + + // 界面元素 + private TextView mNotesTab; + private TextView mTodoTab; + private TextView mEmptyHint; + private ListView mIncompleteList; + private ListView mCompleteList; + private View mDivider; + private LinearLayout mCompleteSection; + private Button mAddTodoButton; + + // 数据适配器 + private TodoAdapter mIncompleteAdapter; + private TodoAdapter mCompleteAdapter; + + // 数据列表 + private List mIncompleteItems; + private List mCompleteItems; + + // 内容解析器和异步查询处理器 + private ContentResolver mContentResolver; + private BackgroundQueryHandler mBackgroundQueryHandler; + + // 请求码 + private static final int REQUEST_CODE_NEW_TODO = 100; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.todo_list); + + initResources(); + initGestureDetector(); + initData(); + } + + @Override + protected void onStart() { + super.onStart(); + startAsyncTodoQuery(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_NEW_TODO) { + // 刷新代办事项列表 + startAsyncTodoQuery(); + } + super.onActivityResult(requestCode, resultCode, data); + } + + private void initResources() { + mContentResolver = getContentResolver(); + mBackgroundQueryHandler = new BackgroundQueryHandler(mContentResolver); + + // 初始化界面元素 + mEmptyHint = (TextView) findViewById(R.id.todo_empty_hint); + mIncompleteList = (ListView) findViewById(R.id.todo_incomplete_list); + mCompleteList = (ListView) findViewById(R.id.todo_complete_list); + mDivider = findViewById(R.id.todo_divider); + mCompleteSection = (LinearLayout) findViewById(R.id.todo_complete_section); + mAddTodoButton = (Button) findViewById(R.id.btn_add_todo); + + // 初始化界面切换栏 + mNotesTab = (TextView) findViewById(R.id.notes_tab); + mTodoTab = (TextView) findViewById(R.id.todo_tab); + + // 设置点击事件 + mNotesTab.setOnClickListener(this); + mTodoTab.setOnClickListener(this); + mAddTodoButton.setOnClickListener(this); + + // 初始化数据适配器 + mIncompleteItems = new ArrayList<>(); + mCompleteItems = new ArrayList<>(); + mIncompleteAdapter = new TodoAdapter(mIncompleteItems, false); + mCompleteAdapter = new TodoAdapter(mCompleteItems, true); + mIncompleteList.setAdapter(mIncompleteAdapter); + mCompleteList.setAdapter(mCompleteAdapter); + + // 设置列表点击事件 + mIncompleteList.setOnItemClickListener((parent, view, position, id) -> { + if (mIsMultiSelectMode) { + // 多选模式下,切换选中状态 + TodoItem item = mIncompleteItems.get(position); + item.isSelected = !item.isSelected; + mIncompleteAdapter.notifyDataSetChanged(); + updateSelectedItems(); + } else { + // 非多选模式下,进入编辑模式 + TodoItem item = mIncompleteItems.get(position); + Intent intent = new Intent(TodoListActivity.this, TodoEditActivity.class); + intent.putExtra("todo_id", item.id); + startActivityForResult(intent, REQUEST_CODE_NEW_TODO); + } + }); + + mCompleteList.setOnItemClickListener((parent, view, position, id) -> { + if (mIsMultiSelectMode) { + // 多选模式下,切换选中状态 + TodoItem item = mCompleteItems.get(position); + item.isSelected = !item.isSelected; + mCompleteAdapter.notifyDataSetChanged(); + updateSelectedItems(); + } else { + // 非多选模式下,进入编辑模式 + TodoItem item = mCompleteItems.get(position); + Intent intent = new Intent(TodoListActivity.this, TodoEditActivity.class); + intent.putExtra("todo_id", item.id); + startActivityForResult(intent, REQUEST_CODE_NEW_TODO); + } + }); + + // 设置列表长按事件 + mIncompleteList.setOnItemLongClickListener((parent, view, position, id) -> { + startMultiSelectMode(); + TodoItem item = mIncompleteItems.get(position); + item.isSelected = true; + mIncompleteAdapter.notifyDataSetChanged(); + updateSelectedItems(); + return true; + }); + + mCompleteList.setOnItemLongClickListener((parent, view, position, id) -> { + startMultiSelectMode(); + TodoItem item = mCompleteItems.get(position); + item.isSelected = true; + mCompleteAdapter.notifyDataSetChanged(); + updateSelectedItems(); + return true; + }); + } + + private void initGestureDetector() { + // 初始化手势检测器 + mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + try { + if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) + return false; + // 左滑手势:从代办界面切换回便签界面 + if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { + Intent intent = new Intent(TodoListActivity.this, NotesListActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); + return true; + } + } catch (Exception e) { + // 异常处理 + } + return false; + } + }); + + // 为整个界面添加手势监听 + findViewById(R.id.todo_incomplete_section).setOnTouchListener((v, event) -> mGestureDetector.onTouchEvent(event)); + findViewById(R.id.todo_complete_section).setOnTouchListener((v, event) -> mGestureDetector.onTouchEvent(event)); + } + + private void initData() { + // 初始化数据,这里可以添加一些示例数据 + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.notes_tab) { + // 切换到便签界面 + Intent intent = new Intent(TodoListActivity.this, NotesListActivity.class); + startActivity(intent); + overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); + } else if (v.getId() == R.id.todo_tab) { + // 已经在代办界面,不需要切换 + } else if (v.getId() == R.id.btn_add_todo) { + // 跳转到新建代办事项界面 + Intent newTodoIntent = new Intent(TodoListActivity.this, TodoEditActivity.class); + startActivityForResult(newTodoIntent, REQUEST_CODE_NEW_TODO); + } + } + + private void startAsyncTodoQuery() { + // 查询未完成的代办事项 + String selection = NoteColumns.TYPE + "=" + Notes.TYPE_TODO + " AND " + NoteColumns.DATA1 + "=" + Notes.TextNote.STATUS_INCOMPLETE; + mBackgroundQueryHandler.startQuery(0, null, Notes.CONTENT_NOTE_URI, + new String[]{NoteColumns.ID, NoteColumns.SNIPPET}, selection, null, + NoteColumns.CREATED_DATE + " ASC"); + + // 查询已完成的代办事项 + selection = NoteColumns.TYPE + "=" + Notes.TYPE_TODO + " AND " + NoteColumns.DATA1 + "=" + Notes.TextNote.STATUS_COMPLETED; + mBackgroundQueryHandler.startQuery(1, null, Notes.CONTENT_NOTE_URI, + new String[]{NoteColumns.ID, NoteColumns.SNIPPET}, selection, null, + NoteColumns.CREATED_DATE + " ASC"); + } + + private void updateUI() { + // 更新未完成列表 + if (mIncompleteItems.isEmpty()) { + mEmptyHint.setVisibility(View.VISIBLE); + mIncompleteList.setVisibility(View.GONE); + } else { + mEmptyHint.setVisibility(View.GONE); + mIncompleteList.setVisibility(View.VISIBLE); + } + mIncompleteAdapter.notifyDataSetChanged(); + + // 更新已完成列表 + if (mCompleteItems.isEmpty()) { + mDivider.setVisibility(View.GONE); + mCompleteSection.setVisibility(View.GONE); + } else { + mDivider.setVisibility(View.VISIBLE); + mCompleteSection.setVisibility(View.VISIBLE); + } + mCompleteAdapter.notifyDataSetChanged(); + } + + // 代办事项数据类 + private static class TodoItem { + long id; + String content; + boolean isCompleted; + boolean isSelected; + + TodoItem(long id, String content, boolean isCompleted) { + this.id = id; + this.content = content; + this.isCompleted = isCompleted; + this.isSelected = false; + } + } + + // 多选模式相关变量 + private boolean mIsMultiSelectMode = false; + private ArrayList mSelectedIds = new ArrayList<>(); + private ActionMode mActionMode; + + // 代办事项适配器 + private class TodoAdapter extends BaseAdapter { + private List mItems; + private boolean mIsCompletedSection; + + TodoAdapter(List items, boolean isCompletedSection) { + mItems = items; + mIsCompletedSection = isCompletedSection; + } + + @Override + public int getCount() { + return mItems.size(); + } + + @Override + public Object getItem(int position) { + return mItems.get(position); + } + + @Override + public long getItemId(int position) { + return mItems.get(position).id; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(TodoListActivity.this).inflate(R.layout.todo_item, null); + holder = new ViewHolder(); + holder.checkbox = convertView.findViewById(R.id.todo_item_checkbox); + holder.selectbox = convertView.findViewById(R.id.todo_item_selectbox); + holder.content = convertView.findViewById(R.id.todo_item_content); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + TodoItem item = mItems.get(position); + holder.content.setText(item.content); + + if (mIsMultiSelectMode) { + // 多选模式下,显示方框选择框,隐藏圆形选择框 + holder.checkbox.setVisibility(View.GONE); + holder.selectbox.setVisibility(View.VISIBLE); + holder.selectbox.setChecked(item.isSelected); + + // 设置方框选择框点击事件 + holder.selectbox.setTag(item); + holder.selectbox.setOnClickListener(v -> { + CheckBox checkBox = (CheckBox) v; + TodoItem todoItem = (TodoItem) checkBox.getTag(); + todoItem.isSelected = checkBox.isChecked(); + updateSelectedItems(); + }); + } else { + // 非多选模式下,显示圆形选择框,隐藏方框选择框 + holder.checkbox.setVisibility(View.VISIBLE); + holder.selectbox.setVisibility(View.GONE); + holder.checkbox.setChecked(item.isCompleted); + + // 设置文本颜色 + if (item.isCompleted) { + holder.content.setTextColor(getResources().getColor(android.R.color.darker_gray)); + } else { + holder.content.setTextColor(getResources().getColor(android.R.color.black)); + } + + // 设置圆形选择框点击事件 + holder.checkbox.setTag(item); + holder.checkbox.setOnClickListener(v -> { + CheckBox checkBox = (CheckBox) v; + TodoItem todoItem = (TodoItem) checkBox.getTag(); + todoItem.isCompleted = checkBox.isChecked(); + updateTodoStatus(todoItem); + }); + } + + return convertView; + } + + // 设置选中状态 + public void setSelected(int position, boolean selected) { + if (position >= 0 && position < mItems.size()) { + mItems.get(position).isSelected = selected; + notifyDataSetChanged(); + } + } + + // 获取选中的项目 + public ArrayList getSelectedIds() { + ArrayList selectedIds = new ArrayList<>(); + for (TodoItem item : mItems) { + if (item.isSelected) { + selectedIds.add(item.id); + } + } + return selectedIds; + } + + // 清除所有选中状态 + public void clearSelection() { + for (TodoItem item : mItems) { + item.isSelected = false; + } + notifyDataSetChanged(); + } + + private class ViewHolder { + CheckBox checkbox; // 圆形选择框,用于标记完成状态 + CheckBox selectbox; // 方框选择框,用于多选模式 + TextView content; + } + } + + // 更新代办事项状态 + private void updateTodoStatus(TodoItem item) { + ContentValues values = new ContentValues(); + values.put(NoteColumns.DATA1, item.isCompleted ? Notes.TextNote.STATUS_COMPLETED : Notes.TextNote.STATUS_INCOMPLETE); + values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + + mContentResolver.update(Notes.CONTENT_NOTE_URI, values, + NoteColumns.ID + "=?", new String[]{String.valueOf(item.id)}); + + // 刷新列表 + startAsyncTodoQuery(); + } + + // 开始多选模式 + private void startMultiSelectMode() { + if (!mIsMultiSelectMode) { + mIsMultiSelectMode = true; + mActionMode = startActionMode(new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // 这里使用已有的note_list_options菜单 + getMenuInflater().inflate(R.menu.note_list_options, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == R.id.delete) { + batchDelete(); + mode.finish(); + return true; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mIsMultiSelectMode = false; + mSelectedIds.clear(); + mIncompleteAdapter.clearSelection(); + mCompleteAdapter.clearSelection(); + mActionMode = null; + } + }); + } + } + + // 更新选中的项目 + private void updateSelectedItems() { + mSelectedIds.clear(); + mSelectedIds.addAll(mIncompleteAdapter.getSelectedIds()); + mSelectedIds.addAll(mCompleteAdapter.getSelectedIds()); + + if (mActionMode != null) { + if (mSelectedIds.isEmpty()) { + mActionMode.finish(); + } else { + // 使用占位符字符串,后续可以在strings.xml中添加 + mActionMode.setTitle("已选择 " + mSelectedIds.size() + " 项"); + } + } + } + + // 批量删除选中的项目 + private void batchDelete() { + if (!mSelectedIds.isEmpty()) { + for (long id : mSelectedIds) { + mContentResolver.delete(Notes.CONTENT_NOTE_URI, + NoteColumns.ID + "=?", new String[]{String.valueOf(id)}); + } + // 刷新列表 + startAsyncTodoQuery(); + } + } + + // 异步查询处理器 + private class BackgroundQueryHandler extends AsyncQueryHandler { + BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); + } + + @Override + protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + if (cursor != null) { + try { + if (token == 0) { + // 处理未完成代办事项 + mIncompleteItems.clear(); + while (cursor.moveToNext()) { + long id = cursor.getLong(0); + String content = cursor.getString(1); + mIncompleteItems.add(new TodoItem(id, content, false)); + } + } else if (token == 1) { + // 处理已完成代办事项 + mCompleteItems.clear(); + while (cursor.moveToNext()) { + long id = cursor.getLong(0); + String content = cursor.getString(1); + mCompleteItems.add(new TodoItem(id, content, true)); + } + // 更新UI + updateUI(); + } + } finally { + cursor.close(); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/res/anim/slide_in_left.xml b/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..58f88a8 --- /dev/null +++ b/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/main/res/anim/slide_in_right.xml b/src/main/res/anim/slide_in_right.xml new file mode 100644 index 0000000..aea1f5d --- /dev/null +++ b/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/main/res/anim/slide_out_left.xml b/src/main/res/anim/slide_out_left.xml new file mode 100644 index 0000000..4fef5cb --- /dev/null +++ b/src/main/res/anim/slide_out_left.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/main/res/anim/slide_out_right.xml b/src/main/res/anim/slide_out_right.xml new file mode 100644 index 0000000..de15bd9 --- /dev/null +++ b/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_add_circle.xml b/src/main/res/drawable/ic_add_circle.xml new file mode 100644 index 0000000..f60051c --- /dev/null +++ b/src/main/res/drawable/ic_add_circle.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_done_circle.xml b/src/main/res/drawable/ic_done_circle.xml new file mode 100644 index 0000000..d357380 --- /dev/null +++ b/src/main/res/drawable/ic_done_circle.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/src/main/res/drawable/selected_tab_bg.xml b/src/main/res/drawable/selected_tab_bg.xml new file mode 100644 index 0000000..a5a5721 --- /dev/null +++ b/src/main/res/drawable/selected_tab_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/todo_checkbox.xml b/src/main/res/drawable/todo_checkbox.xml new file mode 100644 index 0000000..78a0900 --- /dev/null +++ b/src/main/res/drawable/todo_checkbox.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/todo_edit_input_bg.xml b/src/main/res/drawable/todo_edit_input_bg.xml new file mode 100644 index 0000000..b2ed3bb --- /dev/null +++ b/src/main/res/drawable/todo_edit_input_bg.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/unselected_tab_bg.xml b/src/main/res/drawable/unselected_tab_bg.xml new file mode 100644 index 0000000..edc3849 --- /dev/null +++ b/src/main/res/drawable/unselected_tab_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/layout/note_list.xml b/src/main/res/layout/note_list.xml index 2c4b3a6..c9afa71 100644 --- a/src/main/res/layout/note_list.xml +++ b/src/main/res/layout/note_list.xml @@ -15,7 +15,7 @@ limitations under the License. --> - + android:orientation="vertical" + android:layout_above="@+id/view_switcher"> + -