Android(安卓)Sample--NotePad解析
Sample导入
NotePad是早期Android版本的Sample,笔者并没有找到其Android Studio版本的代码,猜想主要是由于该Sample采用的很多技术都已经被废弃,于是将其早前Eclipse版本转化而来,具体的代码见Github: NotePad
整个NotePad的目录结构如下图所示:
一共包含了6个类,其中4个Activity,一个ContentProvider,还有一个数据契约类。
- NotesList 应用程序的入口,笔记本的首页面会显示笔记的列表
- NoteEditor 编辑笔记内容的Activity
- TitleEditor 编辑笔记标题的Activity
- NotesLiveFolder ContentProvider的LiveFolder(实时文件夹),这个功能在Android API 14后被废弃,不再支持。因此代码中所有涉及LiveFolder的内容将不再阐述。
- NotePadProvider 这是笔记本应用的ContentProvider,也是整个应用的关键所在
先行知识
URI
[URI][1]: Uniform Resource Identifier, 统一资源标识符,用来标识唯一的资源。我们所熟知的URL,实际是URI的一个特例。URI的语法如下:
[scheme:]scheme-specific-part[#fragment]
[ ]表示可选,层次化的URI的scheme-specific-part部分又可以进一步分解为:
[scheme:][//authority][path][?query][#fragment]
一些URI的例子:
http://java.sun.com/j2se/1.3/
file:///~/calendar
content://com.google.provider.NotePad/notes
URI协议使用“content:”就表示使用了ContentProvider。
MIME
MIME(Multipurpose Internet Mail Extensions),全称多用途互联网邮件扩展类型,Android使用MIME指定数据的类型。ContentProvider的getType(Uri uri)返回一个MIME格式的字符串,其描述内容为uri参数所对应的数据类型。
对于文本、HTML 或 JPEG 等常见数据类型,getType() 应该为该数据返回标准 MIME 类型。
对于指向一个或多个表数据行的内容 URI,getType() 应该以 Android 供应商特有 MIME 格式返回 MIME 类型,包括3个部分:<类型部分><子类型部分><提供程序特有部分>
- 类型部分:vnd
- 子类型部分:
- 如果 URI 模式用于单个行:android.cursor.item/
- 如果 URI 模式用于多个行:android.cursor.dir/
- 提供程序特有部分:vnd.<name>.<type>,<name> 值应具有全局唯一性,<type> 值应在对应的 URI 模式中具有唯一性。
MIME举例:
vnd.android.cursor.dir/vnd.com.example.provider.table1
表示table1中多行记录的MIME类型;
vnd.android.cursor.item/vnd.com.example.provider.table1
表示table1中单行记录的MIME类型。
NotePad相关类解析
NotePad
NotePad是数据契约类,用来提供一种统一的数据访问格式。整个笔记本应用只有一张表——“notes”。notes表共包含5个属性列:
列名 | 类型 | 说明 |
---|---|---|
_ID | INT | 继承自BaseColumns的主键 |
title | TEXT | 笔记的标题 |
note | TEXT | 笔记的内容 |
created | INT | 笔记的创建时间 |
modified | INT | 笔记的修改时间 |
NotePad定义了两种URI,这里URI对应三部分内容:[scheme:][//authority][path]。[scheme://]对应于“content://”; authority部分是”com.google.provider.NotePad”;path则根据不同URI模式而不同。对于整个表操作的URI:
content://com.google.provider.NotePad/notes
;对于表中某一行进行操作的URI:
content://com.google.provider.NotePad/notes/
同时,NotePad也定义与上述两种Uri匹配的两种MIME类型。对应URI模式用于多行(对应于整个表的情况):
vnd.android.cursor.dir/vnd.google.note
对应URI模式用于单行(对应于表中某一行的情况):
vnd.android.cursor.item/vnd.google.note
NoteList
应用程序入口,是一个ListActivity,以列表方式显示笔记本条目。NoteList的运行截图:
没有笔记被复制时,“Paste”菜单不可用
“Hello World”笔记被复制时,“Paste”菜单可用
长按笔记之后弹出的上下文菜单
数据装配
NoteList使用SimpleCursorAdapter来装配数据,首先查询数据库的内容,如下代码所示,这里使用ContentProvider默认的URI。
Cursor cursor = managedQuery( getIntent().getData(), PROJECTION, null, null, NotePad.Notes.DEFAULT_SORT_ORDER);
然后通过SimpleCursorAdapter来进行装配:
SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, R.layout.noteslist_item, cursor, dataColumns, viewIDs);
这里NoteList包含的ListView只显示title这一列,如果要在ListView中显示更多表属性列内容的话,可以在这里做文章。
菜单设计
NoteList包含两种菜单的设计,Options Menu(可选菜单)和Context Menu(上下文菜单)。Options Menu实现了动态菜单(基于Intent的菜单项),使用menu.addIntentOptions()方法,具体可参见[4]。
onCreateOptionsMenu方法只在菜单创建时调用一次,而onPrepareOptionsMenu方法则在每次显示菜单之前被调用。在onPrepareOptionsMenu方法中,Paste菜单项会根据剪贴板中是否有内容来决定是否enable;如果当前List中的条目数非空,则获取相应的ListItem ID,拼接URI,添加至addIntentOptions()。
页面跳转
不管是可选菜单、上下文菜单中的操作,还是单击列表中的笔记条目,其相应的页面跳转都是通过Intent的Action+URI进行。比如startActivity(new Intent(Intent.ACTION_EDIT, noteUri)),这里就会寻找能够进行EDIT的Activity跳转,同时传递出URI数据。所有的Intent过滤规则都在AndroidManifest.xml中定义。
NotePadProvider
NotePad的ContentProvider,NotePad的内容提供者,这里要注意实际上NotePad应用并不允许其他程序共享其数据,因为它在AndroidManifest.xml中声明provider标签的exported属性为false:
<provider android:name="NotePadProvider" android:authorities="com.google.provider.NotePad" android:exported="false"> <grant-uri-permission android:pathPattern=".*" />provider>
DatabaseHelper
DatabaseHelper是SQLiteOpenHelper的子类,用来辅助数据库的管理。其onCreate方法和onUpgrade方法必须被重写,通常用来创建数据库表和升级数据库。而构造函数通常也是必须的,用来调用父类(SQLiteOpenHelper)的构造函数来创建数据库。具体内容可以参考:[3]
UriMatcher实用类
UriMatcher,它会将内容 URI“模式”映射到整型值。 这样就可以在一个 switch 语句中使用这些整型值,为匹配特定模式的一个或多个内容 URI 选择所需操作,具体可参考[2]。NotePad的UriMatcher设定了两个URI的对应关系
content://com.google.provider.NotePad/notes 映射整数 1
content://com.google.provider.NotePad/notes/# 映射整数 2
ContentProvider的基本方法
ContentProvider有6个基本方法必须被重写:
方法 | 说明 |
---|---|
onCreate | ContentProvider创建时被调用 |
getType | 获取MIME类型 |
insert | 插入记录时被调用 |
delete | 删除记录时被调用 |
update | 更新记录时被调用 |
query | 查询记录时被调用 |
ContentProvider基本方法的具体实现看似很复杂,其实有很多相似的套路:
- 通过SQLiteOpenHelper的实例(mOpenHelper)的getWritableDatabase或者getReadableDatabase方法获取SQLiteDatabase的对象实例(db)
- 通过db实例的增、删、改、查方法来操作数据库,但是要注意这里要区分上文提到的两种URI:对应于表的多行的URI和对应于表的单行的URI。
NoteEditor
笔记内容编辑页面,页面截图如下:
NoteEditor通过扩展EditText定义了笔记本“编辑框“,主要自定义了画笔,重写了onDraw函数。其onCreate函数获取了Intent中的Action和URI,随后根据不同Action,进行不同的操作。
onResume函数有个小细节,当处于EDIT(编辑)和INSERT(插入)状态时,NoteEditor显示的标题不同。
TitleEditor
笔记标题编辑的Activity,页面截图如下:
TitleEditor设计对对话框的样式,查看AndroidManifest.xml中的主题定义:
android:theme=”@android:style/Theme.Holo.Dialog”
参考文献
注:Android开发文档来自于developer.android.com,也可以参考相关的国内镜像
[1] Android开发文档,URI,
[2] Android开发文档,创建内容提供程序
[3] Android开发文档,创建数据库
[4] Android开发文档,菜单
更多相关文章
- Android(安卓)Studio开发之 JNI 篇
- Android(安卓)中 startService()启动service的过程分析
- Android(安卓)数字签名学习笔记
- kotlin开发Android入门篇一
- 手把手教你打造android侧滑框架
- Android易混淆缩写笔记
- 使用MNN在Android上部署mnist模型
- Android环境建立之编译Android内核源码笔记---2
- 安卓学习笔记-----我所不熟悉的TexetView的一些属性