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开发文档,菜单

更多相关文章

  1. Android(安卓)Studio开发之 JNI 篇
  2. Android(安卓)中 startService()启动service的过程分析
  3. Android(安卓)数字签名学习笔记
  4. kotlin开发Android入门篇一
  5. 手把手教你打造android侧滑框架
  6. Android易混淆缩写笔记
  7. 使用MNN在Android上部署mnist模型
  8. Android环境建立之编译Android内核源码笔记---2
  9. 安卓学习笔记-----我所不熟悉的TexetView的一些属性

随机推荐

  1. Android(安卓)获取经纬度的服务
  2. Android~Fragment的替代方案
  3. android:Adb connection Error:远程主机强
  4. android学习笔记1——webview相关
  5. 关于 Android(安卓)四种启动模式和应用场
  6. Android横竖屏切换及其对应布局加载问题
  7. [Android]通过剪切板实现Activity之间传
  8. Android(安卓)Studio创建AIDL文件并实现
  9. Android(安卓)Launcher研究(三)---------
  10. Android(安卓)开发之旅:深入分析布局文件&