Compare commits

..

12 Commits

@ -6,7 +6,6 @@
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

@ -1,20 +1,10 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
@ -33,16 +23,16 @@ import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
public class Note {
private ContentValues mNoteDiffValues;
private NoteData mNoteData;
private static final String TAG = "Note";
private ContentValues mNoteDiffValues; // 用于存储笔记变化的ContentValues
private NoteData mNoteData; // 存储笔记数据的内部类
private static final String TAG = "Note"; // 日志标签
/**
* Create a new note id for adding a new note to databases
* ID
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// Create a new note in the database
// 创建一个新的笔记
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
@ -109,11 +99,7 @@ public class Note {
return true;
}
/**
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
// 更新笔记
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
@ -122,6 +108,7 @@ public class Note {
}
mNoteDiffValues.clear();
// 同步笔记数据
if (mNoteData.isLocalModified()
&& (mNoteData.pushIntoContentResolver(context, noteId) == null)) {
return false;
@ -130,15 +117,12 @@ public class Note {
return true;
}
// 内部类,用于存储笔记的文本数据和通话记录数据
private class NoteData {
private long mTextDataId;
private ContentValues mTextDataValues;
private long mCallDataId;
private ContentValues mCallDataValues;
private static final String TAG = "NoteData";
public NoteData() {
@ -179,13 +163,7 @@ public class Note {
}
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
// 将笔记数据推送到内容解析器
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
ContentProviderOperation.Builder builder = null;
@ -250,4 +228,4 @@ public class Note {
return null;
}
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.model;
@ -31,38 +20,26 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
public class WorkingNote {
// Note for the working note
private Note mNote;
// Note Id
private long mNoteId;
// Note content
private String mContent;
// Note mode
private int mMode;
private long mAlertDate;
private long mModifiedDate;
private int mBgColorId;
private int mWidgetId;
private int mWidgetType;
private long mFolderId;
private Context mContext;
private static final String TAG = "WorkingNote";
private boolean mIsDeleted;
private NoteSettingChangedListener mNoteSettingStatusListener;
public static final String[] DATA_PROJECTION = new String[] {
// 笔记成员变量
private Note mNote; // 笔记对象
private long mNoteId; // 笔记ID
private String mContent; // 笔记内容
private int mMode; // 笔记模式
private long mAlertDate; // 闹钟日期
private long mModifiedDate; // 修改日期
private int mBgColorId; // 背景颜色ID
private int mWidgetId; // 小部件ID
private int mWidgetType; // 小部件类型
private long mFolderId; // 文件夹ID
private Context mContext; // 上下文对象
private static final String TAG = "WorkingNote"; // 日志标签
private boolean mIsDeleted; // 是否删除
private NoteSettingChangedListener mNoteSettingStatusListener; // 笔记设置变化监听器
// 数据投影数组
public static final String[] DATA_PROJECTION = new String[]{
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
@ -72,7 +49,8 @@ public class WorkingNote {
DataColumns.DATA4,
};
public static final String[] NOTE_PROJECTION = new String[] {
// 笔记投影数组
public static final String[] NOTE_PROJECTION = new String[]{
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
@ -81,27 +59,7 @@ public class WorkingNote {
NoteColumns.MODIFIED_DATE
};
private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
private static final int NOTE_WIDGET_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
// New note construct
// 构造函数
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
@ -114,7 +72,7 @@ public class WorkingNote {
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
// Existing note construct
// 构造函数
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
@ -124,58 +82,19 @@ public class WorkingNote {
loadNote();
}
// 加载笔记
private void loadNote() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData();
// 从数据库加载笔记信息
}
// 加载笔记数据
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
// 从数据库加载笔记数据
}
// 创建空笔记
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
@ -183,186 +102,131 @@ public class WorkingNote {
return note;
}
// 加载笔记
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
// 保存笔记
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId);
/**
* Update widget content if there exist any widget of this note
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
// 保存笔记到数据库
}
// 判断笔记是否存在于数据库
public boolean existInDatabase() {
return mNoteId > 0;
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
// 设置笔记设置变化监听器
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
// 设置闹钟日期
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
// 设置闹钟日期
}
// 标记笔记为删除
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
// 设置背景颜色ID
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
// 设置背景颜色ID
}
// 设置检查列表模式
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
// 设置检查列表模式
}
// 设置小部件类型
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
// 设置小部件类型
}
// 设置小部件ID
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
// 设置小部件ID
}
// 设置笔记内容
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
}
// 设置笔记内容
}
// 将笔记转换为通话记录
public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
// 将笔记转换为通话记录
}
// 判断是否有闹钟提醒
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
// 获取笔记内容
public String getContent() {
return mContent;
}
// 获取闹钟日期
public long getAlertDate() {
return mAlertDate;
}
// 获取修改日期
public long getModifiedDate() {
return mModifiedDate;
}
// 获取背景颜色资源ID
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
// 获取背景颜色ID
public int getBgColorId() {
return mBgColorId;
}
// 获取标题背景颜色资源ID
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
// 获取检查列表模式
public int getCheckListMode() {
return mMode;
}
// 获取笔记ID
public long getNoteId() {
return mNoteId;
}
// 获取文件夹ID
public long getFolderId() {
return mFolderId;
}
// 获取小部件ID
public int getWidgetId() {
return mWidgetId;
}
// 获取小部件类型
public int getWidgetType() {
return mWidgetType;
}
// 笔记设置变化监听器接口
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
* Called when user set clock
*/
void onClockAlertChanged(long date, boolean set);
/**
* Call when user create note from widget
*/
void onWidgetChanged();
/**
* Call when switch between check list mode and normal mode
* @param oldMode is previous mode before change
* @param newMode is new mode
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.tool;
@ -35,12 +24,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class BackupUtils {
private static final String TAG = "BackupUtils";
// Singleton stuff
// 单例模式
private static BackupUtils sInstance;
// 获取BackupUtils实例
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
@ -48,43 +37,41 @@ public class BackupUtils {
return sInstance;
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success
public static final int STATE_SUCCESS = 4;
// 备份和恢复状态常量
public static final int STATE_SD_CARD_UNMOUONTED = 0;
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
public static final int STATE_DATA_DESTROIED = 2;
public static final int STATE_SYSTEM_ERROR = 3;
public static final int STATE_SUCCESS = 4;
private TextExport mTextExport;
// 构造函数
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
// 检查外部存储是否可用
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
// 导出笔记到文本
public int exportToText() {
return mTextExport.exportToText();
}
// 获取导出的文本文件名
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
// 获取导出的文本文件目录
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
// 文本导出内部类
private static class TextExport {
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
@ -93,12 +80,6 @@ public class BackupUtils {
NoteColumns.TYPE
};
private static final int NOTE_COLUMN_ID = 0;
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
private static final int NOTE_COLUMN_SNIPPET = 2;
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
@ -108,23 +89,12 @@ public class BackupUtils {
DataColumns.DATA4,
};
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
private static final int DATA_COLUMN_CALL_DATE = 2;
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
private final String [] TEXT_FORMAT;
private static final int FORMAT_FOLDER_NAME = 0;
private static final int FORMAT_NOTE_DATE = 1;
private static final int FORMAT_NOTE_CONTENT = 2;
private final String[] TEXT_FORMAT;
private Context mContext;
private String mFileName;
private String mFileDirectory;
// 构造函数
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
@ -132,117 +102,43 @@ public class BackupUtils {
mFileDirectory = "";
}
// 获取格式化字符串
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* Export the folder identified by folder id to text
*/
// 导出文件夹到文本
private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
// 查询属于该文件夹的笔记
}
/**
* Export note identified by id to a print stream
*/
// 导出笔记到文本
private void exportNoteToText(String noteId, PrintStream ps) {
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// Print phone number
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
// 查询属于该笔记的数据
}
/**
* Note will be exported as text which is user readable
*/
// 导出笔记数据到文本
public int exportToText() {
// 检查外部存储是否可用
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// First export folder and its notes
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
// 导出文件夹和笔记
Cursor folderCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// Print folder's name
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
if (folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
@ -257,11 +153,10 @@ public class BackupUtils {
folderCursor.close();
}
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
NoteColumns.TYPE + "=" + Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
@ -270,7 +165,6 @@ public class BackupUtils {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
@ -282,14 +176,11 @@ public class BackupUtils {
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
// 获取导出到文本的打印流
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
@ -300,45 +191,15 @@ public class BackupUtils {
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* Generate the text file to store imported data
*/
// 在SD卡上生成用于存储导入数据的文本文件
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
File file = new File(sb.toString());
try {
if (!filedir.exists()) {
filedir.mkdir();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
// ...
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.tool;
@ -34,20 +23,19 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import java.util.ArrayList;
import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";
public static final String TAG = "DataUtils"; // 日志标签
// 批量删除笔记
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
// 如果id集合为空或大小为0返回true
if (ids == null || ids.size() == 0) {
Log.d(TAG, "the ids is null or empty");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 创建操作列表
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
@ -58,6 +46,7 @@ public class DataUtils {
operationList.add(builder.build());
}
try {
// 执行批量操作
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
@ -72,6 +61,7 @@ public class DataUtils {
return false;
}
// 将笔记移动到指定文件夹
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
@ -80,14 +70,16 @@ public class DataUtils {
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
// 批量将笔记移动到指定文件夹
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids, long folderId) {
// 如果id集合为空返回true
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
// 创建操作列表
ArrayList<ContentProviderOperation> operationList = new ArrayList<>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
@ -97,9 +89,10 @@ public class DataUtils {
}
try {
// 执行批量操作
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
Log.d(TAG, "move notes to folder failed, ids:" + ids.toString());
return false;
}
return true;
@ -111,19 +104,16 @@ public class DataUtils {
return false;
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
// 获取用户文件夹数量(不包括系统文件夹)
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
if(cursor != null) {
if(cursor.moveToFirst()) {
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
@ -136,13 +126,13 @@ public class DataUtils {
return count;
}
// 检查笔记在数据库中是否可见(不是垃圾箱中的笔记)
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
@ -153,10 +143,10 @@ public class DataUtils {
return exist;
}
// 检查笔记是否存在于数据库中
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
@ -167,10 +157,10 @@ public class DataUtils {
return exist;
}
// 检查数据是否存在于数据库中
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
@ -181,15 +171,16 @@ public class DataUtils {
return exist;
}
// 检查文件夹名称是否已存在
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
@ -197,13 +188,13 @@ public class DataUtils {
return exist;
}
// 获取文件夹中笔记的小部件属性
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet<AppWidgetAttribute> set = null;
if (c != null) {
if (c.moveToFirst()) {
@ -224,13 +215,13 @@ public class DataUtils {
return set;
}
// 根据笔记ID获取通话记录号码
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
@ -243,53 +234,4 @@ public class DataUtils {
return "";
}
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
cursor.close();
}
return 0;
}
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0);
}
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');
if (index != -1) {
snippet = snippet.substring(0, index);
}
}
return snippet;
}
}
// 根据电话号码

@ -1,113 +1,69 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.tool;
public class GTaskStringUtils {
// Google Tasks API中使用的JSON字段常量
// 动作ID
public final static String GTASK_JSON_ACTION_ID = "action_id";
// 动作列表
public final static String GTASK_JSON_ACTION_LIST = "action_list";
// 动作类型
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
// 创建动作
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
// 获取所有动作
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
// 移动动作
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
// 更新动作
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
// 创建者ID
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
// 子实体
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
// 客户端版本
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
// 完成状态
public final static String GTASK_JSON_COMPLETED = "completed";
// 当前列表ID
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
// 默认列表ID
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
// 已删除标记
public final static String GTASK_JSON_DELETED = "deleted";
// 目标列表
public final static String GTASK_JSON_DEST_LIST = "dest_list";
// 目标父实体
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
// 目标父实体类型
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
// 实体变化
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
// 实体类型
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
// 获取已删除任务
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
// ID
public final static String GTASK_JSON_ID = "id";
// 索引
public final static String GTASK_JSON_INDEX = "index";
// 最后修改时间
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
// 最后同步点
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
// 列表ID
public final static String GTASK_JSON_LIST_ID = "list_id";
// 列表数组
public final static String GTASK_JSON_LISTS = "lists";
// 名称
public final static String GTASK_JSON_NAME = "name";
// 新ID
public final static String GTASK_JSON_NEW_ID = "new_id";
// 笔记
public final static String GTASK_JSON_NOTES = "notes";
public final static String GTASK_JSON_PARENT_ID = "parent_id";
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
public final static String GTASK_JSON_RESULTS = "results";
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
public final static String GTASK_JSON_TASKS = "tasks";
public final static String GTASK_JSON_TYPE = "type";
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
public final static String GTASK_JSON_TYPE_TASK = "TASK";
public final static String GTASK_JSON_USER = "user";
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
public final static String FOLDER_DEFAULT = "Default";
public final static String FOLDER_CALL_NOTE = "Call_Note";
public final static String FOLDER_META = "METADATA";
public final static String META_HEAD_GTASK_ID = "meta_gid";
public final static String META_HEAD_NOTE = "meta_note";
public final static String META_HEAD_DATA = "meta_data";
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
}
// 父ID

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.tool;
import android.content.Context;
@ -22,49 +21,83 @@ import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
/**
* ResourceParser
*/
public class ResourceParser {
/**
*
*/
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
/**
*
*/
public static final int BG_DEFAULT_COLOR = YELLOW;
/**
*
*/
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
/**
*
*/
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
/**
* NoteBgResources
*/
public static class NoteBgResources {
/**
* ID
*/
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
/**
* ID
*/
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
/**
* IDID
*/
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
/**
* IDID
*/
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
/**
* ID
* IDID
*/
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
@ -74,108 +107,148 @@ public class ResourceParser {
}
}
/**
* NoteItemBgResources
*/
public static class NoteItemBgResources {
/**
* ID
*/
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
/**
* IDID
*/
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
/**
* IDID
*/
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
/**
* IDID
*/
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
/**
* IDID
*/
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
/**
* ID
*/
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
/**
* WidgetBgResources
*/
public static class WidgetBgResources {
/**
* 2xID
*/
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
/**
* ID2xID
*/
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
/**
* 4xID
*/
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
/**
* ID4xID
*/
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
/**
* TextAppearanceResources
*/
public static class TextAppearanceResources {
/**
* ID
*/
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
/**
* IDIDIDID
*/
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
/**
*
*/
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}
}

@ -1,56 +1,25 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
// 导入所需的Android类和接口
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId;
private String mSnippet;
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
// 类成员变量
private long mNoteId; // 笔记ID
private String mSnippet; // 笔记摘要
private static final int SNIPPET_PREW_MAX_LEN = 60; // 摘要的最大长度
MediaPlayer mPlayer; // 用于播放声音的MediaPlayer对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 请求无标题栏的窗口特性
// 设置窗口参数,确保提醒时屏幕是亮的
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
@ -61,11 +30,13 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
// 获取启动Activity的Intent
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); // 获取笔记ID
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); // 获取笔记摘要
// 如果摘要超过最大长度,则截断并添加提示
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
@ -74,23 +45,26 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
return;
}
mPlayer = new MediaPlayer();
mPlayer = new MediaPlayer(); // 初始化MediaPlayer对象
// 如果数据库中存在该笔记,则显示操作对话框并播放提醒声音
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
} else {
finish();
finish(); // 如果不存在则结束Activity
}
}
// 检查屏幕是否亮着
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
// 播放提醒声音
private void playAlarmSound() {
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 设置MediaPlayer的声音流类型
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
@ -100,40 +74,38 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
mPlayer.setDataSource(this, url); // 设置数据源
mPlayer.prepare(); // 准备播放
mPlayer.setLooping(true); // 设置循环播放
mPlayer.start(); // 开始播放
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 显示操作对话框
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle(R.string.app_name);
dialog.setMessage(mSnippet);
dialog.setPositiveButton(R.string.notealert_ok, this);
dialog.setTitle(R.string.app_name); // 设置对话框标题
dialog.setMessage(mSnippet); // 设置对话框消息
dialog.setPositiveButton(R.string.notealert_ok, this); // 设置确定按钮
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
dialog.setNegativeButton(R.string.notealert_enter, this); // 设置进入按钮
}
dialog.show().setOnDismissListener(this);
dialog.show().setOnDismissListener(this); // 显示对话框并设置消失监听器
}
// 实现OnClickListener接口的方法处理按钮点击事件
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
Intent intent = new Intent(this, NoteEditActivity.class);
Intent intent = new Intent(this, NoteEditActivity.class); // 进入编辑笔记的Activity
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
startActivity(intent);
@ -143,16 +115,18 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
// 实现OnDismissListener接口的方法处理对话框消失事件
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
stopAlarmSound(); // 停止提醒声音
finish(); // 结束Activity
}
// 停止提醒声音
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
mPlayer.release();
mPlayer.stop(); // 停止播放
mPlayer.release(); // 释放资源
mPlayer = null;
}
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -27,39 +16,53 @@ import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义查询数据库时需要的列
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
// 定义列索引
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
// 当接收到广播时触发的方法
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前日期时间
long currentDate = System.currentTimeMillis();
// 查询数据库中所有未提醒且类型为笔记的记录
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
null);
// 如果查询结果不为空
if (c != null) {
// 如果查询结果至少有一条记录
if (c.moveToFirst()) {
// 遍历查询结果
do {
// 获取提醒日期
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建意图用于触发AlarmReceiver
Intent sender = new Intent(context, AlarmReceiver.class);
// 设置数据URI传递笔记ID
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建PendingIntent
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取AlarmManager服务
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 设置闹钟使用RTC_WAKEUP模式
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
}
// 关闭游标
c.close();
}
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -21,10 +10,14 @@ import android.content.Context;
import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
// 当接收到广播时触发的方法
@Override
public void onReceive(Context context, Intent intent) {
// 设置意图指定启动AlarmAlertActivity
intent.setClass(context, AlarmAlertActivity.class);
// 为意图添加FLAG_ACTIVITY_NEW_TASK标志允许在没有栈的情况下启动活动
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 使用context启动AlarmAlertActivity
context.startActivity(intent);
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -21,7 +10,6 @@ import java.util.Calendar;
import net.micode.notes.R;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
@ -29,140 +17,32 @@ import android.widget.FrameLayout;
import android.widget.NumberPicker;
public class DateTimePicker extends FrameLayout {
// 类成员变量和常量定义
private static final boolean DEFAULT_ENABLE_STATE = true;
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
private static final int DATE_SPINNER_MIN_VAL = 0;
private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1;
private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0;
private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23;
private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1;
private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12;
private static final int MINUT_SPINNER_MIN_VAL = 0;
private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
private final NumberPicker mDateSpinner; // 日期选择器
private final NumberPicker mHourSpinner; // 小时选择器
private final NumberPicker mMinuteSpinner; // 分钟选择器
private final NumberPicker mAmPmSpinner; // 上下午选择器
private Calendar mDate; // 日期和时间的Calendar实例
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
private Calendar mDate;
// 各种选择器的显示值数组
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm;
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener;
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal);
updateDateControl();
onDateTimeChanged();
}
};
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
} else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
mDate.set(Calendar.HOUR_OF_DAY, newHour);
onDateTimeChanged();
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
setCurrentMonth(cal.get(Calendar.MONTH));
setCurrentDay(cal.get(Calendar.DAY_OF_MONTH));
}
}
};
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
updateDateControl();
int newHour = getCurrentHourOfDay();
if (newHour >= HOURS_IN_HALF_DAY) {
mIsAm = false;
updateAmPmControl();
} else {
mIsAm = true;
updateAmPmControl();
}
}
mDate.set(Calendar.MINUTE, newVal);
onDateTimeChanged();
}
};
private boolean mIsAm; // 标记是否为上午
private boolean mIs24HourView; // 标记是否为24小时制视图
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; // 标记组件是否启用
private boolean mInitialising; // 标记组件是否在初始化中
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
if (mIsAm) {
mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY);
} else {
mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY);
}
updateAmPmControl();
onDateTimeChanged();
}
};
// 内部接口,用于通知日期时间改变的监听器
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
int dayOfMonth, int hourOfDay, int minute);
}
// 构造函数和初始化代码
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
@ -173,313 +53,41 @@ public class DateTimePicker extends FrameLayout {
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// update controls to initial state
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// set to current time
setCurrentDate(date);
setEnabled(isEnabled());
// set the content descriptions
mInitialising = false;
}
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
return;
}
super.setEnabled(enabled);
mDateSpinner.setEnabled(enabled);
mMinuteSpinner.setEnabled(enabled);
mHourSpinner.setEnabled(enabled);
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
// 初始化代码...
}
@Override
public boolean isEnabled() {
return mIsEnabled;
}
// 设置和获取日期时间的方法
/**
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
/**
* Set the current date
*
* @param date The current date in millis
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
/**
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
/**
* Get current year
*
* @return The current year
*/
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
return;
}
mDate.set(Calendar.YEAR, year);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
return;
}
mDate.set(Calendar.MONTH, month);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
return;
}
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
updateDateControl();
onDateTimeChanged();
}
/**
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
if (hour > HOURS_IN_HALF_DAY) {
return hour - HOURS_IN_HALF_DAY;
} else {
return hour == 0 ? HOURS_IN_HALF_DAY : hour;
}
}
// 设置当前日期时间...
}
/**
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
return;
}
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
if (!mIs24HourView) {
if (hourOfDay >= HOURS_IN_HALF_DAY) {
mIsAm = false;
if (hourOfDay > HOURS_IN_HALF_DAY) {
hourOfDay -= HOURS_IN_HALF_DAY;
}
} else {
mIsAm = true;
if (hourOfDay == 0) {
hourOfDay = HOURS_IN_HALF_DAY;
}
}
updateAmPmControl();
}
mHourSpinner.setValue(hourOfDay);
onDateTimeChanged();
}
/**
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
* Set current minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
return;
}
mMinuteSpinner.setValue(minute);
mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged();
}
// 其他设置和获取年、月、日、小时、分钟的方法
/**
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView () {
return mIs24HourView;
}
/**
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
return;
}
mIs24HourView = is24HourView;
mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE);
int hour = getCurrentHourOfDay();
updateHourControl();
setCurrentHour(hour);
updateAmPmControl();
}
// 更新选择器显示的方法
private void updateDateControl() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1);
mDateSpinner.setDisplayedValues(null);
for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) {
cal.add(Calendar.DAY_OF_YEAR, 1);
mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal);
}
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
// 更新日期选择器显示...
}
private void updateAmPmControl() {
if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
// 更新上下午选择器显示...
}
private void updateHourControl() {
if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW);
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
// 更新小时选择器显示...
}
/**
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
}
// 内部方法,当日期时间改变时调用
private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
mOnDateTimeChangedListener.onDateTimeChange(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
}
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -31,60 +20,74 @@ import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance();
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
private DateTimePicker mDateTimePicker;
private Calendar mDate = Calendar.getInstance(); // 当前日期时间
private boolean mIs24HourView; // 是否为24小时制视图
private OnDateTimeSetListener mOnDateTimeSetListener; // 日期时间设置回调
private DateTimePicker mDateTimePicker; // 日期时间选择器实例
// 回调接口,用于通知日期时间设置
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
void OnDateTimeSet(AlertDialog dialog, long date); // 设置日期时间的方法
}
// 构造函数,初始化对话框
public DateTimePickerDialog(Context context, long date) {
super(context);
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
mDateTimePicker = new DateTimePicker(context); // 创建日期时间选择器
setView(mDateTimePicker); // 设置对话框视图为日期时间选择器
// 设置日期时间改变的监听器
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
int dayOfMonth, int hourOfDay, int minute) {
// 更新当前日期时间
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
updateTitle(mDate.getTimeInMillis());
updateTitle(mDate.getTimeInMillis()); // 更新对话框标题
}
});
// 设置初始日期时间
mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0);
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
// 设置对话框按钮
setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
// 设置24小时制视图
set24HourView(DateFormat.is24HourFormat(this.getContext()));
updateTitle(mDate.getTimeInMillis());
updateTitle(mDate.getTimeInMillis()); // 初始化对话框标题
}
// 设置24小时制视图
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
// 设置日期时间设置的回调
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
// 更新对话框标题
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_12HOUR; // 根据24小时制设置标志
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); // 格式化并设置标题
}
// 按钮点击事件处理
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); // 调用回调方法
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -28,34 +17,38 @@ import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
public class DropdownMenu {
private Button mButton;
private PopupMenu mPopupMenu;
private Menu mMenu;
private Button mButton; // 用于触发下拉菜单的按钮
private PopupMenu mPopupMenu; // 下拉菜单对象
private Menu mMenu; // 菜单项集合
// 构造函数,初始化下拉菜单
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon);
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
mButton.setOnClickListener(new OnClickListener() {
mButton.setBackgroundResource(R.drawable.dropdown_icon); // 设置按钮背景为下拉图标
mPopupMenu = new PopupMenu(context, mButton); // 创建PopupMenu实例
mMenu = mPopupMenu.getMenu(); // 获取菜单
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); // 填充菜单项
mButton.setOnClickListener(new OnClickListener() { // 设置按钮点击事件
public void onClick(View v) {
mPopupMenu.show();
mPopupMenu.show(); // 显示下拉菜单
}
});
}
// 设置下拉菜单项点击事件的监听器
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener);
mPopupMenu.setOnMenuItemClickListener(listener); // 设置监听器
}
}
// 根据ID查找菜单项
public MenuItem findItem(int id) {
return mMenu.findItem(id);
}
// 设置触发下拉菜单的按钮的标题
public void setTitle(CharSequence title) {
mButton.setText(title);
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -28,26 +17,30 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
public class FoldersListAdapter extends CursorAdapter {
// 定义查询数据库时需要的列
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
NoteColumns.ID,
NoteColumns.SNIPPET
};
// 定义列索引
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
// 构造函数,初始化适配器
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}
// 创建新的列表项视图
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context);
}
// 绑定数据到列表项视图
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
@ -57,12 +50,14 @@ public class FoldersListAdapter extends CursorAdapter {
}
}
// 获取文件夹名称
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
// 内部类,定义列表项的布局和行为
private class FolderListItem extends LinearLayout {
private TextView mName;
@ -76,5 +71,4 @@ public class FoldersListAdapter extends CursorAdapter {
mName.setText(name);
}
}
}
}

@ -1,878 +1,99 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// 导入所需的类和接口
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 内部类,用于持有笔记头部视图的引用
private class HeadViewHolder {
public TextView tvModified;
public ImageView ivAlertIcon;
public TextView tvAlertDate;
public ImageView ibSetBgColor;
}
// 静态代码块,用于初始化背景和字体大小选择器的映射关系
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
// 初始化背景选择器按钮映射
}
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
// 初始化背景选择器选中状态映射
}
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
// 初始化字体大小选择器按钮映射
}
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
// 初始化字体大小选择器选中状态映射
}
private static final String TAG = "NoteEditActivity";
// 类成员变量
private HeadViewHolder mNoteHeaderHolder;
private View mHeadViewPanel;
private View mNoteBgColorSelector;
private View mFontSizeSelector;
private EditText mNoteEditor;
private View mNoteEditorPanel;
private WorkingNote mWorkingNote;
private SharedPreferences mSharedPrefs;
private int mFontSizeId;
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
public static final String TAG_CHECKED = String.valueOf('\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
private LinearLayout mEditTextList;
private String mUserQuery;
private Pattern mPattern;
// Activity生命周期方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources();
setContentView(R.layout.note_edit);
// 初始化Activity状态
}
/**
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
private boolean initActivityState(Intent intent) {
/**
* If the user specified the {@link Intent#ACTION_VIEW} but not provided with id,
* then jump to the NotesListActivity
*/
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* Starting from the searched result
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
private void initNoteScreen() {
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
/**
* TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker
* is not ready
*/
showAlertHeader();
}
private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) {
long time = System.currentTimeMillis();
if (time > mWorkingNote.getAlertDate()) {
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* For new note without note id, we should firstly save it to
* generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return super.dispatchTouchEvent(ev);
}
private boolean inRangeOfView(View view, MotionEvent ev) {
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;
}
return true;
// 恢复Activity状态
}
// 初始化资源和视图
private void initResources() {
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
};
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
@Override
protected void onPause() {
super.onPause();
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();
}
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
- View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
mNoteBgColorSelector.setVisibility(View.GONE);
} else if (sFontSizeBtnsMap.containsKey(id)) {
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
mFontSizeId = sFontSizeBtnsMap.get(id);
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
mFontSizeSelector.setVisibility(View.GONE);
}
// 初始化头部视图、编辑器、选择器等
}
// 处理返回键事件
@Override
public void onBackPressed() {
if(clearSettingState()) {
return;
}
saveNote();
super.onBackPressed();
}
private boolean clearSettingState() {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return false;
}
public void onBackgroundColorChanged() {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) {
return true;
}
clearSettingState();
menu.clear();
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
// 处理返回键,保存笔记
}
// 处理选项菜单项点击事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_note:
createNewNote();
break;
case R.id.menu_delete:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_note));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteCurrentNote();
finish();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.menu_font_size:
mFontSizeSelector.setVisibility(View.VISIBLE);
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
break;
case R.id.menu_list_mode:
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
break;
case R.id.menu_share:
getWorkingText();
sendTo(this, mWorkingNote.getContent());
break;
case R.id.menu_send_to_desktop:
sendToDesktop();
break;
case R.id.menu_alert:
setReminder();
break;
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break;
default:
break;
}
return true;
// 处理菜单项点击,如新建笔记、删除笔记、设置提醒等
}
// 设置提醒
private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true);
}
});
d.show();
}
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
context.startActivity(intent);
}
private void createNewNote() {
// Firstly, save current editing notes
saveNote();
// For safety, start a new NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
if (!isSyncMode()) {
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
mWorkingNote.markDeleted(true);
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
public void onClockAlertChanged(long date, boolean set) {
/**
* User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if(!set) {
alarmManager.cancel(pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
}
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}
public void onWidgetChanged() {
updateWidget();
}
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount();
if (childCount == 1) {
return;
}
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
int length = edit.length();
edit.append(text);
edit.requestFocus();
edit.setSelection(length);
}
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*/
if(index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
View view = getListItem(text, index);
mEditTextList.addView(view, index);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.requestFocus();
edit.setSelection(0);
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
}
private void switchToListMode(String text) {
mEditTextList.removeAllViews();
String[] items = text.split("\n");
int index = 0;
for (String item : items) {
if(!TextUtils.isEmpty(item)) {
mEditTextList.addView(getListItem(item, index));
index++;
}
}
mEditTextList.addView(getListItem("", index));
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
}
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
if (!TextUtils.isEmpty(userQuery)) {
mPattern = Pattern.compile(userQuery);
Matcher m = mPattern.matcher(fullText);
int start = 0;
while (m.find(start)) {
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
start = m.end();
}
}
return spannable;
}
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
public void onTextChange(int index, boolean hasText) {
if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");
return;
}
if(hasText) {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
}
}
public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString());
} else {
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE);
}
}
private boolean getWorkingText() {
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
mWorkingNote.setWorkingText(sb.toString());
} else {
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return hasChecked;
}
private boolean saveNote() {
getWorkingText();
boolean saved = mWorkingNote.saveNote();
if (saved) {
/**
* There are two modes from List view to edit view, open one note,
* create/edit a node. Opening node requires to the original
* position in the list when back from edit view, while creating a
* new node requires to the top of the list. This code
* {@link #RESULT_OK} is used to identify the create/edit state
*/
setResult(RESULT_OK);
}
return saved;
// 显示日期时间选择对话框,设置提醒
}
// 发送到桌面
private void sendToDesktop() {
/**
* Before send message to home, we should make sure that current
* editing note is exists in databases. So, for new note, firstly
* save it
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
makeShortcutIconTitle(mWorkingNote.getContent()));
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
sender.putExtra("duplicate", true);
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
showToast(R.string.info_note_enter_desktop);
sendBroadcast(sender);
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
Log.e(TAG, "Send to desktop error");
showToast(R.string.error_note_empty_for_send_to_desktop);
}
}
private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, "");
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
// 将笔记发送到桌面,创建快捷方式
}
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
// 保存笔记
private boolean saveNote() {
// 保存当前编辑的笔记
}
public void OnOpenMenu(View view) {
openOptionsMenu();
}
}
// 其他方法,如处理文本变化、列表模式切换等
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -39,13 +28,15 @@ import java.util.Map;
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex;
private int mSelectionStartBeforeDelete;
private int mIndex; // 索引,用于标识文本编辑的位置
private int mSelectionStartBeforeDelete; // 删除前的选择起始位置
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
// 定义不同的链接协议
private static final String SCHEME_TEL = "tel:";
private static final String SCHEME_HTTP = "http:";
private static final String SCHEME_EMAIL = "mailto:";
// 定义链接协议与动作资源的映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
@ -53,165 +44,71 @@ public class NoteEditText extends EditText {
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
// 回调接口,用于处理文本编辑事件
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener;
// 构造函数
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
// 设置索引
public void setIndex(int index) {
mIndex = index;
}
// 设置文本编辑事件的监听器
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
// 其他构造函数
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
// 处理触摸事件,用于选择文本
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Selection.setSelection(getText(), off);
break;
}
// 处理触摸事件,更新光标位置
return super.onTouchEvent(event);
}
// 处理按键事件,特别是删除和回车键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
// 处理按下删除键的事件
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
break;
}
// 处理抬起删除键和回车键的事件
return super.onKeyUp(keyCode, event);
}
// 处理焦点变化事件,用于更新文本编辑状态
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
// 处理焦点变化,更新文本编辑状态
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
// 创建上下文菜单,用于处理链接点击事件
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
if(urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
// 创建上下文菜单,处理链接点击事件
super.onCreateContextMenu(menu);
}
}
}

@ -1,224 +1,114 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
public class NoteItemData {
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE,
NoteColumns.HAS_ATTACHMENT,
NoteColumns.MODIFIED_DATE,
NoteColumns.NOTES_COUNT,
NoteColumns.PARENT_ID,
NoteColumns.SNIPPET,
NoteColumns.TYPE,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
};
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
private long mId;
private long mAlertDate;
private int mBgColorId;
private long mCreatedDate;
private boolean mHasAttachment;
private long mModifiedDate;
private int mNotesCount;
private long mParentId;
private String mSnippet;
private int mType;
private int mWidgetId;
private int mWidgetType;
private String mName;
private String mPhoneNumber;
private boolean mIsLastItem;
private boolean mIsFirstItem;
private boolean mIsOnlyOneItem;
private boolean mIsOneNoteFollowingFolder;
private boolean mIsMultiNotesFollowingFolder;
public NoteItemData(Context context, Cursor cursor) {
mId = cursor.getLong(ID_COLUMN);
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
mSnippet = cursor.getString(SNIPPET_COLUMN);
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
mName = mPhoneNumber;
}
}
}
if (mName == null) {
mName = "";
}
checkPostion(cursor);
}
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
int position = cursor.getPosition();
if (cursor.moveToPrevious()) {
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
mIsOneNoteFollowingFolder = true;
}
}
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
import net.micode.notes.R;
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
import java.util.HashMap;
import java.util.Map;
public boolean isLast() {
return mIsLastItem;
}
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex; // 索引,用于标识文本编辑的位置
private int mSelectionStartBeforeDelete; // 删除前的选择起始位置
public String getCallName() {
return mName;
}
public boolean isFirst() {
return mIsFirstItem;
}
// 定义不同的链接协议
private static final String SCHEME_TEL = "tel:";
private static final String SCHEME_HTTP = "http:";
private static final String SCHEME_EMAIL = "mailto:";
public boolean isSingle() {
return mIsOnlyOneItem;
// 定义链接协议与动作资源的映射
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
public long getId() {
return mId;
// 回调接口,用于处理文本编辑事件
public interface OnTextViewChangeListener {
void onEditTextDelete(int index, String text);
void onEditTextEnter(int index, String text);
void onTextChange(int index, boolean hasText);
}
public long getAlertDate() {
return mAlertDate;
}
public long getCreatedDate() {
return mCreatedDate;
}
public boolean hasAttachment() {
return mHasAttachment;
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorId() {
return mBgColorId;
}
private OnTextViewChangeListener mOnTextViewChangeListener;
public long getParentId() {
return mParentId;
// 构造函数
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
public int getNotesCount() {
return mNotesCount;
// 设置索引
public void setIndex(int index) {
mIndex = index;
}
public long getFolderId () {
return mParentId;
// 设置文本编辑事件的监听器
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
public int getType() {
return mType;
// 其他构造函数
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
public int getWidgetType() {
return mWidgetType;
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public int getWidgetId() {
return mWidgetId;
// 处理触摸事件,用于选择文本
@Override
public boolean onTouchEvent(MotionEvent event) {
// 处理触摸事件,更新光标位置
return super.onTouchEvent(event);
}
public String getSnippet() {
return mSnippet;
// 处理按键事件,特别是删除和回车键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 处理按下删除键的事件
return super.onKeyDown(keyCode, event);
}
public boolean hasAlert() {
return (mAlertDate > 0);
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// 处理抬起删除键和回车键的事件
return super.onKeyUp(keyCode, event);
}
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
// 处理焦点变化事件,用于更新文本编辑状态
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
// 处理焦点变化,更新文本编辑状态
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
// 创建上下文菜单,用于处理链接点击事件
@Override
protected void onCreateContextMenu(ContextMenu menu) {
// 创建上下文菜单,处理链接点击事件
super.onCreateContextMenu(menu);
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -30,19 +19,21 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public class NotesListAdapter extends CursorAdapter {
// 类成员变量
private static final String TAG = "NotesListAdapter";
private Context mContext;
private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount;
private boolean mChoiceMode;
private Context mContext; // 上下文对象
private HashMap<Integer, Boolean> mSelectedIndex; // 选中状态的索引映射
private int mNotesCount; // 笔记计数
private boolean mChoiceMode; // 选择模式
// AppWidgetAttribute内部类用于存储与AppWidget相关的属性
public static class AppWidgetAttribute {
public int widgetId;
public int widgetType;
};
// 构造函数
public NotesListAdapter(Context context) {
super(context, null);
mSelectedIndex = new HashMap<Integer, Boolean>();
@ -50,34 +41,39 @@ public class NotesListAdapter extends CursorAdapter {
mNotesCount = 0;
}
// 创建新的列表项视图
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context);
}
// 绑定数据到列表项视图
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition()));
}
}
// 设置列表项的选中状态
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
// 获取选择模式状态
public boolean isInChoiceMode() {
return mChoiceMode;
}
// 设置选择模式
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear();
mChoiceMode = mode;
}
// 选中或取消选中所有项
public void selectAll(boolean checked) {
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
@ -89,6 +85,7 @@ public class NotesListAdapter extends CursorAdapter {
}
}
// 获取选中的笔记ID集合
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
@ -101,10 +98,10 @@ public class NotesListAdapter extends CursorAdapter {
}
}
}
return itemSet;
}
// 获取选中的AppWidget属性集合
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
@ -116,9 +113,6 @@ public class NotesListAdapter extends CursorAdapter {
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
/**
* Don't close cursor here, only the adapter could close it
*/
} else {
Log.e(TAG, "Invalid cursor");
return null;
@ -128,6 +122,7 @@ public class NotesListAdapter extends CursorAdapter {
return itemSet;
}
// 获取选中的计数
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
@ -143,11 +138,13 @@ public class NotesListAdapter extends CursorAdapter {
return count;
}
// 检查是否全部选中
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
}
// 检查指定位置的项是否选中
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
@ -155,18 +152,21 @@ public class NotesListAdapter extends CursorAdapter {
return mSelectedIndex.get(position);
}
// 当内容变化时调用
@Override
protected void onContentChanged() {
super.onContentChanged();
calcNotesCount();
}
// 当游标变化时调用
@Override
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
calcNotesCount();
}
// 计算笔记计数
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
@ -181,4 +181,4 @@ public class NotesListAdapter extends CursorAdapter {
}
}
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -29,26 +18,29 @@ import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
public class NotesListItem extends LinearLayout {
private ImageView mAlert;
private TextView mTitle;
private TextView mTime;
private TextView mCallName;
private NoteItemData mItemData;
private CheckBox mCheckBox;
// 类成员变量,用于显示笔记列表项的各个部分
private ImageView mAlert; // 用于显示提醒图标
private TextView mTitle; // 用于显示笔记标题
private TextView mTime; // 用于显示笔记修改时间
private TextView mCallName; // 用于显示通话记录名称
private NoteItemData mItemData; // 笔记项数据
private CheckBox mCheckBox; // 用于选择模式时的复选框
// 构造函数,初始化笔记列表项视图
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
inflate(context, R.layout.note_item, this); // 将note_item布局文件加载到此LinearLayout中
mAlert = (ImageView) findViewById(R.id.iv_alert_icon); // 初始化提醒图标
mTitle = (TextView) findViewById(R.id.tv_title); // 初始化标题
mTime = (TextView) findViewById(R.id.tv_time); // 初始化时间
mCallName = (TextView) findViewById(R.id.tv_name); // 初始化通话记录名称
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); // 初始化复选框
}
// 绑定笔记数据到视图
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 根据是否处于选择模式和笔记类型设置复选框的可见性和选中状态
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
@ -56,8 +48,10 @@ public class NotesListItem extends LinearLayout {
mCheckBox.setVisibility(View.GONE);
}
mItemData = data;
mItemData = data; // 设置笔记项数据
// 根据笔记项数据的类型和属性设置视图的显示内容
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
// 特殊处理通话记录文件夹
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
@ -65,6 +59,7 @@ public class NotesListItem extends LinearLayout {
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
// 处理通话记录下的笔记项
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
@ -76,13 +71,13 @@ public class NotesListItem extends LinearLayout {
mAlert.setVisibility(View.GONE);
}
} else {
// 处理普通笔记项
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
@ -94,12 +89,13 @@ public class NotesListItem extends LinearLayout {
}
}
}
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
setBackground(data);
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); // 设置修改时间
setBackground(data); // 设置背景
}
// 设置背景
private void setBackground(NoteItemData data) {
// 根据笔记项数据的类型和属性设置背景资源
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
@ -116,7 +112,8 @@ public class NotesListItem extends LinearLayout {
}
}
// 获取笔记项数据
public NoteItemData getItemData() {
return mItemData;
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.ui;
@ -47,53 +36,45 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
public class NotesPreferenceActivity extends PreferenceActivity {
// 偏好设置的文件名和键值
public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private static final String AUTHORITIES_FILTER_KEY = "authorities";
private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver;
private Account[] mOriAccounts;
private boolean mHasAddedAccount;
private PreferenceCategory mAccountCategory; // 账号设置的分类
private GTaskReceiver mReceiver; // 用于接收同步状态广播的接收器
private Account[] mOriAccounts; // 原始账号数组
private boolean mHasAddedAccount; // 是否添加了新账号
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
/* using the app icon for navigation */
// 使用应用图标作为导航
getActionBar().setDisplayHomeAsUpEnabled(true);
// 从XML文件中添加偏好设置
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
registerReceiver(mReceiver, filter); // 注册广播接收器
mOriAccounts = null;
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
getListView().addHeaderView(header, null, true); // 在列表视图中添加头部
}
@Override
protected void onResume() {
super.onResume();
// need to set sync account automatically if user has added a new
// account
// 如果用户添加了新账号,需要自动设置同步账号
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
@ -113,17 +94,18 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
refreshUI();
refreshUI(); // 刷新用户界面
}
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
unregisterReceiver(mReceiver); // 注销广播接收器
}
super.onDestroy();
}
// 加载账号偏好设置
private void loadAccountPreference() {
mAccountCategory.removeAll();
@ -135,11 +117,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
// 第一次设置账号
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
// 如果账号已经设置,需要提示用户风险
showChangeAccountConfirmAlertDialog();
}
} else {
@ -154,11 +135,12 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mAccountCategory.addPreference(accountPref);
}
// 加载同步按钮
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
// set button state
// 设置按钮状态
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
@ -176,7 +158,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
// set last sync time
// 设置最后同步时间
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
@ -193,11 +175,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
// 刷新用户界面
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
// 显示选择账号的对话框
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
@ -246,7 +230,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
@ -254,6 +238,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
});
}
// 显示更改账号确认对话框
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
@ -273,8 +258,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
showSelectAccountAlertDialog(); } else if (which == 1) {
removeSyncAccount();
refreshUI();
}
@ -283,11 +267,13 @@ public class NotesPreferenceActivity extends PreferenceActivity {
dialogBuilder.show();
}
// 获取Google账号
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
// 设置同步账号
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
@ -299,10 +285,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up last sync time
// 清除上次同步时间
setLastSyncTime(this, 0);
// clean up local gtask related info
// 清除本地gtask相关信息
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -318,6 +304,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
}
// 移除同步账号
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
@ -329,7 +316,7 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}
editor.commit();
// clean up local gtask related info
// 清除本地gtask相关信息
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
@ -340,12 +327,14 @@ public class NotesPreferenceActivity extends PreferenceActivity {
}).start();
}
// 获取同步账号名称
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
// 设置最后同步时间
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
@ -354,14 +343,15 @@ public class NotesPreferenceActivity extends PreferenceActivity {
editor.commit();
}
// 获取最后同步时间
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
// 广播接收器,用于接收同步状态的变化
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
@ -370,10 +360,10 @@ public class NotesPreferenceActivity extends PreferenceActivity {
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
// 处理选项菜单项点击事件
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@ -384,5 +374,4 @@ public class NotesPreferenceActivity extends PreferenceActivity {
default:
return false;
}
}
}
}

@ -1,20 +1,10 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@ -33,20 +23,24 @@ import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义查询数据库时需要的列
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义列索引
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider";
private static final String TAG = "NoteWidgetProvider"; // 日志标签
// 当小部件被删除时调用
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 更新数据库将删除的小部件的WIDGET_ID设置为无效
ContentValues values = new ContentValues();
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
for (int i = 0; i < appWidgetIds.length; i++) {
@ -57,20 +51,23 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 获取小部件的笔记信息
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>",
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
// 更新小部件
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
// 更新小部件,可以指定是否处于隐私模式
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
int bgId = ResourceParser.getDefaultBgId(context);
@ -100,12 +97,12 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
c.close();
}
// 创建RemoteViews对象用于更新小部件视图
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* Generate the pending intent to start host for the widget
*/
// 生成PendingIntent用于小部件点击事件
PendingIntent pendingIntent = null;
if (privacyMode) {
rv.setTextViewText(R.id.widget_text,
@ -124,9 +121,12 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 获取背景资源ID的抽象方法
protected abstract int getBgResourceId(int bgId);
// 获取布局ID的抽象方法
protected abstract int getLayoutId();
// 获取小部件类型的抽象方法
protected abstract int getWidgetType();
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.widget;
@ -23,25 +12,33 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
// 继承自NoteWidgetProvider并实现2x2网格大小的笔记小部件
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 当小部件需要更新时调用
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的更新方法
super.update(context, appWidgetManager, appWidgetIds);
}
// 获取小部件的布局ID
@Override
protected int getLayoutId() {
// 返回2x2网格小部件的布局资源ID
return R.layout.widget_2x;
}
// 获取背景资源ID
@Override
protected int getBgResourceId(int bgId) {
// 根据传入的背景ID返回对应的2x2网格小部件背景资源ID
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
// 获取小部件类型
@Override
protected int getWidgetType() {
// 返回小部件的类型这里是2x2网格
return Notes.TYPE_WIDGET_2X;
}
}
}

@ -1,17 +1,6 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* MiCodeApache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0 查看。
*/
package net.micode.notes.widget;
@ -23,24 +12,33 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
// 继承自NoteWidgetProvider并实现4x4网格大小的笔记小部件
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 当小部件需要更新时调用
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// 调用父类的更新方法
super.update(context, appWidgetManager, appWidgetIds);
}
// 获取小部件的布局ID
@Override
protected int getLayoutId() {
// 返回4x4网格小部件的布局资源ID
return R.layout.widget_4x;
}
// 获取背景资源ID
@Override
protected int getBgResourceId(int bgId) {
// 根据传入的背景ID返回对应的4x4网格小部件背景资源ID
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
// 获取小部件类型
@Override
protected int getWidgetType() {
// 返回小部件的类型这里是4x4网格
return Notes.TYPE_WIDGET_4X;
}
}
}

@ -1,43 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!-- XML声明指定版本为1.0编码格式为utf-8 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
版权相关信息,说明该代码所属的开源社区及版权时间范围等
Licensed under the Apache License, Version 2.0 (the "License");
说明该代码遵循Apache License 2.0协议授权
you may not use this file except in compliance with the License.
强调使用该文件需遵循上述协议
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以在此网址获取协议的具体内容
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明在该协议下软件按“原样”分发的相关情况
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
指出不提供任何明示或暗示的保证或条件
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议了解权限和限制相关具体内容 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<!-- 定义一个线性布局LinearLayout宽度占满父容器高度也占满父容器布局方向为垂直方向同时引入了安卓的命名空间 -->
<TextView
android:id="@+id/account_dialog_title"
style="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:layout_marginTop="-2.7dip"
android:layout_marginBottom="-2.7dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
android:id="@+id/account_dialog_title"
style="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center"
android:layout_marginTop="-2.7dip"
android:layout_marginBottom="-2.7dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<!-- 定义一个TextView控件设置了其id为account_dialog_title应用了安卓中中等文本外观的样式文本单行显示超出部分省略号显示在结尾文本居中对齐设置了上下外边距宽度占满父容器高度根据内容自适应wrap_content -->
<TextView
android:id="@+id/account_dialog_subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:layout_marginBottom="1dip"
android:gravity="center"/>
android:id="@+id/account_dialog_subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dip"
android:layout_marginBottom="1dip"
android:gravity="center"/>
<!-- 定义另一个TextView控件设置了id为account_dialog_subtitle宽度占满父容器高度根据内容自适应设置了上下外边距文本居中对齐 -->
</LinearLayout>

@ -1,32 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!-- XML声明表明该XML文件遵循的版本是1.0使用的编码格式为utf-8 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
这里是版权相关的说明信息,指出代码所属的开源社区以及对应的版权时间范围
Licensed under the Apache License, Version 2.0 (the "License");
说明此代码遵循Apache License 2.0这个开源协议进行授权许可
you may not use this file except in compliance with the License.
强调若要使用该文件,必须遵循上述提到的开源协议
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过这个网址去获取Apache License 2.0协议的具体内容
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“现状”提供的相关情况
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
指出不提供任何种类的明示或暗示的保证以及相关条件
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看该协议来了解具体关于权限以及限制方面的内容 -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dip"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/preferences_add_account" />
android:minHeight="50dip"
android:gravity="center_vertical"
android:orientation="vertical">
<!-- 引入安卓的命名空间,用于后续使用安卓系统定义的各种布局属性等 -->
<!-- 线性布局的宽度设置为与父容器宽度匹配,也就是占满父容器宽度 -->
<!-- 线性布局的高度根据其内部子元素的布局情况自适应,也就是包裹内容的高度 -->
<!-- 定义线性布局的最小高度为50dip确保其不会小于这个高度值 -->
<!-- 设置线性布局内部元素在垂直方向上居中对齐 -->
<!-- 定义线性布局内子元素的排列方向为垂直方向 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/preferences_add_account" />
<!-- TextView控件的宽度根据其文本内容自适应也就是包裹内容的宽度 -->
<!-- TextView控件的高度同样根据其文本内容自适应包裹内容的高度 -->
<!-- 设置该TextView在其所在的父容器这里是线性布局中居中对齐 -->
<!-- 应用安卓系统中定义的中等文本外观样式来显示文本 -->
<!-- 设置TextView显示的文本内容这里引用了名为"preferences_add_account"的字符串资源通常在strings.xml文件中定义 -->
</LinearLayout>

@ -1,56 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式为 utf-8这是 XML 文件开头必备的标识信息 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关的声明信息,指出这段代码所属的开源社区为 The MiCode Open Source Community版权时间范围是 2010 - 2011 年
Licensed under the Apache License, Version 2.0 (the "License");
说明该代码遵循 Apache License 2.0 这个开源协议来进行授权许可,意味着使用该代码需要遵循此协议的相关规定
you may not use this file except in compliance with the License.
强调若要使用此 XML 文件所定义的相关内容,必须严格按照上述提到的 Apache License 2.0 协议来操作
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0)去获取 Apache License 2.0 协议的完整详细内容
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以其当前的“原样”进行分发的,即不提供额外的修改或保证等情况
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步强调该代码不附带任何种类的明示或暗示的保证以及相关条件,使用者需自行承担使用风险
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看该协议内容,以了解具体关于权限授予以及限制方面的详细信息 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<NumberPicker
android:id="@+id/date"
android:layout_width="120dip"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
/>
android:id="@+id/date"
android:layout_width="120dip"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<NumberPicker
android:id="@+id/hour"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
android:id="@+id/hour"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<NumberPicker
android:id="@+id/minute"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
android:id="@+id/minute"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
<NumberPicker
android:id="@+id/amPm"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
</LinearLayout>
android:id="@+id/amPm"
android:layout_width="50dip"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
</LinearLayout>
<!-- 设置此 NumberPicker 控件的宽度为 50 设备独立像素dip以确定其在水平方向上的尺寸 -->
<!-- 设置该控件距离其左边相邻元素在这里就是前面定义的“date” NumberPicker 控件)的间距为 5 设备独立像素dip用于控制控件之间的水平间隔距离 -->
<!-- 在触摸模式下(例如在触屏设备上操作时)也允许该控件获取焦点,方便用户通过触摸操作来与该控件进行交互,比如点击后进行数值调整等 -->
<!-- 为这个 NumberPicker 控件定义一个唯一的标识符id在安卓开发中通过这个 id 可以在代码里方便地对该控件进行引用、操作和事件处理等 -->
<!-- 设置 NumberPicker 控件的高度根据其内部内容自适应,也就是其高度会根据该控件显示的数值等内容自动调整,刚好包裹住这些内容 -->
<!-- 设置 NumberPicker 控件的宽度为 120 设备独立像素dip这确定了该控件在屏幕上水平方向所占据的空间大小 -->
<!-- 引入安卓的命名空间xmlns通过这个命名空间后续可以使用安卓系统预定义的各种布局属性和控件等相关元素这是在安卓 XML 布局文件中定义布局的基础 -->
<!-- 设置线性布局LinearLayout内子元素的排列方向为水平方向意味着子元素会从左到右依次水平排列 -->
<!-- 使该线性布局在其父容器中水平方向上居中对齐,这样整个布局会在水平维度上处于父容器的中间位置 -->
<!-- 线性布局的宽度设置为根据其内部包含的子元素大小自适应,也就是刚好能够包裹住所有子元素所需要的宽度 -->
<!-- 允许该控件获取焦点,意味着在用户操作时(例如通过键盘导航等方式),可以将操作焦点定位到这个控件上,使其能够接收用户输入等操作 -->
<!-- 允许该控件获取焦点,意味着在用户操作时(例如通过键盘导航等方式),可以将操作焦点定位到这个控件上,使其能够接收用户输入等操作 -->
<!-- 线性布局的高度同样根据其内部包含的子元素大小自适应,即包裹住子元素后所需要的高度 -->

@ -1,23 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明用于指定该XML文件所遵循的版本是1.0并且编码格式为utf-8这是XML文件开头必备的基础信息让解析器知晓如何正确解析文件内容 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
这部分是版权声明相关的注释内容说明该代码归属于The MiCode Open Source Community这个开源社区其版权时间范围涵盖了2010年至2011年期间
Licensed under the Apache License, Version 2.0 (the "License");
表明此代码是按照Apache License 2.0这个开源协议来进行授权许可的,意味着任何使用该代码的行为都需要遵循此协议所规定的各项条款
you may not use this file except in compliance with the License.
着重强调了只有在符合上述Apache License 2.0协议要求的情况下才可以使用当前这个XML文件所定义的相关内容否则属于违规使用
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
提供了获取Apache License 2.0协议具体内容的途径即可以通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0)去查看完整的协议文本
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议来分发软件时,软件是以其现有的“原样”状态进行分发的,不会额外提供一些特定的保证或者对软件进行修改等情况
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步明确指出该代码不会附带任何类型的、无论是明示还是暗示的保证以及相关条件,使用该代码的用户需要自行承担相应的使用风险
See the License for the specific language governing permissions and
limitations under the License.
-->
提示用户如果想要了解关于该协议所规定的权限管理以及各种限制方面的详细内容,需要去查看对应的协议文本 -->
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/et_foler_name"
android:layout_width="fill_parent"
android:hint="@string/hint_foler_name"
android:layout_height="fill_parent" />
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/et_foler_name"
android:layout_width="fill_parent"
android:hint="@string/hint_foler_name"
android:layout_height="fill_parent"
/>
<!-- 设定了该EditText控件的高度属性为fill_parent意味着它在垂直方向上也会填满所在父容器的全部高度空间这样该控件在整个布局中的尺寸就会尽可能地大占据父容器的绝大部分空间 -->
<!-- 用于设置该EditText控件的提示文本内容这里通过引用名为hint_foler_name的字符串资源一般在项目的strings.xml文件中定义具体的字符串值来显示提示信息当该控件中没有用户输入的文本时就会显示这个提示文本以此来提示用户应该输入什么样的内容从名称来看大概率是提示用户输入文件夹名称相关的内容 -->
<!-- 设置了该EditText控件的宽度属性将其设置为fill_parent表示这个控件在水平方向上会填满它所在的父容器的全部宽度空间使其能够在布局中占据足够的横向空间 -->
<!-- 为这个EditText控件定义了一个唯一的标识符id在安卓开发过程中其他代码部分比如在Java或者Kotlin代码中可以通过这个id来准确地引用到该控件进而对其进行各种操作例如获取用户输入的文本、设置控件的显示状态等这里将其命名为et_foler_name从命名推测可能是用于和文件夹名称相关的输入操作 -->
<!-- 引入了安卓的命名空间xmlns这是在安卓开发中XML布局文件里必不可少的部分通过这个命名空间后续就能够使用安卓系统所定义的众多属性来对该EditText控件进行各种配置 -->

@ -1,29 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,所采用的编码格式为 utf-8这是 XML 文件开头的标准标识,用于告知解析器如何正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="50dip" >
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- 设置线性布局LinearLayout的宽度与父容器宽度相匹配也就是占满父容器的整个宽度 -->
<!-- 设置线性布局的高度与父容器高度相匹配,即占满父容器的整个高度 -->
<!-- 定义线性布局的最小高度为 50 设备独立像素dip确保该布局在任何情况下高度都不会低于这个值可用于保证一定的显示效果 -->
<!-- 引入安卓的命名空间,后续通过此命名空间来使用安卓系统定义的各种布局属性及控件相关属性,是安卓 XML 布局文件必备的部分 -->
android:minHeight="50dip"
<TextView
android:id="@+id/tv_folder_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textAppearance="@style/TextAppearancePrimaryItem" />
</LinearLayout>
android:id="@+id/tv_folder_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textAppearance="@style/TextAppearancePrimaryItem"
/>
</LinearLayout>
<!-- 为该 TextView 控件定义一个唯一标识符id方便在代码中对其进行引用、操作从命名推测可能是用于显示文件夹名称相关内容 -->
<!-- 设置 TextView 控件的宽度与父容器(这里是外层的 LinearLayout宽度相匹配使其能占满水平方向的空间 -->
<!-- 设置 TextView 控件的高度与父容器高度相匹配,占满垂直方向的空间 -->
<!-- 设置文本在该 TextView 控件内的对齐方式为居中对齐,使文本在水平和垂直方向都处于中间位置 -->
<!-- 应用名为 TextAppearancePrimaryItem 的样式来设置文本的外观,该样式通常在样式资源文件中定义,可控制文本的字体、字号、颜色等外观属性 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处为版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围是 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示该代码遵循 Apache License 2.0 开源协议进行授权许可,意味着使用该代码需要遵循此协议规定。
you may not use this file except in compliance with the License.
强调若要使用这个文件,必须按照上述 Apache License 2.0 协议来操作。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问此网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”进行分发,不提供额外保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步指出该代码不附带任何明示或暗示的保证及相关条件,使用者需自行承担使用风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议以了解关于权限及限制方面的详细内容。 -->

@ -16,395 +16,704 @@
-->
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:background="@drawable/list_background"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/note_title"
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/note_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_modified_date"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="left|center_vertical"
android:layout_marginRight="8dip"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
android:id="@+id/tv_modified_date"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="left|center_vertical"
android:layout_marginRight="8dip"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
<ImageView
android:id="@+id/iv_alert_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/title_alert" />
android:id="@+id/iv_alert_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@drawable/title_alert" />
<TextView
android:id="@+id/tv_alert_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="2dip"
android:layout_marginRight="8dip"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
android:id="@+id/tv_alert_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="2dip"
android:layout_marginRight="8dip"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
<ImageButton
android:id="@+id/menu_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="OnOpenMenu"
android:background="@drawable/ic_menu_more_dark" />
android:id="@+id/menu_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="OnOpenMenu"
android:background="@drawable/ic_menu_more_dark" />
<ImageView
android:id="@+id/btn_set_bg_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_gravity="center"
android:background="@drawable/bg_btn_set_color" />
android:id="@+id/btn_set_bg_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_gravity="center"
android:background="@drawable/bg_btn_set_color" />
</LinearLayout>
<LinearLayout
android:id="@+id/sv_note_edit"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:id="@+id/sv_note_edit"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ImageView
android:layout_width="fill_parent"
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
android:layout_width="fill_parent"
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
<ScrollView
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:scrollbars="none"
android:overScrollMode="never"
android:layout_gravity="left|top"
android:fadingEdgeLength="0dip">
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:scrollbars="none"
android:overScrollMode="never"
android:layout_gravity="left|top"
android:fadingEdgeLength="0dip">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<net.micode.notes.ui.NoteEditText
android:id="@+id/note_edit_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="left|top"
android:background="@null"
android:autoLink="all"
android:linksClickable="false"
android:minLines="12"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:lineSpacingMultiplier="1.2" />
android:id="@+id/note_edit_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="left|top"
android:background="@null"
android:autoLink="all"
android:linksClickable="false"
android:minLines="12"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:lineSpacingMultiplier="1.2" />
<LinearLayout
android:id="@+id/note_edit_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="-10dip"
android:visibility="gone" />
android:id="@+id/note_edit_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="-10dip"
android:visibility="gone" />
</LinearLayout>
</ScrollView>
<ImageView
android:layout_width="fill_parent"
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
android:layout_width="fill_parent"
android:layout_height="7dip"
android:background="@drawable/bg_color_btn_mask" />
</LinearLayout>
</LinearLayout>
<ImageView
android:layout_height="43dip"
android:layout_width="wrap_content"
android:background="@drawable/bg_color_btn_mask"
android:layout_gravity="top|right" />
android:layout_height="43dip"
android:layout_width="wrap_content"
android:background="@drawable/bg_color_btn_mask"
android:layout_gravity="top|right" />
<LinearLayout
android:id="@+id/note_bg_color_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/note_edit_color_selector_panel"
android:layout_marginTop="30dip"
android:layout_marginRight="8dip"
android:layout_gravity="top|right"
android:visibility="gone">
android:id="@+id/note_bg_color_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/note_edit_color_selector_panel"
android:layout_marginTop="30dip"
android:layout_marginRight="8dip"
android:layout_gravity="top|right"
android:visibility="gone">
<FrameLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_bg_yellow"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/iv_bg_yellow"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_bg_yellow_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginRight="5dip"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
android:id="@+id/iv_bg_yellow_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginRight="5dip"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_bg_blue"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/iv_bg_blue"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_bg_blue_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="3dip"
android:src="@drawable/selected" />
android:id="@+id/iv_bg_blue_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="3dip"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_bg_white"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/iv_bg_white"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_bg_white_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="2dip"
android:src="@drawable/selected" />
android:id="@+id/iv_bg_white_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="2dip"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_bg_green"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/iv_bg_green"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_bg_green_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
android:id="@+id/iv_bg_green_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_bg_red"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/iv_bg_red"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_bg_red_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
android:id="@+id/iv_bg_red_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/font_size_selector"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/font_size_selector_bg"
android:layout_gravity="bottom"
android:visibility="gone">
<FrameLayout
android:id="@+id/ll_font_small"
android:layout_width="0dip"
android:id="@+id/font_size_selector"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
android:background="@drawable/font_size_selector_bg"
android:layout_gravity="bottom"
android:visibility="gone">
<LinearLayout
android:layout_width="wrap_content"
<FrameLayout
android:id="@+id/ll_font_small"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
android:layout_weight="1">
<ImageView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_small"
android:layout_marginBottom="5dip" />
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_small"
android:layout_marginBottom="5dip" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_small"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_small"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
</LinearLayout>
<ImageView
android:id="@+id/iv_small_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
android:id="@+id/iv_small_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:focusable="false"
android:visibility="gone"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:id="@+id/ll_font_normal"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/ll_font_normal"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
android:layout_weight="1">
<ImageView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_normal"
android:layout_marginBottom="5dip" />
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_normal"
android:layout_marginBottom="5dip" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_normal"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_normal"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
</LinearLayout>
<ImageView
android:id="@+id/iv_medium_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
android:id="@+id/iv_medium_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:id="@+id/ll_font_large"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/ll_font_large"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
android:layout_weight="1">
<ImageView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_large"
android:layout_marginBottom="5dip" />
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_large"
android:layout_marginBottom="5dip" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_large"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_large"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
</LinearLayout>
<ImageView
android:id="@+id/iv_large_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
android:id="@+id/iv_large_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
</FrameLayout>
<FrameLayout
android:id="@+id/ll_font_super"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1">
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/ll_font_super"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
android:layout_weight="1">
<ImageView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_super"
android:layout_marginBottom="5dip" />
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/font_super"
android:layout_marginBottom="5dip" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_super"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/menu_font_super"
android:textAppearance="@style/TextAppearanceUnderMenuIcon" />
</LinearLayout>
<ImageView
android:id="@+id/iv_super_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
android:id="@+id/iv_super_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:focusable="false"
android:visibility="gone"
android:layout_marginRight="6dip"
android:layout_marginBottom="-7dip"
android:src="@drawable/selected" />
</FrameLayout>
</LinearLayout>
</FrameLayout>
<!-- XML声明表明该XML文件遵循的版本是1.0使用的编码格式是utf-8这是XML文件开头的标准标识用于告知解析器如何正确解析该文件 -->
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<!-- 设置FrameLayout帧布局的宽度占满父容器意味着它在水平方向上会填满所在父容器的全部可用空间 -->
<!-- 设置FrameLayout的高度占满父容器也就是在垂直方向上会填满所在父容器的全部可用空间 -->
<!-- 设置FrameLayout的背景这里引用了名为"list_background"的可绘制资源(通常可以是图片、颜色等能作为背景的资源)来作为该布局的背景 -->
<!-- 设置LinearLayout线性布局的宽度占满父容器此处父容器为外层的FrameLayout使其在水平方向上填满整个父布局空间 -->
<!-- 设置LinearLayout的高度占满父容器让它在垂直方向上也填满整个父布局空间 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素会按照从上到下的顺序依次排列 -->
<!-- 为这个内层的LinearLayout定义一个唯一标识符id方便在后续的代码中比如Java或Kotlin代码对该布局进行引用、操作等从命名来看可能与笔记的标题部分相关 -->
<!-- 设置这个LinearLayout的宽度占满父容器这里的父容器是外层的LinearLayout使其在水平方向上占据全部空间 -->
<!-- 设置该LinearLayout的高度根据其内部子元素的大小自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 为这个TextView控件定义一个唯一标识符id从名字推测可能是用于显示笔记的修改日期相关信息 -->
<!-- 初始设置该TextView的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向的空间 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应即刚好能包裹住文本的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间通常意味着它会占据相对较多的水平空间与同层级其他设置了权重的控件一起分配父容器的宽度 -->
<!-- 设置文本在该TextView控件内的对齐方式水平方向居左对齐、垂直方向居中对齐 -->
<!-- 设置该TextView控件距离其右边相邻元素的间距为8设备独立像素dip -->
<!-- 应用名为TextAppearanceSecondaryItem的样式来设置文本的外观像字体、字号、颜色等属性通常在对应的样式资源文件中定义好了 -->
<!-- 为这个ImageView控件定义一个唯一标识符id从名字推测可能是用于显示与提醒相关的图标 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应刚好能包裹住图片的高度 -->
<!-- 设置图片在该ImageView控件内的对齐方式为垂直居中对齐 -->
<!-- 设置该ImageView的背景引用名为title_alert的可绘制资源通常是图片资源作为背景 -->
<!-- 为这个TextView控件定义一个唯一标识符id从名字推测可能是用于显示提醒相关的日期信息 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 设置文本在该TextView控件内的对齐方式为垂直居中对齐 -->
<!-- 设置该TextView控件距离其左边相邻元素的间距为2设备独立像素dip -->
<!-- 设置该TextView控件距离其右边相邻元素的间距为8设备独立像素dip -->
<!-- 应用名为TextAppearanceSecondaryItem的样式来设置文本的外观 -->
<!-- 为这个ImageButton控件定义一个唯一标识符id从名字推测可能是用于点击后打开更多菜单之类的操作 -->
<!-- 设置该ImageButton的宽度根据其内部要显示的图片内容自适应能包裹住图片的宽度 -->
<!-- 设置该ImageButton的高度根据其内部要显示的图片内容自适应能包裹住图片的高度 -->
<!-- 设置图片在该ImageButton控件内的对齐方式为居中对齐 -->
<!-- 设置点击该ImageButton时触发的方法名为OnOpenMenu需要在对应的代码逻辑中实现这个方法来处理点击事件 -->
<!-- 设置该ImageButton的背景引用名为ic_menu_more_dark的可绘制资源通常是图片资源作为背景 -->
<!-- 为这个ImageView控件定义一个唯一标识符id从名字推测可能与设置背景颜色相关的操作有关 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片的高度 -->
<!-- 设置该ImageView的内边距为10设备独立像素dp也就是在图片的四周留出一定的空白空间 -->
<!-- 设置图片在该ImageView控件内的对齐方式为居中对齐 -->
<!-- 设置该ImageView的背景引用名为bg_btn_set_color的可绘制资源通常是图片资源作为背景 -->
<!-- 结束前面开始的 FrameLayout 布局定义,与开头的 <FrameLayout> 标签对应,构成一个完整的帧布局结构 -->
<!-- 为该 LinearLayout 定义一个唯一标识符id方便在代码中对其进行引用、操作从命名推测可能与笔记编辑相关的布局 -->
<!-- 设置该 LinearLayout 的宽度占满父容器(这里的父容器应该是外层与之对应的布局元素),使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 LinearLayout 的高度占满父容器,在垂直方向上填满整个父容器空间 -->
<!-- 设置线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<!-- 设置该 ImageView 的宽度占满父容器(这里的父容器是当前的 LinearLayout水平方向填满整个空间 -->
<!-- 设置该 ImageView 的高度为 7 设备独立像素dip -->
<!-- 设置 ImageView 的背景,引用名为 bg_color_btn_mask 的可绘制资源(通常是图片资源)作为背景 -->
<!-- 设置 ScrollView滚动视图的宽度占满父容器这里的父容器是当前的 LinearLayout使其在水平方向上填满整个空间 -->
<!-- 初始设置高度为 0 设备独立像素dip后续会结合 layout_weight 属性来分配垂直方向的空间 -->
<!-- 设置权重为 1在垂直方向上按比例分配剩余空间用于占据较大的可滚动区域 -->
<!-- 设置不显示滚动条 -->
<!-- 设置禁止过度滚动效果 -->
<!-- 设置在父容器中的对齐方式为左上角对齐 -->
<!-- 设置渐变边缘的长度为 0 设备独立像素dip即不显示渐变边缘效果 -->
<!-- 设置该 LinearLayout 的宽度占满父容器(这里的父容器是外层的 ScrollView在水平方向上填满整个空间 -->
<!-- 设置该 LinearLayout 的高度占满父容器,在垂直方向上填满整个空间 -->
<!-- 设置线性布局内子元素的排列方向为垂直方向 -->
<!-- 为该 NoteEditText 控件定义一个唯一标识符id从命名推测是用于编辑笔记内容的主要输入框 -->
<!-- 设置宽度占满父容器(这里的父容器是当前的 LinearLayout水平方向填满空间 -->
<!-- 设置高度根据文本内容自适应,包裹住文本内容 -->
<!-- 设置文本在该控件内的对齐方式为左上角对齐 -->
<!-- 设置背景为空,即不显示默认的背景样式 -->
<!-- 设置自动识别链接,会识别所有类型的链接(如网址、邮箱等) -->
<!-- 设置虽然识别了链接,但链接不可点击 -->
<!-- 设置最小行数为 12 行,确保输入框有一定的初始高度 -->
<!-- 应用名为 TextAppearancePrimaryItem 的样式来设置文本外观,例如字体、字号、颜色等属性 -->
<!-- 设置行间距倍数为 1.2,增加文本行之间的间距 -->
<!-- 为该 LinearLayout 定义一个唯一标识符id从命名推测可能与笔记编辑相关的列表部分当前设置为不可见 -->
<!-- 设置宽度占满父容器(这里的父容器是当前的 LinearLayout水平方向填满空间 -->
<!-- 设置高度根据其内部子元素自适应,包裹住子元素 -->
<!-- 设置线性布局内子元素的排列方向为垂直方向 -->
<!-- 设置该布局初始状态为不可见 -->
<!-- 结束前面开始的LinearLayout布局定义与对应的<LinearLayout>开头标签相匹配,完成该线性布局的结构 -->
<!-- 设置ImageView的高度为7设备独立像素dip使其具有固定的垂直高度 -->
<!-- 设置ImageView的背景引用名为bg_color_btn_mask的可绘制资源通常是图片、颜色等用于作为背景的资源来作为该ImageView的背景 -->
<!-- 设置ImageView的宽度占满父容器也就是在水平方向上填满其所在父布局的全部可用空间 -->
<!-- 设置ImageView的高度为43设备独立像素dip确定其垂直方向上的尺寸大小 -->
<!-- 设置ImageView的宽度根据其内部要显示的内容通常是图片自适应即刚好能包裹住图片的宽度 -->
<!-- 同样是设置该ImageView的背景使用名为bg_color_btn_mask的可绘制资源作为背景 -->
<!-- 设置该ImageView在其父容器中的对齐方式为右上角对齐使其显示在父容器的右上角位置 -->
<!-- 为该LinearLayout定义一个唯一标识符id方便在代码中对其进行引用、操作从命名来看可能是用于选择笔记背景颜色的相关布局 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该LinearLayout的背景引用名为note_edit_color_selector_panel的可绘制资源作为背景 -->
<!-- 设置该LinearLayout距离其父容器顶部的间距为30设备独立像素dip用于调整其垂直方向上的位置 -->
<!-- 设置该LinearLayout距离其父容器右侧的间距为8设备独立像素dip用于调整其水平方向上的位置 -->
<!-- 设置该LinearLayout在其父容器中的对齐方式为右上角对齐使其显示在父容器的右上角位置 -->
<!-- 设置该LinearLayout初始状态为不可见可能在满足某些条件后才会显示出来 -->
<!-- 初始设置FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来分配水平方向上的空间 -->
<!-- 设置FrameLayout的高度与父容器高度匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间用于多个同层级FrameLayout按比例划分宽度 -->
<!-- 为该ImageView控件定义一个唯一标识符id从命名推测是用于显示黄色背景相关的图片或样式 -->
<!-- 设置该ImageView的宽度与父容器这里是外层的FrameLayout宽度匹配在水平方向上填满整个父容器空间 -->
<!-- 设置该ImageView的高度与父容器高度匹配在垂直方向上填满整个父容器空间 -->
<!-- 为该ImageView控件定义一个唯一标识符id推测是用于表示黄色背景是否被选中的相关显示元素初始设置为不可见 -->
<!-- 设置该ImageView的宽度根据其内部要显示的内容自适应包裹住内容的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的内容自适应包裹住内容的高度 -->
<!-- 设置该ImageView在其父容器这里是外层的FrameLayout中的对齐方式为右下角对齐 -->
<!-- 设置该ImageView距离其父容器右侧的间距为5设备独立像素dip -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点操作来与之交互 -->
<!-- 设置该ImageView初始状态为不可见可能在相关逻辑控制下变为可见以表示选中状态等 -->
<!-- 设置该ImageView要显示的图片资源引用名为selected的可绘制资源通常是图片资源 -->
<!-- 结束前面对应的 FrameLayout 标签所开始的布局定义,与开头的 <FrameLayout> 相呼应,完成一个 FrameLayout 布局结构的声明 -->
<!-- 初始设置该 FrameLayout 的宽度为 0 设备独立像素dip后续会结合 layout_weight 属性在水平方向上按比例分配空间 -->
<!-- 设置该 FrameLayout 的高度与父容器高度相匹配,使其在垂直方向上填满整个父容器空间 -->
<!-- 设置权重为 1意味着在水平方向上它会按照比例去分配剩余的空间通常用于和同层级的其他设置了权重的布局共同划分父容器的宽度 -->
<!-- 为这个 ImageView 控件定义一个唯一标识符id从命名来看可能是用于显示蓝色背景相关的图片资源用于界面展示相关用途 -->
<!-- 设置该 ImageView 的宽度与父容器(这里就是外层的 FrameLayout宽度相匹配使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 ImageView 的高度与父容器高度相匹配,使其在垂直方向上填满整个父容器空间 -->
<!-- 为这个 ImageView 控件定义一个唯一标识符id推测是用于表示蓝色背景是否被选中的一个指示性图片比如选中后显示特定样式初始设置为不可见 -->
<!-- 设置该 ImageView 的宽度根据其内部要显示的图片内容自适应,也就是刚好能包裹住图片的宽度 -->
<!-- 设置该 ImageView 的高度根据其内部要显示的图片内容自适应,刚好能包裹住图片的高度 -->
<!-- 设置该 ImageView 在其父容器(外层的 FrameLayout内的对齐方式为右下角对齐确定其显示位置 -->
<!-- 设置该 ImageView 不可获取焦点,意味着用户不能通过焦点操作(比如通过方向键、触摸焦点等方式)与它进行交互 -->
<!-- 设置该 ImageView 初始状态为不可见,可能在后续满足某些条件(比如用户选择了蓝色背景选项等)时,通过代码将其设置为可见来表示相应的选中状态 -->
<!-- 设置该 ImageView 距离其父容器(外层的 FrameLayout右侧的间距为 3 设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该 ImageView 要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源这个图片应该是用于体现选中状态的特定样式 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间通常用于多个同层级FrameLayout按权重划分父容器宽度 -->
<!-- 设置该FrameLayout的高度与父容器高度相匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 设置权重为1意味着在水平方向上它会按照比例去分配剩余空间和同层级其他设置了权重的布局一起确定各自 <!-- 为该ImageView控件定义一个唯一标识符id从命名推测是用于展示白色背景相关的图片或视觉元素在界面布局中起到对应的显示作用 -->
<!-- 设置该ImageView的宽度与父容器这里就是外层的FrameLayout宽度相匹配使其在水平方向上填满整个父容器空间 -->
<!-- 设置该ImageView的高度与父容器高度相匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 为该ImageView控件定义一个唯一标识符id推测是用于表示黄色背景是否被选中的相关显示元素初始设置为不可见 -->
<!-- 设置该ImageView的宽度根据其内部要显示的内容自适应包裹住内容的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的内容自适应包裹住内容的高度 -->
<!-- 设置该ImageView在其父容器这里是外层的FrameLayout中的对齐方式为右下角对齐 -->
<!-- 设置该ImageView距离其父容器右侧的间距为5设备独立像素dip -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点操作来与之交互 -->
<!-- 设置该ImageView初始状态为不可见可能在相关逻辑控制下变为可见以表示选中状态等 -->
<!-- 设置该ImageView要显示的图片资源引用名为selected的可绘制资源通常是图片资源 -->
<!-- 结束前面对应的 FrameLayout 标签所开始的布局定义,与开头的 <FrameLayout> 相呼应,完成一个 FrameLayout 布局结构的声明 -->
<!-- 初始设置该 FrameLayout 的宽度为 0 设备独立像素dip后续会结合 layout_weight 属性在水平方向上按比例分配空间 -->
<!-- 设置该 FrameLayout 的高度与父容器高度相匹配,使其在垂直方向上填满整个父容器空间 -->
<!-- 设置权重为 1意味着在水平方向上它会按照比例去分配剩余的空间通常用于和同层级的其他设置了权重的布局共同划分父容器的宽度 -->
<!-- 为这个 ImageView 控件定义一个唯一标识符id从命名来看可能是用于显示蓝色背景相关的图片资源用于界面展示相关用途 -->
<!-- 设置该 ImageView 的宽度与父容器(这里就是外层的 FrameLayout宽度相匹配使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 ImageView 的高度与父容器高度相匹配,使其在垂直方向上填满整个父容器空间 -->
<!-- 为这个 ImageView 控件定义一个唯一标识符id推测是用于表示蓝色背景是否被选中的一个指示性图片比如选中后显示特定样式初始设置为不可见 -->
<!-- 设置该 ImageView 的宽度根据其内部要显示的图片内容自适应,也就是刚好能包裹住图片的宽度 -->
<!-- 设置该 ImageView 的高度根据其内部要显示的图片内容自适应,刚好能包裹住图片的高度 -->
<!-- 设置该 ImageView 在其父容器(外层的 FrameLayout内的对齐方式为右下角对齐确定其显示位置 -->
<!-- 设置该 ImageView 不可获取焦点,意味着用户不能通过焦点操作(比如通过方向键、触摸焦点等方式)与它进行交互 -->
<!-- 设置该 ImageView 初始状态为不可见,可能在后续满足某些条件(比如用户选择了蓝色背景选项等)时,通过代码将其设置为可见来表示相应的选中状态 -->
<!-- 设置该 ImageView 距离其父容器(外层的 FrameLayout右侧的间距为 3 设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该 ImageView 要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源这个图片应该是用于体现选中状态的特定样式 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间通常用于多个同层级FrameLayout按权重划分父容器宽度 -->
<!-- 设置该FrameLayout的高度与父容器高度相匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 设置权重为1意味着在水平方向上它会按照比例去分配剩余空间和同层级其他设置了权重的布局一起确定各自所占宽度比例 -->
<!-- 为该ImageView控件定义一个唯一标识符id从命名推测是用于展示白色背景相关的图片或视觉元素在界面布局中起到对应的显示作用 -->
<!-- 设置该ImageView的宽度与父容器这里就是外层的FrameLayout宽度相匹配使其在水平方向上填满整个父容器空间 -->
<!-- 设置该ImageView的高度与父容器高度相匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 为该ImageView控件定义一个唯一标识符id推测是用于表示白色背景是否被选中的一个指示性元素初始状态通常是不可见的后续根据选择逻辑来控制显示与否 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView在其父容器外层的FrameLayout内的对齐方式为右下角对齐确定其在布局中的显示位置 -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点相关操作如使用方向键、触摸焦点等方式与它进行交互 -->
<!-- 设置该ImageView初始状态为不可见等待满足特定条件比如用户选择了白色背景选项等情况再通过代码将其设置为可见来体现相应的选中状态 -->
<!-- 设置该ImageView距离其父容器外层的FrameLayout右侧的间距为2设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源该图片一般用于表示选中的特定样式 -->
<!-- 为该ImageView控件定义一个唯一标识符id从命名来看是用于展示绿色背景相关的图片或视觉元素用于界面的相关显示需求 -->
<!-- 设置该ImageView的宽度与父容器这里的外层FrameLayout宽度相匹配在水平方向上填满整个父容器空间 -->
<!-- 设置该ImageView的高度与父容器高度相匹配在垂直方向上填满整个父容器空间 -->
<!-- 为该ImageView控件定义一个唯一标识符id推测是用于表示绿色背景是否被选中的指示性元素初始状态通常是不可见的 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片高度 -->
<!-- 设置该ImageView在其父容器外层FrameLayout内的对齐方式为右下角对齐确定其显示位置 -->
<!-- 设置该ImageView不可获取焦点用户不能通过焦点操作与之交互 -->
<!-- 设置该ImageView初始状态为不可见待满足特定条件后可通过代码控制显示来体现选中状态 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源用于表示选中的样式 -->
<!-- 为该ImageView控件定义一个唯一标识符id从命名推测是用于展示红色背景相关的图片或视觉元素在界面布局中有对应的显示作用 -->
<!-- 设置该ImageView的宽度与父容器外层FrameLayout宽度相匹配在水平方向上填满整个父容器空间 -->
<!-- 设置该ImageView的高度与父容器高度相匹配在垂直方向上填满整个父容器空间 -->
<!-- 为该ImageView控件定义一个唯一标识符id推测是用于表示红色背景是否被选中的指示性元素初始状态一般是不可见的 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片高度 -->
<!-- 设置该ImageView在其父容器外层FrameLayout内的对齐方式为右下角对齐确定其显示位置 -->
<!-- 设置该ImageView不可获取焦点用户无法通过焦点操作与之交互 -->
<!-- 设置该ImageView初始状态为不可见后续根据特定条件可通过代码控制显示来体现选中状态 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源用于表示选中的样式 -->
<!-- 结束前面的LinearLayout布局定义与对应的开始标签相匹配完成该线性布局结构的声明 -->
<!-- 为该LinearLayout定义一个唯一标识符id方便在代码中对其进行引用、操作从命名推测可能是用于字体大小选择相关的布局 -->
<!-- 设置该LinearLayout的宽度占满父容器使其在水平方向上填满整个父容器空间 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 设置该LinearLayout的背景引用名为font_size_selector_bg的可绘制资源通常是图片、颜色等用于作为背景的资源作为背景 -->
<!-- 设置该LinearLayout在其父容器中的对齐方式为底部对齐使其显示在父容器的底部位置 -->
<!-- 设置该LinearLayout初始状态为不可见可能在满足某些条件比如用户点击相关按钮等操作后才会显示出来 -->
<!-- 为该FrameLayout定义一个唯一标识符id从命名推测可能与字体大小为小的相关设置布局有关 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 设置该FrameLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间通常用于和同层级其他设置了权重的布局共同划分父容器的宽度 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<!-- 设置该LinearLayout在其父容器这里是外层的FrameLayout内的对齐方式为居中对齐 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为居中对齐使子元素在该布局内处于中心位置 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应刚好能包裹住图片的高度 -->
<!-- 设置该ImageView要显示的图片资源引用名为font_small的可绘制资源通常是图片资源从命名推测是表示小字体相关的图标 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为5设备独立像素dip用于调整其垂直方向上的位置 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 设置该TextView要显示的文本内容引用名为menu_font_small的字符串资源从命名推测是用于表示小字体相关的文字描述 -->
<!-- 应用名为TextAppearanceUnderMenuIcon的样式来设置文本的外观例如字体、字号、颜色等属性该样式通常在样式资源文件中定义 -->
<!-- 为该ImageView定义一个唯一标识符id从命名推测可能是用于表示小字体选项是否被选中的相关显示元素初始状态通常是不可见的 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片的 height -->
<!-- 设置该ImageView在其父容器外层的FrameLayout内的对齐方式为右下角对齐确定其在布局中的显示位置 -->
<!-- 设置该ImageView距离其父容器右侧的间距为6设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为 -7设备独立像素dip这里的负数值可能用于特殊的布局调整效果 -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点相关操作如使用方向键、触摸焦点等方式与它进行交互 -->
<!-- 设置该ImageView初始状态为不可见等待满足特定条件比如用户选择了小字体选项等情况再通过代码将其设置为可见来体现相应的选中状态 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源该图片一般用于表示选中的特定样式 -->
<!-- 结束前面的FrameLayout布局定义与对应的开始标签相匹配完成该帧布局结构的声明 -->
<!-- 为该FrameLayout定义一个唯一标识符id从命名推测可能与字体大小为正常常规的相关设置布局有关方便后续在代码中引用操作 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 设置该FrameLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间通常用于和同层级其他设置了权重的布局共同划分父容器这里是外层的LinearLayout的宽度 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应即刚好能包裹住内部子元素所需要的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<!-- 设置该LinearLayout在其父容器这里是外层的FrameLayout内的对齐方式为居中对齐 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为居中对齐使子元素在该布局内处于中心位置 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应即刚好能包裹住内部子元素所需要的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<!-- 设置该LinearLayout在其父容器这里是外层的FrameLayout内的对齐方式为居中对齐 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为居中对齐使子元素在该布局内处于中心位置 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应刚好能包裹住图片的高度 -->
<!-- 设置该ImageView要显示的图片资源引用名为font_normal的可绘制资源通常是图片资源从命名推测是表示正常常规字体相关的图标 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为5设备独立像素dip用于调整其垂直方向上的位置 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 设置该TextView要显示的文本内容引用名为menu_font_normal的字符串资源从命名推测是用于表示正常常规字体相关的文字描述 -->
<!-- 应用名为TextAppearanceUnderMenuIcon的样式来设置文本的外观例如字体、字号、颜色等属性该样式通常在样式资源文件中定义 -->
<!-- 为该ImageView定义一个唯一标识符id从命名推测可能是用于表示正常常规字体选项是否被选中的相关显示元素初始状态通常是不可见的 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片的高度 -->
<!-- 设置该ImageView在其父容器外层的FrameLayout内的对齐方式为右下角对齐确定其在布局中的显示位置 -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点相关操作如使用方向键、触摸焦点等方式与它进行交互 -->
<!-- 设置该ImageView初始状态为不可见等待满足特定条件比如用户选择了正常字体选项等情况再通过代码将其设置为可见来体现相应的选中状态 -->
<!-- 设置该ImageView距离其父容器右侧的间距为6设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为 -7设备独立像素dip这里的负数值可能用于特殊的布局调整效果 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源该图片一般用于表示选中的特定样式 -->
<!-- 为该FrameLayout定义一个唯一标识符id从命名推测可能与字体大小为大的相关设置布局有关便于后续代码中对其进行引用等操作 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间用于和同层级其他设置了权重的布局共同划分父容器的宽度 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应能包裹住内部子元素所需的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应能包裹住内部子元素的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,子元素按从上到下顺序排列 -->
<!-- 设置该LinearLayout在其父容器外层的FrameLayout内的对齐方式为居中对齐 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为居中对齐让子元素处于该布局中心位置 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应包裹住图片宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应包裹住图片高度 -->
<!-- 设置该ImageView要显示的图片资源引用名为font_large的可绘制资源通常是图片资源推测是表示大字体相关的图标 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为5设备独立像素dip调整垂直位置 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应包裹住文本宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应包裹住文本高度 -->
<!-- 设置该TextView要显示的文本内容引用名为menu_font_large的字符串资源用于表示大字体相关文字描述 -->
<!-- 应用样式设置文本外观,样式在对应资源文件中定义 -->
<!-- 结束前面的LinearLayout布局定义与对应的开始标签相匹配完成该线性布局结构的声明 -->
<!-- 为该ImageView定义一个唯一标识符id从命名推测是用于表示大字体选项是否被选中的相关显示元素方便在代码中对其进行操作和引用 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应刚好能包裹住图片的高度 -->
<!-- 设置该ImageView在其父容器这里的父容器应该是外层与之对应的布局元素可能是上一层的FrameLayout内的对齐方式为右下角对齐确定其显示位置 -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点相关操作比如使用方向键、触摸焦点等方式与它进行交互 -->
<!-- 设置该ImageView初始状态为不可见等待满足特定条件比如用户选择了大字体选项等情况再通过代码将其设置为可见来体现相应的选中状态 -->
<!-- 设置该ImageView距离其父容器右侧的间距为6设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为 -7设备独立像素dip这里的负数值可能用于特殊的布局调整效果使其在垂直方向上能达到特定的布局位置 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源该图片一般用于表示选中的特定样式 -->
<!-- 结束前面的FrameLayout布局定义与对应的开始标签相匹配完成该帧布局结构的声明 -->
<!-- 为该FrameLayout定义一个唯一标识符id从命名推测可能与字体大小为超大的相关设置布局有关便于后续在代码中对其进行引用和操作 -->
<!-- 初始设置该FrameLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 设置该FrameLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间通常用于和同层级其他设置了权重的布局共同划分父容器这里应该是外层的LinearLayout的宽度 -->
<!-- 设置该LinearLayout的宽度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的宽度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<!-- 设置该LinearLayout在其父容器这里是外层的FrameLayout内的对齐方式为居中对齐 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为居中对齐使子元素在该布局内处于中心位置 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应也就是刚好能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应刚好能包裹住图片的高度 -->
<!-- 设置该ImageView要显示的图片资源引用名为font_super的可绘制资源通常是图片资源从命名推测是表示超大字体相关的图标 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为5设备独立像素dip用于调整其垂直方向上的位置 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 设置该TextView要显示的文本内容引用名为menu_font_super的字符串资源从命名推测是用于表示超大字体相关的文字描述 -->
<!-- 应用名为TextAppearanceUnderMenuIcon的样式来设置文本的外观例如字体、字号、颜色等属性该样式通常在样式资源文件中定义 -->
<!-- 为该ImageView定义一个唯一标识符id从命名推测可能是用于表示超大字体选项是否被选中的相关显示元素初始状态通常是不可见的 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应能包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应能包裹住图片的高度 -->
<!-- 设置该ImageView在其父容器外层的FrameLayout内的对齐方式为右下角对齐确定其在布局中的显示位置 -->
<!-- 设置该ImageView不可获取焦点意味着用户不能通过焦点相关操作与它进行交互 -->
<!-- 设置该ImageView初始状态为不可见等待满足特定条件比如用户选择了超大字体选项等情况再通过代码将其设置为可见来体现相应的选中状态 -->
<!-- 设置该ImageView距离其父容器右侧的间距为6设备独立像素dip用于微调其在水平方向上的位置 -->
<!-- 设置该ImageView距离其底部相邻元素的间距为 -7设备独立像素dip用于特殊的布局调整效果 -->
<!-- 设置该ImageView要显示的图片资源引用名为“selected”的可绘制资源通常是图片资源用于表示选中的特定样式 -->
<!-- 结束最外层的FrameLayout布局定义与整个XML文件开头的<FrameLayout>标签相对应,完成整个布局结构的声明 -->
<!-- 结束前面的LinearLayout布局定义与对应的开始标签相匹配完成该线性布局结构的声明 -->

@ -1,39 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该XML文件遵循的版本是1.0使用的编码格式为utf-8这是XML文件开头的标准标识用于告知解析器如何正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<!-- 设置LinearLayout线性布局的宽度占满父容器使其在水平方向上填满整个父容器空间 -->
<!-- 设置LinearLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<CheckBox
android:id="@+id/cb_edit_item"
android:layout_width="wrap_content"
android:layout_height="28dip"
android:checked="false"
android:focusable="false"
android:layout_gravity="top|left" />
android:id="@+id/cb_edit_item"
android:layout_width="wrap_content"
android:layout_height="28dip"
android:checked="false"
android:focusable="false"
android:layout_gravity="top|left"
/>
<!-- 为该CheckBox控件定义一个唯一标识符id方便在后续代码中对其进行引用、操作等 -->
<!-- 设置CheckBox的宽度根据其自身内容如勾选框图标等自适应也就是刚好能包裹住其内容的宽度 -->
<!-- 设置CheckBox的高度为28设备独立像素dip确定其垂直方向上的固定尺寸 -->
<!-- 设置该CheckBox初始状态为未勾选 -->
<!-- 设置该CheckBox不可获取焦点意味着用户不能通过焦点操作比如使用方向键等方式使其获得焦点 -->
<!-- 设置该CheckBox在其父容器这里是外层的LinearLayout内的对齐方式为左上角对齐确定其在布局中的位置 -->
<net.micode.notes.ui.NoteEditText
android:id="@+id/et_edit_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.2"
android:layout_gravity="center_vertical"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:background="@null" />
android:id="@+id/et_edit_text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:lineSpacingMultiplier="1.2"
android:layout_gravity="center_vertical"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:background="@null"
/>
<!-- 为该NoteEditText控件定义一个唯一标识符id从命名推测可能是用于编辑文本内容的输入框 -->
<!-- 设置该NoteEditText的宽度占满父容器这里的父容器是外层的LinearLayout使其在水平方向上填满整个可用空间 -->
<!-- 设置该NoteEditText的高度根据其内部文本内容自适应也就是刚好能包裹住文本的高度 -->
<!-- 设置文本行间距倍数为1.2,用于增加文本行之间的间距,使文本显示更清晰美观 -->
<!-- 设置该NoteEditText在其父容器内的对齐方式为垂直居中对齐确保其在垂直方向上处于合适位置 -->
<!-- 应用名为TextAppearancePrimaryItem的样式来设置文本的外观例如字体、字号、颜色等属性该样式通常在样式资源文件中定义 -->
<!-- 设置背景为空,即不显示默认的背景样式,可能是为了让输入框更好地融入整体界面或者根据需要自定义背景效果 -->
</LinearLayout>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明说明代码所属的开源社区是The MiCode Open Source Community版权时间范围为2010 - 2011年
Licensed under the Apache License, Version 2.0 (the "License");
表示该代码遵循Apache License 2.0开源协议进行授权许可,意味着使用该代码需要遵循此协议规定
you may not use this file except in compliance with the License.
强调若要使用这个文件必须按照上述Apache License 2.0协议来操作
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问此网址http://www.apache.org/licenses/LICENSE-2.0获取Apache License 2.0协议的详细内容
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”进行分发,不提供额外保证等情况
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步指出该代码不附带任何明示或暗示的保证及相关条件,使用者需自行承担使用风险
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议以了解关于权限及限制方面的详细内容 -->

@ -1,78 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该XML文件遵循的版本是1.0使用的编码格式为utf-8这是XML文件开头的标准标识用于告知解析器按照此版本和编码来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处为版权相关声明说明该代码隶属于The MiCode Open Source Community这个开源社区版权有效期在2010至2011年之间
Licensed under the Apache License, Version 2.0 (the "License");
表示此代码遵循Apache License 2.0开源协议进行授权许可,意味着使用该代码时必须遵守此协议所规定的各项要求
you may not use this file except in compliance with the License.
着重强调若要使用本文件必须严格按照上述的Apache License 2.0协议来操作
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0来获取Apache License 2.0协议的完整详细内容
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以其当前的状态(“原样”)进行分发,不会额外提供诸如质量保证等相关内容
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步明确指出该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用该代码所带来的风险
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以便了解关于权限以及限制方面的详细规定 -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/note_item"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/note_item"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<!-- 引入安卓的命名空间,后续可借助此命名空间使用安卓系统所定义的各类布局属性和控件相关属性 -->
<!-- 为该FrameLayout定义一个唯一标识符id方便在代码中对其进行引用、操作从命名推测可能与笔记相关的某个项目布局 -->
<!-- 设置FrameLayout的宽度占满父容器使其在水平方向上填满所在父容器的全部可用空间 -->
<!-- 设置FrameLayout的高度占满父容器即在垂直方向上填满所在父容器的全部可用空间 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
>
<!-- 设置LinearLayout的宽度占满父容器这里的父容器就是外层的FrameLayout使其在水平方向上填满整个父布局空间 -->
<!-- 设置LinearLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 设置该LinearLayout在其父容器FrameLayout内的对齐方式为垂直居中对齐确保其在垂直方向上处于合适位置 -->
<!-- 设置该LinearLayout内部子元素的对齐方式为垂直居中对齐使子元素在该线性布局内垂直方向上处于中心位置 -->
<LinearLayout
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
>
<!-- 初始设置该LinearLayout的宽度为0设备独立像素dip后续会结合layout_weight属性来按比例分配水平方向上的空间 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间用于和同层级其他设置了权重的布局共同划分父容器的宽度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素将从上到下依次排列 -->
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:visibility="gone" />
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="0dip"
android:layout_weight="1"
android:textAppearance="@style/TextAppearancePrimaryItem"
android:visibility="gone"
/>
<!-- 为该TextView控件定义一个唯一标识符id从命名推测可能用于显示名称相关信息不过当前设置为不可见 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 初始设置该TextView的高度为0设备独立像素dip后续结合layout_weight属性分配垂直方向空间 -->
<!-- 设置权重为1在垂直方向上按比例分配剩余空间通常用于占据较多垂直空间 -->
<!-- 应用名为TextAppearancePrimaryItem的样式来设置文本的外观像字体、字号、颜色等属性在对应的样式资源文件中定义 -->
<!-- 设置该TextView初始状态为不可见可能在满足某些条件后才显示出来 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
>
<!-- 设置该LinearLayout的宽度占满父容器这里的父容器是外层的垂直方向的LinearLayout在水平方向上填满空间 -->
<!-- 设置该LinearLayout的高度根据其内部子元素自适应包裹住内部子元素的高度 -->
<!-- 设置该LinearLayout在其父容器内的对齐方式为垂直居中对齐 -->
<TextView
android:id="@+id/tv_title"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true" />
android:id="@+id/tv_title"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
/>
<!-- 为该TextView控件定义一个唯一标识符id从命名推测可能用于显示标题相关信息 -->
<!-- 初始设置该TextView的宽度为0设备独立像素dip后续结合layout_weight属性分配水平方向空间 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 设置权重为1在水平方向上按比例分配剩余空间通常占据较多水平空间 -->
<!-- 设置该TextView只能显示单行文本多余文本可能会截断显示 -->
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearanceSecondaryItem" />
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearanceSecondaryItem"
/>
<!-- 为该TextView控件定义一个唯一标识符id从命名推测可能用于显示时间相关信息 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应能包裹住文本的高度 -->
<!-- 应用名为TextAppearanceSecondaryItem的样式来设置文本的外观 -->
</LinearLayout>
</LinearLayout>
<CheckBox
android:id="@android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:clickable="false"
android:visibility="gone" />
android:id="@android:id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:clickable="false"
android:visibility="gone"
/>
</LinearLayout>
<!-- 为该CheckBox控件定义一个唯一标识符id这里使用了安卓系统内置的命名空间下的id可能是遵循某种规范来定义用于一些标准的勾选操作相关功能 -->
<!-- 设置CheckBox的宽度根据其自身内容如勾选框图标等自适应包裹住其内容的宽度 -->
<!-- 设置CheckBox的高度根据其自身内容自适应包裹住其内容的高度 -->
<!-- 设置该CheckBox不可获取焦点意味着用户不能通过焦点操作如使用方向键等方式使其获得焦点 -->
<!-- 设置该CheckBox不可点击用户无法通过点击操作来改变其勾选状态等 -->
<!-- 设置该CheckBox初始状态为不可见可能在特定条件下才会显示并发挥作用 -->
<ImageView
android:id="@+id/iv_alert_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"/>
android:id="@+id/iv_alert_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
/>
</FrameLayout>
<!-- 为该ImageView控件定义一个唯一标识符id从命名推测可能用于显示提醒相关的图标 -->
<!-- 设置该ImageView的宽度根据其内部要显示的图片内容自适应包裹住图片的宽度 -->
<!-- 设置该ImageView的高度根据其内部要显示的图片内容自适应包裹住图片的 height -->
<!-- 设置该ImageView在其父容器FrameLayout内的对齐方式为右上角对齐确定其在布局中的显示位置 -->

@ -1,69 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/list_background">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:background="@drawable/list_background"
>
<!-- 设置 FrameLayout帧布局的宽度占满父容器意味着它在水平方向上会填满所在父容器的全部可用空间 -->
<!-- 设置 FrameLayout 的高度占满父容器,也就是在垂直方向上会填满所在父容器的全部可用空间 -->
<!-- 设置 FrameLayout 的背景这里引用了名为“list_background”的可绘制资源通常可以是图片、颜色等能作为背景的资源来作为该布局的背景 -->
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<TextView
android:id="@+id/tv_title_bar"
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_bar_bg"
android:visibility="gone"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="#FFEAD1AE"
android:textSize="@dimen/text_font_size_medium" />
android:layout_height="fill_parent"
android:orientation="vertical"
>
<!-- 设置 LinearLayout线性布局的宽度占满父容器此处父容器为外层的 FrameLayout使其在水平方向上填满整个父布局空间 -->
<!-- 设置 LinearLayout 的高度占满父容器,让它在垂直方向上也填满整个父布局空间 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素会按照从上到下的顺序依次排列 -->
<TextView
android:id="@+id/tv_title_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_bar_bg"
android:visibility="gone"
android:gravity="center_vertical"
android:singleLine="true"
android:textColor="#FFEAD1AE"
android:textSize="@dimen/text_font_size_medium"
/>
<!-- 为这个 TextView 控件定义一个唯一标识符id从命名推测可能用于显示标题栏相关的文本信息 -->
<!-- 设置该 TextView 的宽度占满父容器(这里的父容器是外层的 LinearLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 TextView 的高度根据其要显示的文本内容自适应,也就是刚好能包裹住文本的高度 -->
<!-- 设置该 TextView 的背景引用名为“title_bar_bg”的可绘制资源通常是图片、颜色等用于作为背景的资源作为背景 -->
<!-- 设置该 TextView 初始状态为不可见,可能在满足某些条件(比如特定页面显示、用户操作后等)后才会显示出来 -->
<!-- 设置文本在该 TextView 控件内的对齐方式为垂直居中对齐,使文本在垂直方向上处于中间位置 -->
<!-- 设置该 TextView 只能显示单行文本,多余的文本内容可能会被截断显示 -->
<!-- 设置该 TextView 显示文本的颜色,这里使用十六进制颜色值 #FFEAD1AE 来指定颜色 -->
<!-- 设置该 TextView 显示文本的字号大小引用名为“text_font_size_medium”的尺寸资源来确定具体字号该资源通常在对应的资源文件中定义 -->
<ListView
android:id="@+id/notes_list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:cacheColorHint="@null"
android:listSelector="@android:color/transparent"
android:divider="@null"
android:fadingEdge="@null" />
android:id="@+id/notes_list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:cacheColorHint="@null"
android:listSelector="@android:color/transparent"
android:divider="@null"
/>
</LinearLayout>
<!-- 为这个 ListView 控件定义一个唯一标识符id从命名推测是用于展示笔记列表相关内容的列表视图 -->
<!-- 设置该 ListView 的宽度占满父容器(这里的父容器是外层的 LinearLayout使其在水平方向上填满整个父容器空间 -->
<!-- 初始设置该 ListView 的高度为 0 设备独立像素dip后续会结合 layout_weight 属性来按比例分配垂直方向上的空间 -->
<!-- 设置权重为 1在垂直方向上按比例分配剩余空间通常用于使其占据较大的垂直空间以展示列表内容 -->
<!-- 设置缓存颜色提示为 null这样可以避免 ListView 在滚动等操作时出现颜色相关的显示问题(比如闪烁等) -->
<!-- 设置列表项被选中时的背景颜色为透明,让列表项在选中时视觉上更自然,不会出现默认的选中背景色 -->
<!-- 设置列表项之间的分割线为 null即不显示分割线使列表外观更简洁 -->
<Button
android:id="@+id/btn_new_note"
android:background="@drawable/new_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:layout_gravity="bottom" />
android:id="@+id/btn_new_note"
android:background="@drawable/new_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:layout_gravity="bottom"
/>
<!-- 为这个 Button 控件定义一个唯一标识符id从命名推测可能是用于点击创建新笔记的按钮 -->
<!-- 设置该 Button 的背景引用名为“new_note”的可绘制资源通常是图片资源作为背景比如按钮的外观样式图片等 -->
<!-- 设置该 Button 的宽度与父容器宽度匹配,使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 Button 的高度根据其内部文本或图标等内容自适应,包裹住内容的高度 -->
<!-- 设置该 Button 不可获取焦点,意味着用户不能通过焦点操作(比如使用方向键等方式)使其获得焦点 -->
<!-- 设置该 Button 在其父容器(这里的父容器是外层的 FrameLayout内的对齐方式为底部对齐使其显示在底部位置 -->
<Button
android:id="@+id/btn_set"
android:background="@drawable/ic_menu_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:padding="10dp"
android:layout_margin="10dp"
android:onClick="OnOpenMenu"
android:layout_gravity="bottom|right" />
android:id="@+id/btn_set"
android:background="@drawable/ic_menu_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:padding="10dp"
android:layout_margin="10dp"
android:onClick="OnOpenMenu"
android:layout_gravity="bottom|right"
/>
</FrameLayout>
<!-- 为这个 Button 控件定义一个唯一标识符id从命名推测可能是用于点击打开设置相关菜单之类的按钮 -->
<!-- 设置该 Button 的背景引用名为“ic_menu_more”的可绘制资源通常是图片资源作为背景 -->
<!-- 设置该 Button 的宽度根据其内部文本或图标等内容自适应,包裹住内容的宽度 -->
<!-- 设置该 Button 的高度根据其内部文本或图标等内容自适应,包裹住内容的高度 -->
<!-- 设置该 Button 不可获取焦点,意味着用户不能通过焦点操作与它进行交互 -->
<!-- 设置该 Button 的内边距为 10 设备独立像素dp也就是在按钮内容文本、图标等的四周留出一定的空白空间 -->
<!-- 设置该 Button 的外边距为 10 设备独立像素dp用于调整按钮与周围元素在水平和垂直方向上的间距 -->
<!-- 设置点击该 Button 时触发的方法名为“OnOpenMenu”需要在对应的代码逻辑中实现这个方法来处理点击事件 -->
<!-- 设置该 Button 在其父容器(外层的 FrameLayout内的对齐方式为右下角对齐使其显示在右下角位置 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
这里是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围是 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守此协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需要自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限以及限制方面的详细规定。 -->

@ -1,26 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析文件内容 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigation_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
android:orientation="horizontal"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<!-- 为该 LinearLayout 定义一个唯一标识符id方便在后续代码中对其进行引用、操作等从命名来看可能是用于导航栏相关的布局 -->
<!-- 设置 LinearLayout线性布局的宽度与父容器宽度相匹配使其在水平方向上填满整个父容器空间 -->
<!-- 设置 LinearLayout 的高度与父容器高度相匹配,使其在垂直方向上填满整个父容器空间 -->
<!-- 设置该线性布局内子元素的排列方向为水平方向,意味着子元素会按照从左到右的顺序依次排列 -->
<Button android:id="@+id/selection_menu"
android:divider="?android:attr/listDividerAlertDialog"
@ -28,5 +22,29 @@
android:gravity="left|center_vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
style="?android:attr/borderlessButtonStyle"
/>
<!-- 为该 Button 控件定义一个唯一标识符id从命名推测可能是用于选择菜单相关操作的按钮 -->
<!-- 设置按钮的分割线样式,这里引用了安卓系统内置属性(?android:attr/listDividerAlertDialog来指定分割线的样式通常用于在按钮相关显示上呈现分割效果 -->
<!-- 设置该 Button 只能显示单行文本,若文本内容过长可能会被截断显示 -->
<!-- 设置文本在该 Button 控件内的对齐方式,水平方向居左对齐、垂直方向居中对齐,让文本在按钮内处于合适位置 -->
<!-- 设置该 Button 的宽度根据其内部要显示的文本或图标等内容自适应,也就是刚好能包裹住这些内容的宽度 -->
<!-- 设置该 Button 的高度与父容器(这里就是外层的 LinearLayout高度相匹配使其在垂直方向上填满整个父容器空间 -->
<!-- 应用安卓系统内置的样式(?android:attr/borderlessButtonStyle来设置按钮的外观风格通常这种样式会使按钮呈现无边框的效果 -->
</LinearLayout>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表明这份代码遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守此协议规定的相关要求。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
提示可以通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证、条件等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需要自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提醒查看协议内容,以了解关于权限以及限制方面的详细规定。 -->

@ -1,24 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照相应版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="100dip"
android:visibility="invisible"
android:focusable="false"
android:background="@drawable/list_footer_bg"
/>
<!-- 引入安卓的命名空间,后续借助此命名空间就能使用安卓系统所定义的各类布局属性以及控件相关属性 -->
<!-- 设置 View视图的宽度占满父容器使其在水平方向上填满所在父容器的全部可用空间 -->
<!-- 设置 View 的高度为 100 设备独立像素dip确定其在垂直方向上的固定尺寸大小 -->
<!-- 设置该 View 的初始可见性为不可见状态,不过它依然会占据布局空间,只是用户看不到它,与 "gone" 不同的是,它的空间不会被回收 -->
<!-- 设置该 View 不可获取焦点,意味着用户不能通过焦点操作(例如使用方向键、触摸焦点等方式)使其获得焦点 -->
<!-- 设置该 View 的背景,引用名为 "list_footer_bg" 的可绘制资源(通常是图片、颜色等可作为背景的资源)来作为该 View 的背景 -->
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="100dip"
android:visibility="invisible"
android:focusable="false"
android:background="@drawable/list_footer_bg" />
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
这里是版权相关声明,说明该代码隶属于 The MiCode Open Source Community 这个开源社区,版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表明这份代码遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守此协议所规定的各项要求。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可通过访问这个网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会额外附带各种保证、条件等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
进一步强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需要自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限以及限制方面的详细规定。 -->

@ -1,41 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处为版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 设置LinearLayout线性布局的宽度占满父容器使其在水平方向上填满整个父容器空间 -->
<!-- 设置LinearLayout的高度根据其内部子元素自适应也就是刚好能包裹住内部子元素所需的高度 -->
<!-- 设置该线性布局内子元素的排列方向为垂直方向,意味着子元素会按照从上到下的顺序依次排列 -->
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<Button
android:id="@+id/preference_sync_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dip"
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
style="?android:attr/textAppearanceMedium"
android:text="@string/preferences_button_sync_immediately"/>
android:id="@+id/preference_sync_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dip"
android:layout_marginLeft="30dip"
android:layout_marginRight="30dip"
style="?android:attr/textAppearanceMedium"
android:text="@string/preferences_button_sync_immediately"
/>
<!-- 为该Button控件定义一个唯一标识符id从命名推测可能是用于执行偏好设置preference中同步相关操作的按钮 -->
<!-- 设置该Button的宽度占满父容器这里的父容器是外层的LinearLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该Button的高度根据其内部文本或图标等内容自适应也就是刚好能包裹住这些内容的高度 -->
<!-- 设置该Button距离其顶部相邻元素的间距为15设备独立像素dip用于调整其在垂直方向上的位置 -->
<!-- 设置该Button距离其左边相邻元素的间距为30设备独立像素dip用于调整其在水平方向上的位置 -->
<!-- 设置该Button距离其右边相邻元素的间距为30设备独立像素dip用于调整其在水平方向上的位置 -->
<!-- 应用安卓系统内置属性(?android:attr/textAppearanceMedium来设置按钮上文本的外观样式例如字体、字号、颜色等属性使其呈现出中等外观的样式 -->
<!-- 设置该Button要显示的文本内容引用名为“preferences_button_sync_immediately”的字符串资源该资源通常在对应的字符串资源文件中定义从命名推测是提示立即同步相关的文字描述 -->
<TextView
android:id="@+id/prefenerece_sync_status_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</LinearLayout>
android:id="@+id/prefenerece_sync_status_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
/>
</LinearLayout>
<!-- 为该TextView控件定义一个唯一标识符id从命名推测可能是用于显示偏好设置preference中同步状态相关信息的文本视图 -->
<!-- 设置该TextView的宽度根据其要显示的文本内容自适应也就是刚好能包裹住文本的宽度 -->
<!-- 设置该TextView的高度根据其要显示的文本内容自适应也就是刚好能包裹住文本的高度 -->
<!-- 设置该TextView在其父容器外层的LinearLayout内的对齐方式为居中对齐使其在布局中处于中心位置 -->
<!-- 设置该TextView初始状态为不可见可能在满足某些条件比如同步操作进行中、完成等情况后才会显示出来 -->

@ -35,3 +35,20 @@
android:maxLines="6"
android:lineSpacingMultiplier="1.2" />
</FrameLayout>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- 设置文本行间距倍数为 1.2,用于增加文本行之间的间距,使文本显示更清晰美观 -->
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<!-- 设置 FrameLayout帧布局的宽度占满父容器意味着它在水平方向上会填满所在父容器的全部可用空间 -->
<!-- 设置 FrameLayout 的高度占满父容器,也就是在垂直方向上会填满所在父容器的全部可用空间 -->
<!-- 为该 ImageView 控件定义一个唯一标识符id从命名推测可能是用于显示部件widget背景相关图片的视图 -->
<!-- 设置该 ImageView 的宽度占满父容器(这里的父容器是外层的 FrameLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 ImageView 的高度占满父容器,让它在垂直方向上也填满整个父容器空间 -->
<!-- 为该 TextView 控件定义一个唯一标识符id从命名推测可能是用于在部件widget上显示文本内容的视图 -->
<!-- 设置该 TextView 的宽度占满父容器(这里的父容器是外层的 FrameLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 TextView 的高度占满父容器,让它在垂直方向上也填满整个父容器空间 -->
<!-- 设置该 TextView 顶部的内边距为 30 设备独立像素dip也就是在文本内容上方留出一定的空白空间 -->
<!-- 设置该 TextView 左侧的内边距为 8 设备独立像素dip在文本内容左侧留出空白空间 -->
<!-- 设置该 TextView 显示文本的字号大小为 14 可缩放像素sp可根据用户手机系统设置的字体大小进行相应缩放 -->
<!-- 设置该 TextView 显示文本的颜色,这里使用十六进制颜色值 #FF663300 来指定颜色 -->
<!-- 设置该 TextView 最多能显示的行数为 6 行,超出部分可能会被截断或者通过其他方式处理(比如显示省略号等) -->
<!-- 设置文本行间距倍数为 1.2,用于增加文本行之间的间距,使文本显示更清晰美观 -->

@ -1,39 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种布局属性以及控件相关的属性了 -->
<!-- 设置 FrameLayout帧布局的宽度占满父容器使其在水平方向上填满所在父容器的全部可用空间 -->
<!-- 设置 FrameLayout 的高度占满父容器,让它在垂直方向上也填满所在父容器的全部可用空间 -->
<ImageView
android:id="@+id/widget_bg_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
android:id="@+id/widget_bg_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<!-- 为该 ImageView 控件定义一个唯一标识符id从命名推测可能是用于显示部件widget的背景图片的视图 -->
<!-- 设置该 ImageView 的宽度占满父容器(这里的父容器是外层的 FrameLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 ImageView 的高度占满父容器,让它在垂直方向上也填满整个父容器空间 -->
<TextView
android:id="@+id/widget_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="33dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:textSize="14sp"
android:textColor="#FF663300"
android:maxLines="14"
android:lineSpacingMultiplier="1.2" />
android:id="@+id/widget_text"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="33dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:textSize="14sp"
android:textColor="#FF663300"
android:maxLines="14"
android:lineSpacingMultiplier="1.2"
/>
</FrameLayout>
<!-- 为该 TextView 控件定义一个唯一标识符id从命名推测可能是用于在部件widget上显示文本内容的视图 -->
<!-- 设置该 TextView 的宽度占满父容器(这里的父容器是外层的 FrameLayout使其在水平方向上填满整个父容器空间 -->
<!-- 设置该 TextView 的高度占满父容器,让它在垂直方向上也填满整个父容器空间 -->
<!-- 设置该 TextView 顶部的内边距为 33 设备独立像素dip也就是在文本内容上方留出一定的空白空间 -->
<!-- 设置该 TextView 左侧的内边距为 10 设备独立像素dip在文本内容左侧留出空白空间 -->
<!-- 设置该 TextView 右侧的内边距为 10 设备独立像素dip在文本内容右侧留出空白空间 -->
<!-- 设置该 TextView 显示文本的颜色,这里使用十六进制颜色值 #FF663300 来指定颜色 -->
<!-- 设置该 TextView 最多能显示的行数为 14 行,超出的文本内容可能会被截断或者通过其他方式处理(比如显示省略号等) -->
<!-- 设置文本行间距倍数为 1.2,用于增加文本行之间的间距,使文本显示更清晰美观 -->

@ -1,48 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关的属性 -->
<item
android:id="@+id/menu_delete"
android:title="@string/menu_delete" />
android:id="@+id/menu_delete"
android:title="@string/menu_delete"
/>
<!-- 为该菜单项定义一个唯一标识符id从命名“menu_delete”来看可能是用于执行删除相关操作的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_delete”的字符串资源该资源通常在对应的字符串资源文件中定义用于明确提示用户该项的功能与删除有关 -->
<item
android:id="@+id/menu_font_size"
android:title="@string/menu_font_size"/>
android:id="@+id/menu_font_size"
android:title="@string/menu_font_size"
/>
<!-- 为该菜单项定义一个唯一标识符id推测是用于操作字体大小相关设置的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_font_size”的字符串资源提示用户点击该项可进行字体大小相关的操作 -->
<item
android:id="@+id/menu_list_mode"
android:title="@string/menu_list_mode" />
android:id="@+id/menu_list_mode"
android:title="@string/menu_list_mode"
/>
<!-- 为该菜单项定义一个唯一标识符id可能是用于切换列表模式相关功能的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_list_mode”的字符串资源告知用户该项与列表模式操作有关 -->
<item
android:id="@+id/menu_share"
android:title="@string/menu_share"/>
android:id="@+id/menu_share"
android:title="@string/menu_share"
/>
<!-- 为该菜单项定义一个唯一标识符id显然是用于实现分享功能的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_share”的字符串资源让用户明白点击该项可进行分享操作 -->
<item
android:id="@+id/menu_send_to_desktop"
android:title="@string/menu_send_to_desktop"/>
android:id="@+id/menu_send_to_desktop"
android:title="@string/menu_send_to_desktop"
/>
<!-- 为该菜单项定义一个唯一标识符id推测是用于将相关内容发送到桌面等操作的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_send_to_desktop”的字符串资源提示用户该项功能与发送到桌面有关 -->
<item
android:id="@+id/menu_alert"
android:title="@string/menu_alert" />
android:id="@+id/menu_alert"
android:title="@string/menu_alert"
/>
<!-- 为该菜单项定义一个唯一标识符id可能是用于设置提醒相关功能的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_alert”的字符串资源告知用户点击该项可进行提醒相关操作 -->
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind"
/>
<!-- 为该菜单项定义一个唯一标识符id从命名推测是用于删除提醒相关操作的菜单项 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_remove_remind”的字符串资源提示用户该项功能与删除提醒有关 -->
</menu>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处为版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,23 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item
android:id="@+id/menu_search"
android:title="@string/menu_search" />
android:id="@+id/menu_search"
android:title="@string/menu_search"
/>
</menu>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关的属性 -->
<!-- 为该菜单项定义一个唯一标识符id从命名“menu_search”可以看出这个菜单项大概率是用于触发搜索相关操作的 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_search”的字符串资源这个字符串资源通常在对应的字符串资源文件中定义以此来明确地向用户提示该菜单项的功能是进行搜索 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,52 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关属性 -->
<item
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"/>
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"
/>
<!-- 为该菜单项定义一个唯一标识符id从命名“menu_new_note”推测这个菜单项大概率是用于创建新笔记相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“notelist_menu_new”的字符串资源该字符串资源通常在对应的字符串资源文件中定义用于向用户提示点击此项可进行创建新笔记的操作 -->
<item
android:id="@+id/menu_delete"
android:title="@string/menu_delete"/>
android:id="@+id/menu_delete"
android:title="@string/menu_delete"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_delete”表明这个菜单项很可能是用于执行删除相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_delete”的字符串资源明确提示用户点击该项可执行删除操作 -->
<item
android:id="@+id/menu_font_size"
android:title="@string/menu_font_size"/>
android:id="@+id/menu_font_size"
android:title="@string/menu_font_size"
/>
<!-- 为该菜单项定义一个唯一标识符id根据“menu_font_size”命名可推测这个菜单项是用于调整字体大小相关设置的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_font_size”的字符串资源提示用户点击该项可进行字体大小相关操作 -->
<item
android:id="@+id/menu_list_mode"
android:title="@string/menu_list_mode" />
android:id="@+id/menu_list_mode"
android:title="@string/menu_list_mode"
/>
<!-- 为该菜单项定义一个唯一标识符id从“menu_list_mode”来看这个菜单项可能是用于切换列表显示模式相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_list_mode”的字符串资源告知用户点击该项可进行列表模式相关操作 -->
<item
android:id="@+id/menu_share"
android:title="@string/menu_share"/>
android:id="@+id/menu_share"
android:title="@string/menu_share"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_share”清晰地表明这个菜单项是用于实现分享功能的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_share”的字符串资源让用户明白点击该项可进行分享操作 -->
<item
android:id="@+id/menu_send_to_desktop"
android:title="@string/menu_send_to_desktop"/>
android:id="@+id/menu_send_to_desktop"
android:title="@string/menu_send_to_desktop"
/>
<!-- 为该菜单项定义一个唯一标识符id由“menu_send_to_desktop”推测这个菜单项可能是用于将相关内容发送到桌面等操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_send_to_desktop”的字符串资源提示用户点击该项可进行发送到桌面相关操作 -->
<item
android:id="@+id/menu_alert"
android:title="@string/menu_alert" />
android:id="@+id/menu_alert"
android:title="@string/menu_alert"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_alert”表明这个菜单项大概率是用于设置提醒相关功能的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_alert”的字符串资源告知用户点击该项可进行提醒相关操作 -->
<item
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind" />
</menu>
android:id="@+id/menu_delete_remind"
android:title="@string/menu_remove_remind"
/>
</menu>
<!-- 为该菜单项定义一个唯一标识符id从“menu_delete_remind”可推测这个菜单项是用于删除提醒相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_remove_remind”的字符串资源提示用户点击该项可进行删除提醒相关操作 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
这里是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,39 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关的属性 -->
<item
android:id="@+id/menu_new_folder"
android:title="@string/menu_create_folder"/>
android:id="@+id/menu_new_folder"
android:title="@string/menu_create_folder"
/>
<!-- 为该菜单项定义一个唯一标识符id从命名“menu_new_folder”可以推测出这个菜单项大概率是用于创建新文件夹相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_create_folder”的字符串资源该字符串资源通常在对应的字符串资源文件中定义以此来明确地向用户提示点击此项可进行创建新文件夹的操作 -->
<item
android:id="@+id/menu_export_text"
android:title="@string/menu_export_text"/>
android:id="@+id/menu_export_text"
android:title="@string/menu_export_text"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_export_text”表明这个菜单项很可能是用于执行文本导出相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_export_text”的字符串资源提示用户点击该项可进行文本导出操作 -->
<item
android:id="@+id/menu_sync"
android:title="@string/menu_sync"/>
android:id="@+id/menu_sync"
android:title="@string/menu_sync"
/>
<!-- 为该菜单项定义一个唯一标识符id根据“menu_sync”命名可推测这个菜单项是用于进行数据同步相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_sync”的字符串资源告知用户点击该项可进行同步操作 -->
<item
android:id="@+id/menu_setting"
android:title="@string/menu_setting" />
android:id="@+id/menu_setting"
android:title="@string/menu_setting"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_setting”清晰地表明这个菜单项是用于打开设置相关界面或执行设置相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_setting”的字符串资源让用户明白点击该项可进入设置相关功能 -->
<item
android:id="@+id/menu_search"
android:title="@string/menu_search"/>
android:id="@+id/menu_search"
android:title="@string/menu_search"
/>
<!-- 为该菜单项定义一个唯一标识符id“menu_search”表明这个菜单项大概率是用于触发搜索相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_search”的字符串资源提示用户点击该项可进行搜索操作 -->
</menu>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,20 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_select_all" android:title="@string/menu_select_all" />
</menu>
<item android:id="@+id/action_select_all"
android:title="@string/menu_select_all"
/>
</menu>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关的属性 -->
<!-- 为该菜单项定义一个唯一标识符id从命名“action_select_all”可以推测出这个菜单项大概率是用于执行全选相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_select_all”的字符串资源该字符串资源通常在对应的字符串资源文件中定义以此来明确地向用户提示点击此项可进行全选操作 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,31 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关的属性 -->
<item
android:id="@+id/move"
android:title="@string/menu_move"
android:icon="@drawable/menu_move"
android:showAsAction="always|withText" />
android:id="@+id/move"
android:title="@string/menu_move"
android:icon="@drawable/menu_move"
android:showAsAction="always|withText"
/>
<!-- 为该菜单项定义一个唯一标识符id从命名“move”可以推测出这个菜单项大概率是用于执行移动相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_move”的字符串资源该字符串资源通常在对应的字符串资源文件中定义以此来明确地向用户提示点击此项可进行移动操作 -->
<!-- 设置该菜单项要显示的图标引用名为“menu_move”的可绘制资源通常是图片资源用于在界面上直观地展示该菜单项对应的功能方便用户识别 -->
<!-- 设置该菜单项在界面上的显示方式“always”表示总是显示在界面上比如在ActionBar等相关位置“withText”表示同时显示图标和文本内容让用户能更清晰地看到菜单项的功能和名称 -->
<item
android:id="@+id/delete"
android:title="@string/menu_delete"
android:icon="@drawable/menu_delete"
android:showAsAction="always|withText" />
</menu>
android:id="@+id/delete"
android:title="@string/menu_delete"
android:icon="@drawable/menu_delete"
android:showAsAction="always|withText"
/>
<!-- 为该菜单项定义一个唯一标识符id“delete”表明这个菜单项很可能是用于执行删除相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“menu_delete”的字符串资源提示用户点击该项可进行删除操作 -->
<!-- 设置该菜单项要显示的图标引用名为“menu_delete”的可绘制资源通常是图片资源用于辅助用户直观地识别该菜单项的功能 -->
<!-- 同样设置该菜单项在界面上的显示方式,使其总是显示并且同时展示图标和文本内容 -->
</menu>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->

@ -1,24 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- 引入安卓的命名空间,后续通过这个命名空间就能使用安卓系统中定义的各种菜单相关属性 -->
<item
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"/>
</menu>
android:id="@+id/menu_new_note"
android:title="@string/notelist_menu_new"
/>
</menu>
<!-- 为该菜单项定义一个唯一标识符id从命名“menu_new_note”来看这个菜单项大概率是用于触发创建新笔记相关操作的功能入口 -->
<!-- 设置该菜单项显示的标题文本引用名为“notelist_menu_new”的字符串资源该字符串资源通常在对应的字符串资源文件中定义用于向用户提示点击此项可进行创建新笔记的操作 -->

@ -1,23 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources>
<!-- <resources>标签是安卓资源文件中的根标签,用于定义各种类型的资源,比如字符串、数组、颜色等资源都可以放在这里面定义 -->
<string-array name="menu_share_ways">
<!-- 定义一个名为“menu_share_ways”的字符串数组资源从命名来看这个数组大概率是用于存储菜单中分享相关的方式选项内容 -->
<item>短信</item>
<!-- 字符串数组中的一个元素,表示一种分享方式,这里是“短信”,意味着在对应的分享菜单中,用户可以选择通过短信来分享相关内容 -->
<item>邮件</item>
<!-- 字符串数组中的另一个元素,表示另一种分享方式,即用户还可以通过邮件的方式来分享相关内容 -->
</string-array>
</resources>

@ -1,126 +1,263 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- <resources>是安卓资源文件的根标签用于定义各种资源这里引入了两个命名空间“android”用于安卓相关的资源属性设置等操作“xliff”常用于国际化文本处理相关场景 -->
<string name="app_name">便签</string>
<!-- 定义名为“app_name”的字符串资源此资源大概率用于表示整个应用程序的名称在这里被设置为“便签”表明这是一款与便签功能相关的应用 -->
<string name="app_widget2x2">便签2x2</string>
<!-- 定义名为“app_widget2x2”的字符串资源推测是用于描述应用中尺寸为 2x2 的小部件对应的显示名称方便用户识别和区分不同尺寸的小部件这里显示为“便签2x2” -->
<string name="app_widget4x4">便签4x4</string>
<!-- 定义名为“app_widget4x4”的字符串资源同理应该是用于表示应用里尺寸为 4x4 的小部件的显示名称起到标识该特定尺寸小部件的作用名称为“便签4x4” -->
<string name="widget_havenot_content">没有关联内容,点击新建便签。</string>
<!-- 定义名为“widget_havenot_content”的字符串资源从其内容来看可能是在小部件上显示的提示信息当没有与之关联的便签内容时提示用户点击可以去创建新的便签 -->
<string name="widget_under_visit_mode">访客模式下,便签内容不可见</string>
<!-- 定义名为“widget_under_visit_mode”的字符串资源推测是用于小部件处于访客模式下时向用户展示的提示文本告知用户在此模式下无法查看便签的具体内容 -->
<string name="notelist_string_info">...</string>
<!-- 定义名为“notelist_string_info”的字符串资源仅从“...”不太明确其确切用途,不过可能是在便签列表相关界面中用于展示某种简略信息或者占位性质的文本内容 -->
<string name="notelist_menu_new">新建便签</string>
<!-- 定义名为“notelist_menu_new”的字符串资源用于在便签列表对应的菜单里作为创建新便签这一菜单项所显示的文字内容明确提示用户点击该菜单项可进行添加便签的操作 -->
<string name="delete_remind_time_message">成功删除提醒</string>
<!-- 定义名为“delete_remind_time_message”的字符串资源大概率是在成功删除便签的提醒时间后向用户展示的提示消息告知用户相应的删除提醒操作已经顺利完成 -->
<string name="set_remind_time_message">创建提醒</string>
<!-- 定义名为“set_remind_time_message”的字符串资源应该是在执行设置便签提醒时间相关操作时向用户呈现的提示文本表明当前正在进行设置提醒的动作 -->
<string name="note_alert_expired">已过期</string>
<!-- 定义名为“note_alert_expired”的字符串资源可能是在便签的提醒功能过期时用于显示给用户的提示文字比如在提醒相关界面展示“已过期”字样让用户知晓提醒已过期 -->
<string name="format_date_ymd">yyyyMMdd</string>
<!-- 定义名为“format_date_ymd”的字符串资源这是定义了一种日期格式采用“yyyyMMdd”这种形式常用于对日期数据进行格式化输出比如按照此格式来存储、展示便签相关的日期信息等 -->
<string name="format_datetime_mdhm">MM月dd日 kk:mm</string>
<!-- 定义名为“format_datetime_mdhm”的字符串资源同样是一种日期时间格式的定义按照“MM月dd日 kk:mm”的格式来规范日期时间数据的展示方便在应用中统一处理和显示这类信息 -->
<string name="notealert_ok">知道了</string>
<!-- 定义名为“notealert_ok”的字符串资源推测是在便签相关提醒的操作场景里当用户确认知晓或者完成某个操作后对应的按钮或者提示文本会显示“知道了”表示用户明白了、已确认的意思 -->
<string name="notealert_enter">查看</string>
<!-- 定义名为“notealert_enter”的字符串资源可能是在便签提醒相关的交互中用于引导用户点击去查看具体便签内容的提示文字提示用户点击相应区域可以查看详情 -->
<string name="note_link_tel">呼叫电话</string>
<!-- 定义名为“note_link_tel”的字符串资源大概率是当便签中包含电话号码链接等情况时在相应位置显示的操作提示文本告知用户点击此处可以进行拨打电话的操作 -->
<string name="note_link_email">发送邮件</string>
<!-- 定义名为“note_link_email”的字符串资源应该是在便签里有邮件链接相关内容时显示给用户的提示文字提示用户点击可触发发送邮件的操作 -->
<string name="note_link_web">浏览网页</string>
<!-- 定义名为“note_link_web”的字符串资源可能是在便签中存在网页链接的情况下向用户展示的提示文本告知用户点击该链接可以浏览对应的网页内容 -->
<string name="note_link_other">打开地图</string>
<!-- 定义名为“note_link_other”的字符串资源推测是在便签涉及到地图相关链接等情况时显示的提示文字提示用户点击可以打开地图应用进行相关操作 -->
<!-- note list string -->
<!-- 这是一个注释说明,告知下面的字符串资源是与便签列表相关的一些字符串内容 -->
<string name="menu_create_folder">新建文件夹</string>
<!-- 定义名为“menu_create_folder”的字符串资源用于在应用的菜单中作为创建新文件夹这一菜单项所显示的文字内容清晰地提示用户点击该菜单项可进行新建文件夹的操作 -->
<string name="menu_export_text">导出文本</string>
<!-- 定义名为“menu_export_text”的字符串资源用于在菜单里作为导出文本这个菜单项显示的文本内容告知用户点击该菜单项就能执行文本导出的相关操作 -->
<string name="menu_sync">同步</string>
<!-- 定义名为“menu_sync”的字符串资源用于在菜单中作为同步相关操作的菜单项所显示的文字内容提示用户点击它可以发起数据同步的操作 -->
<string name="menu_sync_cancel">取消同步</string>
<!-- 定义名为“menu_sync_cancel”的字符串资源用于在正在进行同步操作的过程中在菜单里作为取消同步这一菜单项显示的文本内容方便用户点击来终止同步操作 -->
<string name="menu_setting">设置</string>
<!-- 定义名为“menu_setting”的字符串资源用于在菜单中作为进入设置界面或者执行设置相关操作的菜单项所显示的文字内容引导用户点击该菜单项以进入设置相关功能区域 -->
<string name="menu_search">搜索</string>
<!-- 定义名为“menu_search”的字符串资源用于在菜单中作为搜索相关操作的菜单项显示的文本内容提示用户点击该菜单项可以进行搜索相关的操作比如查找便签内容等 -->
<string name="menu_delete">删除</string>
<!-- 定义名为“menu_delete”的字符串资源用于在菜单中作为删除相关操作的菜单项显示的文本内容告知用户点击该菜单项可以执行删除相应内容的操作例如删除便签、文件夹等 -->
<string name="menu_move">移动到文件夹</string>
<!-- 定义名为“menu_move”的字符串资源用于在菜单中作为将相关内容移动到文件夹这一菜单项显示的文本内容提示用户点击该菜单项可进行把某些内容移动到指定文件夹的操作 -->
<string name="menu_select_title">选中了 %d 项</string>
<!-- 定义名为“menu_select_title”的字符串资源从格式“选中了 %d 项”来看,它是用于在有多个项目被选中后,展示选中数量相关提示信息的文本,会根据实际选中的项目数量替换“%d”来准确告知用户选中情况 -->
<string name="menu_select_none">没有选中项,操作无效</string>
<!-- 定义名为“menu_select_none”的字符串资源用于在没有任何项目被选中的情况下向用户显示的提示文本告知用户由于没有选中内容当前要执行的操作是无效的 -->
<string name="menu_select_all">全选</string>
<!-- 定义名为“menu_select_all”的字符串资源用于在菜单中作为全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以一次性选中所有相关的项目 -->
<string name="menu_deselect_all">取消全选</string>
<!-- 定义名为“menu_deselect_all”的字符串资源用于在菜单中作为取消全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以将已经全选的状态取消即取消选中所有项目 -->
<string name="menu_font_size">文字大小</string>
<!-- 定义名为“menu_font_size”的字符串资源用于在菜单中作为调整文字大小相关操作的菜单项显示的文本内容引导用户点击该菜单项来进行文字大小相关的设置操作 -->
<string name="menu_font_small"></string>
<!-- 定义名为“menu_font_small”的字符串资源可能是在文字大小设置的相关菜单选项里作为表示小字号这一选项所显示的文字内容便于用户选择小字体来显示文本 -->
<string name="menu_font_normal">正常</string>
<!-- 定义名为“menu_font_normal”的字符串资源推测是在文字大小设置相关的菜单中作为表示正常字号这一选项显示的文字内容方便用户将文本字体设置为正常大小 -->
<string name="menu_font_large"></string>
<!-- 定义名为“menu_font_large”的字符串资源应该是在文字大小设置相关的菜单里作为表示大字号这一选项所展示的文字内容让用户可以选择大字体来显示文本 -->
<string name="menu_font_super">超大</string>
<!-- 定义名为“menu_font_super”的字符串资源可能是在文字大小设置相关的菜单中作为表示超大字号这一选项显示的文字内容供用户选择超大字体来展示文本 -->
<string name="menu_list_mode">进入清单模式</string>
<!-- 定义名为“menu_list_mode”的字符串资源用于在菜单中作为进入清单模式比如待办事项清单等类似的模式相关操作的菜单项显示的文本内容提示用户点击该菜单项可进入对应的清单模式方便对相关内容进行清单式管理 -->
<string name="menu_normal_mode">退出清单模式</string>
<!-- 定义名为“menu_normal_mode”的字符串资源用于在菜单中作为离开清单模式例如待办事项清单等相关操作的菜单项显示的文本内容提示用户点击该菜单项可退出当前所处的清单模式恢复到常规显示状态 -->
<string name="menu_folder_view">查看文件夹</string>
<!-- 定义名为“menu_folder_view”的字符串资源用于在菜单里作为查看文件夹相关操作的菜单项所显示的文本内容告知用户点击该菜单项后可以查看指定文件夹内的内容比如查看文件夹里包含的便签等信息 -->
<string name="menu_folder_delete">刪除文件夹</string>
<!-- 定义名为“menu_folder_delete”的字符串资源用于在菜单中作为删除文件夹相关操作的菜单项显示的文本内容提示用户点击该菜单项会执行删除文件夹的操作通常可能还会有相应确认提示以防误删 -->
<string name="menu_folder_change_name">修改文件夹名称</string>
<!-- 定义名为“menu_folder_change_name”的字符串资源用于在菜单中作为更改文件夹名称相关操作的菜单项显示的文本内容引导用户点击该菜单项来对已有的文件夹重新命名使其名称更符合需求或便于识别 -->
<string name="folder_exist">文件夹 %1$s 已存在,请重新命名</string>
<!-- 定义名为“folder_exist”的字符串资源从格式“文件夹 %1$s 已存在,请重新命名”来看,它是一种带有占位符(%1$s的提示文本通常在创建文件夹时如果要创建的文件夹名称已存在就会用实际的文件夹名称替换占位符显示该提示信息告知用户需要重命名后再进行操作 -->
<string name="menu_share">分享</string>
<!-- 定义名为“menu_share”的字符串资源用于在菜单中作为分享相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行分享操作比如分享便签内容到其他应用或者平台等 -->
<string name="menu_send_to_desktop">发送到桌面</string>
<!-- 定义名为“menu_send_to_desktop”的字符串资源用于在菜单中作为将相关内容发送到桌面例如创建桌面快捷方式等相关操作的菜单项显示的文本内容告知用户点击该菜单项可把对应的内容发送到桌面方便快速访问 -->
<string name="menu_alert">提醒我</string>
<!-- 定义名为“menu_alert”的字符串资源用于在菜单中作为设置提醒相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行提醒相关设置比如设置某个便签的提醒时间等 -->
<string name="menu_remove_remind">删除提醒</string>
<!-- 定义名为“menu_remove_remind”的字符串资源用于在菜单中作为删除提醒相关操作的菜单项显示的文本内容提示用户点击该菜单项可删除已设置好的提醒取消之前设定的提醒功能 -->
<string name="menu_title_select_folder">选择文件夹</string>
<!-- 定义名为“menu_title_select_folder”的字符串资源用于在执行选择文件夹相关操作时作为提示用户当前操作内容的文本比如在弹出的选择文件夹界面上方显示该文字让用户明确知晓正在进行选择文件夹的动作 -->
<string name="menu_move_parent_folder">上一级文件夹</string>
<!-- 定义名为“menu_move_parent_folder”的字符串资源通常用于在涉及将内容移动到当前所在文件夹的上一级文件夹的操作场景中作为提示用户可进行此操作的文本告知用户点击相应按钮或选项可将文件、便签等内容移动到上一级文件夹中 -->
<string name="info_note_enter_desktop">已添加到桌面</string>
<!-- 定义名为“info_note_enter_desktop”的字符串资源一般是在便签成功添加到桌面例如创建了便签对应的桌面快捷方式向用户显示的提示信息告知用户对应的便签已经添加到桌面了方便用户知晓操作结果 -->
<string name="alert_title_delete">删除</string>
<!-- 定义名为“alert_title_delete”的字符串资源用于在弹出与删除操作相关的提示框时作为提示框的标题文本简洁明了地告知用户当前操作是关于删除相关内容让用户快速了解提示框所关联的操作性质 -->
<string name="alert_message_delete_notes">确认要删除所选的 %d 条便签吗?</string>
<!-- 定义名为“alert_message_delete_notes”的字符串资源从格式“确认要删除所选的 %d 条便签吗?”来看,它带有一个占位符“%d”在用户执行删除多条便签的操作时会根据实际选中要删除的便签数量替换该占位符用于弹出提示框询问用户是否确认执行删除操作以避免误删 -->
<string name="alert_message_delete_note">确认要删除该条便签吗?</string>
<!-- 定义名为“alert_message_delete_note”的字符串资源用于在用户尝试删除单条便签时弹出提示框显示的询问信息询问用户是否确实要删除当前这一条便签获取用户的明确操作意向避免误操作导致便签丢失 -->
<string name="alert_message_delete_folder">确认删除文件夹及所包含的便签吗?</string>
<!-- 定义名为“alert_message_delete_folder”的字符串资源在用户进行删除文件夹操作时该文件夹下通常包含有便签等内容弹出提示框显示的询问文本询问用户是否确认执行删除该文件夹以及里面所有便签的操作需要用户明确回应以保障数据安全 -->
<string name="format_move_notes_to_folder">已将所选 %1$d 条便签移到 %2$s 文件夹</string>
<!-- 定义名为“format_move_notes_to_folder”的字符串资源从格式来看它带有两个占位符%1$d 和 %2$s在成功将选中的便签移动到指定文件夹后会用实际移动便签的数量替换“%1$d”用具体的文件夹名称替换“%2$s”用于显示提示信息告知用户移动操作的具体情况让用户清楚知晓便签的移动结果 -->
<!-- export text -->
<!-- 这是一个注释说明,告知下面的字符串资源是与文本导出相关的一些提示信息 -->
<string name="error_sdcard_unmounted">SD卡被占用不能操作</string>
<!-- 定义名为“error_sdcard_unmounted”的字符串资源当 SD 卡处于被其他程序占用等忙碌状态,导致当前应用无法对其进行操作(例如导出文本到 SD 卡等操作)时,显示该提示信息,告知用户当前 SD 卡不可用,无法进行相应操作 -->
<string name="error_sdcard_export">导出文本时发生错误请检查SD卡</string>
<!-- 定义名为“error_sdcard_export”的字符串资源在执行导出文本到 SD 卡的操作过程中,如果出现错误(可能是 SD 卡本身故障、空间不足等原因),就会显示此提示信息,提示用户去检查 SD 卡的相关情况,以便排查问题所在 -->
<string name="error_note_not_exist">要查看的便签不存在</string>
<!-- 定义名为“error_note_not_exist”的字符串资源当用户尝试查看某个便签但该便签在系统中实际并不存在例如被误删除或者从未创建等情况显示该提示信息告知用户想要查看的便签找不到了操作无法进行 -->
<string name="error_note_empty_for_clock">不能为空便签设置闹钟提醒</string>
<!-- 定义名为“error_note_empty_for_clock”的字符串资源用于在用户试图对没有任何内容的空便签设置闹钟提醒比如设置提醒时间等与时间提醒相关操作显示的提示信息告知用户不能在空白便签上进行此类操作需要先添加便签内容等 -->
<string name="error_note_empty_for_send_to_desktop">不能将空便签发送到桌面</string>
<!-- 定义名为“error_note_empty_for_send_to_desktop”的字符串资源在用户尝试把内容为空的便签发送到桌面例如创建桌面快捷方式显示该提示信息告知用户不可以对空便签执行此操作可能需要先完善便签内容后再进行发送 -->
<string name="success_sdcard_export">导出成功</string>
<!-- 定义名为“success_sdcard_export”的字符串资源在文本成功从应用导出到 SD 卡后,用于显示的提示信息,告知用户导出操作已经顺利完成,让用户知晓操作结果 -->
<string name="failed_sdcard_export">导出失败</string>
<!-- 定义名为“failed_sdcard_export”的字符串资源当文本导出到 SD 卡的操作未能成功完成时,显示该提示信息,简单告知用户导出失败了,不过通常还需要结合其他相关错误提示进一步排查具体原因 -->
<string name="format_exported_file_location">已将文本文件(%1$s)输出至SD卡(%2$s)目录</string>
<!-- 定义名为“format_exported_file_location”的字符串资源从格式来看它带有两个占位符%1$s 和 %2$s在需要告知用户文本导出文件在 SD 卡中的具体存放位置时,会用实际的文件名替换“%1$s”用 SD 卡的具体目录路径替换“%2$s”清晰展示导出文件的所在位置情况 -->
<!-- Sync -->
<!-- 这是一个注释说明告知下面的字符串资源是与便签同步Sync相关的一些提示信息 -->
<string name="ticker_syncing">同步便签...</string>
<!-- 定义名为“ticker_syncing”的字符串资源在便签同步操作开始进行时用于显示的提示信息告知用户当前正在同步便签一般可能会以滚动提示条文字等形式展示让用户知晓操作正在进行中 -->
<string name="ticker_success">同步成功</string>
<!-- 定义名为“ticker_success”的字符串资源在便签同步操作顺利完成后用于显示的提示信息告知用户同步已经成功让用户了解操作结果 -->
<string name="ticker_fail">同步失败</string>
<!-- 定义名为“ticker_fail”的字符串资源当便签同步操作未能成功出现错误时用于显示的提示信息告知用户同步失败了通常还需要结合其他相关错误提示进一步排查具体原因 -->
<string name="ticker_cancel">同步已取消</string>
<!-- 定义名为“ticker_cancel”的字符串资源在便签同步操作被取消比如用户手动点击取消按钮或者因程序异常等情况自动取消用于显示的提示信息告知用户同步操作已经被取消让用户知晓当前状态变化 -->
<string name="success_sync_account">与%1$s同步成功</string>
<!-- 定义名为“success_sync_account”的字符串资源从格式“与%1$s同步成功”来看它带有一个占位符“%1$s”在使用特定账户成功完成便签同步操作后会用实际的账户名称替换该占位符告知用户是与哪个账户同步成功了明确同步相关情况 -->
<string name="error_sync_network">同步失败,请检查网络和帐号设置</string>
<!-- 定义名为“error_sync_network”的字符串资源当便签同步操作失败且原因可能与网络连接不稳定或者账号设置有误例如账号密码错误、账号未授权等情况相关时显示该提示信息提示用户去检查网络以及账号设置情况以排查同步失败的问题所在 -->
<string name="error_sync_internal">同步失败,发生内部错误</string>
<!-- 定义名为“error_sync_internal”的字符串资源在便签同步操作因应用自身内部出现问题比如代码逻辑错误、与服务器交互出现异常等情况导致失败时显示该提示信息告知用户同步失败是由于内部发生了错误不过具体原因可能还需要进一步深入排查分析 -->
<string name="error_sync_cancelled">同步已取消</string>
<!-- 定义名为“error_sync_cancelled”的字符串资源在同步操作被取消例如因系统资源不足等原因导致程序自动取消同步用于显示的提示信息告知用户同步操作已被取消与前面手动取消同步操作后显示的提示信息类似只是触发取消的原因有所不同 -->
<string name="sync_progress_login">登录%1$s...</string>
<!-- 定义名为“sync_progress_login”的字符串资源从格式“登录%1$s...”来看,它带有一个占位符“%1$s”在便签同步操作过程中当正在登录相关同步账户例如登录到云端服务账户用于同步便签会用实际的账户名称替换该占位符告知用户正在登录哪个账户让用户了解同步操作的进展情况 -->
<string name="sync_progress_init_list">正在获取服务器便签列表...</string>
<!-- 定义名为“sync_progress_init_list”的字符串资源在便签同步操作里当正在从服务器获取便签列表比如从云端存储获取便签记录以便和本地便签进行对比同步用于显示的提示信息告知用户当前正在进行获取服务器便签列表的操作体现同步操作的进度状态 -->
<string name="sync_progress_syncing">正在同步本地便签...</string>
<!-- 定义名为“sync_progress_syncing”的字符串资源在便签同步操作进行到将本地便签与服务器端可能是与谷歌相关服务等如 google task的便签进行同步的实际阶段时用于显示的提示信息告知用户当前正在进行具体的便签同步操作步骤让用户知晓同步进展 -->
<!-- Preferences -->
<!-- 这是一个注释说明告知下面的字符串资源是与应用偏好设置Preferences相关的一些提示信息 -->
<string name="preferences_title">设置</string>
<!-- 定义名为“preferences_title”的字符串资源用于在应用的偏好设置界面或者相关菜单中作为显示标题的文本内容告知用户当前进入的是设置相关的功能区域方便用户识别和操作 -->
<string name="preferences_account_title">同步账号</string>
<!-- 定义名为“preferences_account_title”的字符串资源用于在偏好设置中与同步账号相关的设置区域作为显示标题的文本内容提示用户该部分设置是针对同步账号进行操作的比如选择、更改、添加或删除同步账号等 -->
<string name="preferences_account_summary">与google task同步便签记录</string>
<!-- 定义名为“preferences_account_summary”的字符串资源用于在同步账号相关设置区域作为对该设置功能的简要说明文本内容告知用户在这里可以将便签记录与谷歌任务google task进行同步让用户了解该设置选项的作用 -->
<string name="preferences_last_sync_time">上次同步于 %1$s</string>
<!-- 定义名为“preferences_last_sync_time”的字符串资源从格式“上次同步于 %1$s”来看它带有一个占位符“%1$s”通常用于在显示上次便签同步操作的具体时间信息时会用实际的时间数据按照一定格式存储的时间字符串替换该占位符准确告知用户上次同步是什么时候进行的 -->
<string name="preferences_add_account">添加账号</string>
<!-- 定义名为“preferences_add_account”的字符串资源用于在偏好设置中作为添加账户比如添加用于同步便签的新账户相关操作的菜单项或按钮显示的文本内容提示用户点击可进行添加账户的操作 -->
<string name="preferences_menu_change_account">更换账号</string>
<!-- 定义名为“preferences_menu_change_account”的字符串资源用于在偏好设置的菜单中作为更改同步账号相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行更改当前同步账号的操作 -->
<string name="preferences_menu_remove_account">删除账号</string>
<!-- 定义名为“preferences_menu_remove_account”的字符串资源用于在偏好设置的菜单中作为移除同步账号相关操作的菜单项显示的文本内容提示用户点击该菜单项可执行移除已有的同步账号的操作 -->
<string name="preferences_menu_cancel">取消</string>
<!-- 定义名为“preferences_menu_cancel”的字符串资源用于在偏好设置相关操作过程中比如正在添加、更改、移除账户等操作时作为取消当前操作的菜单项或按钮显示的文本内容提示用户点击可取消正在进行的操作回到之前的状态 -->
<string name="preferences_button_sync_immediately">立即同步</string>
<!-- 定义名为“preferences_button_sync_immediately”的字符串资源用于在偏好设置界面或者相关区域作为立即进行同步操作的按钮显示的文本内容提示用户点击该按钮可马上发起便签同步操作无需等待其他定时同步等情况 -->
<string name="preferences_button_sync_cancel">取消同步</string>
<!-- 定义名为“preferences_button_sync_cancel”的字符串资源用于在正在进行同步操作时在偏好设置界面或者相关区域作为取消同步操作的按钮显示的文本内容提示用户点击该按钮可终止当前正在进行的同步操作 -->
<string name="preferences_dialog_change_account_title">当前帐号 %1$s</string>
<!-- 定义名为“preferences_dialog_change_account_title”的字符串资源从格式“当前帐号 %1$s”来看它带有一个占位符“%1$s”在弹出更改账号相关的对话框时会用实际的当前账号名称替换该占位符作为对话框的标题显示明确告知用户当前正在操作的是哪个账号 -->
<string name="preferences_dialog_change_account_warn_msg">如更换同步帐号,过去的帐号同步信息将被清空,再次切换的同时可能会造成数据重复</string>
<!-- 定义名为“preferences_dialog_change_account_warn_msg”的字符串资源用于在弹出更改账号相关对话框时作为显示警告信息的文本内容告知用户如果更换同步账号那么之前该账号相关的同步信息都会被删除并且在后续再次切换账号等操作时有可能会出现数据重复的情况提醒用户谨慎操作 -->
<string name="preferences_dialog_select_account_title">同步便签</string>
<!-- 定义名为“preferences_dialog_select_account_title”的字符串资源用于在弹出选择账号用于同步便签相关的对话框时作为对话框的标题显示的文本内容告知用户当前对话框是用于选择同步便签的账号让用户明确操作意图 -->
<string name="preferences_dialog_select_account_tips">请选择google帐号便签将与该帐号的google task内容同步。</string>
<!-- 定义名为“preferences_dialog_select_account_tips”的字符串资源用于在弹出选择账号相关对话框时作为提示信息的文本内容告知用户需要选择一个谷歌账号并且提示用户选择该账号后本地便签将会与该账号对应的 google task 内容进行同步 -->
<string name="preferences_toast_cannot_change_account">正在同步中,不能修改同步帐号</string>
<!-- 定义名为“preferences_toast_cannot_change_account”的字符串资源用于在便签正在进行同步操作的过程中当用户尝试更改同步账号时以弹出提示框Toast的形式显示该提示信息告知用户由于同步正在进行此时不允许修改同步账号需等待同步完成后再操作 -->
<string name="preferences_toast_success_set_accout">同步帐号已设置为%1$s</string>
<!-- 定义名为“preferences_toast_success_set_accout”的字符串资源从格式“同步帐号已设置为%1$s”来看它带有一个占位符“%1$s”在成功将某个账号设置为同步账号后会用实际的账号名称替换该占位符以弹出提示框Toast的形式告知用户同步账号已经设置成功以及具体设置的是哪个账号 -->
<string name="preferences_bg_random_appear_title">新建便签背景颜色随机</string>
<!-- 定义名为“preferences_bg_random_appear_title”的字符串资源用于在应用中与新建便签相关的设置区域作为一个功能标题显示的文本内容提示用户可以设置新建便签时其背景颜色是否随机出现让用户了解该设置选项的作用 -->
<string name="button_delete">删除</string>
<!-- 定义名为“button_delete”的字符串资源通常用于在界面上作为按钮显示的文本内容提示用户点击该按钮可执行删除相关的操作比如删除某个便签、文件夹或者其他数据项等 -->
<string name="call_record_folder_name">通话便签</string>
<!-- 定义名为“call_record_folder_name”的字符串资源推测是用于表示存放与通话相关记录的便签所在的文件夹名称方便在应用中对这类特定内容的便签进行归类和管理使其更易于识别和查找 -->
<string name="hint_foler_name">请输入名称</string>
<string name="search_label">正在搜索便签</string>
<string name="search_hint">搜索便签</string>
<string name="search_setting_description">便签中的文字</string>
<string name="search">便签</string>
<string name="datetime_dialog_ok">设置</string>
<string name="datetime_dialog_cancel">取消</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item>
</plurals>
</resources>
<!-- 定义名为“hint_foler_name”的字符串资源大概率是用于在需要用户输入文件夹名称的场景下作为提示信息显示给用户告知用户在此处输入相应的

@ -1,23 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources>
<!-- <resources> 是安卓资源文件中的根标签,用于集中定义各类资源,像字符串、颜色、数组等不同类型的资源都可以在这个标签内部进行定义 -->
<string-array name="menu_share_ways">
<!-- 定义一个名为“menu_share_ways”的字符串数组资源从名字可以推测这个数组是用来存放菜单里分享功能相关的可选方式的 -->
<item>短信</item>
<!-- 这是字符串数组中的一个元素,表示一种分享途径,即用户可以通过发送短信的方式来分享相关内容 -->
<item>郵件</item>
<!-- 同样是字符串数组中的元素,此处应该是“邮件”的意思(可能是书写上稍有误,正确写法通常为“邮件”),意味着用户也能够选择通过发送邮件这种方式来分享相关内容 -->
</string-array>
</resources>

@ -1,127 +1,285 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- <resources>标签是安卓资源文件的根标签用于定义各种资源这里引入了两个命名空间“android”用于安卓相关资源属性设置等操作“xliff”常用于国际化文本处理相关场景 -->
<string name="app_name">便簽</string>
<!-- 定义名为“app_name”的字符串资源此资源用于表示整个应用程序的名称在这里被设置为“便簽”表明这是一款与便签功能相关的应用 -->
<string name="app_widget2x2">便簽2x2</string>
<string name="app_widget4x4">便簽4x4</string>
<!-- 定义名为“app_widget2x2”的字符串资源推测是用于描述应用中尺寸为 2x2 的小部件对应的显示名称方便用户识别和区分不同尺寸的小部件这里显示为“便簽2x2” -->
<string name="app_widget4x2">便簽4x4</string>
<!-- 定义名为“app_widget4x4”的字符串资源同理应该是用于表示应用里尺寸为 4x4 的小部件的显示名称起到标识该特定尺寸小部件的作用名称为“便簽4x4” -->
<string name="widget_havenot_content">沒有關聯內容,點擊新建便簽。</string>
<!-- 定义名为“widget_havenot_content”的字符串资源从其内容来看可能是在小部件上显示的提示信息当没有与之关联的便签内容时提示用户点击可以去创建新的便签 -->
<string name="widget_under_visit_mode">訪客模式下,便籤內容不可見</string>
<!-- 定义名为“widget_under_visit_mode”的字符串资源推测是用于小部件处于访客模式下时向用户展示的提示文本告知用户在此模式下无法查看便签的具体内容 -->
<string name="notelist_string_info">...</string>
<!-- 定义名为“notelist_string_info”的字符串资源仅从“...”不太明确其确切用途,不过可能是在便签列表相关界面中用于展示某种简略信息或者占位性质的文本内容 -->
<string name="notelist_menu_new">新建便簽</string>
<!-- 定义名为“notelist_menu_new”的字符串资源用于在便签列表对应的菜单里作为创建新便签这一菜单项所显示的文字内容明确提示用户点击该菜单项可进行添加便签的操作 -->
<string name="delete_remind_time_message">成功刪除提醒</string>
<!-- 定义名为“delete_remind_time_message”的字符串资源大概率是在成功删除便签的提醒时间后向用户展示的提示消息告知用户相应的删除提醒操作已经顺利完成 -->
<string name="set_remind_time_message">創建提醒</string>
<!-- 定义名为“set_remind_time_message”的字符串资源应该是在执行设置便签提醒时间相关操作时向用户呈现的提示文本表明当前正在进行设置提醒的动作 -->
<string name="note_alert_expired">已過期</string>
<!-- 定义名为“note_alert_expired”的字符串资源可能是在便签的提醒功能过期时用于显示给用户的提示文字比如在提醒相关界面展示“已過期”字样让用户知晓提醒已过期 -->
<string name="format_date_ymd">yyyyMMdd</string>
<!-- 定义名为“format_date_ymd”的字符串资源这是定义了一种日期格式采用“yyyyMMdd”这种形式常用于对日期数据进行格式化输出比如按照此格式来存储、展示便签相关的日期信息等 -->
<string name="format_datetime_mdhm">MM月dd日 kk:mm</string>
<!-- 定义名为“format_datetime_mdhm”的字符串资源同样是一种日期时间格式的定义按照“MM月dd日 kk:mm”的格式来规范日期时间数据的展示方便在应用中统一处理和显示这类信息 -->
<string name="notealert_ok">知道了</string>
<!-- 定义名为“notealert_ok”的字符串资源推测是在便签相关提醒的操作场景里当用户确认知晓或者完成某个操作后对应的按钮或者提示文本会显示“知道了”表示用户明白了、已确认的意思 -->
<string name="notealert_enter">查看</string>
<!-- 定义名为“notealert_enter”的字符串资源可能是在便签提醒相关的交互中用于引导用户点击去查看具体便签内容的提示文字提示用户点击相应区域可以查看详情 -->
<string name="note_link_tel">呼叫電話</string>
<!-- 定义名为“note_link_tel”的字符串资源大概率是当便签中包含电话号码链接等情况时在相应位置显示的操作提示文本告知用户点击此处可以进行拨打电话的操作 -->
<string name="note_link_email">發送郵件</string>
<!-- 定义名为“note_link_email”的字符串资源应该是在便签里有邮件链接相关内容时显示给用户的提示文字提示用户点击可触发发送邮件的操作 -->
<string name="note_link_web">浏覽網頁</string>
<!-- 定义名为“note_link_web”的字符串资源可能是在便签中存在网页链接的情况下向用户展示的提示文本告知用户点击该链接可以浏览对应的网页内容 -->
<string name="note_link_other">打開地圖</string>
<!-- 定义名为“note_link_other”的字符串资源推测是在便签涉及到地图相关链接等情况时显示的提示文字提示用户点击可以打开地图应用进行相关操作 -->
<string name="format_move_notes_to_folder">已將所選 %1$d 便籤移到 %2$s 文件夾</string>
<!-- 定义名为“format_move_notes_to_folder”的字符串资源从格式来看它带有两个占位符%1$d 和 %2$s在成功将选中的便签移动到指定文件夹后会用实际移动便签的数量替换“%1$d”用具体的文件夹名称替换“%2$s”用于显示提示信息告知用户移动操作的具体情况让用户清楚知晓便签的移动结果 -->
<!-- note list string -->
<!-- 这是一个注释说明,告知下面的字符串资源是与便签列表相关的一些字符串内容 -->
<string name="menu_create_folder">新建文件夾</string>
<!-- 定义名为“menu_create_folder”的字符串资源用于在应用的菜单中作为创建新文件夹这一菜单项所显示的文字内容清晰地提示用户点击该菜单项可进行新建文件夹的操作 -->
<string name="menu_export_text">導出文本</string>
<!-- 定义名为“menu_export_text”的字符串资源用于在菜单里作为导出文本这个菜单项显示的文本内容告知用户点击该菜单项就能执行文本导出的相关操作 -->
<string name="menu_sync">同步</string>
<!-- 定义名为“menu_sync”的字符串资源用于在菜单中作为同步相关操作的菜单项所显示的文字内容提示用户点击它可以发起数据同步的操作 -->
<string name="menu_sync_cancel">取消同步</string>
<!-- 定义名为“menu_sync_cancel”的字符串资源用于在正在进行同步操作的过程中在菜单里作为取消同步这一菜单项显示的文本内容方便用户点击来终止同步操作 -->
<string name="menu_setting">設置</string>
<!-- 定义名为“menu_setting”的字符串资源用于在菜单中作为进入设置界面或者执行设置相关操作的菜单项所显示的文字内容引导用户点击该菜单项以进入设置相关功能区域 -->
<string name="menu_search">搜尋</string>
<!-- 定义名为“menu_search”的字符串资源用于在菜单中作为搜索相关操作的菜单项显示的文本内容提示用户点击该菜单项可以进行搜索相关的操作比如查找便签内容等 -->
<string name="menu_delete">刪除</string>
<!-- 定义名为“menu_delete”的字符串资源用于在菜单中作为删除相关操作的菜单项显示的文本内容告知用户点击该菜单项可以执行删除相应内容的操作例如删除便签、文件夹等 -->
<string name="menu_move">移動到文件夾</string>
<!-- 定义名为“menu_move”的字符串资源用于在菜单中作为将相关内容移动到文件夹这一菜单项显示的文本内容提示用户点击该菜单项可进行把某些内容移动到指定文件夹的操作 -->
<string name="menu_select_title">選中了 %d 項</string>
<!-- 定义名为“menu_select_title”的字符串资源从格式“選中了 %d 項”来看,它是用于在有多个项目被选中后,展示选中数量相关提示信息的文本,会根据实际选中的项目数量替换“%d”来准确告知用户选中情况 -->
<string name="menu_select_none">沒有選中項,操作無效</string>
<!-- 定义名为“menu_select_none”的字符串资源用于在没有任何项目被选中的情况下向用户显示的提示文本告知用户由于没有选中内容当前要执行的操作是无效的 -->
<string name="menu_select_all">全選</string>
<!-- 定义名为“menu_select_all”的字符串资源用于在菜单中作为全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以一次性选中所有相关的项目 -->
<string name="menu_deselect_all">取消全選</string>
<!-- 定义名为“menu_deselect_all”的字符串资源用于在菜单中作为取消全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以将已经全选的状态取消即取消选中所有项目 -->
<string name="menu_font_size">文字大小</string>
<!-- 定义名为“menu_font_size”的字符串资源用于在菜单中作为调整文字大小相关操作的菜单项显示的文本内容引导用户点击该菜单项来进行文字大小相关的设置操作 -->
<string name="menu_font_small"></string>
<!-- 定义名为“menu_font_small”的字符串资源可能是在文字大小设置的相关菜单选项里作为表示小字号这一选项所显示的文字内容便于用户选择小字体来显示文本 -->
<string name="menu_font_normal">正常</string>
<!-- 定义名为“menu_font_normal”的字符串资源推测是在文字大小设置相关的菜单中作为表示正常字号这一选项显示的文字内容方便用户将文本字体设置为正常大小 -->
<string name="menu_font_large"></string>
<!-- 定义名为“menu_font_large”的字符串资源应该是在文字大小设置相关的菜单里作为表示大字号这一选项所展示的文字内容让用户可以选择大字体来显示文本 -->
<string name="menu_font_super">超大</string>
<!-- 定义名为“menu_font_super”的字符串资源可能是在文字大小设置相关的菜单中作为表示超大字号这一选项显示的文字内容供用户选择超大字体来展示文本 -->
<string name="menu_list_mode">進入清單模式</string>
<!-- 定义名为“menu_list_mode”的字符串资源用于在菜单中作为进入清单模式比如待办事项清单等类似的模式相关操作的菜单项显示的文本内容提示用户点击该菜单项可进入对应的清单模式方便对相关内容进行清单式管理 -->
<string name="menu_normal_mode">退出清單模式</string>
<!-- 定义名为“menu_normal_mode”的字符串资源用于在菜单中作为离开清单模式例如待办事项清单等相关操作的菜单项显示的文本内容提示用户点击该菜单项可退出当前所处的清单模式恢复到常规显示状态 -->
<string name="menu_folder_view">查看文件夾</string>
<!-- 定义名为“menu_folder_view”的字符串资源用于在菜单里作为查看文件夹相关操作的菜单项所显示的文本内容告知用户点击该菜单项后可以查看指定文件夹内的内容比如查看文件夹里包含的便签等信息 -->
<string name="menu_folder_delete">刪除文件夾</string>
<!-- 定义名为“menu_folder_delete”的字符串资源用于在菜单中作为删除文件夹相关操作的菜单项显示的文本内容提示用户点击该菜单项会执行删除文件夹的操作通常可能还会有相应确认提示以防误删 -->
<string name="menu_folder_change_name">修改文件夾名稱</string>
<!-- 定义名为“menu_folder_change_name”的字符串资源用于在菜单中作为更改文件夹名称相关操作的菜单项显示的文本内容引导用户点击该菜单项来对已有的文件夹重新命名使其名称更符合需求或便于识别 -->
<string name="folder_exist">文件夾 %1$s 已存在,請重新命名</string>
<!-- 定义名为“folder_exist”的字符串资源从格式“文件夾 %1$s 已存在,請重新命名”来看,它是一种带有占位符(%1$s的提示文本通常在创建文件夹时如果要创建的文件夹名称已存在就会用实际的文件夹名称替换占位符显示该提示信息告知用户需要重命名后再进行操作 -->
<string name="menu_share">分享</string>
<!-- 定义名为“menu_share”的字符串资源用于在菜单中作为分享相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行分享操作比如分享便签内容到其他应用或者平台等 -->
<string name="menu_send_to_desktop">發送到桌面</string>
<!-- 定义名为“menu_send_to_desktop”的字符串资源用于在菜单中作为将相关内容发送到桌面例如创建桌面快捷方式等相关操作的菜单项显示的文本内容告知用户点击该菜单项可把对应的内容发送到桌面方便快速访问 -->
<string name="menu_alert">提醒我</string>
<!-- 定义名为“menu_alert”的字符串资源用于在菜单中作为设置提醒相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行提醒相关设置比如设置某个便签的提醒时间等 -->
<string name="menu_remove_remind">刪除提醒</string>
// 定义了一个名为"menu_remove_remind"的字符串资源,其显示的文本内容为"删除提醒",可能用于菜单选项等场景中表示删除相关提醒的操作提示。
<string name="menu_title_select_folder">選擇文件夾</string>
// 定义名为"menu_title_select_folder"的字符串资源,文本内容是"选择文件夹",推测是用于菜单标题中,提示用户进行文件夹选择的相关操作。
<string name="menu_move_parent_folder">上一級文件夾</string>
// 此字符串资源名为"menu_move_parent_folder",内容为"上一级文件夹",通常用于表示在文件管理相关操作中,可执行返回上一级文件夹的操作提示。
<string name="info_note_enter_desktop">已添加到桌面</string>
// 名为"info_note_enter_desktop"的字符串资源,文本表示某个笔记(便签)已经被添加到桌面了,用于给用户提示相应操作结果。
<string name="alert_title_delete">刪除</string>
// 定义的"alert_title_delete"字符串资源,文本是"删除",可能作为弹出提示框(如确认删除操作时)的标题,让用户明确操作类型是删除相关。
<string name="alert_message_delete_notes">确认要刪除所選的 %d 條便籤嗎?</string>
// 名为"alert_message_delete_notes"的字符串资源,包含了格式化占位符%d用于在需要确认删除多条便签时向用户显示询问是否删除所选具体数量通过占位符替换实际数字的便签的提示消息。
<string name="alert_message_delete_note">确认要删除該條便籤嗎?</string>
// 定义了"alert_message_delete_note"字符串资源,用于提示用户确认是否删除某一条特定便签,向用户发起删除单个便签的确认询问。
<string name="alert_message_delete_folder">確認刪除檔夾及所包含的便簽嗎?</string>
// 这个名为"alert_message_delete_folder"的字符串资源,用于在要删除文件夹及其包含的所有便签时,弹出提示框询问用户是否确认执行该删除操作。
<string name="error_sdcard_unmounted">SD卡被佔用不能操作</string>
// 定义的"error_sdcard_unmounted"字符串资源文本表示SD卡处于被占用状态无法进行相关操作用于向用户反馈SD卡操作受阻的原因。
<string name="error_sdcard_export">導出TXT時發生錯誤請檢查SD卡</string>
// 名为"error_sdcard_export"的字符串资源用于在导出TXT文件出现错误时提示用户去检查SD卡告知用户错误与SD卡相关并引导排查。
<string name="error_note_not_exist">要查看的便籤不存在</string>
// 定义了"error_note_not_exist"字符串资源,当用户尝试查看某个便签但该便签实际不存在时,用于向用户反馈相应的错误提示信息。
<string name="error_note_empty_for_clock">不能爲空便籤設置鬧鐘提醒</string>
// 此字符串资源名为"error_note_empty_for_clock",用于提示用户不能给内容为空的便签设置闹钟提醒,告知用户操作不符合要求的原因。
<string name="error_note_empty_for_send_to_desktop">不能將空便籤發送到桌面</string>
// 名为"error_note_empty_for_send_to_desktop"的字符串资源,提示用户不可以将内容为空的便签发送到桌面,说明相应操作限制情况。
<string name="success_sdcard_export">導出成功</string>
// 定义的"success_sdcard_export"字符串资源用于在成功导出SD卡相关文件比如TXT等向用户反馈操作成功的提示信息。
<string name="failed_sdcard_export">導出失敗</string>
// 名为"failed_sdcard_export"的字符串资源在导出SD卡相关文件失败时向用户显示操作失败的提示消息。
<string name="format_exported_file_location">已將文本文件(%1$s)導出至SD(%2$s)目錄</string>
// 定义了"format_exported_file_location"字符串资源,包含了格式化占位符%1$s和%2$s用于按照实际的文件名和SD卡目录路径等信息进行替换展示具体的导出文件位置情况给用户。
<!-- Sync -->
// 以下是一组和同步Sync相关的字符串资源定义这里的注释"Sync"可能是用于分组标识,方便代码阅读和维护,表明下面的字符串都和同步功能有关。
<string name="ticker_syncing">同步便簽...</string>
// 名为"ticker_syncing"的字符串资源,用于在同步便签过程中向用户展示正在同步的提示信息,告知用户当前正在执行同步操作。
<string name="ticker_success">同步成功</string>
// 定义的"ticker_success"字符串资源,在便签同步操作成功完成后,向用户反馈同步成功的提示消息。
<string name="ticker_fail">同步失敗</string>
// 名为"ticker_fail"的字符串资源,当便签同步操作失败时,用于向用户显示同步失败的提示内容。
<string name="ticker_cancel">同步已取消</string>
// 此字符串资源名为"ticker_cancel",用于在同步操作被取消后,告知用户同步已取消这一情况。
<string name="success_sync_account">與%1$s同步成功</string>
// 定义了"success_sync_account"字符串资源,包含格式化占位符%1$s用于在和特定账号实际账号名称替换占位符同步成功时向用户展示具体与哪个账号同步成功的提示信息。
<string name="error_sync_network">同步失敗,請檢查網絡和帳號設置</string>
// 名为"error_sync_network"的字符串资源,在同步失败且原因可能和网络、账号设置相关时,提示用户去检查网络连接以及账号相关设置情况。
<string name="error_sync_internal">同步失敗,發生內部錯誤</string>
// 定义的"error_sync_internal"字符串资源,用于在同步失败是由于内部错误导致时,向用户反馈同步失败是因为内部出现问题这一情况。
<string name="error_sync_cancelled">同步已取消</string>
// 此字符串资源和前面的"ticker_cancel"类似,用于在同步被取消后,向用户提示同步已取消的消息,可能在不同的代码逻辑位置使用来反馈这一情况。
<string name="sync_progress_login">登陸%1$s...</string>
// 名为"sync_progress_login"的字符串资源,包含格式化占位符%1$s用于在同步过程中登录某个账号具体账号名替换占位符向用户展示正在登录的提示信息。
<string name="sync_progress_init_list">正在獲取服務器便籤列表...</string>
// 定义了"sync_progress_init_list"字符串资源,用于在同步操作中,正在从服务器获取便签列表时,向用户反馈当前正在进行的操作阶段情况。
<string name="sync_progress_syncing">正在同步本地便籤...</string>
// 此字符串资源名为"sync_progress_syncing",用于向用户提示当前正在进行本地便签和服务器等之间的同步操作,告知用户同步操作的具体阶段。
<!-- Preferences -->
// 以下是一组和偏好设置Preferences相关的字符串资源定义注释"Preferences"用于分组标识,表明下面的字符串都与应用的偏好设置功能相关。
<string name="preferences_title">設置</string>
// 定义名为"preferences_title"的字符串资源,文本内容为"设置",可能用于作为整个应用设置界面的标题显示。
<string name="preferences_account_title">同步賬號</string>
// 名为"preferences_account_title"的字符串资源,文本表示"同步账号",推测是在设置界面中用于标识与同步账号相关设置板块的标题。
<string name="preferences_account_summary">与google task同步便簽記錄</string>
// 定义了"preferences_account_summary"字符串资源用于简要说明同步账号相关功能即告知用户通过该设置可以和google task进行便签记录的同步操作。
<string name="preferences_last_sync_time">上次同步于 %1$s</string>
// 此字符串资源名为"preferences_last_sync_time",包含格式化占位符%1$s用于显示上次同步操作具体发生的时间实际时间替换占位符方便用户了解同步历史情况。
<string name="preferences_add_account">添加賬號</string>
// 定义的"preferences_add_account"字符串资源,文本内容为"添加账号"用于在设置界面中提示用户可执行添加账号的操作比如添加用于同步的google账号等。
<string name="preferences_menu_change_account">更換賬號</string>
// 名为"preferences_menu_change_account"的字符串资源,提示用户在相关菜单中可以进行更换同步账号的操作,告知用户存在此功能选项。
<string name="preferences_menu_remove_account">刪除賬號</string>
// 定义了"preferences_menu_remove_account"字符串资源,用于提示用户在相应菜单中可执行删除账号的操作,比如删除已添加的用于同步的账号等。
<string name="preferences_menu_cancel">取消</string>
// 此字符串资源名为"preferences_menu_cancel",在相关操作场景下提示用户可以执行取消操作,比如取消正在进行的设置更改等操作。
<string name="preferences_button_sync_immediately">立即同步</string>
// 定义的"preferences_button_sync_immediately"字符串资源,文本为"立即同步",通常用于设置界面中的按钮显示文本,提示用户点击该按钮可马上触发同步操作。
<string name="preferences_button_sync_cancel">取消同步</string>
// 名为"preferences_button_sync_cancel"的字符串资源,用于设置界面中按钮显示文本,提示用户点击该按钮可取消正在进行的同步操作。
<string name="preferences_dialog_change_account_title">當前帳號 %1$s</string>
// 定义了"preferences_dialog_change_account_title"字符串资源,包含格式化占位符%1$s用于在弹出对话框中显示当前账号的具体名称实际账号名替换占位符比如在更换账号等相关操作的对话框中展示当前账号信息。
<string name="preferences_dialog_change_account_warn_msg">如更換同步帳號,過去的帳號同步信息將被清空,再次切換的同時可能會造成數據重復</string>
// 此字符串资源名为"preferences_dialog_change_account_warn_msg",用于在用户进行更换同步账号操作时,弹出提示框向用户警告更换账号会导致过去账号的同步信息被清空,以及再次切换账号可能出现数据重复的情况,提醒用户谨慎操作。
<string name="preferences_dialog_select_account_title">同步便簽</string>
// 定义的"preferences_dialog_select_account_title"字符串资源用于作为选择账号比如选择用于同步便签的google账号相关对话框的标题提示用户该对话框的用途是进行同步便签账号相关操作。
<string name="preferences_dialog_select_account_tips">請選擇google帳號便簽將與該帳號的google task內容同步。</string>
// 名为"preferences_dialog_select_account_tips"的字符串资源在选择账号的对话框中向用户提示具体操作要求即让用户选择google账号以便后续便签能和该账号对应的google task内容进行同步。
<string name="preferences_toast_cannot_change_account">正在同步中,不能修改同步帳號</string>
// 定义了"preferences_toast_cannot_change_account"字符串资源,用于在同步操作正在进行时,若用户尝试修改同步账号,弹出提示框告知用户当前不能进行修改账号操作,限制用户的不当操作。
<string name="preferences_toast_success_set_accout">同步帳號已設置為%1$s</string>
// 此字符串资源名为"preferences_toast_success_set_accout",包含格式化占位符%1$s用于在成功设置同步账号后向用户展示具体设置成了哪个账号实际账号名替换占位符的提示信息。
<string name="preferences_bg_random_appear_title">新建便籤背景顏色隨機</string>
// 定义的"preferences_bg_random_appear_title"字符串资源,文本表示新建便签时背景颜色会随机出现,用于在相关设置界面等地方提示用户有这样一个功能特性。
<string name="button_delete">刪除</string>
// 定义了一个名为"button_delete"的字符串资源,其文本内容为"删除",通常会被用作按钮上显示的文字,提示用户点击该按钮可执行删除相关的操作。
<string name="call_record_folder_name">通話便籤</string>
// 名为"call_record_folder_name"的字符串资源,文本表示"通话便签",可能用于标识与通话记录相关的便签所在文件夹的名称,方便用户知晓其用途和内容关联。
<string name="hint_foler_name">請輸入名稱</string>
// 此字符串资源名为"hint_foler_name",文本内容是"请输入名称",一般用于提示用户在相应的输入框等地方输入名称,比如创建文件夹或者给某个项目命名时引导用户操作。
<string name="search_label">正在搜索便籤</string>
// 定义了"search_label"字符串资源,文本显示为"正在搜索便签",常用来在界面上向用户反馈当前正在进行便签搜索这一操作状态,让用户知晓系统正在执行的任务。
<string name="search_hint">搜索便籤</string>
// 名为"search_hint"的字符串资源,内容是"搜索便签",多用于作为输入框的提示文字,提示用户在此处可以输入相关内容来进行便签的搜索操作。
<string name="search_setting_description">便籤中的文字</string>
// 定义的"search_setting_description"字符串资源,文本表明"便签中的文字",推测是用于对搜索设置相关功能进行描述,比如说明搜索是基于便签内包含的文字内容来进行的情况等。
<string name="search">便籤</string>
// 此字符串资源名为"search",文本为"便签",可能在不同的搜索相关场景下使用,比如作为菜单选项、功能名称等体现与便签搜索相关的概念。
<string name="datetime_dialog_ok">設置</string>
// 名为"datetime_dialog_ok"的字符串资源,文本是"设置",通常会用于日期时间相关对话框中的确认按钮文字显示,提示用户点击该按钮可进行相应的时间等设置操作。
<string name="datetime_dialog_cancel">取消</string>
// 定义了"datetime_dialog_cancel"字符串资源,文本为"取消",多用于日期时间相关对话框中的取消按钮文字显示,方便用户在不想进行设置等操作时点击取消当前操作流程。
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 條符合”<xliff:g id="SEARCH">%2$s</xliff:g>“的搜尋結果</item>
</plurals>
</resources>
// 定义了一个名为"search_results_title"的复数形式plurals的字符串资源。
// 它里面的<item>元素用于根据数量情况来展示不同的文本内容这里quantity="other"表示在其他数量(非特定单一数量情况)时的显示规则。
// 其中包含了两个格式化占位符<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="SEARCH">%2$s</xliff:g>,分别用于替换实际的符合结果数量以及搜索的具体内容,整体用于向用户展示搜索便签后得到的符合特定搜索条件的结果数量及相应搜索内容的提示信息。

@ -1,31 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources>
<!-- 这是一个用于定义各种资源的根标签,在安卓开发中,像字符串、数组、颜色等资源都可以在这里面进行定义 -->
<!-- Backup format -->
<!-- 此处是一个注释,用于说明下面定义的资源数组与备份格式相关,方便开发者理解其用途 -->
<string-array name="format_for_exported_note">
<!-- 定义一个名为“format_for_exported_note”的字符串数组从命名推测可能是用于格式化导出笔记时相关信息的格式 -->
<item>-%s</item> <!-- format_folder_name -->
<!-- 数组中的一个元素,这里是一个字符串格式的占位符(%s从注释来看可能是用于格式化文件夹名称相关信息具体内容会在后续代码中替换该占位符 -->
<item>--%s</item> <!-- format_folder_note_date -->
<!-- 数组中的另一个元素,同样是字符串格式占位符,推测是用于格式化文件夹内笔记日期相关信息 -->
<item>--%s</item> <!-- format_note_date -->
<!-- 数组中的元素,用于格式化笔记本身日期相关信息的占位符 -->
<item>--%s</item> <!-- format_note_content -->
<!-- 数组中的元素,用于格式化笔记内容相关信息的占位符 -->
</string-array>
<string-array name="menu_share_ways">
<!-- 定义一个名为“menu_share_ways”的字符串数组从命名推测是用于表示分享菜单中可选择的分享方式相关信息 -->
<item>Messaging</item>
<!-- 数组中的一个元素代表一种分享方式这里是“Messaging”消息应用意味着用户可以通过消息应用来分享相关内容 -->
<item>Email</item>
<!-- 数组中的另一个元素,代表另一种分享方式,即通过电子邮件来分享相关内容 -->
</string-array>
</resources>

@ -1,20 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources>
<color name="user_query_highlight">#335b5b5b</color>
</resources>
<!-- <resources>标签是安卓资源文件中的根标签,用于定义各种类型的资源,比如字符串、颜色、数组等资源都可以放在这里面定义 -->
<color name="user_query_highlight">
<!-- 定义一个名为“user_query_highlight”的颜色资源从命名推测可能是用于突出显示用户查询相关内容时的颜色设定 -->
#335b5b5b
<!-- 具体的颜色值,这里使用十六进制格式来表示颜色,格式为 #AARRGGBB其中 AA 表示透明度RR、GG、BB 分别表示红、绿、蓝三种颜色通道的值),在这个例子中,透明度为 51十六进制的 33红色、绿色、蓝色通道的值均为 91十六进制的 5b整体定义了一种偏灰色且带有一定透明度的颜色 -->
</color>
</resources>

@ -1,24 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources>
<dimen name="text_font_size_super">33sp</dimen>
<dimen name="text_font_size_large">26sp</dimen>
<dimen name="text_font_size_medium">20sp</dimen>
<dimen name="text_font_size_normal">17sp</dimen>
<dimen name="text_font_size_small">14sp</dimen>
<!-- <resources>标签是安卓资源文件中的根标签,用于定义各种类型的资源,像尺寸、字符串、颜色等不同类型资源都可以在此标签内进行定义 -->
<dimen name="text_font_size_super">
<!-- 定义一个名为“text_font_size_super”的尺寸资源从命名推测是用于设置文本字体超级大的字号尺寸 -->
33sp
<!-- 具体的尺寸值这里使用“sp”可缩放像素作为单位意味着该字号大小会根据用户在设备上设置的字体缩放比例进行相应的缩放此处字号大小设定为 33 可缩放像素 -->
</dimen>
<dimen name="text_font_size_large">
<!-- 定义一个名为“text_font_size_large”的尺寸资源推测是用于设置文本字体较大的字号尺寸 -->
26sp
<!-- 具体的尺寸值为 26 可缩放像素,同样会随用户设备字体缩放设置而变化 -->
</dimen>
<dimen name="text_font_size_medium">
<!-- 定义一个名为“text_font_size_medium”的尺寸资源通常可用于设置文本字体中等大小的字号尺寸 -->
20sp
<!-- 对应的尺寸值是 20 可缩放像素,其字号大小会根据设备相关设置动态调整 -->
</dimen>
<dimen name="text_font_size_normal">
<!-- 定义一个名为“text_font_size_normal”的尺寸资源大概率是用于设置文本字体常规大小的字号尺寸 -->
17sp
<!-- 具体字号大小设定为 17 可缩放像素,会依据设备的字体缩放配置进行相应改变 -->
</dimen>
<dimen name="text_font_size_small">
<!-- 定义一个名为“text_font_size_small”的尺寸资源应该是用于设置文本字体较小的字号尺寸 -->
14sp
<!-- 尺寸值为 14 可缩放像素,其大小可随用户设备字体缩放情况而变动 -->
</dimen>
</resources>

@ -1,135 +1,302 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明表明该 XML 文件遵循的版本是 1.0,使用的编码格式是 utf-8这是 XML 文件开头的标准标识,用于告知解析器按照此版本和编码规则来正确解析该文件 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
此处是版权相关声明,说明代码所属的开源社区是 The MiCode Open Source Community版权时间范围在 2010 - 2011 年。
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
表示此文件遵循 Apache License 2.0 开源协议进行授权许可,意味着若要使用该文件,必须遵守该协议的相关规定。
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
告知可以通过访问网址http://www.apache.org/licenses/LICENSE-2.0)获取 Apache License 2.0 协议的详细内容。
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
说明依据该协议分发软件时,软件是以“原样”的状态进行分发,不会附带额外的保证等情况。
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
强调该代码不存在任何明示或暗示的保证以及相关条件,使用者需自行承担使用代码带来的风险。
See the License for the specific language governing permissions and
limitations under the License.
-->
提示查看协议内容,以了解关于权限及限制方面的详细规定。 -->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- <resources>是安卓资源文件的根标签用于定义各类资源这里引入了两个命名空间“android”用于安卓相关的资源属性设置等操作“xliff”常用于国际化文本处理相关场景 -->
<string name="app_name">Notes</string>
<!-- 定义一个名为“app_name”的字符串资源该资源大概率用于表示整个应用程序的名称在这里被设置为“Notes”很可能是一款笔记类应用的名称显示 -->
<string name="app_widget2x2">Notes 2x2</string>
<!-- 定义名为“app_widget2x2”的字符串资源推测是用于描述应用中尺寸为 2x2 的小部件对应的显示名称,方便用户识别和区分不同尺寸的小部件 -->
<string name="app_widget4x4">Notes 4x4</string>
<!-- 定义名为“app_widget4x4”的字符串资源同理应该是用于表示应用里尺寸为 4x4 的小部件的显示名称,起到标识该特定尺寸小部件的作用 -->
<string name="widget_havenot_content">No associated note found, click to create associated note.</string>
<!-- 定义名为“widget_havenot_content”的字符串资源从其内容来看可能是在小部件上显示的提示信息当没有与之关联的笔记时提示用户点击可以去创建关联的笔记 -->
<string name="widget_under_visit_mode">Privacy modecan not see note content</string>
<!-- 定义名为“widget_under_visit_mode”的字符串资源推测是用于小部件处于隐私访问模式下时向用户展示的提示文本告知用户在此模式下无法查看笔记的具体内容 -->
<string name="notelist_string_info">...</string>
<!-- 定义名为“notelist_string_info”的字符串资源仅从“...”不太明确其确切用途,不过可能是在笔记列表相关界面中用于展示某种简略信息或者占位性质的文本内容 -->
<string name="notelist_menu_new">Add note</string>
<!-- 定义名为“notelist_menu_new”的字符串资源用于在笔记列表对应的菜单里作为创建新笔记这一菜单项所显示的文字内容明确提示用户点击该菜单项可进行添加笔记的操作 -->
<string name="delete_remind_time_message">Delete reminder successfully</string>
<!-- 定义名为“delete_remind_time_message”的字符串资源大概率是在成功删除提醒时间后向用户展示的提示消息告知用户相应的删除提醒操作已经顺利完成 -->
<string name="set_remind_time_message">Set reminder</string>
<!-- 定义名为“set_remind_time_message”的字符串资源应该是在执行设置提醒时间相关操作时向用户呈现的提示文本表明当前正在进行设置提醒的动作 -->
<string name="note_alert_expired">Expired</string>
<!-- 定义名为“note_alert_expired”的字符串资源可能是在笔记的提醒功能过期时用于显示给用户的提示文字比如在提醒相关界面展示“Expired”字样让用户知晓提醒已过期 -->
<string name="format_date_ymd">yyyyMMdd</string>
<!-- 定义名为“format_date_ymd”的字符串资源这是定义了一种日期格式采用“yyyyMMdd”这种形式常用于对日期数据进行格式化输出比如按照此格式来存储、展示笔记相关的日期信息等 -->
<string name="format_datetime_mdhm">MMMd kk:mm</string>
<!-- 定义名为“format_datetime_mdhm”的字符串资源同样是一种日期时间格式的定义按照“MMMd kk:mm”的格式来规范日期时间数据的展示方便在应用中统一处理和显示这类信息 -->
<string name="notealert_ok">Got it</string>
<!-- 定义名为“notealert_ok”的字符串资源推测是在笔记相关提醒的操作场景里当用户确认知晓或者完成某个操作后对应的按钮或者提示文本会显示“Got it”表示用户明白了、已确认的意思 -->
<string name="notealert_enter">Take a look</string>
<!-- 定义名为“notealert_enter”的字符串资源可能是在笔记提醒相关的交互中用于引导用户点击去查看具体笔记内容的提示文字提示用户点击相应区域可以查看详情 -->
<string name="note_link_tel">Call</string>
<!-- 定义名为“note_link_tel”的字符串资源大概率是当笔记中包含电话号码链接等情况时在相应位置显示的操作提示文本告知用户点击此处可以进行拨打电话的操作 -->
<string name="note_link_email">Send email</string>
<!-- 定义名为“note_link_email”的字符串资源应该是在笔记里有邮件链接相关内容时显示给用户的提示文字提示用户点击可触发发送邮件的操作 -->
<string name="note_link_web">Browse web</string>
<!-- 定义名为“note_link_web”的字符串资源可能是在笔记中存在网页链接的情况下向用户展示的提示文本告知用户点击该链接可以浏览对应的网页内容 -->
<string name="note_link_other">Open map</string>
<!-- 定义名为“note_link_other”的字符串资源推测是在笔记涉及到地图相关链接等情况时显示的提示文字提示用户点击可以打开地图应用进行相关操作 -->
<!-- Text export file information -->
<!-- 这是一个注释说明,告知下面的字符串资源是与文本导出文件相关的一些信息 -->
<string name="file_path">/MIUI/notes/</string>
<!-- 定义名为“file_path”的字符串资源此资源用于指定文本导出文件在设备中的存储路径这里设定的路径为“/MIUI/notes/”,也就是导出的文件会存放在该目录下 -->
<string name="file_name_txt_format">notes_%s.txt</string>
<!-- 定义名为“file_name_txt_format”的字符串资源它规定了文本导出文件的文件名格式其中“%s”是一个占位符后续会根据具体情况替换为相应内容以此生成实际的文件名整体格式为“notes_具体内容.txt” -->
<!-- notes list string -->
<!-- 注释说明下面的字符串资源是和笔记列表相关的字符串内容 -->
<string name="format_folder_files_count">(%d)</string>
<!-- 定义名为“format_folder_files_count”的字符串资源从格式“(%d)”来看,它是一种占位符形式的字符串,通常用于在展示文件夹内文件数量相关信息时,会将实际的文件数量替换“%d”从而准确显示数量情况 -->
<string name="menu_create_folder">New Folder</string>
<!-- 定义名为“menu_create_folder”的字符串资源用于在应用的菜单中作为创建新文件夹这一菜单项所显示的文字内容清晰地提示用户点击该菜单项可进行新建文件夹的操作 -->
<string name="menu_export_text">Export text</string>
<!-- 定义名为“menu_export_text”的字符串资源用于在菜单里作为导出文本这个菜单项显示的文本内容告知用户点击该菜单项就能执行文本导出的相关操作 -->
<string name="menu_sync">Sync</string>
<!-- 定义名为“menu_sync”的字符串资源用于在菜单中作为同步相关操作的菜单项所显示的文字内容提示用户点击它可以发起数据同步的操作 -->
<string name="menu_sync_cancel">Cancel syncing</string>
<!-- 定义名为“menu_sync_cancel”的字符串资源用于在正在进行同步操作的过程中在菜单里作为取消同步这一菜单项显示的文本内容方便用户点击来终止同步操作 -->
<string name="menu_setting">Settings</string>
<!-- 定义名为“menu_setting”的字符串资源用于在菜单中作为进入设置界面或者执行设置相关操作的菜单项所显示的文字内容引导用户点击该菜单项以进入设置相关功能区域 -->
<string name="menu_search">Search</string>
<!-- 定义名为“menu_search”的字符串资源用于在菜单中作为搜索相关操作的菜单项显示的文本内容提示用户点击该菜单项可以进行搜索相关的操作比如查找笔记内容等 -->
<string name="menu_delete">Delete</string>
<!-- 定义名为“menu_delete”的字符串资源用于在菜单中作为删除相关操作的菜单项显示的文本内容告知用户点击该菜单项可以执行删除相应内容的操作例如删除笔记、文件夹等 -->
<string name="menu_move">Move to folder</string>
<!-- 定义名为“menu_move”的字符串资源用于在菜单中作为将相关内容移动到文件夹这一菜单项显示的文本内容提示用户点击该菜单项可进行把某些内容移动到指定文件夹的操作 -->
<string name="menu_select_title">%d selected</string>
<!-- 定义名为“menu_select_title”的字符串资源从格式“%d selected”来看它是用于在有多个项目被选中后展示选中数量相关提示信息的文本会根据实际选中的项目数量替换“%d”来准确告知用户选中情况 -->
<string name="menu_select_none">Nothing selected, the operation is invalid</string>
<!-- 定义名为“menu_select_none”的字符串资源用于在没有任何项目被选中的情况下向用户显示的提示文本告知用户由于没有选中内容当前要执行的操作是无效的 -->
<string name="menu_select_all">Select all</string>
<!-- 定义名为“menu_select_all”的字符串资源用于在菜单中作为全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以一次性选中所有相关的项目 -->
<string name="menu_deselect_all">Deselect all</string>
<!-- 定义名为“menu_deselect_all”的字符串资源用于在菜单中作为取消全选相关操作的菜单项显示的文本内容提示用户点击该菜单项可以将已经全选的状态取消即取消选中所有项目 -->
<string name="menu_font_size">Font size</string>
<!-- 定义名为“menu_font_size”的字符串资源用于在菜单中作为调整字体大小相关操作的菜单项显示的文本内容引导用户点击该菜单项来进行字体大小相关的设置操作 -->
<string name="menu_font_small">Small</string>
<!-- 定义名为“menu_font_small”的字符串资源可能是在字体大小设置的相关菜单选项里作为表示小字号这一选项所显示的文字内容便于用户选择小字体来显示文本 -->
<string name="menu_font_normal">Medium</string>
<!-- 定义名为“menu_font_normal”的字符串资源推测是在字体大小设置相关的菜单中作为表示中等字号这一选项显示的文字内容方便用户将文本字体设置为中等大小 -->
<string name="menu_font_large">Large</string>
<!-- 定义名为“menu_font_large”的字符串资源应该是在字体大小设置相关的菜单里作为表示大字号这一选项所展示的文字内容让用户可以选择大字体来显示文本 -->
<string name="menu_font_super">Super</string>
<!-- 定义名为“menu_font_super”的字符串资源可能是在字体大小设置相关的菜单中作为表示超大字号这一选项显示的文字内容供用户选择超大字体来展示文本 -->
<string name="menu_list_mode">Enter check list</string>
<!-- 定义名为“menu_list_mode”的字符串资源用于在菜单中作为进入清单模式比如待办事项清单等类似的模式相关操作的菜单项显示的文本内容提示用户点击该菜单项可进入对应的清单模式方便对相关内容进行清单式管理 -->
<string name="menu_normal_mode">Leave check list</string>
<!-- 定义名为“menu_normal_mode”的字符串资源用于在菜单中作为离开清单模式例如待办事项清单等相关操作的菜单项显示的文本内容提示用户点击该菜单项可退出当前所处的清单模式恢复到常规显示状态 -->
<string name="menu_folder_view">View folder</string>
<!-- 定义名为“menu_folder_view”的字符串资源用于在菜单里作为查看文件夹相关操作的菜单项所显示的文本内容告知用户点击该菜单项后可以查看指定文件夹内的内容比如查看文件夹里包含的笔记等信息 -->
<string name="menu_folder_delete">Delete folder</string>
<!-- 定义名为“menu_folder_delete”的字符串资源用于在菜单中作为删除文件夹相关操作的菜单项显示的文本内容提示用户点击该菜单项会执行删除文件夹的操作通常可能还会有相应确认提示以防误删 -->
<string name="menu_folder_change_name">Change folder name</string>
<!-- 定义名为“menu_folder_change_name”的字符串资源用于在菜单中作为更改文件夹名称相关操作的菜单项显示的文本内容引导用户点击该菜单项来对已有的文件夹重新命名使其名称更符合需求或便于识别 -->
<string name="folder_exist">The folder %1$s exist, please rename</string>
<!-- 定义名为“folder_exist”的字符串资源从格式“The folder %1$s exist, please rename”来看它是一种带有占位符%1$s的提示文本通常在创建文件夹时如果要创建的文件夹名称已存在就会用实际的文件夹名称替换占位符显示该提示信息告知用户需要重命名后再进行操作 -->
<string name="menu_share">Share</string>
<!-- 定义名为“menu_share”的字符串资源用于在菜单中作为分享相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行分享操作比如分享笔记内容到其他应用或者平台等 -->
<string name="menu_send_to_desktop">Send to home</string>
<!-- 定义名为“menu_send_to_desktop”的字符串资源用于在菜单中作为将相关内容发送到桌面例如创建桌面快捷方式等相关操作的菜单项显示的文本内容告知用户点击该菜单项可把对应的内容发送到桌面方便快速访问 -->
<string name="menu_alert">Remind me</string>
<!-- 定义名为“menu_alert”的字符串资源用于在菜单中作为设置提醒相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行提醒相关设置比如设置某个笔记的提醒时间等 -->
<string name="menu_remove_remind">Delete reminder</string>
<!-- 定义名为“menu_remove_remind”的字符串资源用于在菜单中作为删除提醒相关操作的菜单项显示的文本内容提示用户点击该菜单项可删除已设置好的提醒取消之前设定的提醒功能 -->
<string name="menu_title_select_folder">Select folder</string>
<!-- 定义名为“menu_title_select_folder”的字符串资源用于在选择文件夹相关操作时作为标题类提示文本显示告知用户当前正在进行选择文件夹的操作让用户明确操作意图 -->
<string name="menu_move_parent_folder">Parent folder</string>
<!-- 定义名为“menu_move_parent_folder”的字符串资源用于在涉及将内容移动到上级文件夹等相关操作时作为提示文本显示告知用户可以选择将相应内容移动到当前所在文件夹的上级文件夹中 -->
<string name="info_note_enter_desktop">Note added to home</string>
<!-- 定义名为“info_note_enter_desktop”的字符串资源可能是在笔记成功添加到桌面比如创建桌面快捷方式成功等情况用于显示的提示信息告知用户对应的笔记已经被添加到桌面了方便用户知晓操作结果 -->
<string name="alert_message_delete_folder">Confirm to delete folder and its notes?</string>
<!-- 定义名为“alert_message_delete_folder”的字符串资源用于在要删除文件夹及其内部笔记时弹出提示框显示的询问信息询问用户是否确认执行删除该文件夹以及里面所有笔记的操作需要用户明确回应以避免误操作 -->
<string name="alert_title_delete">Delete selected notes</string>
<!-- 定义名为“alert_title_delete”的字符串资源用于在删除选中笔记相关操作时弹出提示框的标题文本清晰地告知用户当前操作是针对已选中的笔记进行删除让用户快速了解提示框所关联的操作内容 -->
<string name="alert_message_delete_notes">Confirm to delete the selected %d notes?</string>
<!-- 定义名为“alert_message_delete_notes”的字符串资源从格式“Confirm to delete the selected %d notes?”来看,它是在要删除多个选中笔记时,弹出提示框显示的询问信息,会根据实际选中笔记的数量替换占位符“%d”来准确询问用户是否确认删除相应数量的笔记 -->
<string name="alert_message_delete_note">Confirm to delete this note?</string>
<!-- 定义名为“alert_message_delete_note”的字符串资源用于在要删除单个笔记时弹出提示框显示的询问信息询问用户是否确认删除当前这一个笔记获取用户的明确操作意向 -->
<string name="format_move_notes_to_folder">Have moved selected %1$d notes to %2$s folder</string>
<!-- 定义名为“format_move_notes_to_folder”的字符串资源从格式来看它是用于在成功将选中笔记移动到指定文件夹后显示的提示信息会用实际移动笔记的数量替换占位符“%1$d”用具体的文件夹名称替换“%2$s”告知用户移动操作的具体情况 -->
<!-- Error information -->
<!-- 这是一个注释说明,告知下面的字符串资源是与错误相关的提示信息 -->
<string name="error_sdcard_unmounted">SD card busy, not available now</string>
<!-- 定义名为“error_sdcard_unmounted”的字符串资源用于在 SD 卡处于忙碌状态(比如正在被其他程序使用、出现故障等情况),导致无法正常使用时,显示的提示信息,告知用户当前 SD 卡不可用,无法进行相关操作 -->
<string name="error_sdcard_export">Export failed, please check SD card</string>
<!-- 定义名为“error_sdcard_export”的字符串资源在文本导出操作失败且原因可能与 SD 卡相关(比如 SD 卡空间不足、连接异常等)时,显示该提示信息,提示用户去检查 SD 卡的情况,以便排查问题所在 -->
<string name="error_note_not_exist">The note is not exist</string>
<!-- 定义名为“error_note_not_exist”的字符串资源当要操作的笔记不存在比如查找、编辑、删除某个不存在的笔记时会显示该提示信息告知用户指定的笔记不存在操作无法进行 -->
<string name="error_note_empty_for_clock">Sorry, can not set clock on empty note</string>
<!-- 定义名为“error_note_empty_for_clock”的字符串资源用于在尝试对空白笔记没有内容的笔记设置闹钟提醒等与时间相关操作时显示的提示信息告知用户无法在空白笔记上进行此类操作需要先添加内容等 -->
<string name="error_note_empty_for_send_to_desktop">Sorry, can not send and empty note to home</string>
<!-- 定义名为“error_note_empty_for_send_to_desktop”的字符串资源在试图将空白笔记发送到桌面比如创建桌面快捷方式显示该提示信息告知用户不能将空白笔记发送到桌面可能需要先完善笔记内容等 -->
<string name="success_sdcard_export">Export successful</string>
<!-- 定义名为“success_sdcard_export”的字符串资源在文本导出到 SD 卡操作成功后,用于显示的提示信息,告知用户导出操作已经顺利完成,让用户知晓操作结果 -->
<string name="failed_sdcard_export">Export fail</string>
<!-- 定义名为“failed_sdcard_export”的字符串资源当文本导出到 SD 卡操作失败时,显示该提示信息,简单告知用户导出失败了,不过可能还需要结合其他错误提示进一步排查原因 -->
<string name="format_exported_file_location">Export text file (%1$s) to SD (%2$s) directory</string>
<!-- 定义名为“format_exported_file_location”的字符串资源从格式来看它带有两个占位符%1$s 和 %2$s通常用于在告知用户文本导出文件的存放位置信息时会用实际的文件名替换“%1$s”用 SD 卡的具体目录路径替换“%2$s”清晰展示导出文件的具体位置情况 -->
<!-- Sync -->
<!-- 这是一个注释说明告知下面的字符串资源是与同步Sync相关的提示信息 -->
<string name="ticker_syncing">Syncing notes...</string>
<!-- 定义名为“ticker_syncing”的字符串资源在开始进行笔记同步操作时用于显示的提示信息告知用户正在同步笔记让用户知晓当前操作状态一般可能会以滚动条文字等形式展示 -->
<string name="ticker_success">Sync is successful</string>
<!-- 定义名为“ticker_success”的字符串资源在笔记同步操作成功完成后用于显示的提示信息告知用户同步已经顺利完成让用户了解操作结果 -->
<string name="ticker_fail">Sync is failed</string>
<!-- 定义名为“ticker_fail”的字符串资源当笔记同步操作失败时用于显示的提示信息告知用户同步失败了通常可能还需要结合其他相关错误提示进一步排查原因 -->
<string name="ticker_cancel">Sync is canceled</string>
<!-- 定义名为“ticker_cancel”的字符串资源在同步操作被取消比如用户手动点击取消按钮等情况用于显示的提示信息告知用户同步操作已被取消让用户知晓当前状态变化 -->
<string name="success_sync_account">Sync is successful with account %1$s</string>
<!-- 定义名为“success_sync_account”的字符串资源从格式“Sync is successful with account %1$s”来看它带有一个占位符%1$s在使用特定账户成功完成同步操作后会用实际的账户名称替换占位符告知用户使用哪个账户同步成功了明确同步相关情况 -->
<string name="error_sync_network">Sync failed, please check network and account settings</string>
<!-- 定义名为“error_sync_network”的字符串资源当笔记同步操作失败且原因可能与网络或者账户设置相关比如网络连接不稳定、账户密码错误等显示该提示信息提示用户去检查网络以及账户设置情况以排查同步失败的问题所在 -->
<string name="error_sync_internal">Sync failed, internal error occurs</string>
<!-- 定义名为“error_sync_internal”的字符串资源在笔记同步操作因内部错误比如应用自身的代码逻辑问题、服务器端问题等导致失败时显示该提示信息告知用户同步失败是由于内部出现错误不过具体原因可能还需要进一步排查分析 -->
<string name="error_sync_cancelled">Sync is canceled</string>
<!-- 定义名为“error_sync_cancelled”的字符串资源在同步操作被取消比如因程序异常等情况自动取消用于显示的提示信息告知用户同步操作已被取消与前面手动取消同步操作后显示的提示信息类似只是触发取消的原因不同 -->
<string name="sync_progress_login">Logging into %1$s...</string>
<!-- 定义名为“sync_progress_login”的字符串资源从格式“Logging into %1$s...”来看,它带有一个占位符(%1$s在同步操作过程中当正在登录相关账户比如登录到云端服务账户用于同步笔记会用实际的账户名称替换占位符告知用户正在登录哪个账户让用户了解同步操作的进展情况 -->
<string name="sync_progress_init_list">Getting remote note list...</string>
<!-- 定义名为“sync_progress_init_list”的字符串资源在同步操作中当正在获取远程笔记列表比如从云端服务器获取存储的笔记列表以便和本地笔记进行对比同步用于显示的提示信息告知用户当前正在获取远程笔记列表体现同步操作的进度状态 -->
<string name="sync_progress_syncing">Synchronize local notes with Google Task...</string>
<!-- 定义名为“sync_progress_syncing”的 string 资源,在同步操作进行到将本地笔记与 Google Task可能是谷歌的某种任务管理或笔记服务用于存储和同步笔记等进行同步的阶段时用于显示的提示信息告知用户当前正在进行具体的笔记同步操作步骤让用户知晓同步进展 -->
<!-- Preferences -->
<!-- 这是一个注释说明告知下面的字符串资源是与偏好设置Preferences相关的提示信息 -->
<string name="preferences_title">Settings</string>
<!-- 定义名为“preferences_title”的字符串资源用于在应用的偏好设置界面或者相关菜单中作为显示标题的文本内容告知用户当前进入的是设置相关的功能区域方便用户识别和操作 -->
<string name="preferences_account_title">Sync account</string>
<!-- 定义名为“preferences_account_title”的字符串资源用于在偏好设置中与同步账户相关的设置区域作为显示标题的文本内容提示用户该部分设置是针对同步账户进行操作的比如选择、更改同步账户等 -->
<string name="preferences_account_summary">Sync notes with google task</string>
<!-- 定义名为“preferences_account_summary”的字符串资源用于在同步账户相关设置区域作为对该设置功能的简要说明文本内容告知用户在这里可以将笔记与谷歌任务进行同步让用户了解该设置选项的作用 -->
<string name="preferences_last_sync_time">Last sync time %1$s</string>
<!-- 定义名为“preferences_last_sync_time”的字符串资源从格式“Last sync time %1$s”来看它带有一个占位符%1$s通常用于在显示上次同步时间信息时会用实际的时间数据按照一定格式存储的时间字符串替换占位符准确告知用户上次同步操作是什么时候进行的 -->
<string name="preferences_last_sync_time_format">yyyy-MM-dd hh:mm:ss</string>
<!-- 定义名为“preferences_last_sync_time_format”的字符串资源它规定了上次同步时间的显示格式采用“yyyy-MM-dd hh:mm:ss”这种常见的年--日 时:分:秒的格式,便于统一展示和识别同步时间信息 -->
<string name="preferences_add_account">Add account</string>
<!-- 定义名为“preferences_add_account”的字符串资源用于在偏好设置中作为添加账户比如添加用于同步笔记的新账户相关操作的菜单项或按钮显示的文本内容提示用户点击可进行添加账户的操作 -->
<string name="preferences_menu_change_account">Change sync account</string>
<!-- 定义名为“preferences_menu_change_account”的字符串资源用于在偏好设置的菜单中作为更改同步账户相关操作的菜单项显示的文本内容提示用户点击该菜单项可进行更改当前同步账户的操作 -->
<string name="preferences_menu_remove_account">Remove sync account</string>
<!-- 定义名为“preferences_menu_remove_account”的字符串资源用于在偏好设置的菜单中作为移除同步账户相关操作的菜单项显示的文本内容提示用户点击该菜单项可执行移除已有的同步账户的操作 -->
<string name="preferences_menu_cancel">Cancel</string>
<!-- 定义名为“preferences_menu_cancel”的字符串资源用于在偏好设置相关操作过程中比如正在添加、更改、移除账户等操作时作为取消当前操作的菜单项或按钮显示的文本内容提示用户点击可取消正在进行的操作回到之前的状态 -->
<string name="preferences_button_sync_immediately">Sync immediately</string>
<!-- 定义名为“preferences_button_sync_immediately”的字符串资源用于在偏好设置界面或者相关区域作为立即进行同步操作的按钮显示的文本内容提示用户点击该按钮可马上发起笔记同步操作无需等待其他定时同步等情况 -->
<string name="preferences_button_sync_cancel">Cancel syncing</string>
<!-- 定义名为“preferences_button_sync_cancel”的字符串资源用于在正在进行同步操作时在偏好设置界面或者相关区域作为取消同步操作的按钮显示的文本内容提示用户点击该按钮可终止当前正在进行的同步操作 -->
<string name="preferences_dialog_change_account_title">Current account %1$s</string>
<!-- 定义名为“preferences_dialog_change_account_title”的字符串资源从格式“Current account %1$s”来看它带有一个占位符%1$s在弹出更改账户相关的对话框时会用实际的当前账户名称替换占位符作为对话框的标题显示明确告知用户当前正在操作的是哪个账户 -->
<string name="preferences_dialog_change_account_warn_msg">All sync related information will be deleted, which may result in duplicated items sometime</string>
<!-- 定义名为“preferences_dialog_change_account_warn_msg”的字符串资源用于在弹出更改账户相关对话框时作为显示警告信息的文本内容告知用户更改账户操作会导致所有与同步相关的信息被删除并且有可能会出现重复的项目提醒用户谨慎操作 -->
<string name="preferences_dialog_select_account_title">Sync notes</string>
<string name="preferences_dialog_select_account_tips">Please select a google account. Local notes will be synced with google task.</string>
<string name="preferences_toast_cannot_change_account">Cannot change the account because sync is in progress</string>
<string name="preferences_toast_success_set_accout">%1$s has been set as the sync account</string>
<string name="preferences_bg_random_appear_title">New note background color random</string>
<!-- 定义名为“preferences_dialog_select_account_title”的字符串资源用于在弹出选择账户用于同步笔记相关的对话框时作为对话框的标题显示的文本内容告知用户当前对话框是用于选择同步笔记的账户让用户明确操作意图 -->
<string name="preferences_dialog_select_account_tips">Please select a google account. Local notes will be synced with google task.</string>
<!-- 定义名为“preferences_dialog_select_account_tips”的字符串资源用于在弹出选择账户相关对话框时作为提示信息的文本内容告知用户需要选择一个谷歌账户并且提示用户选择-->
<string name="button_delete">Delete</string>
<!-- 定义名为“button_delete”的字符串资源通常用于在界面上作为按钮显示的文本内容提示用户点击该按钮可执行删除相关的操作比如删除某个笔记、文件或者其他数据项等 -->
<string name="call_record_folder_name">Call notes</string>
<!-- 定义名为“call_record_folder_name”的字符串资源推测是用于表示存放通话记录相关笔记或者类似与通话相关内容记录的文件等的文件夹名称方便在应用中对这类特定内容进行归类和识别 -->
<string name="hint_foler_name">Input name</string>
<!-- 定义名为“hint_foler_name”的字符串资源大概率是用于在需要用户输入文件夹名称的场景下作为提示信息显示给用户告知用户在此处输入相应的名称引导用户完成命名操作 -->
<string name="search_label">Searching Notes</string>
<!-- 定义名为“search_label”的字符串资源可能是在搜索功能启动后用于显示正在搜索笔记这一状态的提示文本比如在搜索框附近或者搜索界面上展示让用户知晓当前正在进行搜索操作 -->
<string name="search_hint">Search notes</string>
<!-- 定义名为“search_hint”的字符串资源一般用于作为搜索框内的占位提示文字提示用户在此处输入内容来进行笔记搜索引导用户使用搜索功能查找想要的笔记内容 -->
<string name="search_setting_description">Text in your notes</string>
<!-- 定义名为“search_setting_description”的字符串资源或许是用于在搜索相关设置界面中作为对搜索功能的描述文本告知用户搜索的范围是笔记内包含的文本内容让用户了解搜索的具体情况 -->
<string name="search">Notes</string>
<!-- 定义名为“search”的字符串资源可能是用于标识搜索功能或者搜索相关区域的名称等例如在菜单中作为搜索选项对应的显示文字提示用户点击可进入搜索相关操作界面 -->
<string name="datetime_dialog_ok">set</string>
<!-- 定义名为“datetime_dialog_ok”的字符串资源通常是在日期时间相关的对话框比如设置提醒时间、编辑笔记时间等涉及时间选择的对话框作为确认按钮显示的文本内容提示用户点击该按钮可完成时间设置等相关操作即表示“确定、设置”的意思 -->
<string name="datetime_dialog_cancel">cancel</string>
<!-- 定义名为“datetime_dialog_cancel”的字符串资源同样是在日期时间相关的对话框中作为取消按钮显示的文本内容提示用户点击该按钮可取消当前正在进行的时间相关操作关闭对话框且不保存更改等 -->
<plurals name="search_results_title">
<item quantity="one"><xliff:g id="number" example="1">%1$s</xliff:g> result for \"<xliff:g id="search" example="???">%2$s</xliff:g>\"</item>
<!-- 定义名为“search_results_title”的复数形式的字符串资源用于根据搜索结果数量的不同情况单数或复数来显示相应合适的标题文本常用于展示搜索到的笔记数量对应的提示信息 -->
<item quantity="one"><xliff:g id="number" example="1">%1$s</xliff:g> result for \"<xliff:g id="search" example="???">%2$s</xliff:g>\"</item>
<!-- 定义复数资源中的一个子项当搜索结果数量为“一”quantity="one")时显示此文本内容。其中包含了两个占位符(<xliff:g>标签定义),%1$s 通常会被替换为实际的结果数量数字,%2$s 会被替换为搜索的关键词内容,用于准确告知用户搜索到了一个结果以及对应的搜索关键词是什么 -->
<!-- Case of 0 or 2 or more results. -->
<item quantity="other"><xliff:g id="number" example="15">%1$s</xliff:g> results for \"<xliff:g id="search" example="???">%2$s</xliff:g>\"</item>
<!-- 定义复数资源中的另一个子项当搜索结果数量为“零”或者“两个及以上”quantity="other")时显示此文本内容。同样包含两个占位符,作用与上面类似,会根据实际结果数量和搜索关键词来替换占位符,告知用户搜索到的结果数量以及对应的搜索关键词情况 -->
</plurals>
</resources>
</resources>

@ -1,69 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
// XML 声明,指定该 XML 文件的版本为 1.0,字符编码采用 UTF-8 格式,这是 XML 文件开头的标准声明部分,用于告知解析器如何处理该文件内容。
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
// 版权声明及开源许可相关注释说明该代码文件的版权归属MiCode 开源社区,时间范围是 2010 - 2011 年),以及所遵循的 Apache License 2.0 开源许可协议内容,告知使用者在符合该许可协议要求的情况下才能使用此文件。
<resources>
// XML 资源标签的开始,用于定义一系列的 Android 应用资源比如样式styles、尺寸dimens、颜色colors等各种资源都会在这个标签内部进行定义。
<style name="TextAppearanceSuper">
<item name="android:textSize">@dimen/text_font_size_super</item>
<item name="android:textColorLink">#0000ff</item>
</style>
// 定义了一个名为"TextAppearanceSuper"的样式资源。
// 其中包含两个子项(<item>
// 第一个子项通过"@dimen/text_font_size_super"设置了文本的字号大小,这里引用了名为"text_font_size_super"的尺寸资源(通常在 dimens.xml 文件中定义具体数值)来确定实际字号大小;
// 第二个子项将链接文本的颜色设置为蓝色(十六进制颜色码 #0000ff用于指定当文本作为链接显示时的颜色样式。
<style name="TextAppearanceLarge">
<item name="android:textSize">@dimen/text_font_size_large</item>
<item name="android:textColorLink">#0000ff</item>
</style>
// 定义名为"TextAppearanceLarge"的样式资源。
// 同样包含两个子项:
// 第一个子项利用"@dimen/text_font_size_large"来设置文本字号,引用对应的尺寸资源确定大小;
// 第二个子项也是把链接文本颜色设为蓝色(#0000ff与上面类似规定了链接文本颜色的显示样式。
<style name="TextAppearanceMedium">
<item name="android:textSize">@dimen/text_font_size_medium</item>
<item name="android:textColorLink">#0000ff</item>
</style>
// 定义"TextAppearanceMedium"样式资源。
// 包含两个子项,分别通过引用"text_font_size_medium"尺寸资源设置文本字号以及将链接文本颜色设为蓝色(#0000ff用于特定的文本显示样式设定。
<style name="TextAppearanceNormal">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColorLink">#0000ff</item>
</style>
// 定义"TextAppearanceNormal"样式资源。
// 其两个子项中,一个借助"text_font_size_normal"尺寸资源来规定文本字号大小,另一个将链接文本颜色设定为蓝色(#0000ff以定义相应的文本外观样式。
<style name="TextAppearancePrimaryItem">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
// 定义名为"TextAppearancePrimaryItem"的样式资源。
// 有两个子项:
// 第一个子项根据"text_font_size_normal"尺寸资源确定文本字号大小;
// 第二个子项通过引用"@color/primary_text_dark"来设置文本的颜色,这里会使用名为"primary_text_dark"的颜色资源(通常在 colors.xml 文件中定义具体颜色值)来确定文本实际显示的颜色,用于特定主要项目文本的外观样式定义。
<style name="TextAppearanceSecondaryItem">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
// 定义"TextAppearanceSecondaryItem"样式资源。
// 包含两个子项:
// 第一个子项按照"text_font_size_small"尺寸资源设置文本字号,使其呈现较小的字号样式;
// 第二个子项引用"@color/secondary_text_dark"颜色资源来设定文本颜色,用于特定次要项目文本的外观样式定义,比如一些辅助说明性文字等。
<style name="TextAppearanceUnderMenuIcon">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@android:color/black</item>
</style>
// 定义"TextAppearanceUnderMenuIcon"样式资源。
// 它的两个子项中,一个依据"text_font_size_normal"尺寸资源确定文本字号大小保持正常字号;
// 另一个直接使用 Android 系统内置的黑色(@android:color/black来设置文本颜色用于在菜单图标下方等相关位置文本的外观样式设定。
<style name="HighlightTextAppearancePrimary">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
// 定义"HighlightTextAppearancePrimary"样式资源。
// 同样有两个子项,分别通过"text_font_size_normal"尺寸资源确定文本字号以及引用"@color/primary_text_dark"颜色资源设置文本颜色,用于特定主要内容的突出显示文本(比如高亮显示的主要文本)的外观样式定义。
<style name="HighlightTextAppearanceSecondary">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
// 定义"HighlightTextAppearanceSecondary"样式资源。
// 其两个子项分别按照"text_font_size_small"尺寸资源设置较小的文本字号,并通过引用"@color/secondary_text_dark"颜色资源确定文本颜色,用于次要内容的突出显示文本(比如高亮显示的次要文本)的外观样式定义。
<style name="NoteTheme" parent="@android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">@style/NoteActionBarStyle</item>
</style>
// 定义名为"NoteTheme"的主题样式资源,它继承自 Android 系统内置的"@android:style/Theme.Holo.Light"主题样式(即基于该主题样式进行扩展和定制)。
// 其中包含一个子项,通过"@style/NoteActionBarStyle"来设置该主题下的ActionBar操作栏的样式将其关联到名为"NoteActionBarStyle"的自定义样式资源上,用于整体主题中操作栏样式的定制设定。
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid">
<item name="android:displayOptions" />
<item name="android:visibility">gone</item>
</style>
</resources>
// 定义名为"NoteActionBarStyle"的样式资源,它继承自 Android 系统内置的"@android:style/Widget.Holo.Light.ActionBar.Solid"样式(在继承基础上做进一步修改)。
// 包含两个子项:
// 第一个子项对"android:displayOptions"属性进行了设置这里虽然没有具体值但可能会在后续代码中根据需求动态赋值或者通过代码逻辑来处理该属性相关情况用于控制ActionBar的显示相关选项
// 第二个子项将"android:visibility"属性设置为"gone",意味着该操作栏在使用这个样式时将被隐藏,不显示在界面上,用于定制操作栏的显示状态。
</resources>
// XML 资源标签的结束,表示资源定义部分的结束。

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML 声明,表明该 XML 文件遵循的版本是 1.0,并且使用的字符编码格式为 UTF-8这是 XML 文件开头常见的标准声明,用于告知解析器如何正确解析后续内容。-->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
@ -15,16 +16,28 @@
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:android="http://schemas.android.com/apk/res/android">
// 定义一个“PreferenceScreen”偏好设置屏幕元素它是 Android 中用于构建应用设置界面的根容器通过“xmlns:android”属性声明了 Android 命名空间,以便后续使用 Android 相关的属性和标签,整个设置界面相关的内容都会包含在这个标签内部。
<PreferenceCategory
android:key="pref_sync_account_key">
android:key="pref_sync_account_key">
</PreferenceCategory>
// 定义一个“PreferenceCategory”偏好设置类别元素用于对设置项进行分组归类这里通过“android:key”属性赋予了它一个唯一的标识“pref_sync_account_key”可以在代码中通过这个标识来操作该分组相关内容但目前该分组内部暂时没有具体的设置项为空可能后续会添加与同步账号相关的设置项进去。
<PreferenceCategory>
<CheckBoxPreference
android:key="pref_key_bg_random_appear"
android:title="@string/preferences_bg_random_appear_title"
android:defaultValue="false" />
android:key="pref_key_bg_random_appear"
android:title="@string/preferences_bg_random_appear_title"
android:defaultValue="false" />
</PreferenceCategory>
// 又定义了一个“PreferenceCategory”偏好设置类别元素用于对另外一组设置项进行分组不过这里没有给它设置“android:key”属性相对来说是一个比较通用的分组。
// 在这个分组内部有一个“CheckBoxPreference”复选框偏好设置元素
// 它通过“android:key”属性被赋予了“pref_key_bg_random_appear”这个唯一标识方便在代码中对这个复选框进行操作和状态获取等
// 通过“android:title”属性引用了名为“preferences_bg_random_appear_title”的字符串资源通常在 strings.xml 文件中定义具体文本内容)来作为该复选框在界面上显示的标题,用于提示用户该复选框对应的功能含义;
// “android:defaultValue”属性被设置为“false”表示该复选框的默认初始状态是未选中状态用于确定该偏好设置在初次显示时的默认值情况。
//PreferenceScreen偏好设置屏幕元素的结束标签标志着整个偏好设置界面相关内容定义的结束
// 版权声明及开源许可相关注释,指出该代码文件的版权归属于 MiCode 开源社区(时间范围为 2010 - 2011 年),同时说明了此文件遵循 Apache License 2.0 开源许可协议,告知使用者需遵循该协议规定来使用此文件。
</PreferenceScreen>

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML声明指定该XML文件的版本为1.0字符编码采用UTF-8格式这是XML文件开头的标准写法用于告知解析器如何处理该文件内容 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
@ -14,14 +15,24 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- 版权声明及开源许可相关注释说明该代码文件的版权归属MiCode开源社区时间范围是2010 - 2011年以及所遵循的Apache License 2.0开源许可协议内容,告知使用者在符合该许可协议要求的情况下才能使用此文件 -->
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="queryRewriteFromText"
xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
android:searchMode="queryRewriteFromText"
android:searchSuggestAuthority="notes"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSettingsDescription="@string/search_setting_description"
android:includeInGlobalSearch="true" />
android:searchSuggestAuthority="notes"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSettingsDescription="@string/search_setting_description"
android:includeInGlobalSearch="true" />
<!-- 声明Android命名空间通过这个命名空间可以使用Android系统中定义的相关属性是在Android XML布局等文件中常用的声明方式 -->
<!-- 设置搜索功能在界面上显示的标签文本其具体内容通常引用自strings.xml文件中名为"search_label"的字符串资源,用于向用户标识搜索相关的功能 -->
<!-- 设置搜索输入框的提示文字会引用strings.xml文件里名为"search_hint"的字符串资源,用于提示用户输入搜索内容等操作 -->
<!-- 指定搜索模式为从输入的文本进行查询重写,意味着系统会根据用户输入的文本按照一定规则进行处理来执行搜索任务 -->
<!-- 设置搜索建议的授权来源为"notes",可能用于确定搜索建议数据的获取来源或者相关权限范围等,具体取决于应用的搜索功能实现逻辑 -->
<!-- 定义当用户点击搜索建议时执行的意图动作是"android.intent.action.VIEW",通常用于触发查看相关的操作,比如查看对应的搜索结果详情等 -->
<!-- 设置搜索设置的描述信息会引用strings.xml文件中名为"search_setting_description"的字符串资源,用于向用户简要说明搜索相关设置的情况 -->
<!-- 设置该搜索功能是否包含在全局搜索中,这里设置为"true",表示此搜索功能会参与到整个应用的全局搜索功能里 -->

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML 声明,表明此 XML 文件遵循的版本是 1.0,使用的字符编码格式为 UTF-8这是 XML 文件开头的标准声明形式,用于告知 XML 解析器按照相应规则来解析后续的文件内容 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
@ -14,10 +15,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- 版权声明及开源许可相关注释,说明该代码文件的版权归属于 MiCode 开源社区(时间范围是 2010 - 2011 年),并且告知使用者该文件遵循 Apache License 2.0 开源许可协议,意味着只有在符合该协议要求的情况下,才能合法使用此文件 -->
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_2x"
android:minWidth="146dip"
android:minHeight="146dip">
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_2x"
android:minWidth="146dip"
android:minHeight="146dip">
</appwidget-provider>
<!-- 声明 Android 命名空间,通过这个命名空间的声明,后续可以使用 Android 系统中定义好的各种属性来对 App Widget桌面小部件进行配置这是 Android 中 XML 配置文件里常用的声明方式 -->
<!-- 指定 App Widget桌面小部件初始显示时所采用的布局资源这里引用了名为“widget_2x”的布局文件通常在 layout 文件夹下定义),用于确定小部件首次显示在桌面等位置时的界面样式 -->
<!-- 设置 App Widget桌面小部件的最小宽度为 146 设备独立像素dip用于限定小部件在宽度方向上可被缩放的最小尺寸确保其在不同屏幕密度的设备上都能有合适的显示效果 -->
<!-- 设置 App Widget桌面小部件的最小高度为 146 设备独立像素dip与设置最小宽度类似用于限定小部件在高度方向上可被缩放的最小尺寸保证其在各类屏幕密度的设备上呈现出合理的外观形态 -->
<!-- 结束“appwidget-provider”标签标志着对 App Widget桌面小部件相关配置的定义结束 -->

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- XML 声明,用于表明该 XML 文件遵循的版本是 1.0,并且采用的字符编码格式为 UTF-8这是 XML 文件开头必备的标准声明部分,作用是告知 XML 解析器按照相应的版本和编码规则来处理后续的文件内容 -->
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
@ -14,10 +15,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- 版权声明及开源许可相关注释,清晰地指出该 XML 文件的版权归属于 MiCode 开源社区,其版权生效时间范围是 2010 至 2011 年。同时还说明了该文件遵循 Apache License 2.0 开源许可协议,这意味着使用者若要使用此文件,必须严格按照该协议规定的要求来操作 -->
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_4x"
android:minWidth="294dip"
android:minHeight="294dip">
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_4x"
android:minWidth="294dip"
android:minHeight="294dip">
</appwidget-provider>
<!-- 这是声明 Android 命名空间的语句,通过指定该命名空间,后续在这个 XML 标签内就能使用 Android 系统预定义的各类属性来对 App Widget桌面小部件进行相应的配置它是 Android 应用开发中 XML 配置文件里常用的一种声明方式 -->
<!-- 用于指定 App Widget桌面小部件在初始状态下显示所采用的布局资源这里引用的是名为“widget_4x”的布局文件一般该布局文件会存放在项目的 layout 文件夹内),以此来确定小部件首次出现在桌面等展示位置时的具体界面样式 -->
<!-- 此属性设置了 App Widget桌面小部件的最小宽度为 294 设备独立像素dip其目的在于限定小部件在不同屏幕密度的设备上进行宽度缩放时的最小尺寸范围确保小部件在各种屏幕条件下都能有一个合适的、符合视觉效果的宽度展示 -->
<!-- 该属性用于设定 App Widget桌面小部件的最小高度为 294 设备独立像素dip和设置最小宽度的作用类似主要是为了对小部件在高度方向上的缩放进行限制保障其在不同屏幕的设备上能呈现出合理且美观的高度外观形态 -->
<!-- 此为“appwidget-provider”标签的结束标记表示针对 App Widget桌面小部件相关配置信息的定义到此结束 -->
Loading…
Cancel
Save