You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2Q1/ui/NoteEditActivity.java

1368 lines
142 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010 - 2011, The MiCode Open Source Community (www.micode.net)
* 版权声明部分表明该代码遵循Apache License 2.0协议,同时说明了版权所属的开源社区以及相关许可的获取途径等信息。
*/
/*
* 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.
*/
// 声明该类所属的包名表明这个类位于net.micode.notes.ui包下在项目的整体包结构中有其特定的组织和访问层级关系。
package net.micode.notes.ui;
// 导入一系列Android系统相关的类以下是各导入类的简要说明
// ActivityAndroid应用中界面的基本组成单元承载用户界面及相关交互逻辑。
// AlarmManager用于设置定时任务、闹钟等可在特定时间触发指定操作。
// AlertDialog创建弹出式对话框实现提示、确认等交互功能。
// PendingIntent在不同组件间传递意图常用于延迟执行或跨进程操作触发场景。
// SearchManager处理应用内搜索功能相关逻辑如管理搜索框、展示搜索结果等。
// AppWidgetManager管理应用的桌面小部件相关操作例如更新小部件显示内容、布局等。
// Context获取应用的上下文环境信息是很多Android操作的基础如资源获取、系统服务调用等。
// DialogInterface处理对话框相关交互逻辑比如按钮点击等事件。
// Intent用于在Android组件之间传递消息、启动其他组件或执行特定操作是组件间通信的重要方式。
// SharedPreferences存储和获取应用的轻量级配置数据以键值对形式保存常用于保存用户偏好设置等信息。
// Paint用于设置绘制文本、图形等的样式属性比如颜色、字体等。
// Bundle在Activity之间传递数据常用于启动Activity或保存恢复Activity状态时。
// PreferenceManager方便获取应用的默认SharedPreferences实例简化相关操作代码编写。
// Spannable和SpannableString处理文本样式设置可对文本部分内容应用不同样式如设置背景色、字体等实现富文本效果。
// TextUtils提供文本处理相关实用工具方法如判断文本是否为空等操作。
// DateUtils处理日期时间相关的格式化、比较等操作便于在界面展示日期时间信息或进行时间相关逻辑判断。
// BackgroundColorSpan给文本部分内容设置背景颜色常与Spannable相关类配合使用实现文本样式设置。
// Log用于在应用中输出日志信息方便调试和查看应用运行状态及错误信息等。
// LayoutInflater将XML布局文件实例化为对应的View对象是动态创建UI界面的重要工具。
// Menu和MenuItem用于创建和管理应用的菜单相关功能如定义菜单项、响应菜单项点击事件等。
// MotionEvent处理触摸屏幕相关事件如触摸按下、滑动、抬起等操作的监听和处理。
// ViewAndroid中所有可视化组件的基类构建和操作界面上的各种视图元素OnClickListener用于监听视图点击事件以执行相应逻辑。
// WindowManager管理应用的窗口相关属性如获取窗口大小、设置窗口显示状态等操作。
// CheckBox常见UI组件提供可勾选的选项功能用户可选择或取消选择能监听其勾选状态变化。
// CompoundButtonCheckBox等具有两种状态切换组件的父类OnCheckedChangeListener用于监听这类组件的勾选状态变化事件以执行相应逻辑。
// EditText供用户输入文本信息的UI组件常用于填写表单、编辑内容等场景。
// ImageView在界面展示图片资源可加载本地或网络图片并设置图片显示样式等属性。
// LinearLayout按水平或垂直方向排列子视图元素的常见布局方式构建界面布局结构。
// TextView用于在界面展示文本信息的常用组件。
// Toast在界面短暂弹出提示信息给用户常用于显示简单操作反馈、提示消息等。
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;
// 导入项目中自动生成的资源类R通过它访问项目中的各种资源如布局文件、图片资源、字符串资源等此处主要用于获取相关布局、字符串等资源。
// 同时导入与笔记数据相关的类Notes、TextNote用于处理笔记具体数据结构和操作。
// 导入WorkingNote类及其中定义的NoteSettingChangedListener接口用于处理笔记相关设置变更的监听和逻辑处理。
// 导入一些工具类如DataUtils用于数据处理通用操作ResourceParser用于解析资源相关信息如颜色、字体大小等资源对应的标识
// 还导入与日期时间选择对话框、笔记编辑文本变化监听、桌面小部件相关的类和接口,用于实现相应功能逻辑。
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;
// 导入Java标准库中的相关类HashMap用于存储键值对形式的数据方便按键查找对应值HashSet用于存储不重复元素集合常用于去重等操作
// Map是映射接口定义键值对数据结构基本操作方法HashMap是其常用实现类之一Pattern和Matcher用于处理正则表达式相关操作比如匹配文本特定模式内容等。
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// NoteEditActivity类继承自Activity类是一个Android的Activity用于实现笔记编辑相关功能界面和交互逻辑
// 同时实现了OnClickListener、NoteSettingChangedListener、OnTextViewChangeListener接口分别用于处理视图点击事件、笔记设置变更事件以及笔记编辑文本变化事件等。
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 定义一个内部私有类HeadViewHolder用于将笔记编辑界面头部相关视图元素的引用进行封装方便统一管理和操作这些视图
// 提高代码可读性和可维护性,使代码结构更清晰,后续对头部视图元素的操作可以通过该类的成员变量来进行。
private class HeadViewHolder {
// 用于显示笔记修改时间相关信息的TextView组件引用通过这个变量可以操作该TextView的属性如设置文本内容、文本样式等。
public TextView tvModified;
// 用于显示提醒图标可能表示笔记是否设置了提醒等情况的ImageView组件引用可用于设置图片资源、控制图片显示隐藏等操作。
public ImageView ivAlertIcon;
// 用于显示提醒日期时间信息的TextView组件引用同样可以对其文本显示相关操作进行设置比如更新显示的提醒时间等。
public TextView tvAlertDate;
// 用于设置笔记背景颜色的ImageView组件引用通常可以设置点击事件监听器让用户点击它来触发选择背景颜色的操作等。
public ImageView ibSetBgColor;
}
// 定义一个静态的HashMap类型的成员变量sBgSelectorBtnsMap用于建立背景颜色选择按钮的ID与对应的颜色资源标识之间的映射关系。
// 这样在代码中就可以方便地根据按钮的ID来获取对应的颜色设置值例如在处理用户点击某个背景颜色选择按钮时通过该映射快速确定要设置的颜色。
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
// 静态代码块用于初始化sBgSelectorBtnsMap将各个背景颜色选择按钮的ID这些ID通常在布局文件中定义对应界面上的具体按钮与对应的颜色资源标识由ResourceParser类定义的相关常量代表不同的颜色进行关联映射。
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);
}
// 定义一个静态的HashMap类型的成员变量sBgSelectorSelectionMap用于建立颜色资源标识与对应的颜色选择按钮选中状态图标ID之间的映射关系。
// 其目的是方便根据当前选择的颜色来确定应该显示哪个选中状态的图标,以此提示用户当前选中的颜色选项,增强用户界面的交互反馈效果。
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
// 静态代码块用于初始化sBgSelectorSelectionMap将各个颜色资源标识由ResourceParser类定义的不同颜色常量与对应的颜色选择按钮选中状态图标ID同样是在布局文件中定义的用于显示选中效果的图标对应的ID进行关联映射。
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);
}
// 定义一个静态的HashMap类型的成员变量sFontSizeBtnsMap用于建立字体大小选择按钮所在布局的ID与对应的字体大小资源标识之间的映射关系。
// 通过这种映射后续在代码中可以依据按钮所在布局的ID快速获取对应的字体大小设置值方便实现根据用户点击不同布局按钮来切换笔记编辑文本字体大小的功能。
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
// 静态代码块用于初始化sFontSizeBtnsMap将各个代表字体大小选择按钮所在布局的ID如R.id.ll_font_large等这些ID在对应的布局文件中定义对应界面上不同的字体大小选择按钮所在的布局区域
// 与对应的字体大小资源标识由ResourceParser类定义的相关常量如ResourceParser.TEXT_LARGE等表示不同的字体大小规格进行关联映射。
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);
}
// 定义一个静态的HashMap类型的成员变量sFontSelectorSelectionMap用于建立字体大小资源标识与对应的字体大小选择按钮选中状态图标ID之间的映射关系。
// 借助这个映射关系,在用户选择了某种字体大小后,可以依据所选字体大小对应的资源标识,快速确定要显示哪个选中状态的图标,从而直观地向用户展示当前所选中的字体大小选项。
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
// 静态代码块用于初始化sFontSelectorSelectionMap将各个字体大小资源标识如ResourceParser.TEXT_LARGE等与对应的字体大小选择按钮选中状态图标ID如R.id.iv_large_select等这些ID对应界面上用于显示字体大小选中状态的图标进行关联映射。
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);
}
// 定义一个静态的字符串常量TAG用于在日志输出通过Log类进行日志记录中标识当前类方便在调试应用以及查看日志信息时能够快速区分是由哪个类输出的内容
// 通常会将其设置为当前类的简单且具有辨识度的名称,这里就是"NoteEditActivity"。
private static final String TAG = "NoteEditActivity";
// 定义一个HeadViewHolder类型的成员变量mNoteHeaderHolder用于存储笔记编辑界面头部视图元素的引用通过前面定义的HeadViewHolder类来统一管理这些视图组件
// 后续可以通过这个变量方便地操作头部的各个视图,比如获取或设置它们的显示内容、样式等属性。
private HeadViewHolder mNoteHeaderHolder;
// 定义一个View类型的成员变量mHeadViewPanel它可能代表笔记编辑界面头部的整个面板视图通过这个变量可以对头部区域进行整体的操作例如控制其显示隐藏、调整布局等。
private View mHeadViewPanel;
// 定义一个View类型的成员变量mNoteBgColorSelector它大概率用于表示背景颜色选择相关的视图区域比如包含了多个用于选择笔记背景颜色的按钮等方便用户进行背景颜色的选择操作。
private View mNoteBgColorSelector;
// 定义一个View类型的成员变量mFontSizeSelector它可能用于表示字体大小选择相关的视图区域例如包含了不同字体大小选择按钮所在的布局等供用户选择笔记编辑文本的字体大小。
private View mFontSizeSelector;
// 定义一个EditText类型的成员变量mNoteEditor这是用于用户输入和编辑笔记文本内容的核心组件用户可以在这个文本框中输入文字、进行文本编辑操作
// 并且可以通过代码对其文本内容、样式等属性进行获取和设置。
private EditText mNoteEditor;
// 定义一个View类型的成员变量mNoteEditorPanel它可能用于表示包含笔记编辑文本框mNoteEditor以及相关编辑功能按钮等的整个面板视图通过这个变量可以对编辑区域进行整体的操作
// 比如调整整个编辑面板的布局、显示隐藏等。
private View mNoteEditorPanel;
// 定义一个WorkingNote类型的成员变量mWorkingNote用于存储正在编辑的笔记的相关数据和状态信息WorkingNote类应该封装了笔记的各种属性如内容、设置像背景颜色、字体大小等以及其他业务相关的属性
// 通过这个变量可以方便地调用WorkingNote类提供的方法来操作和管理笔记的业务逻辑。
private WorkingNote mWorkingNote;
// 定义一个SharedPreferences类型的成员变量mSharedPrefs它用于获取应用的共享偏好设置数据借助这个变量可以读取和保存用户在应用中设置的一些偏好选项
// 例如字体大小偏好等信息,方便在应用下次启动或者不同界面间保持用户设置的一致性。
private SharedPreferences mSharedPrefs;
// 定义一个int类型的成员变量mFontSizeId用于存储当前选择的字体大小对应的资源标识后续可以依据这个标识来设置笔记编辑文本的字体大小等相关操作确保文本显示的字体大小符合用户的选择。
private int mFontSizeId;
// 定义一个静态的字符串常量PREFERENCE_FONT_SIZE用于作为存储字体大小偏好设置的键值在使用SharedPreferences存储和获取用户设置的字体大小偏好信息时
// 通过这个键来准确地定位和操作对应的偏好值,保持数据的一致性和可访问性。
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
// 定义一个静态的整数常量SHORTCUT_ICON_TITLE_MAX_LEN用于限定快捷方式图标标题的最大长度在创建笔记快捷方式等相关场景下
// 可以依据这个常量来控制标题显示的长度,避免标题过长导致显示不美观或者不符合界面设计要求等问题。
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
// 定义两个静态的字符串常量TAG_CHECKED和TAG_UNCHECKED分别用于表示勾选状态可能是某种标记为已选中的符号和未勾选状态可能是某种标记为未选中的符号的文本表示
// 这里通过字符的Unicode编码值来创建对应的字符串具体用途可能是在显示勾选框等相关场景下作为显示的文本内容方便直观地向用户展示勾选与否的状态。
public static final String TAG_CHECKED = String.valueOf('\u221A');
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
// 定义一个LinearLayout类型的成员变量mEditTextList从变量名推测它可能用于存放多个编辑文本相关的视图元素不过仅从当前代码不太能明确其具体用途
// 也许在更复杂的文本编辑场景下,用于展示多个可编辑文本的列表或者相关的布局结构等情况。
private LinearLayout mEditTextList;
// 定义一个String类型的成员变量mUserQuery它可能用于存储用户输入的查询内容比如在笔记内进行搜索操作时用户输入的搜索关键词等信息会存储在这个变量中
// 以便后续依据这个查询内容进行相应的搜索逻辑处理。
private String mUserQuery;
// 定义一个Pattern类型的成员变量mPattern用于存储正则表达式的编译模式通常在需要使用正则表达式对文本进行匹配、查找等操作时会先将正则表达式编译为Pattern对象
// 然后利用这个对象结合Matcher类来进行具体的文本处理操作比如查找符合特定模式的文本内容等。
// onCreate方法是Android系统在创建Activity时调用的方法用于进行Activity的初始化操作如设置界面布局、初始化成员变量等。
// 首先调用父类Activity类的onCreate方法确保完成系统要求的基础初始化工作然后通过setContentView方法设置当前Activity的界面布局为R.layout.note_edit所对应的布局文件
// 该布局文件定义了笔记编辑界面的整体结构和各个视图组件的布局方式。接着判断savedInstanceState是否为null表示是否是首次创建Activity
// 如果是首次创建且initActivityState方法根据传入的Intent初始化Activity状态返回false则直接调用finish方法结束当前Activity
// 否则继续执行后续的initResources方法来进一步初始化相关资源虽然当前代码中未看到initResources方法的具体实现但推测是用于初始化一些资源相关的操作比如加载图片、初始化样式等
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null &&!initActivityState(getIntent())) {
finish();
return;
}
initResources();
}
// onRestoreInstanceState方法在Activity由于系统内存不足等原因被销毁后重新创建时被调用用于恢复Activity之前的状态比如之前用户输入的内容、选择的设置等信息。
// 首先调用父类的onRestoreInstanceState方法确保完成系统要求的基础恢复操作然后判断savedInstanceState是否不为null且包含了Intent.EXTRA_UID这个键通常用于标识某个唯一的资源或数据标识
// 如果满足条件则创建一个新的Intent对象设置其动作为Intent.ACTION_VIEW并将从savedInstanceState中获取的Intent.EXTRA_UID对应的长整型值作为额外数据放入新的Intent中
// 接着调用initActivityState方法尝试依据这个新的Intent来恢复Activity的状态如果该方法返回false则调用finish方法结束当前Activity否则在日志中输出恢复信息通过Log.d方法记录调试信息表示从被销毁的Activity中成功恢复了状态。
@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");
}
}
// initActivityState方法用于依据传入的Intent来初始化Activity的状态比如根据Intent中的信息加载对应的笔记数据等。
// 首先将mWorkingNote设置为null表示初始化前先清除之前可能存在的笔记相关数据然后判断传入的Intent的动作是否为Intent.ACTION_VIEW通常用于查看某个具体资源的动作
// 如果是则从Intent中获取名为Intent.EXTRA_UID的长整型额外数据这里应该是用于标识笔记的唯一ID如果获取不到即返回值为0则使用默认值0同时将mUserQuery设置为空字符串
// 接下来代码中的注释提到了"Starting from the searched result"推测后续可能会根据这个ID以及是否是从搜索结果进入等情况来进一步加载和初始化笔记相关的数据以及界面状态不过此处代码未完整展示相关逻辑。
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
*/
// 判断传入的Intent是否包含SearchManager.EXTRA_DATA_KEY这个额外数据通常用于传递搜索相关的关键数据如果包含则说明当前Activity可能是从搜索结果进入的。
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
// 从Intent中获取SearchManager.EXTRA_DATA_KEY对应的字符串数据并将其转换为长整型数值赋值给noteId变量这里假设该数据是用于标识笔记的唯一ID
// 通过这种方式获取到从搜索结果中对应的笔记ID信息以便后续加载相关笔记数据。
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
// 同时从Intent中获取SearchManager.USER_QUERY对应的字符串数据赋值给mUserQuery变量这个数据应该就是用户在搜索框中输入的查询内容
// 可以用于后续在笔记中进行相关的搜索匹配等操作,或者在界面上展示用户输入的搜索关键词等情况。
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// 调用DataUtils类的visibleInNoteDatabase方法传入当前应用的内容解析器getContentResolver方法获取用于操作应用的内容数据、noteId前面获取到的笔记ID以及Notes.TYPE_NOTE可能是表示笔记类型的常量
// 该方法用于判断指定的笔记在笔记数据库中是否可见(可能涉及到权限、数据有效性等多方面的判断逻辑),如果不可见,说明对应的笔记不存在或者不符合显示条件。
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
// 创建一个新的Intent用于启动NotesListActivity可能是笔记列表展示的Activity表示要跳转到笔记列表界面因为当前要查看的笔记不存在或者不可见。
Intent jump = new Intent(this, NotesListActivity.class);
// 启动刚创建的Intent对应的Activity即跳转到NotesListActivity让用户可以在笔记列表中进行其他操作比如选择查看其他笔记等。
startActivity(jump);
// 调用showToast方法虽然当前代码中未看到其具体实现但推测是用于弹出一个短暂提示信息给用户的方法传入R.string.error_note_not_exist对应的字符串资源ID
// 目的是给用户弹出一个提示,告知用户要查看的笔记不存在。
showToast(R.string.error_note_not_exist);
// 调用finish方法结束当前的NoteEditActivity因为对应的笔记不存在没必要继续展示当前编辑界面了。
finish();
// 返回false表示初始化Activity状态失败方便调用该方法的地方根据返回值进行相应的后续处理比如结束Activity等操作。
return false;
} else {
// 如果笔记在数据库中是可见的也就是存在且符合显示条件则调用WorkingNote类的load方法传入当前的Activity上下文this以及noteId
// 尝试从数据库或者其他存储位置加载对应的笔记数据并将加载后的笔记对象赋值给mWorkingNote变量以便后续在当前Activity中对该笔记进行编辑等操作。
mWorkingNote = WorkingNote.load(this, noteId);
// 判断加载后的笔记对象是否为null如果是null说明加载笔记数据失败可能是数据库出现问题或者数据损坏等原因导致。
if (mWorkingNote == null) {
// 通过Log.e方法输出错误日志信息TAG是前面定义的用于标识当前类的常量方便在查看日志时定位到是这个类中出现的加载笔记失败的错误
// 日志内容中还包含了具体的笔记ID信息以便更详细地排查问题所在。
Log.e(TAG, "load note failed with note id" + noteId);
// 调用finish方法结束当前的NoteEditActivity因为无法正常加载笔记数据没办法进行后续的编辑操作了。
finish();
// 返回false表示初始化Activity状态失败方便调用该方法的地方根据返回值进行相应的后续处理比如结束Activity等操作。
return false;
}
}
// 设置当前Activity窗口的软键盘显示模式通过按位或操作组合了两个软键盘相关的参数
// WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN表示软键盘初始状态是隐藏的即进入该Activity时软键盘不会自动弹出
// WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE表示当软键盘弹出时Activity的布局会自动调整大小以适应软键盘显示而不被遮挡保证界面的可视性和交互性。
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())) {
// 如果传入的Intent的动作是Intent.ACTION_INSERT_OR_EDIT通常表示要插入新笔记或者编辑现有笔记的动作则进入这个分支以下是创建或编辑笔记相关的初始化操作。
// 从Intent中获取名为Notes.INTENT_EXTRA_FOLDER_ID的长整型额外数据赋值给folderId变量这个数据可能是用于指定笔记所属的文件夹ID如果获取不到则使用默认值0
// 该文件夹ID可以用于后续将新建的笔记关联到对应的文件夹等操作方便笔记的分类管理。
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
// 从Intent中获取名为Notes.INTENT_EXTRA_WIDGET_ID的整型额外数据赋值给widgetId变量如果获取不到则使用AppWidgetManager.INVALID_APPWIDGET_ID这个默认的无效小部件ID值
// 该数据可能与桌面小部件相关比如新建的笔记是否要关联到某个特定的桌面小部件等情况这里先获取对应的ID信息。
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
// 从Intent中获取名为Notes.INTENT_EXTRA_WIDGET_TYPE的整型额外数据赋值给widgetType变量如果获取不到则使用Notes.TYPE_WIDGET_INVALIDE这个默认的无效小部件类型值
// 该数据用于确定小部件的类型,可能会影响笔记在小部件上的显示方式等相关业务逻辑。
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
// 从Intent中获取名为Notes.INTENT_EXTRA_BACKGROUND_ID的整型额外数据赋值给bgResId变量如果获取不到则调用ResourceParser类的getDefaultBgId方法传入当前上下文this获取默认的背景资源ID
// 这个背景资源ID可能用于设置新建笔记的初始背景样式等情况确保笔记有一个合适的背景显示效果。
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// 从Intent中获取名为Intent.EXTRA_PHONE_NUMBER的字符串数据赋值给phoneNumber变量这个数据可能与通话记录相关从变量名推测比如可能是要创建一个基于通话记录信息的笔记
// 这里先获取对应的电话号码信息,后续会根据该信息以及其他相关数据来判断如何创建笔记。
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
// 从Intent中获取名为Notes.INTENT_EXTRA_CALL_DATE的长整型额外数据赋值给callDate变量这个数据可能是通话的日期时间信息同样从变量名推测与通话记录相关
// 结合前面获取的电话号码信息,可以更全面地判断是否要基于通话记录创建笔记以及如何创建笔记等情况。
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate!= 0 && phoneNumber!= null) {
// 判断获取到的电话号码是否为空字符串如果是空字符串说明电话号码数据可能存在问题通过Log.w方法输出一个警告日志信息
// TAG是用于标识当前类的常量方便在查看日志时定位到是这个类中出现的电话号码为空的警告情况虽然这种情况可能不影响程序继续运行但提示可能存在数据异常。
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
// 调用DataUtils类的getNoteIdByPhoneNumberAndCallDate方法传入当前应用的内容解析器getContentResolver方法获取、phoneNumber前面获取的电话号码以及callDate通话日期时间
// 该方法可能用于根据电话号码和通话日期时间去数据库中查找对应的笔记ID如果查找到的笔记ID大于0说明存在对应的笔记记录。
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
// 如果查找到对应的笔记ID则调用WorkingNote类的load方法传入当前的Activity上下文this以及noteId尝试加载对应的笔记数据并将加载后的笔记对象赋值给mWorkingNote变量
// 以便后续对该笔记进行编辑等操作,这里相当于找到了已有的基于通话记录的笔记,直接加载进行编辑。
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
// 如果加载笔记数据失败返回的笔记对象为null通过Log.e方法输出错误日志信息包含具体的笔记ID信息方便排查加载失败的原因
// 然后调用finish方法结束当前的NoteEditActivity因为无法正常加载笔记数据没办法进行后续的编辑操作了。
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
// 如果根据电话号码和通话日期时间没有查找到对应的笔记ID即noteId小于等于0则调用WorkingNote类的createEmptyNote方法传入当前的Activity上下文this、folderId文件夹ID
// widgetId小部件ID、widgetType小部件类型以及bgResId背景资源ID创建一个新的空笔记对象并将其赋值给mWorkingNote变量后续可以对这个新笔记进行编辑等操作。
// 接着调用新创建笔记对象mWorkingNote的convertToCallNote方法传入phoneNumber电话号码和callDate通话日期时间
// 目的是将这个新创建的空笔记转换为基于通话记录信息的笔记,可能会设置笔记的相关内容、格式等符合通话记录笔记的要求。
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
// 如果通话日期时间为0或者电话号码为null即不满足基于通话记录创建笔记的条件则调用WorkingNote类的createEmptyNote方法传入当前的Activity上下文this、folderId文件夹ID
// widgetId小部件ID、widgetType小部件类型以及bgResId背景资源ID创建一个新的空笔记对象并将其赋值给mWorkingNote变量后续可以对这个新笔记进行编辑等操作
// 这里就是创建一个普通的新笔记,没有基于通话记录信息进行特殊设置。
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
// 设置当前Activity窗口的软键盘显示模式通过按位或操作组合了两个软键盘相关的参数
// WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE表示当软键盘弹出时Activity的布局会自动调整大小以适应软键盘显示而不被遮挡
// WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE表示软键盘初始状态是可见的即进入该Activity时软键盘会自动弹出方便用户直接输入内容符合新建笔记时通常需要输入内容的场景。
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
// 如果传入的Intent的动作既不是Intent.ACTION_VIEW也不是Intent.ACTION_INSERT_OR_EDIT说明传入的Intent不符合预期的动作要求
// 通过Log.e方法输出错误日志信息提示不应该支持这种未指定正确动作的Intent情况方便排查问题然后调用finish方法结束当前的NoteEditActivity
// 因为无法根据这种不符合要求的Intent进行正确的初始化和后续操作了。
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
// 调用mWorkingNote对象代表正在编辑的笔记的setOnSettingStatusChangedListener方法传入当前类this因为当前类实现了NoteSettingChangedListener接口
// 这样就可以监听笔记相关设置(比如背景颜色、字体大小等设置)发生变化的事件,当设置发生改变时,会触发相应的回调方法,方便在界面上进行实时更新等操作,保证笔记的显示和设置保持一致。
mWorkingNote.setOnSettingStatusChangedListener(this);
// 返回true表示成功初始化了Activity的状态方便调用该方法的地方根据返回值进行相应的后续处理比如继续执行其他初始化完成后的操作等。
return true;
}
// onResume方法是在Activity从暂停状态恢复到前台可见时被调用的方法通常用于在Activity重新可见时进行一些数据更新、界面刷新等操作确保界面显示的内容是最新的、符合当前状态的。
// 这里首先调用父类的onResume方法确保完成系统要求的基础恢复操作然后调用initNoteScreen方法虽然当前代码中未看到其具体实现但推测是用于初始化笔记编辑界面的相关显示内容、布局等情况
// 使得笔记编辑界面在恢复可见时能正确展示笔记的相关信息以及各种编辑功能组件等内容,提供良好的用户编辑体验。
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
// initNoteScreen方法用于初始化笔记编辑界面的显示内容和相关布局等确保界面正确展示笔记的各种信息以及编辑功能组件的状态。
private void initNoteScreen() {
// 设置笔记编辑文本框mNoteEditor的文本外观样式通过调用setTextAppearance方法传入当前的Activity上下文this以及通过TextAppearanceResources.getTexAppearanceResource(mFontSizeId)获取的文本外观资源ID
// 这里的mFontSizeId应该是之前确定的字体大小对应的资源标识以此来设置笔记编辑文本显示的字体、颜色、大小等外观样式使其符合用户选择或者默认的设置要求。
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 判断正在编辑的笔记mWorkingNote的复选列表模式getCheckListMode方法获取是否等于TextNote.MODE_CHECK_LIST可能是表示复选列表模式的常量如果是则说明笔记处于复选列表模式。
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 如果是复选列表模式则调用switchToListMode方法传入笔记的内容mWorkingNote.getContent方法获取该方法应该是用于将笔记内容按照复选列表的形式进行展示等相关处理
// 不过从当前代码看不到switchToListMode方法的具体实现细节推测是会将文本内容解析并转换为带有复选框等适合列表展示的形式在界面上显示出来。
switchToListMode(mWorkingNote.getContent());
} else {
// 如果笔记不是复选列表模式则调用getHighlightQueryResult方法传入笔记的内容mWorkingNote.getContent方法获取以及用户查询内容mUserQuery
// 该方法可能是用于在笔记内容中根据用户查询内容进行高亮显示相关的文本匹配处理然后将处理后的结果设置为笔记编辑文本框mNoteEditor的显示文本内容方便用户查看查询结果在笔记中的位置等情况。
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
// 设置笔记编辑文本框mNoteEditor的光标位置为文本的末尾通过调用setSelection方法并传入文本的长度mNoteEditor.getText().length()获取文本长度),
// 这样当界面显示笔记内容后,光标会定位在文本末尾,符合一般的编辑场景,方便用户直接在末尾继续输入内容等操作。
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// 遍历sBgSelectorSelectionMap集合中的所有键这些键应该是代表不同的颜色资源标识通过findViewById方法根据sBgSelectorSelectionMap中每个键对应的ID即颜色选择按钮选中状态图标ID找到对应的视图
// 然后将这些视图的可见性设置为View.GONE即隐藏这些视图目的可能是初始化时先隐藏所有的颜色选择按钮选中状态图标后续根据实际选择的颜色再显示对应的选中图标来提示用户当前选择的颜色。
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// 设置笔记编辑界面头部视图面板mHeadViewPanel的背景资源通过调用setBackgroundResource方法传入正在编辑的笔记mWorkingNote的标题背景资源IDgetTitleBgResId方法获取
// 以此来设置头部面板的背景样式,使其与笔记的相关设置匹配,比如可能是设置一个特定颜色或者图片背景等效果。
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
// 设置笔记编辑文本框所在的面板mNoteEditorPanel的背景资源通过调用setBackgroundResource方法传入正在编辑的笔记mWorkingNote的背景颜色资源IDgetBgColorResId方法获取
// 这样可以设置笔记编辑区域的背景颜色等样式,使整个编辑界面的背景显示符合笔记的设置情况。
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 设置笔记编辑界面头部中用于显示笔记修改时间的TextViewmNoteHeaderHolder.tvModified的文本内容通过调用DateUtils.formatDateTime方法传入当前的Activity上下文this
// 正在编辑的笔记mWorkingNote的修改日期getModifiedDate方法获取以及一些日期时间格式化的标志位如显示日期、显示数字格式的日期、显示时间、显示年份等标志通过按位或操作组合
// 将笔记的修改日期按照指定的格式进行格式化后设置为TextView的显示文本方便用户查看笔记的修改时间信息。
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方法该方法用于根据笔记是否设置了提醒等情况来显示或隐藏相关的提醒头部信息如提醒图标、提醒时间文本等虽然这里有个TODO注释提到要添加设置提醒的菜单
// 但目前因为日期时间选择器DateTimePicker还没准备好所以暂时未实现完整的提醒设置功能只是进行了基本的提醒信息显示处理。
showAlertHeader();
}
// showAlertHeader方法用于根据笔记是否设置了提醒以及提醒时间是否过期等情况来显示或隐藏笔记编辑界面头部的提醒相关视图组件如提醒图标、提醒时间文本等并设置相应的文本内容。
private void showAlertHeader() {
// 判断正在编辑的笔记mWorkingNote是否设置了时钟提醒hasClockAlert方法判断如果设置了时钟提醒则进入以下逻辑。
if (mWorkingNote.hasClockAlert()) {
// 获取当前系统时间的时间戳以毫秒为单位通过System.currentTimeMillis方法获取用于后续与笔记的提醒时间进行比较判断提醒是否过期等情况。
long time = System.currentTimeMillis();
// 判断当前系统时间是否大于笔记的提醒时间mWorkingNote.getAlertDate方法获取提醒时间如果大于则说明提醒已经过期。
if (time > mWorkingNote.getAlertDate()) {
// 如果提醒过期将笔记编辑界面头部用于显示提醒时间的TextViewmNoteHeaderHolder.tvAlertDate的文本内容设置为R.string.note_alert_expired对应的字符串资源可能是显示“提醒已过期”之类的提示文本
// 告知用户该笔记的提醒已经过期了。
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
// 如果提醒未过期则调用DateUtils.getRelativeTimeSpanString方法传入笔记的提醒时间mWorkingNote.getAlertDate、当前系统时间time以及DateUtils.MINUTE_IN_MILLIS可能是用于表示分钟的时间间隔常量用于格式化相对时间的精度等情况
// 该方法会根据传入的时间参数计算并返回一个相对时间的字符串表示比如“10分钟后”之类的格式然后将这个相对时间字符串设置为笔记编辑界面头部用于显示提醒时间的TextViewmNoteHeaderHolder.tvAlertDate的文本内容
// 方便用户直观地了解距离提醒还有多久时间等信息。
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
// 将笔记编辑界面头部用于显示提醒时间的TextViewmNoteHeaderHolder.tvAlertDate的可见性设置为View.VISIBLE即显示该视图以便用户能看到提醒时间相关信息。
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
// 将笔记编辑界面头部用于显示提醒图标可能表示笔记是否设置了提醒等情况的ImageViewmNoteHeaderHolder.ivAlertIcon的可见性设置为View.VISIBLE即显示该视图
// 让用户能看到提醒图标,直观地知道该笔记设置了提醒功能。
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
// 如果正在编辑的笔记mWorkingNote没有设置时钟提醒则将笔记编辑界面头部用于显示提醒时间的TextViewmNoteHeaderHolder.tvAlertDate的可见性设置为View.GONE即隐藏该视图
// 因为没有提醒所以不需要显示提醒时间相关信息。
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
// 将笔记编辑界面头部用于显示提醒图标可能表示笔记是否设置了提醒等情况的ImageViewmNoteHeaderHolder.ivAlertIcon的可见性设置为View.GONE即隐藏该视图
// 同样因为没有设置提醒,不需要显示提醒图标。
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
}
// onNewIntent方法在Activity已经存在于任务栈中又接收到一个新的Intent启动该Activity时被调用用于根据新的Intent来更新Activity的状态等相关操作。
// 首先调用父类的onNewIntent方法确保完成系统要求的基础操作然后调用initActivityState方法传入新接收到的Intent尝试依据这个新Intent来重新初始化Activity的状态
// 比如根据新Intent中的信息加载不同的笔记数据、更新界面显示等操作保证Activity的显示和操作逻辑与新的Intent要求相符。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
// onSaveInstanceState方法在Activity即将被系统销毁例如由于系统内存不足、屏幕旋转等情况时被调用用于保存Activity的当前状态数据以便在Activity重新创建时能够恢复到之前的状态。
@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
*/
// 判断正在编辑的笔记mWorkingNote是否已经存在于数据库中existInDatabase方法判断如果不存在比如是新建的还未保存过的笔记则调用saveNote方法虽然当前代码中未看到其具体实现但推测是用于保存笔记数据到数据库等操作
// 目的是先保存笔记以生成一个笔记ID因为如果笔记不存在数据库中重新创建Activity时就没办法恢复到当前的编辑状态了所以需要先保存获取ID。
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 将正在编辑的笔记mWorkingNote的笔记IDgetNoteId方法获取放入outState这个Bundle对象中以Intent.EXTRA_UID作为键方便在Activity重新创建时通过这个键从保存的状态数据中获取笔记ID
// 进而恢复笔记的相关数据和编辑状态等信息同时通过Log.d方法输出调试日志信息记录保存的笔记ID等情况方便查看和排查问题。
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
// dispatchTouchEvent方法用于处理Activity内的触摸事件分发逻辑它会在触摸事件发生时被调用决定是否将触摸事件继续传递给子视图或者进行相应的处理操作。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 判断笔记背景颜色选择相关的视图mNoteBgColorSelector的可见性是否为View.VISIBLE即是否显示并且判断触摸事件ev是否不在该视图的范围内通过inRangeOfView方法判断虽然当前代码中未看到其具体实现但推测是用于判断触摸点是否在视图范围内的方法
// 如果背景颜色选择视图是显示状态且触摸事件不在其范围内则将该视图的可见性设置为View.GONE即隐藏该视图目的可能是当用户点击了背景颜色选择视图区域外时自动隐藏该选择视图提升用户界面的交互友好性
// 然后返回true表示已经处理了该触摸事件不需要再继续传递给其他子视图或者系统默认的处理逻辑了。
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&&!inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
// 与上面处理背景颜色选择视图类似的逻辑判断字体大小选择相关的视图mFontSizeSelector的可见性是否为View.VISIBLE即是否显示并且判断触摸事件ev是否不在该视图的范围内通过inRangeOfView方法判断
// 如果字体大小选择视图是显示状态且触摸事件不在其范围内则将该视图的可见性设置为View.GONE即隐藏该视图同样是为了在用户点击字体大小选择视图区域外时自动隐藏该选择视图
// 然后返回true表示已经处理了该触摸事件不需要再继续传递给其他子视图或者系统默认的处理逻辑了。
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&&!inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
// 如果上述条件都不满足即触摸事件不需要在这两个视图相关的逻辑中处理则调用父类的dispatchTouchEvent方法将触摸事件继续传递给系统默认的触摸事件处理逻辑或者子视图进行相应的处理
// 并返回父类方法的处理结果保证触摸事件能按照正常的Android系统触摸事件分发机制进行后续处理。
return super.dispatchTouchEvent(ev);
}
// `inRangeOfView`方法用于判断给定的触摸事件(`MotionEvent`)发生的位置是否处于指定的视图(`View`)范围内,返回一个布尔值来表示判断结果。
private boolean inRangeOfView(View view, MotionEvent ev) {
// 创建一个长度为2的整数数组`location`,用于存储视图在屏幕上的坐标位置信息。
// 数组中第一个元素对应视图左上角的x坐标第二个元素对应视图左上角的y坐标。
int []location = new int[2];
// 通过调用视图(`view`)的`getLocationOnScreen`方法,获取该视图在屏幕坐标系中的坐标位置,并将其存储到`location`数组中。
// 这样就能得到视图左上角顶点相对于屏幕左上角的坐标值,方便后续与触摸事件的坐标进行比较。
view.getLocationOnScreen(location);
// 从`location`数组中取出第一个元素即视图左上角的x坐标值赋值给变量`x`用于后续与触摸事件的x坐标进行对比判断。
int x = location[0];
// 从`location`数组中取出第二个元素即视图左上角的y坐标值赋值给变量`y`用于后续与触摸事件的y坐标进行对比判断。
int y = location[1];
// 以下是判断触摸事件的坐标是否在视图范围内的逻辑:
// 首先判断触摸事件的x坐标通过`ev.getX()`获取是否小于视图的x坐标`x`),如果小于,说明触摸点在视图的左侧外部,不在视图范围内,返回`false`。
// 接着判断触摸事件的x坐标是否大于视图的x坐标加上视图的宽度`x + view.getWidth()`),如果大于,意味着触摸点在视图的右侧外部,同样不在视图范围内,返回`false`。
// 然后判断触摸事件的y坐标通过`ev.getY()`获取是否小于视图的y坐标`y`),若小于,表示触摸点在视图的上方外部,不在视图范围内,返回`false`。
// 最后判断触摸事件的y坐标是否大于视图的y坐标加上视图的高度`y + view.getHeight()`),要是大于,说明触摸点在视图的下方外部,不在视图范围内,返回`false`。
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;
}
// 如果上述所有判断条件都不满足,也就是触摸事件的坐标在视图的坐标范围之内,那么返回`true`,表示触摸事件发生在该视图的范围内。
return true;
}
// `initResources`方法主要用于初始化与笔记编辑界面相关的各种资源,例如查找并赋值界面中的各个视图组件、设置点击事件监听器、获取并处理用户偏好设置等,为后续的界面交互操作做准备。
private void initResources() {
// 通过`findViewById`方法依据资源ID`R.id.note_title`)在当前布局中查找对应的视图,并将其赋值给`mHeadViewPanel`变量。
// 这个视图通常代表笔记编辑界面头部的整体面板,后续可以对其进行如设置背景、调整布局等各种操作,以定制头部面板的显示效果。
mHeadViewPanel = findViewById(R.id.note_title);
// 创建一个`HeadViewHolder`类型的实例对象`mNoteHeaderHolder`,用于集中管理笔记编辑界面头部相关的各个视图元素的引用。
// 这样可以更方便地对这些头部视图进行统一操作,提高代码的可读性和可维护性,避免在代码中分散地操作各个头部视图组件。
mNoteHeaderHolder = new HeadViewHolder();
// 通过`findViewById`方法依据资源ID`R.id.tv_modified_date`)查找对应的视图,将其转换为`TextView`类型后赋值给`mNoteHeaderHolder.tvModified`变量。
// 该`TextView`组件用于在界面上显示笔记的修改日期信息,后续可以通过这个变量来设置文本内容、调整文本样式等,以向用户展示正确的笔记修改时间。
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
// 同样使用`findViewById`方法按照资源ID`R.id.iv_alert_icon`)查找视图,转换为`ImageView`类型后赋值给`mNoteHeaderHolder.ivAlertIcon`变量。
// 这个`ImageView`组件可能用于显示与笔记提醒相关的图标,比如用来提示用户该笔记是否设置了提醒、提醒是否已过期等情况,可通过代码对其图片资源、可见性等属性进行操作。
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
// 借助`findViewById`方法根据资源ID`R.id.tv_alert_date`)找到对应的视图,转换为`TextView`类型后赋值给`mNoteHeaderHolder.tvAlertDate`变量。
// 此`TextView`用于在界面上展示笔记的提醒日期和时间信息,方便用户直观地知晓笔记的提醒时间安排,可对其文本内容进行更新等操作以反映最新的提醒时间情况。
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
// 通过`findViewById`方法依据资源ID`R.id.btn_set_bg_color`)查找对应的视图,转换为`ImageView`类型后赋值给`mNoteHeaderHolder.ibSetBgColor`变量。
// 该`ImageView`很可能是用于触发设置笔记背景颜色的操作按钮,例如用户点击它后会弹出可供选择背景颜色的界面等,同时为其设置点击事件监听器(`this`表示当前类实现了`OnClickListener`接口,会在点击时执行对应的点击逻辑)。
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
// 使用`findViewById`方法按照资源ID`R.id.note_edit_view`)查找对应的视图,转换为`EditText`类型后赋值给`mNoteEditor`变量。
// 这个`EditText`组件是笔记编辑界面的核心部分,是供用户输入和编辑笔记文本内容的文本框,后续可以通过代码对其文本内容进行获取、修改、设置样式等各种操作,以实现笔记的编辑功能。
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
// 通过`findViewById`方法依据资源ID`R.id.sv_note_edit`)查找对应的视图,并将其赋值给`mNoteEditorPanel`变量。
// 这个视图大概是包含笔记编辑文本框(`mNoteEditor`)以及其他相关编辑功能按钮等元素的整个面板视图,通过操作这个变量,可以对编辑区域进行整体的布局调整、显示隐藏等操作,方便管理编辑界面的整体显示效果。
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
// 通过`findViewById`方法根据资源ID`R.id.note_bg_color_selector`)查找对应的视图,并将其赋值给`mNoteBgColorSelector`变量。
// 该视图通常用于展示背景颜色选择相关的操作界面,比如包含多个用于选择不同背景颜色的按钮等,用户可以通过操作这个区域来选择自己想要的笔记背景颜色。
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
// 遍历`sBgSelectorBtnsMap`集合中的所有键这些键对应的是各个背景颜色选择按钮的资源ID针对每个键即每个背景颜色选择按钮的资源ID执行以下操作
for (int id : sBgSelectorBtnsMap.keySet()) {
// 通过`findViewById`方法依据当前的资源ID`id`)查找对应的视图,并将其转换为`ImageView`类型后赋值给`iv`变量。
// 这里找到的`ImageView`就是具体的背景颜色选择按钮视图,每个按钮对应一种可选择的背景颜色。
ImageView iv = (ImageView) findViewById(id);
// 为查找到的每个背景颜色选择按钮(`ImageView`)设置点击事件监听器(`this`,因为当前类实现了`OnClickListener`接口),
// 这样当用户点击任意一个背景颜色选择按钮时,就会触发对应的点击事件处理逻辑,实现选择背景颜色的功能。
iv.setOnClickListener(this);
}
// 通过`findViewById`方法依据资源ID`R.id.font_size_selector`)查找对应的视图,并将其赋值给`mFontSizeSelector`变量。
// 这个视图可能用于展示字体大小选择相关的操作界面,例如包含了不同字体大小选择按钮所在的布局区域等,方便用户为笔记编辑文本选择合适的字体大小。
mFontSizeSelector = findViewById(R.id.font_size_selector);
// 遍历`sFontSizeBtnsMap`集合中的所有键这些键对应的是字体大小选择按钮所在布局的资源ID针对每个键即每个字体大小选择按钮所在布局的资源ID执行以下操作
for (int id : sFontSizeBtnsMap.keySet()) {
// 通过`findViewById`方法依据当前的资源ID`id`)查找对应的视图,并将其赋值给`view`变量。
// 这里找到的视图就是具体的字体大小选择按钮所在的布局视图或者相关的操作区域视图,用户点击这些区域就能进行字体大小的选择操作。
View view = findViewById(id);
// 为查找到的字体大小选择按钮所在的布局视图(`View`)设置点击事件监听器(`this`,因为当前类实现了`OnClickListener`接口),
// 以便在用户点击相应区域时执行对应的选择字体大小的操作逻辑,实现改变笔记编辑文本字体大小的功能。
view.setOnClickListener(this);
};
// 通过`PreferenceManager`的`getDefaultSharedPreferences`方法获取应用的默认`SharedPreferences`实例,用于读取和保存用户在应用中设置的偏好信息,将其赋值给`mSharedPrefs`变量。
// 借助这个变量,后续可以方便地操作如字体大小偏好等各种具体的用户偏好设置数据,实现个性化的应用功能。
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// 从`SharedPreferences``mSharedPrefs`)中获取名为`PREFERENCE_FONT_SIZE`(之前定义的用于存储字体大小偏好设置的键)对应的整数值,
// 如果不存在该键对应的值,则使用`ResourceParser.BG_DEFAULT_FONT_SIZE`作为默认值,将获取到的值赋值给`mFontSizeId`变量。
// 这个`mFontSizeId`变量用于记录当前选择的字体大小对应的资源标识,后续会依据这个标识来设置笔记编辑文本的字体大小等相关操作,确保文本显示符合用户的偏好设置。
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}
*/
// 判断`mFontSizeId`的值是否大于等于`TextAppearanceResources.getResourcesSize()`推测是获取所有文本外观资源数量的方法这里用于判断字体大小资源ID是否超出有效范围
// 如果超出了有效范围,说明可能出现了异常情况或者数据错误,为了避免程序出现问题,将`mFontSizeId`重新设置为`ResourceParser.BG_DEFAULT_FONT_SIZE`
// 这是一种简单的容错处理机制以确保后续使用字体大小资源标识时不会因为非法值而导致错误从注释中的“HACKME”可以看出这可能是一种临时的修复手段后续可能需要更完善的处理方式。
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
// 通过`findViewById`方法依据资源ID`R.id.note_edit_list`)查找对应的视图,并将其转换为`LinearLayout`类型后赋值给`mEditTextList`变量。
// 从变量名推测,这个`LinearLayout`可能用于存放多个与编辑文本相关的视图元素,不过具体用途还需要结合更多的代码逻辑来进一步确定,可能用于展示一些辅助编辑的文本内容或者相关操作按钮等。
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
// `onPause`方法是Android系统提供的生命周期方法当Activity即将失去焦点、进入暂停状态时会被调用。
// 通常在这个方法中进行一些数据保存、资源释放或者界面状态清理等操作以确保应用在Activity暂时不可见时的数据一致性和资源合理利用。
@Override
protected void onPause() {
// 首先调用父类(`Activity`类)的`onPause`方法确保执行系统默认的暂停相关操作比如暂停一些动画、释放部分系统资源等这是Android开发规范要求的保证整个Activity生命周期流程的完整性。
super.onPause();
// 调用`saveNote`方法来保存正在编辑的笔记数据,`saveNote`方法的具体实现这里看不到,但推测它会将笔记的相关内容(如文本内容、各种设置等)保存到合适的存储位置(比如数据库等)。
// 如果`saveNote`方法返回`true`,表示笔记数据保存成功,此时通过`Log.d`方法输出一条调试日志信息,用于记录笔记数据已成功保存以及笔记内容的长度情况。
// 其中`TAG`是之前定义的用于标识当前类的常量,方便在查看日志时定位到是这个类中的操作记录,日志内容里包含了笔记内容的长度(通过`mWorkingNote.getContent().length()`获取),有助于后续排查数据保存相关的问题或者了解笔记内容的大致情况。
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
// 调用`clearSettingState`方法该方法用于清理一些界面设置相关的状态比如隐藏某些临时显示的视图像背景颜色选择、字体大小选择相关的视图等确保界面在Activity进入暂停状态时处于一个合理、整洁的状态避免出现显示异常等问题具体的清理逻辑在`clearSettingState`方法中实现。
clearSettingState();
}
// `updateWidget`方法的主要作用是更新与当前正在编辑的笔记相关联的桌面小部件的显示内容,通过发送广播的方式通知对应的小部件进行更新操作。
private void updateWidget() {
// 创建一个新的`Intent`对象,设置其动作为`AppWidgetManager.ACTION_APPWIDGET_UPDATE`这个特定的动作是Android系统用于触发桌面小部件更新的标准动作
// 发送这样的`Intent`广播后,系统就能识别并通知相关的小部件提供类(实现了小部件更新逻辑的类)来执行更新操作,以刷新小部件显示的内容(比如更新小部件上展示的笔记相关信息等)。
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 判断正在编辑的笔记(`mWorkingNote`)的小部件类型(通过`getWidgetType`方法获取)是否等于`Notes.TYPE_WIDGET_2X`可能是表示某种特定2倍尺寸小部件的常量如果是则进入以下逻辑指定要更新的小部件对应的提供类。
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
// 通过`intent`的`setClass`方法,将`Intent`的目标类设置为`NoteWidgetProvider_2x.class`这样当广播发送出去后系统会根据这个设置将更新意图传递给对应的2倍尺寸小部件的提供类
// 由`NoteWidgetProvider_2x`类来处理具体的小部件更新操作,比如重新加载笔记数据并更新小部件上显示的文本、样式等信息,使其与当前笔记的最新状态保持一致。
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
// 类似地,如果笔记的小部件类型是`Notes.TYPE_WIDGET_4X`可能是表示某种特定4倍尺寸小部件的常量则将`Intent`的目标类设置为`NoteWidgetProvider_4x.class`
// 以便广播能准确地将更新意图传递给对应的4倍尺寸小部件的提供类由该类来负责处理小部件的更新逻辑确保4倍尺寸小部件展示的内容能反映当前笔记的最新情况。
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
// 如果笔记的小部件类型既不是`Notes.TYPE_WIDGET_2X`也不是`Notes.TYPE_WIDGET_4X`,说明遇到了不支持的小部件类型,此时通过`Log.e`方法输出一条错误日志信息,
// 记录不支持的小部件类型情况,方便后续排查问题(比如是否是小部件配置错误或者代码逻辑没有涵盖该类型等原因导致),然后直接返回,不再进行后续的广播发送等操作,因为无法确定要更新的目标小部件了。
Log.e(TAG, "Unspported widget type");
return;
}
// 将正在编辑的笔记(`mWorkingNote`的小部件ID通过`getWidgetId`方法获取)放入一个新创建的整数数组中,然后通过`putExtra`方法将这个数组作为`AppWidgetManager.EXTRA_APPWIDGET_IDS`这个额外数据添加到`Intent`中。
// 这样接收广播的小部件提供类就可以根据这个小部件ID来准确地确定具体要更新哪个小部件了避免对其他无关小部件进行不必要的更新操作保证更新操作是针对正确的目标小部件进行的。
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
// 通过调用`sendBroadcast`方法发送创建好的`Intent`广播,将小部件更新的意图传递给系统,系统会根据`Intent`中的各种设置如动作、目标类、小部件ID等信息找到对应的小部件提供类并触发其更新逻辑从而实现小部件显示内容的更新。
sendBroadcast(intent);
// 通过`setResult`方法设置当前Activity返回的结果码为`RESULT_OK`,并将更新小部件的`Intent`作为返回结果数据这样在一些启动该Activity并等待结果返回的场景下比如通过`startActivityForResult`启动),
// 调用方可以根据返回的结果码和数据来判断小部件更新操作是否成功等情况,方便进行后续的流程控制和错误处理。
setResult(RESULT_OK, intent);
}
// `onClick`方法是实现了`OnClickListener`接口后必须重写的方法用于处理各个设置了点击事件监听器的视图被点击时的具体操作逻辑根据被点击视图的不同资源ID来执行相应的功能。
public void onClick(View v) {
// 获取被点击视图的资源ID并赋值给变量`id`,通过这个`id`来判断到底是哪个视图被点击了,进而执行不同的操作逻辑分支,实现不同的功能响应。
int id = v.getId();
// 判断被点击的视图的资源ID是否等于`R.id.btn_set_bg_color`,也就是判断是否是用于触发设置笔记背景颜色的那个`ImageView`按钮被点击了,如果是,则进入以下相关的背景颜色选择显示逻辑。
if (id == R.id.btn_set_bg_color) {
// 将笔记背景颜色选择相关的视图(`mNoteBgColorSelector`)的可见性设置为`View.VISIBLE`(即显示该视图),这样用户就能看到背景颜色选择的相关界面了,例如包含的各种颜色选择按钮等,方便用户进行下一步的背景颜色选择操作。
mNoteBgColorSelector.setVisibility(View.VISIBLE);
// 通过`findViewById`方法,依据`sBgSelectorSelectionMap`中当前笔记背景颜色ID通过`mWorkingNote.getBgColorId`方法获取对应的资源ID查找对应的视图
// 然后将该视图的可见性设置为`View.VISIBLE`(即显示该视图),这个视图可能是用于提示当前选择的背景颜色的选中状态图标,显示出来可以直观地告知用户当前所选择的背景颜色情况。
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {
// 判断`sBgSelectorBtnsMap`这个映射表中是否包含被点击视图的资源ID若包含则说明是背景颜色选择按钮被点击了因为`sBgSelectorBtnsMap`建立了背景颜色选择按钮ID与颜色资源标识的映射关系此时进入以下背景颜色切换逻辑。
// 首先通过`findViewById`方法,依据`sBgSelectorSelectionMap`中当前笔记背景颜色ID通过`mWorkingNote.getBgColorId`方法获取对应的资源ID查找对应的视图
// 并将该视图的可见性设置为`View.GONE`(即隐藏该视图),目的是先隐藏之前显示的背景颜色选中状态图标,因为即将要切换到新的背景颜色了,需要更新显示状态。
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.GONE);
// 根据被点击的背景颜色选择按钮的资源ID通过`sBgSelectorBtnsMap.get(id)`获取对应的颜色资源标识),调用`mWorkingNote`的`setBgColorId`方法来设置正在编辑的笔记的背景颜色ID
// 这样就完成了背景颜色的切换选择操作,将笔记的背景颜色设置更新为用户新选择的颜色了。
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
// 将笔记背景颜色选择相关的视图(`mNoteBgColorSelector`)的可见性设置为`View.GONE`(即隐藏该视图),在完成背景颜色选择后隐藏选择界面,保持界面的简洁和交互友好性,避免选择界面一直显示影响用户正常操作其他功能。
mNoteBgColorSelector.setVisibility(View.GONE);
} else if (sFontSizeBtnsMap.containsKey(id)) {
// 判断`sFontSizeBtnsMap`这个映射表中是否包含被点击视图的资源ID若包含则表示是字体大小选择按钮所在的布局或者相关操作区域被点击了因为`sFontSizeBtnsMap`建立了字体大小选择按钮所在布局ID与字体大小资源标识的映射关系进入以下字体大小切换相关的逻辑。
// 首先通过`findViewById`方法,依据`sFontSelectorSelectionMap`中当前字体大小资源标识(`mFontSizeId`对应的资源ID查找对应的视图
// 并将该视图的可见性设置为`View.GONE`(即隐藏该视图),目的是先隐藏之前显示的字体大小选中状态图标,因为要进行字体大小的切换操作了,需要更新显示状态。
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
// 根据被点击的字体大小选择按钮所在布局的资源ID通过`sFontSizeBtnsMap.get(id)`获取对应的字体大小资源标识),将获取到的字体大小资源标识赋值给`mFontSizeId`变量,
// 以此来更新当前选择的字体大小对应的资源标识,实现字体大小的切换选择,后续会依据这个新的`mFontSizeId`来设置笔记编辑文本的字体大小。
mFontSizeId = sFontSizeBtnsMap.get(id);
// 通过`mSharedPrefs`(之前获取的用于存储用户偏好设置的`SharedPreferences`实例)的`edit`方法获取一个编辑器对象,然后使用这个编辑器对象的`putInt`方法,
// 将新的字体大小资源标识(`mFontSizeId`)存储到名为`PREFERENCE_FONT_SIZE`(之前定义的用于存储字体大小偏好设置的键)的偏好设置项中,最后调用`commit`方法提交修改,
// 这样就将用户新选择的字体大小保存到了用户偏好设置中,下次打开应用或者进入相关界面时可以恢复到这个字体大小设置。
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
// 通过`findViewById`方法,依据`sFontSelectorSelectionMap`中更新后的字体大小资源标识(`mFontSizeId`对应的资源ID查找对应的视图
// 并将该视图的可见性设置为`View.VISIBLE`(即显示该视图),这个视图是用于提示当前选择的字体大小的选中状态图标,显示出来可以让用户直观地看到当前所选择的字体大小情况。
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
// 判断正在编辑的笔记(`mWorkingNote`)的复选列表模式(通过`getCheckListMode`方法获取)是否等于`TextNote.MODE_CHECK_LIST`(可能是表示笔记处于复选列表模式的常量),如果是,则进入以下逻辑。
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 调用`getWorkingText`方法(虽然当前代码中看不到其具体实现,但推测是用于获取笔记编辑文本框中的当前文本内容等相关操作的方法)获取相关文本内容,
// 然后调用`switchToListMode`方法,传入笔记的内容(`mWorkingNote.getContent`方法获取),该方法应该是用于将笔记内容按照复选列表的形式进行重新展示等相关处理,
// 不过从当前代码看不到`switchToListMode`方法的具体实现细节,推测是会将文本内容解析并转换为带有复选框等适合列表展示的形式在界面上显示出来,以适配字体大小变化后的显示效果。
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
// 如果笔记不是处于复选列表模式,则调用`mNoteEditor`(笔记编辑文本框)的`setTextAppearance`方法传入当前的Activity上下文`this`)以及通过`TextAppearanceResources.getTexAppearanceResource(mFontSizeId)`获取的文本外观资源ID
// 以此来设置笔记编辑文本的字体、颜色、大小等外观样式,使其根据用户新选择的字体大小进行相应的更新显示,确保文本显示符合新的字体大小设置要求。
mNoteEditor.setTextAppearance(this,
TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
// 将字体大小选择相关的视图(`mFontSizeSelector`)的可见性设置为`View.GONE`(即隐藏该视图),在完成字体大小选择后隐藏选择界面,保持界面的整洁和便于用户操作其他功能。
mFontSizeSelector.setVisibility(View.GONE);
}
}
// `onBackPressed`方法是Android系统提供的生命周期方法当用户按下设备的返回键时会被调用通常用于处理返回操作相关的逻辑比如保存数据、确认是否退出等。
@Override
public void onBackPressed() {
// 调用`clearSettingState`方法,该方法用于清理一些界面设置相关的状态,比如隐藏某些临时显示的视图(像背景颜色选择、字体大小选择相关的视图等),
// 如果`clearSettingState`方法返回`true`,表示成功清理了相关状态,此时直接返回,不再执行后续的操作,因为一些临时显示的界面元素已经被清理掉了,可能已经满足了返回的前置条件。
if(clearSettingState()) {
return;
}
// 调用`saveNote`方法来保存正在编辑的笔记数据,确保在用户返回前将笔记的最新内容和设置等保存好,避免数据丢失,`saveNote`方法的具体实现这里看不到,但推测它会将笔记相关内容保存到合适的存储位置(比如数据库等)。
saveNote();
// 调用父类(`Activity`类)的`onBackPressed`方法执行系统默认的返回操作逻辑比如关闭当前Activity、返回上一个界面等这是Android开发规范要求的保证整个Activity生命周期流程的完整性以及返回操作的正常执行。
super.onBackPressed();
}
// `clearSettingState`方法用于清理界面上与设置相关的一些临时显示状态,主要是隐藏背景颜色选择和字体大小选择相关的视图,返回一个布尔值表示是否成功进行了状态清理操作。
private boolean clearSettingState() {
// 首先判断笔记背景颜色选择相关的视图(`mNoteBgColorSelector`)的可见性是否等于`View.VISIBLE`,也就是判断该视图当前是否处于显示状态。
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
// 如果背景颜色选择相关视图处于显示状态,则将其可见性设置为`View.GONE`,即将该视图隐藏起来,这样在不需要显示背景颜色选择界面时(比如完成背景颜色选择操作后等情况),可以清理界面,使其更加简洁。
mNoteBgColorSelector.setVisibility(View.GONE);
// 返回`true`表示已经成功进行了一次状态清理操作,即隐藏了背景颜色选择相关的视图。
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
// 如果背景颜色选择相关视图不是处于显示状态(即上述`if`条件不满足),则继续判断字体大小选择相关的视图(`mFontSizeSelector`)的可见性是否等于`View.VISIBLE`,也就是检查字体大小选择界面是否正在显示。
// 如果字体大小选择相关视图处于显示状态,则执行以下操作。
mFontSizeSelector.setVisibility(View.GONE);
// 将字体大小选择相关视图的可见性设置为`View.GONE`,即隐藏该视图,同样是为了在不需要显示字体大小选择界面时(比如完成字体大小选择操作后)清理界面,避免界面元素过多影响用户操作和视觉效果。
return true;
// 返回`true`表示成功进行了这次针对字体大小选择相关视图的状态清理操作。
}
// 如果背景颜色选择相关视图和字体大小选择相关视图都不是处于显示状态(即上述两个`if`条件都不满足),说明没有需要清理的相关设置状态了,此时返回`false`,表示没有进行实际的状态清理操作。
return false;
}
// `onBackgroundColorChanged`方法用于在笔记背景颜色发生改变时,更新界面上相关视图的显示,以反映新的背景颜色设置情况,比如显示对应的选中图标以及更新编辑区域和头部面板的背景资源。
public void onBackgroundColorChanged() {
// 通过`findViewById`方法,依据`sBgSelectorSelectionMap`中当前笔记背景颜色ID通过`mWorkingNote.getBgColorId`方法获取对应的资源ID查找对应的视图
// 然后将该视图的可见性设置为`View.VISIBLE`(即显示该视图),这个视图可能是用于提示当前选择的背景颜色的选中状态图标,通过显示该图标,可以直观地告知用户当前所选择的背景颜色情况,让用户清楚知晓当前生效的背景颜色设置。
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
// 设置笔记编辑文本框所在的面板(`mNoteEditorPanel`)的背景资源,通过调用`setBackgroundResource`方法,传入正在编辑的笔记(`mWorkingNote`的背景颜色资源ID通过`getBgColorResId`方法获取),
// 这样就能根据新选择的背景颜色更新编辑区域的背景显示效果,使其与笔记的背景颜色设置保持一致,提供视觉上统一的编辑界面。
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 设置笔记编辑界面头部视图面板(`mHeadViewPanel`)的背景资源,通过调用`setBackgroundResource`方法,传入正在编辑的笔记(`mWorkingNote`的标题背景资源ID通过`getTitleBgResId`方法获取),
// 以此来更新头部面板的背景样式,使其与笔记的整体背景颜色设置相匹配,确保整个笔记编辑界面的背景风格协调统一。
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
// `onPrepareOptionsMenu`方法是Android系统提供的用于在每次显示选项菜单Options Menu前进行准备工作的方法比如根据当前Activity的状态、数据等来动态调整菜单的显示内容、菜单项的可见性等。
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 首先判断当前Activity是否正在结束通过`isFinishing`方法判断),如果`isFinishing`方法返回`true`表示Activity正在结束过程中此时直接返回`true`
// 这样系统就不会再进行后续的菜单准备操作了因为Activity即将关闭不需要再更新菜单显示了。
if (isFinishing()) {
return true;
}
// 调用`clearSettingState`方法,清理界面上与设置相关的一些临时显示状态,比如隐藏背景颜色选择和字体大小选择相关的视图等,确保在准备显示菜单时界面处于一个合适、整洁的状态,避免菜单显示被这些临时视图遮挡或者影响视觉效果。
clearSettingState();
// 调用`menu`的`clear`方法清空当前菜单中的所有菜单项这是为了重新根据当前的业务逻辑和Activity状态来填充合适的菜单项避免出现旧的、不符合当前情况的菜单项显示在菜单中。
menu.clear();
// 判断正在编辑的笔记(`mWorkingNote`的文件夹ID通过`getFolderId`方法获取)是否等于`Notes.ID_CALL_RECORD_FOLDER`可能是表示通话记录文件夹的常量ID如果相等则说明当前笔记属于通话记录类型的文件夹进入以下逻辑。
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
// 通过`getMenuInflater`方法获取一个菜单填充器对象,然后调用其`inflate`方法,传入`R.menu.call_note_edit`可能是定义通话记录笔记编辑相关菜单布局的资源ID和`menu`对象,
// 这样就会根据`R.menu.call_note_edit`所对应的菜单布局文件来填充`menu`对象,将通话记录笔记编辑场景下合适的菜单项添加到菜单中,以提供符合该类型笔记操作需求的菜单功能选项。
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
// 如果笔记的文件夹ID不等于`Notes.ID_CALL_RECORD_FOLDER`,说明不是通话记录类型的笔记,此时通过`getMenuInflater`方法获取菜单填充器对象,然后调用其`inflate`方法,传入`R.menu.note_edit`可能是定义普通笔记编辑相关菜单布局的资源ID和`menu`对象,
// 按照`R.menu.note_edit`对应的菜单布局文件来填充`menu`对象,将普通笔记编辑场景下常用的菜单项添加到菜单中,满足一般笔记编辑操作的功能需求。
getMenuInflater().inflate(R.menu.note_edit, menu);
}
// 判断正在编辑的笔记(`mWorkingNote`)的复选列表模式(通过`getCheckListMode`方法获取)是否等于`TextNote.MODE_CHECK_LIST`(可能是表示笔记处于复选列表模式的常量),如果是,则进入以下逻辑。
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 通过`menu`对象的`findItem`方法依据资源ID`R.id.menu_list_mode`)查找对应的菜单项,然后调用该菜单项的`setTitle`方法,传入`R.string.menu_normal_mode`对应的字符串资源(可能是表示“普通模式”之类的文本内容),
// 这样就将该菜单项的标题更新为“普通模式”,以提示用户当前处于复选列表模式下,点击该菜单项可以切换回普通模式,符合根据笔记当前模式动态调整菜单显示文本的需求。
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
// 如果笔记不是处于复选列表模式,则通过`menu`对象的`findItem`方法依据资源ID`R.id.menu_list_mode`)查找对应的菜单项,然后调用该菜单项的`setTitle`方法,传入`R.string.menu_list_mode`对应的字符串资源(可能是表示“列表模式”之类的文本内容),
// 将该菜单项的标题更新为“列表模式”,提示用户当前处于普通模式,点击该菜单项可以切换到列表模式,同样是根据笔记当前模式动态调整菜单显示文本,方便用户进行模式切换操作。
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
// 判断正在编辑的笔记(`mWorkingNote`)是否设置了时钟提醒(通过`hasClockAlert`方法判断),如果设置了时钟提醒,则进入以下逻辑。
if (mWorkingNote.hasClockAlert()) {
// 通过`menu`对象的`findItem`方法依据资源ID`R.id.menu_alert`)查找对应的菜单项,然后调用该菜单项的`setVisible`方法,传入`false`,将该菜单项设置为不可见,
// 因为笔记已经设置了时钟提醒,可能就不需要再显示“设置提醒”之类的菜单项了,通过这种方式根据笔记的提醒设置情况动态调整菜单项的可见性,提供更符合实际情况的菜单显示。
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
// 如果笔记没有设置时钟提醒,则通过`menu`对象的`findItem`方法依据资源ID`R.id.menu_delete_remind`)查找对应的菜单项,然后调用该菜单项的`setVisible`方法,传入`false`,将该菜单项设置为不可见,
// 可能意味着在没有设置提醒的情况下,不需要显示“删除提醒”之类的菜单项,同样是根据笔记的提醒设置情况动态调整菜单项的可见性,使菜单显示更贴合当前笔记的实际状态。
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
// 最后返回`true`,表示菜单准备工作已完成,系统可以根据当前`menu`对象的状态来显示菜单了,返回`false`可能会导致菜单不显示或者显示异常等情况,所以正常情况下这里返回`true`。
return true;
}
// `onOptionsItemSelected`方法是Android系统提供的回调方法当用户在选项菜单Options Menu中选择某个菜单项时会被调用用于处理不同菜单项对应的具体操作逻辑。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 通过`switch`语句根据被选中菜单项的资源ID通过`item.getItemId()`获取)来执行不同的操作分支,以实现各种菜单功能。
switch (item.getItemId()) {
// 当菜单项的资源ID等于`R.id.menu_new_note`时,表示用户点击了“新建笔记”菜单项,此时调用`createNewNote`方法来执行新建笔记的相关操作,具体的新建逻辑在`createNewNote`方法中实现。
case R.id.menu_new_note:
createNewNote();
break;
// 当菜单项的资源ID等于`R.id.menu_delete`时,表示用户点击了“删除”菜单项,以下是处理删除笔记相关的逻辑,通过弹出一个确认对话框来让用户确认是否真的要删除笔记。
case R.id.menu_delete:
// 创建一个`AlertDialog.Builder`对象用于构建一个警告对话框传入当前的Activity上下文`this`以便对话框能够正确显示并与当前Activity进行交互。
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置对话框的标题,通过调用`getString`方法获取`R.string.alert_title_delete`对应的字符串资源作为标题文本,这个标题通常用于提示用户当前操作是删除相关的操作,让用户明确操作意图。
builder.setTitle(getString(R.string.alert_title_delete));
// 设置对话框的图标,使用`android.R.drawable.ic_dialog_alert`这个系统自带的图标资源,该图标一般是一个表示警告的图标,用于在视觉上提示用户此操作具有一定风险性(即删除操作不可逆等情况)。
builder.setIcon(android.R.drawable.ic_dialog_alert);
// 设置对话框的消息内容,通过调用`getString`方法获取`R.string.alert_message_delete_note`对应的字符串资源作为消息文本,这个消息文本可能是一些提示用户删除笔记后数据将无法恢复等相关内容,进一步提醒用户谨慎操作。
builder.setMessage(getString(R.string.alert_message_delete_note));
// 设置对话框的“确定”按钮,传入`android.R.string.ok`作为按钮文本(这是系统自带的表示“确定”“好的”之类意思的字符串资源),同时传入一个`DialogInterface.OnClickListener`匿名内部类对象,用于处理用户点击“确定”按钮时的操作逻辑。
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 当用户点击“确定”按钮时,调用`deleteCurrentNote`方法来执行实际的删除当前笔记的操作,该方法会处理与数据库交互等相关的删除逻辑,具体实现见`deleteCurrentNote`方法。
deleteCurrentNote();
// 调用`finish`方法关闭当前的Activity因为笔记已经被删除当前编辑界面可能就不需要再展示了直接结束当前Activity回到上一个界面比如笔记列表界面等
finish();
}
});
// 设置对话框的“取消”按钮,传入`android.R.string.cancel`作为按钮文本(这是系统自带的表示“取消”的字符串资源),并传入`null`表示不设置点击该按钮的额外监听逻辑,点击“取消”按钮时对话框会直接关闭,不执行其他额外操作。
builder.setNegativeButton(android.R.string.cancel, null);
// 调用`builder`的`show`方法显示构建好的警告对话框,将对话框呈现给用户,让用户进行确认操作选择。
builder.show();
break;
// 当菜单项的资源ID等于`R.id.menu_font_size`时,表示用户点击了“字体大小”菜单项,以下是处理字体大小相关操作的逻辑,用于显示字体大小选择相关的界面元素,方便用户进行字体大小选择。
case R.id.menu_font_size:
// 将字体大小选择相关的视图(`mFontSizeSelector`)的可见性设置为`View.VISIBLE`(即显示该视图),这样用户就能看到字体大小选择的相关界面了,例如包含不同字体大小选项的按钮等操作区域。
mFontSizeSelector.setVisibility(View.VISIBLE);
// 通过`findViewById`方法,依据`sFontSelectorSelectionMap`中当前字体大小资源标识(`mFontSizeId`对应的资源ID查找对应的视图
// 然后将该视图的可见性设置为`View.VISIBLE`(即显示该视图),这个视图可能是用于提示当前选择的字体大小的选中状态图标,显示出来可以直观地告知用户当前所选择的字体大小情况,方便用户在选择时对比查看。
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
break;
// 当菜单项的资源ID等于`R.id.menu_list_mode`时,表示用户点击了“列表模式”菜单项,以下是处理笔记列表模式切换相关的逻辑,通过改变笔记的列表模式状态来实现模式切换。
case R.id.menu_list_mode:
// 根据当前笔记(`mWorkingNote`)的复选列表模式(通过`getCheckListMode`方法获取的值来进行切换操作。如果当前模式值为0通常表示非列表模式具体根据业务逻辑定义则将其设置为`TextNote.MODE_CHECK_LIST`(可能是表示列表模式的常量),即切换到列表模式;
// 如果当前模式已经是列表模式(即`getCheckListMode`方法返回非0值则将其设置为0切换回非列表模式以此实现列表模式和非列表模式之间的切换功能。
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0?
TextNote.MODE_CHECK_LIST : 0);
break;
// 当菜单项的资源ID等于`R.id.menu_share`时,表示用户点击了“分享”菜单项,以下是处理笔记分享相关的逻辑,先获取笔记的内容,然后调用`sendTo`方法将笔记内容分享到其他支持分享功能的应用中。
case R.id.menu_share:
// 调用`getWorkingText`方法(虽然当前代码中看不到其具体实现,但推测是用于获取笔记编辑文本框中的当前文本内容等相关操作的方法)获取笔记内容,以便后续进行分享操作。
getWorkingText();
// 调用`sendTo`方法传入当前的Activity上下文`this`)以及获取到的笔记内容(`mWorkingNote.getContent`方法获取),将笔记内容发送出去进行分享,具体的分享逻辑在`sendTo`方法中实现。
sendTo(this, mWorkingNote.getContent());
break;
// 当菜单项的资源ID等于`R.id.menu_send_to_desktop`时,表示用户点击了“发送到桌面”菜单项,此时调用`sendToDesktop`方法来执行将笔记相关内容发送到桌面的操作,具体的发送逻辑在`sendToDesktop`方法中实现(不过从当前代码看该方法未给出具体实现)。
case R.id.menu_send_to_desktop:
sendToDesktop();
break;
// 当菜单项的资源ID等于`R.id.menu_alert`时,表示用户点击了“提醒”菜单项,此时调用`setReminder`方法来设置笔记的提醒功能,具体的提醒设置逻辑在`setReminder`方法中实现,比如弹出时间选择对话框让用户选择提醒时间等操作。
case R.id.menu_alert:
setReminder();
break;
// 当菜单项的资源ID等于`R.id.menu_delete_remind`时,表示用户点击了“删除提醒”菜单项,以下是处理删除笔记提醒相关的逻辑,通过调用`mWorkingNote`的`setAlertDate`方法将提醒日期设置为0表示无提醒并传入`false`(可能表示不启用提醒相关的其他设置等情况,具体根据方法定义)来取消笔记的提醒设置。
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);
break;
// 如果菜单项的资源ID不属于上述任何已定义的情况即默认情况则直接执行`break`跳出`switch`语句,不做任何额外操作,这是一种常规的处理方式,避免出现未处理的异常情况。
default:
break;
}
// 无论执行了哪个菜单项对应的操作分支,最后都返回`true`,表示已经成功处理了菜单项的点击事件,这样系统就不会再对该点击事件进行其他默认处理了,符合`onOptionsItemSelected`方法的返回要求。
return true;
}
// `setReminder`方法用于设置笔记的提醒功能,具体实现是弹出一个日期时间选择对话框,让用户选择提醒的日期和时间,然后将用户选择的时间设置为笔记的提醒时间。
private void setReminder() {
// 创建一个`DateTimePickerDialog`对象传入当前的Activity上下文`this`)以及系统当前时间的时间戳(通过`System.currentTimeMillis()`获取),
// 这个时间戳可能用于初始化日期时间选择对话框的默认显示时间(比如默认显示当前时间,方便用户基于当前时间来选择未来的提醒时间等情况),该对话框用于让用户选择具体的提醒日期和时间。
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
// 为`DateTimePickerDialog`对象设置日期时间设置监听器(`OnDateTimeSetListener`),通过传入一个匿名内部类对象实现该监听器接口,用于处理用户在对话框中选择好日期时间后的操作逻辑。
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
// 当用户在日期时间选择对话框中选择好日期时间后,会触发该回调方法,通过调用`mWorkingNote`的`setAlertDate`方法,传入用户选择的日期时间(`date`参数)以及`true`(可能表示启用提醒相关的设置等情况,具体根据方法定义),
// 将用户选择的日期时间设置为笔记的提醒时间,完成提醒时间的设置操作,使得笔记在到达设定的提醒时间时可以触发相应的提醒功能(比如弹出提醒通知等,具体的提醒触发逻辑可能在其他地方实现)。
mWorkingNote.setAlertDate(date, true);
}
});
// 调用`d`(即`DateTimePickerDialog`对象)的`show`方法,显示日期时间选择对话框,将其呈现给用户,让用户能够进行提醒时间的选择操作。
d.show();
}
// `sendTo`方法用于将笔记内容分享到其他支持分享功能的应用中,通过创建一个分享意图(`Intent`),设置意图的动作、类型以及要分享的文本内容等信息,然后启动该意图来实现分享操作。
// 此方法遵循Android系统中使用`Intent`进行跨应用操作的机制,利用`ACTION_SEND`动作和`text/plain`类型来表示分享纯文本内容的意图。
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
*/
private void sendTo(Context context, String info) {
// 创建一个新的`Intent`对象,设置其动作为`Intent.ACTION_SEND`这是Android系统中用于表示分享操作的标准动作告诉系统接下来要执行分享相关的操作其他应用可以通过识别这个动作来响应分享请求。
Intent intent = new Intent(Intent.ACTION_SEND);
// 通过`putExtra`方法,将笔记内容(`info`参数,即`mWorkingNote.getContent`方法获取的笔记文本内容)作为`Intent.EXTRA_TEXT`这个额外数据添加到`Intent`中,
// 这样接收分享意图的应用就能获取到要分享的具体文本内容了,实现将笔记内容传递给其他应用的目的。
intent.putExtra(Intent.EXTRA_TEXT, info);
// 设置`Intent`的类型为`"text/plain"`,表示要分享的内容是纯文本格式,这有助于系统筛选出能够处理纯文本分享的应用,比如支持接收文本并进行分享的社交应用、笔记应用等,确保分享操作能正确地找到合适的目标应用来接收和处理笔记内容。
intent.setType("text/plain");
// 通过传入的`Context`对象(`context`参数这里是当前的Activity上下文`this`)调用`startActivity`方法,启动创建好的分享意图(`intent`
// 系统会根据意图中的动作、类型以及额外数据等信息,查找并启动能够处理该分享操作的应用,将笔记内容分享出去,完成整个分享流程。
context.startActivity(intent);
}
// `createNewNote`方法用于创建一个新的笔记,其操作逻辑包括先保存当前正在编辑的笔记(如果有),然后结束当前的`NoteEditActivity`,再启动一个新的`NoteEditActivity`,并传递一些必要的参数来初始化新笔记的相关设置。
private void createNewNote() {
// 首先调用`saveNote`方法来保存当前正在编辑的笔记,这是一种数据保护机制,确保在创建新笔记之前,当前编辑的笔记内容和相关设置不会丢失,`saveNote`方法的具体实现这里看不到,但推测它会将笔记相关数据保存到合适的存储位置(比如数据库等)。
// 这样做可以避免用户在新建笔记过程中意外丢失之前未保存的编辑内容,保证数据的完整性和安全性。
// Firstly, save current editing notes
saveNote();
// 为了确保操作的安全性和流程的合理性,调用`finish`方法结束当前的`NoteEditActivity`这一步可能是为了清理当前Activity占用的资源、关闭相关的编辑界面等
// 为启动新的`NoteEditActivity`做好准备,避免出现多个编辑界面混乱或者资源冲突等情况。
// For safety, start a new NoteEditActivity
finish();
// 创建一个新的`Intent`对象,设置其目标类为`NoteEditActivity.class`也就是要启动的新Activity是`NoteEditActivity`,用于进入新的笔记编辑界面,创建新的笔记。
Intent intent = new Intent(this, NoteEditActivity.class);
// 设置`Intent`的动作Action为`Intent.ACTION_INSERT_OR_EDIT`,这个动作通常用于表示插入新笔记或者编辑现有笔记的意图,在这里表示要创建一个新笔记的操作,符合创建新笔记的业务逻辑需求。
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
// 通过`putExtra`方法,将当前正在编辑的笔记(`mWorkingNote`的文件夹ID通过`getFolderId`方法获取)作为`Notes.INTENT_EXTRA_FOLDER_ID`这个额外数据添加到`Intent`中,
// 这样在新启动的`NoteEditActivity`中可以获取到这个文件夹ID信息可能用于将新创建的笔记关联到对应的文件夹等操作方便笔记的分类管理保持文件夹相关逻辑的连贯性。
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
// 通过调用`startActivity`方法,启动创建好的`Intent`,启动新的`NoteEditActivity`,进入新的笔记编辑界面,开始创建新笔记的流程,系统会根据`Intent`中的各种设置(如动作、额外数据等)来初始化新的`NoteEditActivity`。
startActivity(intent);
}
// `deleteCurrentNote`方法用于执行删除当前笔记的实际操作,其逻辑涉及与数据库的交互,根据不同的同步模式(`isSyncMode`方法判断,具体逻辑未给出)来决定是直接删除笔记还是将笔记移动到回收站文件夹等操作,同时还会对笔记对象进行标记删除操作。
private void deleteCurrentNote() {
// 首先判断当前笔记(`mWorkingNote`)是否已经存在于数据库中(通过`existInDatabase`方法判断),只有存在于数据库中的笔记才能进行后续的删除相关操作,避免对不存在的笔记进行无效操作导致异常情况发生。
if (mWorkingNote.existInDatabase()) {
// 创建一个`HashSet<Long>`类型的集合对象`ids`用于存放要操作的笔记的ID这里主要是存放当前要删除的笔记的ID后续根据不同的操作逻辑如批量删除或移动到回收站等来处理这些ID对应的笔记。
HashSet<Long> ids = new HashSet<Long>();
// 获取当前笔记(`mWorkingNote`的笔记ID通过`getNoteId`方法获取),赋值给变量`id`,用于后续判断和操作。
long id = mWorkingNote.getNoteId();
// 判断获取到的笔记ID是否不等于`Notes.ID_ROOT_FOLDER`可能是表示根文件夹的常量ID具体根据业务逻辑定义如果不等于说明不是根文件夹相关的特殊情况可能根文件夹不允许直接删除等原因则将该笔记ID添加到`ids`集合中,准备进行后续的删除或移动操作。
if (id!= Notes.ID_ROOT_FOLDER) {
ids.add(id);
} else {
// 如果笔记ID等于`Notes.ID_ROOT_FOLDER`,说明出现了不应该出现的情况(从注释`Wrong note id, should not happen`可知),通过`Log.d`方法输出一条调试日志信息,记录这种异常情况,方便后续排查问题,不过这里并没有进行实际的删除操作,避免错误删除重要数据。
// `isSyncMode`方法用于判断当前是否处于同步模式。其判断逻辑是通过获取同步账户名称(从`NotesPreferenceActivity`类的相关方法获取),
// 然后检查该名称去除首尾空白字符后的长度是否大于0如果大于0则表示存在同步账户即处于同步模式返回`true`;否则返回`false`。
private boolean isSyncMode() {
// 调用`NotesPreferenceActivity`类的静态方法`getSyncAccountName`传入当前的Activity上下文`this`)来获取同步账户名称,
// 然后使用`trim`方法去除名称字符串的首尾空白字符再通过获取其长度并与0比较来判断是否存在有效的同步账户名称以此确定是否处于同步模式。
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
// `onClockAlertChanged`方法用于处理笔记的时钟提醒相关的变更操作,比如根据用户设置或取消提醒的操作,来与系统的闹钟服务(`AlarmManager`)进行交互,实现提醒的设置或取消,
// 同时在操作前会考虑笔记是否已保存等情况,以确保操作的合理性和数据的完整性。
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
*/
// 判断当前正在编辑的笔记(`mWorkingNote`)是否已经存在于数据库中(通过`existInDatabase`方法判断),
// 因为用户有可能对还未保存的笔记设置时钟提醒,所以如果笔记不存在数据库中,就先调用`saveNote`方法保存笔记,
// `saveNote`方法的具体实现这里看不到,但推测它会将笔记的相关内容(如文本内容、各种设置等)保存到数据库等合适的存储位置,确保后续操作有完整的数据基础。
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 进一步判断笔记(`mWorkingNote`的笔记ID通过`getNoteId`方法获取是否大于0只有有合法笔记ID的情况下才能进行与闹钟相关的设置操作
// 若笔记ID大于0则说明笔记已经有了合适的标识可以与系统的闹钟服务等进行关联操作进入以下逻辑。
if (mWorkingNote.getNoteId() > 0) {
// 创建一个新的`Intent`对象,设置其目标类为`AlarmReceiver.class`,这个`Intent`通常用于触发`AlarmReceiver`广播接收器(具体实现未给出,但推测用于处理闹钟触发后的相关逻辑,比如弹出提醒通知等),
// 当闹钟时间到达时,系统会根据这个`Intent`来找到对应的广播接收器并执行相应的操作。
Intent intent = new Intent(this, AlarmReceiver.class);
// 通过`ContentUris.withAppendedId`方法将笔记的内容URI`Notes.CONTENT_NOTE_URI`与笔记的ID`mWorkingNote.getNoteId()`)进行拼接,
// 然后设置到`intent`的`Data`属性上,这样在`AlarmReceiver`接收广播时,可以通过这个`Data`属性获取到具体是哪个笔记触发的闹钟提醒,方便后续进行针对性的提醒操作(比如展示对应笔记的相关信息等)。
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
// 创建一个`PendingIntent`对象,用于包装前面创建的`Intent`,使其可以在合适的时间(即闹钟触发时间)被系统触发执行,
// 这里通过`PendingIntent.getBroadcast`方法创建一个用于广播的`PendingIntent`传入当前的Activity上下文`this`、请求码这里设置为0通常用于区分不同的`PendingIntent`请求情况)、要包装的`Intent`以及标志位这里设置为0可根据具体需求设置不同的标志来控制`PendingIntent`的行为),
// 这样就创建好了一个可以由系统闹钟服务触发的广播`PendingIntent`,用于后续设置闹钟提醒时关联使用。
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
// 获取系统的`AlarmManager`服务,通过`getSystemService`方法传入`ALARM_SERVICE`常量来获取,`AlarmManager`用于管理系统的闹钟功能,比如设置、取消闹钟等操作,
// 将获取到的`AlarmManager`服务对象赋值给`alarmManager`变量,方便后续调用其相关方法来操作闹钟。
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
// 调用`showAlertHeader`方法,该方法用于更新界面上与提醒相关的头部显示信息(比如提醒时间文本、提醒图标等的显示状态和内容),
// 确保界面上的提醒相关信息能及时反映当前提醒设置的变更情况,让用户看到最新的提醒状态,具体的显示逻辑在`showAlertHeader`方法中实现。
showAlertHeader();
// 根据传入的`set`参数来判断是设置闹钟提醒还是取消闹钟提醒操作,如果`set`为`false`,表示要取消闹钟提醒,进入以下逻辑。
if (!set) {
// 调用`alarmManager`的`cancel`方法,传入前面创建的`PendingIntent``pendingIntent`)来取消对应的闹钟提醒,
// 这样系统就会根据这个`PendingIntent`找到对应的闹钟设置并取消掉,避免不必要的提醒发生,实现用户取消提醒的操作需求。
alarmManager.cancel(pendingIntent);
} else {
// 如果`set`为`true`,表示要设置闹钟提醒,此时调用`alarmManager`的`set`方法来设置闹钟,传入闹钟类型(`AlarmManager.RTC_WAKEUP`表示在指定的实时时钟时间唤醒设备并触发提醒,即使设备处于睡眠状态也会唤醒,常用于重要提醒场景)、
// 提醒的时间(`date`参数,即用户设置的提醒时间对应的时间戳)以及用于触发提醒的`PendingIntent``pendingIntent`),这样系统就会根据传入的参数在指定时间触发闹钟提醒,通过广播触发`AlarmReceiver`来执行相应的提醒逻辑。
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
*/
// 如果笔记的笔记ID不大于0说明可能出现了用户没有输入任何有效内容笔记不值得保存的情况此时没有合适的笔记ID来关联闹钟提醒操作
// 通过`Log.e`方法输出一条错误日志信息记录“Clock alert setting error”时钟提醒设置错误方便后续排查问题了解出现提醒设置异常的原因。
Log.e(TAG, "Clock alert setting error");
// 调用`showToast`方法虽然当前代码中看不到其具体实现但推测是用于弹出一个短暂提示信息的方法类似Toast提示框传入`R.string.error_note_empty_for_clock`对应的字符串资源,
// 向用户弹出一个提示信息,告知用户因为笔记内容为空等原因无法设置时钟提醒,提示用户应该输入一些内容后再尝试设置提醒,增强用户交互的友好性和提示性。
showToast(R.string.error_note_empty_for_clock);
}
}
// `onWidgetChanged`方法比较简单,其功能就是调用`updateWidget`方法来更新与当前笔记相关联的桌面小部件的显示内容,
// 具体的小部件更新逻辑在`updateWidget`方法中实现,比如发送广播通知小部件提供类更新小部件上显示的笔记相关信息等操作。
public void onWidgetChanged() {
updateWidget();
}
// `onEditTextDelete`方法用于处理编辑文本框(从变量名`mEditTextList`推测是包含多个编辑文本框的列表)中某个文本框被删除后的相关操作,
// 比如调整剩余文本框的索引、更新文本内容、重新设置焦点和光标位置等,以保证编辑界面的文本显示和操作逻辑的连贯性和正确性。
public void onEditTextDelete(int index, String text) {
// 获取`mEditTextList`(可能是一个`LinearLayout`等布局容器,用于存放多个编辑文本框相关的视图元素)中包含的子视图数量,也就是编辑文本框的数量,赋值给`childCount`变量,
// 用于后续判断和循环操作,确定要处理的文本框范围等情况。
int childCount = mEditTextList.getChildCount();
// 如果编辑文本框的数量只有1个说明已经是最少的文本框数量了没有其他文本框可以进行后续的删除相关调整操作了直接返回不执行下面的逻辑。
if (childCount == 1) {
return;
}
// 从要删除的文本框的下一个索引(`index + 1`)开始,循环遍历剩余的所有编辑文本框(直到`childCount`结束),对每个文本框执行以下操作,目的是调整剩余文本框的索引值,使其保持连续且正确。
for (int i = index + 1; i < childCount; i++) {
// 通过`mEditTextList`获取当前循环到的子视图(即编辑文本框所在的视图),然后在该子视图中通过`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,
// 将查找到的编辑文本框转换为`NoteEditText`类型(可能是自定义的继承自`EditText`的文本框类,用于扩展一些特定功能,具体未给出),并调用其`setIndex`方法,传入`i - 1`来重新设置该文本框的索引值,
// 这样可以保证文本框的索引在删除操作后依然是连续且符合逻辑的,便于后续的文本处理等操作基于正确的索引进行。
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
// 通过`mEditTextList`调用`removeViewAt`方法,传入要删除的文本框的索引(`index`),从`mEditTextList`布局容器中移除对应的编辑文本框视图,实现文本框的删除操作,更新界面上显示的编辑文本框布局情况。
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
// 根据要删除的文本框的索引(`index`)来判断获取哪个剩余文本框用于后续操作,如果`index`等于0说明删除的是第一个文本框此时获取`mEditTextList`中的第一个子视图(即剩余的第一个编辑文本框所在的视图),
// 然后在该视图中通过`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,将其赋值给`edit`变量,用于后续操作。
if (index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
// 如果`index`不等于0说明删除的不是第一个文本框此时获取`mEditTextList`中索引为`index - 1`的子视图(即删除操作后,原本在要删除文本框之前的那个编辑文本框所在的视图),
// 然后在该视图中通过`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,将其赋值给`edit`变量,用于后续操作,这样就获取到了与删除操作相关的合适的剩余编辑文本框对象。
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
// 获取前面获取到的编辑文本框(`edit`)中的文本长度,赋值给`length`变量,用于后续设置光标位置等操作,确保光标位置能正确定位在合适的位置上。
int length = edit.length();
// 调用`edit`(编辑文本框)的`append`方法,传入要删除的文本框中的文本内容(`text`参数),将该文本内容添加到当前获取的编辑文本框中,实现文本内容的转移和合并操作,保证文本内容不会因为文本框删除而丢失。
edit.append(text);
// 调用`edit`(编辑文本框)的`requestFocus`方法,让该编辑文本框获取焦点,也就是将光标定位到这个文本框中,方便用户继续进行编辑操作,使得编辑流程能够自然衔接,用户体验更好。
edit.requestFocus();
// 调用`edit`(编辑文本框)的`setSelection`方法,传入前面获取的文本长度(`length`),将光标位置设置在刚刚添加的文本内容之后,确保光标定位在合理的位置,便于用户继续输入或编辑文本内容。
edit.setSelection(length);
}
// `onEditTextEnter`方法用于处理在编辑文本框(推测是多个编辑文本框组成的列表,由`mEditTextList`管理)中按下回车键等操作后的逻辑,
// 比如添加新的视图、设置焦点以及调整后续文本框的索引等,以保证编辑界面的显示和操作逻辑的连贯性。
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*/
// 首先进行一个边界检查,判断传入的索引(`index`)是否大于`mEditTextList`(存放编辑文本框相关视图的列表容器)中包含的子视图数量(也就是编辑文本框的实际数量),
// 如果大于,说明出现了不应该出现的越界情况(正常情况下索引应该在有效范围内),通过`Log.e`方法输出一条错误日志信息记录“Index out of mEditTextList boundrary, should not happen”索引超出`mEditTextList`边界,不应出现此情况),方便后续排查问题,了解是否出现了异常的操作导致索引越界问题。
if (index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
// 调用`getListItem`方法(虽然当前代码中看不到其具体实现,但推测是用于创建一个包含编辑文本框等相关视图元素的列表项视图的方法),传入要添加的文本内容(`text`)和索引(`index`),获取对应的视图对象,赋值给`view`变量,这个视图将被添加到`mEditTextList`中作为新的编辑文本框相关的列表项。
View view = getListItem(text, index);
// 通过`mEditTextList`调用`addView`方法,将前面获取到的视图(`view`)添加到`mEditTextList`中,指定添加的位置为`index`,这样就在指定的索引位置插入了新的编辑文本框相关的视图,更新了编辑文本框列表的布局和内容。
mEditTextList.addView(view, index);
// 通过`view`对象调用`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,将其转换为`NoteEditText`类型(可能是自定义的继承自`EditText`的文本框类,用于扩展一些特定功能,具体未给出),赋值给`edit`变量,用于后续操作。
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 调用`edit`(编辑文本框)的`requestFocus`方法,让该编辑文本框获取焦点,也就是将光标定位到这个新添加的文本框中,方便用户继续输入内容,使得编辑操作能够自然地在新添加的文本框处继续进行,提升用户编辑体验。
edit.requestFocus();
// 调用`edit`(编辑文本框)的`setSelection`方法,传入`0`将光标位置设置在文本框内容的开头位置索引为0处确保光标初始定位在合理的位置便于用户开始输入新的文本内容。
edit.setSelection(0);
// 从新添加的文本框的下一个索引(`index + 1`)开始,循环遍历剩余的所有编辑文本框(直到`mEditTextList.getChildCount()`结束),对每个文本框执行以下操作,目的是调整剩余文本框的索引值,使其保持连续且正确。
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
// 通过`mEditTextList`获取当前循环到的子视图(即编辑文本框所在的视图),然后在该子视图中通过`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,
// 将查找到的编辑文本框转换为`NoteEditText`类型,并调用其`setIndex`方法,传入`i`来重新设置该文本框的索引值,这样可以保证文本框的索引在添加新文本框操作后依然是连续且符合逻辑的,便于后续的文本处理等操作基于正确的索引进行。
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
}
// `switchToListMode`方法用于将编辑界面从普通文本编辑模式切换到列表模式,其操作包括清除原有的编辑文本框列表视图、根据传入的文本内容分割并添加新的列表项视图、设置焦点以及切换相关视图的可见性等,以呈现出列表模式的编辑界面效果。
private void switchToListMode(String text) {
// 通过`mEditTextList`调用`removeAllViews`方法,移除`mEditTextList`中所有的子视图(也就是清除原有的编辑文本框相关的所有视图元素),为重新构建列表模式下的编辑文本框列表做准备,清空之前的布局内容。
mEditTextList.removeAllViews();
// 使用`split`方法,以换行符(`\n`)为分隔符,将传入的文本内容(`text`)分割成字符串数组(`items`),每个元素对应列表模式下的一行文本内容,假设传入的文本内容是按照每行表示一个列表项的格式组织的,这样就可以将其解析为各个列表项内容。
String[] items = text.split("\n");
int index = 0;
// 遍历分割后的字符串数组(`items`),对每个非空的字符串元素执行以下操作,目的是将每个有效的列表项内容添加到`mEditTextList`中,构建列表模式下的编辑文本框列表。
for (String item : items) {
if (!TextUtils.isEmpty(item)) {
// 调用`getListItem`方法(虽然当前代码中看不到其具体实现,但推测是用于创建一个包含编辑文本框等相关视图元素的列表项视图的方法),传入当前的列表项文本内容(`item`)和索引(`index`),获取对应的视图对象,然后将其添加到`mEditTextList`中,
// 这样就为每个非空的列表项创建并添加了对应的编辑文本框相关的视图,逐步构建起列表模式下的编辑文本框列表布局。
mEditTextList.addView(getListItem(item, index));
index++;
}
}
// 再调用一次`getListItem`方法,传入空字符串(表示添加一个空白的列表项,可能用于方便用户继续添加新的列表项内容等情况)和当前的索引(`index`),获取对应的视图对象,并添加到`mEditTextList`中,完善列表模式下的编辑文本框列表布局,确保最后有一个可编辑的空白项供用户继续操作。
mEditTextList.addView(getListItem("", index));
// 通过`mEditTextList`获取索引为`index`(也就是最后添加的那个空白列表项对应的视图)的子视图,然后在该子视图中通过`findViewById`方法依据资源ID`R.id.et_edit_text`)查找对应的编辑文本框,
// 并调用该编辑文本框的`requestFocus`方法,让这个空白编辑文本框获取焦点,也就是将光标定位到这个空白文本框中,方便用户直接开始输入新的列表项内容,使得列表模式下的编辑操作更加便捷,用户体验更好。
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
// 将原本用于普通文本编辑的`mNoteEditor`(可能是一个`EditText`等文本编辑视图)的可见性设置为`View.GONE`(即隐藏该视图),因为切换到列表模式后,不需要显示这个普通文本编辑视图了,避免界面显示混乱。
mNoteEditor.setVisibility(View.GONE);
// 将`mEditTextList`(用于列表模式下编辑文本框列表的视图容器)的可见性设置为`View.VISIBLE`(即显示该视图),确保在切换到列表模式后,列表模式下的编辑文本框列表能够正确显示给用户,呈现出列表模式的编辑界面效果。
mEditTextList.setVisibility(View.VISIBLE);
}
// `getHighlightQueryResult`方法用于根据用户输入的查询内容(`userQuery`),在给定的完整文本(`fullText`)中查找匹配的部分,并将匹配的部分设置特定的背景颜色(高亮显示),最后返回处理后的包含高亮显示效果的`Spannable`对象,方便在界面上展示高亮后的文本内容。
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
// 创建一个`SpannableString`对象,传入`fullText`是否为`null`的判断结果,如果`fullText`为`null`则传入空字符串,否则传入`fullText`本身,以此初始化一个可设置文本样式(如设置高亮显示等)的`Spannable`类型的字符串对象,赋值给`spannable`变量,用于后续操作。
SpannableString spannable = new SpannableString(fullText == null? "" : fullText);
// 判断用户输入的查询内容(`userQuery`)是否不为空(即用户有输入查询内容),如果不为空,则进入以下处理逻辑,用于在完整文本中查找匹配的部分并设置高亮显示样式。
if (!TextUtils.isEmpty(userQuery)) {
// 使用`Pattern`类的`compile`方法,传入用户查询内容(`userQuery`)创建一个正则表达式模式对象(`mPattern`),用于后续通过正则表达式来匹配完整文本中的相关内容,这里假设`userQuery`可以作为有效的正则表达式来进行文本匹配操作(具体根据业务需求和使用场景而定)。
mPattern = Pattern.compile(userQuery);
// 通过创建好的正则表达式模式对象(`mPattern`)调用`matcher`方法,传入完整文本(`fullText`)创建一个`Matcher`对象(`m`),这个`Matcher`对象用于实际执行文本匹配操作,查找完整文本中与用户查询内容匹配的部分。
Matcher m = mPattern.matcher(fullText);
int start = 0;
// 使用`while`循环,只要`matcher`对象(`m`)能通过`find`方法找到下一个匹配的部分(从`start`位置开始查找),就进入循环体执行以下操作,目的是逐个找到所有匹配的文本部分并设置高亮显示样式。
while (m.find(start)) {
// 通过`spannable`对象调用`setSpan`方法,传入一个`BackgroundColorSpan`对象(用于设置文本背景颜色的样式类),这个`BackgroundColorSpan`对象通过调用当前Activity的`getResources`方法获取资源对象,再调用其`getColor`方法获取`R.color.user_query_highlight`对应的颜色资源(可能是用于高亮显示的特定颜色)来创建,
// 同时传入匹配部分的起始位置(`m.start()`)和结束位置(`m.end()`)以及`Spannable.SPAN_INCLUSIVE_EXCLUSIVE`(表示设置的样式应用范围是包含起始位置但不包含结束位置的文本部分,是一种常见的`Spannable`样式设置范围标识),这样就为匹配的文本部分设置了特定的背景颜色,实现了高亮显示效果。
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
// 更新`start`变量的值为当前匹配部分的结束位置(`m.end()`),以便下一次循环从这个位置之后继续查找匹配的部分,确保能遍历完整文本中所有匹配的内容,全部进行高亮显示处理。
start = m.end();
}
}
// 最后返回处理好的包含高亮显示效果的`Spannable`对象(`spannable`),这样调用该方法的地方就可以获取到经过高亮处理后的文本内容,用于在界面上展示给用户查看(比如在搜索结果展示等场景下使用)。
return spannable;
}
// 此方法用于获取一个列表项的视图根据传入的文本内容item和索引index来设置视图内各子元素的相关属性及样式等。
private View getListItem(String item, int index) {
// 通过LayoutInflater从当前上下文this加载指定布局R.layout.note_edit_list_item创建视图不指定父视图null
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
// 从加载的视图中找到编辑文本框NoteEditText类型用于后续设置文本外观等操作。
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 根据当前的字体大小资源标识mFontSizeId设置编辑文本框的文本外观样式。
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 从视图中找到复选框CheckBox类型用于后续设置其状态改变监听等操作。
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);
}
}
});
// 判断文本内容是否以特定已选中标记TAG_CHECKED开头若是则设置复选框为选中状态给编辑文本框添加删除线样式并去除标记部分文本。
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)) {
// 若以未选中标记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();
}
// 为编辑文本框设置文本内容变化监听器this表示当前类实现了相关监听器接口
edit.setOnTextViewChangeListener(this);
// 设置编辑文本框的索引值。
edit.setIndex(index);
// 设置编辑文本框的文本内容,经过高亮查询结果处理(根据用户查询内容对文本进行高亮显示相关处理)后的文本。
edit.setText(getHighlightQueryResult(item, mUserQuery));
// 返回构建好的列表项视图。
return view;
}
// 处理文本变化相关逻辑,根据索引及文本是否存在来设置对应列表项中复选框的可见性。
public void onTextChange(int index, boolean hasText) {
// 判断传入的索引是否超出了编辑文本框列表mEditTextList中子视图数量范围若是则输出错误日志并返回说明出现异常情况。
if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");
return;
}
// 如果文本存在hasText为true则设置对应索引的列表项中的复选框可见否则设置为不可见。
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);
}
}
// 处理列表模式改变的相关逻辑根据新模式newMode的值来切换界面显示状态及文本内容等相关操作。
public void onCheckListModeChanged(int oldMode, int newMode) {
// 如果新模式是列表模式TextNote.MODE_CHECK_LIST则调用switchToListMode方法将普通文本编辑器中的文本转换为列表模式展示传入文本编辑器中的文本内容。
if (newMode == TextNote.MODE_CHECK_LIST) {
switchToListMode(mNoteEditor.getText().toString());
} else {
// 如果不是列表模式先尝试获取工作文本通过getWorkingText方法若获取失败对工作文本做一些处理去除特定标记等然后设置文本编辑器的文本内容为经过高亮查询结果处理后的内容
// 最后隐藏编辑文本框列表,显示普通文本编辑器。
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mWorkingNote.setVisibility(View.VISIBLE);
}
}
// 获取工作文本的方法,根据当前笔记的列表模式(是否为复选列表模式)来构造不同格式的文本内容,返回是否存在已选中项的布尔值。
private boolean getWorkingText() {
boolean hasChecked = false;
// 如果是复选列表模式TextNote.MODE_CHECK_LIST遍历编辑文本框列表根据每个列表项中文本内容及复选框状态构造文本用特定标记表示是否选中最后设置工作文本内容同时记录是否存在已选中项。
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方法中为保存笔记做准备。
getWorkingText();
// 调用mWorkingNote对象的saveNote方法来实际保存笔记将保存结果赋值给saved变量。
boolean saved = mWorkingNote.saveNote();
if (saved) {
/**
* 注释说明了在从列表视图进入编辑视图有两种情况(打开已有笔记、新建/编辑笔记保存成功时设置结果为RESULT_OK用于区分创建/编辑状态,方便后续操作判断。
*/
setResult(RESULT_OK);
}
// 返回笔记是否保存成功的结果saved的值
return saved;
}
// 用于将笔记相关内容发送到桌面创建桌面快捷方式相关操作先确保笔记已保存到数据库然后根据笔记ID情况来构建并发送相应的意图以创建快捷方式若出现问题则提示用户。
private void sendToDesktop() {
/**
* 注释说明在向桌面发送消息前要确保当前编辑的笔记已存在数据库中若不存在比如新建笔记还没保存时先调用saveNote方法保存笔记。
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
// 创建一个用于发送广播的Intentsender后续将在这个Intent中添加创建桌面快捷方式相关的各种信息。
Intent sender = new Intent();
// 创建一个意图shortcutIntent用于启动NoteEditActivity设置动作为ACTION_VIEW表明是查看操作同时传入笔记的ID作为额外信息。
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
// 将用于启动NoteEditActivity的意图shortcutIntent添加到sender意图中作为创建桌面快捷方式对应的启动意图。
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
// 设置桌面快捷方式显示的名称通过调用makeShortcutIconTitle方法处理笔记内容来生成名称。
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
makeShortcutIconTitle(mWorkingNote.getContent()));
// 设置桌面快捷方式的图标资源指定为应用内的一个图标资源R.drawable.icon_app
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
// 添加一个额外信息“duplicate”并设为true具体用途可能与快捷方式相关的一些重复创建等逻辑有关需结合具体业务看
sender.putExtra("duplicate", true);
// 设置sender意图的动作用于触发创建桌面快捷方式的相关操作对应系统中接收此动作的组件来处理创建快捷方式逻辑
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
// 弹出一个短暂提示信息告知用户笔记即将进入桌面具体提示内容对应资源ID的字符串
showToast(R.string.info_note_enter_desktop);
// 发送广播,触发创建桌面快捷方式的操作。
sendBroadcast(sender);
} else {
/**
* 注释说明当笔记ID不存在时可能用户没输入有效内容笔记不值得保存的情况此时输出错误日志并提示用户应输入些内容。
*/
Log.e(TAG, "Send to desktop error");
showToast(R.string.error_note_empty_for_send_to_desktop);
}
}
// 用于生成桌面快捷方式图标显示的标题内容,会去除笔记内容中特定的标记(如已选中、未选中相关标记),并根据最大长度限制对内容进行截取处理。
private String makeShortcutIconTitle(String content) {
// 去除笔记内容中已选中相关的标记TAG_CHECKED
content = content.replace(TAG_CHECKED, "");
// 去除笔记内容中未选中相关的标记TAG_UNCHECKED
content = content.replace(TAG_UNCHECKED, "");
// 根据内容长度与最大长度限制SHORTCUT_ICON_TITLE_MAX_LEN比较若超过则截取前面部分内容作为最终返回的标题否则直接返回原内容。
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}
// 用于弹出一个短暂提示信息Toast默认显示时长为短时长具体时长由Toast.LENGTH_SHORT定义实际调用了双参数的showToast方法来实现。
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
// 用于真正弹出一个Toast提示信息根据传入的资源ID对应具体提示内容字符串以及显示时长参数来显示相应的提示信息给用户。
private void showToast(int resId, int duration) {
// 通过Toast的makeText方法创建一个Toast对象传入当前上下文this、资源IDresId以及显示时长duration然后调用show方法显示Toast提示信息。
Toast.makeText(this, resId, duration).show();
}