新增代办功能,增加便签代办切换栏,美化便签列表界面 #16

Merged
psq5hzxpo merged 1 commits from luhaozhe_branch into master 4 weeks ago

@ -162,6 +162,25 @@
android:theme="@android:style/Theme.Holo.Light" >
</activity>
<activity
android:name=".ui.TodoListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:windowSoftInputMode="adjustPan"
android:exported="true">
</activity>
<activity
android:name=".ui.TodoEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:windowSoftInputMode="adjustPan"
android:exported="true">
</activity>
<service
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" >

@ -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 {
* <P> : TEXT </P>
*/
public static final String TAGS = "tags";
/**
* 1
* <P> : INTEGER </P>
*/
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

@ -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 ''");
}
/**
* v9v10
* 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");
}
}

@ -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();
}
}

@ -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<EditText> 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)});
}
}

@ -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<TodoItem> mIncompleteItems;
private List<TodoItem> 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<Long> mSelectedIds = new ArrayList<>();
private ActionMode mActionMode;
// 代办事项适配器
private class TodoAdapter extends BaseAdapter {
private List<TodoItem> mItems;
private boolean mIsCompletedSection;
TodoAdapter(List<TodoItem> 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<Long> getSelectedIds() {
ArrayList<Long> 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();
}
}
}
}
}

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="300" />
</set>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%p"
android:toXDelta="0"
android:duration="300" />
</set>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="-100%p"
android:duration="300" />
</set>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:toXDelta="100%p"
android:duration="300" />
</set>

@ -0,0 +1,30 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 黄色圆形背景 -->
<item>
<shape android:shape="oval">
<solid android:color="#FFC107" />
<size
android:width="64dp"
android:height="64dp" />
</shape>
</item>
<!-- 白色加号 -->
<item
android:left="24dp"
android:right="24dp"
android:top="30dp"
android:bottom="30dp">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF" />
</shape>
</item>
<item
android:left="30dp"
android:right="30dp"
android:top="24dp"
android:bottom="24dp">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF" />
</shape>
</item>
</layer-list>

@ -0,0 +1,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:width="64dp"
android:height="64dp">
<solid android:color="#4CAF50" />
</shape>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#E8F4FD" />
<corners android:radius="20dp" />
</shape>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 选中状态 -->
<item android:state_checked="true">
<layer-list>
<!-- 背景圆圈 -->
<item>
<shape android:shape="oval">
<solid android:color="#007AFF" />
<size android:width="24dp" android:height="24dp" />
</shape>
</item>
<!-- 勾选标记 -->
<item android:drawable="@android:drawable/checkbox_on_background"
android:gravity="center" />
</layer-list>
</item>
<!-- 未选中状态 -->
<item android:state_checked="false">
<shape android:shape="oval">
<solid android:color="#FFFFFF" />
<stroke android:color="#808080" android:width="2dp" />
<size android:width="24dp" android:height="24dp" />
</shape>
</item>
</selector>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#F5F5F5" />
<corners android:radius="8dp" />
<stroke android:color="#E0E0E0" android:width="1dp" />
</shape>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF" />
<corners android:radius="20dp" />
</shape>

@ -15,7 +15,7 @@
limitations under the License.
-->
<FrameLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
@ -24,9 +24,11 @@
android:paddingTop="24dp">
<LinearLayout
android:id="@+id/main_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:orientation="vertical"
android:layout_above="@+id/view_switcher">
<TextView
android:id="@+id/tv_title_bar"
@ -107,13 +109,56 @@
android:listSelector="@android:color/transparent"
android:divider="@null"
android:fadingEdge="@null" />
</LinearLayout>
<Button
android:id="@+id/btn_new_note"
android:background="@drawable/new_note"
android:layout_width="match_parent"
<!-- 界面切换栏 -->
<LinearLayout
android:id="@+id/view_switcher"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:padding="8dp"
android:layout_alignParentBottom="true">
<TextView
android:id="@+id/notes_tab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="便签"
android:textSize="16sp"
android:textColor="#007AFF"
android:gravity="center"
android:padding="12dp"
android:background="@drawable/selected_tab_bg"
android:clickable="true"
android:focusable="true" />
<TextView
android:id="@+id/todo_tab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="代办"
android:textSize="16sp"
android:textColor="#808080"
android:gravity="center"
android:padding="12dp"
android:background="@drawable/unselected_tab_bg"
android:clickable="true"
android:focusable="true" />
</LinearLayout>
<!-- 圆形加号按钮 -->
<Button
android:id="@+id/btn_add_note"
android:background="@drawable/ic_add_circle"
android:layout_width="64dp"
android:layout_height="64dp"
android:focusable="false"
android:layout_gravity="bottom" />
</FrameLayout>
android:layout_alignParentRight="true"
android:layout_above="@+id/view_switcher"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp" />
</RelativeLayout>

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFFFFF"
android:fitsSystemWindows="true">
<!-- 可滚动的输入区域 -->
<ScrollView
android:id="@+id/todo_edit_scroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="16dp"
android:paddingBottom="80dp">
<!-- 输入框容器 -->
<LinearLayout
android:id="@+id/todo_edit_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 第一个输入框 -->
<EditText
android:id="@+id/todo_edit_first_input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="输入代办内容"
android:textSize="16sp"
android:inputType="textMultiLine"
android:minLines="1"
android:maxLines="5"
android:background="@drawable/todo_edit_input_bg"
android:padding="12dp"
android:layout_marginBottom="12dp"
android:imeOptions="actionNext" />
</LinearLayout>
</ScrollView>
<!-- 完成按钮 -->
<Button
android:id="@+id/todo_edit_done_btn"
android:layout_width="100dp"
android:layout_height="40dp"
android:background="@drawable/ic_done_circle"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:focusable="false"
android:text="完成"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:gravity="center"
android:backgroundTint="#4CAF50"
android:shape="rectangle"
android:radius="20dp" />
</FrameLayout>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="输入代办内容"
android:textSize="16sp"
android:inputType="textMultiLine"
android:minLines="1"
android:maxLines="5"
android:background="@drawable/todo_edit_input_bg"
android:padding="12dp"
android:imeOptions="actionNext" />

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="16dp"
android:background="#FFFFFF">
<!-- 圆形选择框(用于标记完成状态) -->
<CheckBox
android:id="@+id/todo_item_checkbox"
android:layout_width="24dp"
android:layout_height="24dp"
android:button="@drawable/todo_checkbox"
android:layout_marginRight="16dp"
android:focusable="false" />
<!-- 方框选择框(用于多选模式) -->
<CheckBox
android:id="@+id/todo_item_selectbox"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="16dp"
android:focusable="false"
android:visibility="gone" />
<!-- 代办事项内容 -->
<TextView
android:id="@+id/todo_item_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"
android:textColor="#000000"
android:singleLine="false" />
</LinearLayout>

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background"
android:fitsSystemWindows="true"
android:paddingTop="24dp">
<LinearLayout
android:id="@+id/main_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_above="@+id/view_switcher">
<!-- 未完成代办事项区域 -->
<LinearLayout
android:id="@+id/todo_incomplete_section"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:orientation="vertical">
<!-- 无代办提示 -->
<TextView
android:id="@+id/todo_empty_hint"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="无代办"
android:textSize="18sp"
android:textColor="#808080"
android:visibility="visible" />
<!-- 未完成代办事项列表 -->
<ListView
android:id="@+id/todo_incomplete_list"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:cacheColorHint="@null"
android:listSelector="@android:color/transparent"
android:divider="@null"
android:fadingEdge="@null"
android:visibility="gone" />
</LinearLayout>
<!-- 分隔线 -->
<View
android:id="@+id/todo_divider"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="#E0E0E0"
android:visibility="gone" />
<!-- 已完成代办事项区域 -->
<LinearLayout
android:id="@+id/todo_complete_section"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:orientation="vertical"
android:visibility="gone">
<!-- 已完成标题 -->
<TextView
android:id="@+id/todo_complete_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="已完成"
android:textSize="16sp"
android:textColor="#808080"
android:background="#F5F5F5" />
<!-- 已完成代办事项列表 -->
<ListView
android:id="@+id/todo_complete_list"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:cacheColorHint="@null"
android:listSelector="@android:color/transparent"
android:divider="@null"
android:fadingEdge="@null" />
</LinearLayout>
</LinearLayout>
<!-- 界面切换栏 -->
<LinearLayout
android:id="@+id/view_switcher"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:padding="8dp"
android:layout_alignParentBottom="true">
<TextView
android:id="@+id/notes_tab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="便签"
android:textSize="16sp"
android:textColor="#808080"
android:gravity="center"
android:padding="12dp"
android:background="@drawable/unselected_tab_bg"
android:clickable="true"
android:focusable="true" />
<TextView
android:id="@+id/todo_tab"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="代办"
android:textSize="16sp"
android:textColor="#007AFF"
android:gravity="center"
android:padding="12dp"
android:background="@drawable/selected_tab_bg"
android:clickable="true"
android:focusable="true" />
</LinearLayout>
<!-- 添加代办事项按钮 -->
<Button
android:id="@+id/btn_add_todo"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@drawable/ic_add_circle"
android:layout_alignParentRight="true"
android:layout_above="@+id/view_switcher"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:focusable="false" />
</RelativeLayout>

@ -21,4 +21,5 @@
<dimen name="text_font_size_medium">20sp</dimen>
<dimen name="text_font_size_normal">17sp</dimen>
<dimen name="text_font_size_small">14sp</dimen>
<dimen name="todo_edit_input_margin_bottom">12dp</dimen>
</resources>
Loading…
Cancel
Save