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个属性列:

列名类型说明
_IDINT继承自BaseColumns的主键
titleTEXT笔记的标题
noteTEXT笔记的内容
createdINT笔记的创建时间
modifiedINT笔记的修改时间

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);

managedQuery已经不推荐使用,可以使用ContentResolver进行数据解析,其本质上就是调用ContentProvider定义的数据使用方法。

然后通过SimpleCursorAdapter来进行装配:

SimpleCursorAdapter adapter
            = new SimpleCursorAdapter(
                      this,                             
                      R.layout.noteslist_item,          
                      cursor,                           
                      dataColumns,
                      viewIDs);

第一个参数指明上下文(Context),第二个参数指明数据列表项(Item)的布局,第三个参数为上文managedQuery已获取的数据的游标,第四个参数为数据库表的投影列,第五个参数为数据库表投影列在ListView上显示所对应的布局ID。这里NoteList包含的ListView只显示title这一列,如果要在ListView中显示更多表属性列内容的话,可以在这里做文章。
此外,5个参数版本的SimpleCursorAdapter已经不推荐使用,Android官方推荐的标准版本为

SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)

多了一个flags,决定adapter的行为,主要有两类:FLAG_AUTO_REQUERY和FLAG_REGISTER_CONTENT_OBSERVER.

菜单设计

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个基本方法必须被重写:

方法说明
onCreateContentProvider创建时被调用
getType获取MIME类型
insert插入记录时被调用
delete删除记录时被调用
update更新记录时被调用
query查询记录时被调用

ContentProvider基本方法的具体实现看似很复杂,其实有很多相似的套路:

  • 通过SQLiteOpenHelper的实例(mOpenHelper)的getWritableDatabase或者getReadableDatabase方法获取SQLiteDatabase的对象实例(db)
  • 通过db实例的增、删、改、查方法来操作数据库,但是要注意这里要区分上文提到的两种URI:对应于表的多行的URI和对应于表的单行的URI。

以下以query为例说明基本的实现逻辑:

ContentProvider的Query方法

基于不同的URI模式匹配来补充查询中的“Where”条件(UriMatcher类)。如果匹配的是NOTES(表),则不作特殊处理;如果匹配的NOTE_ID(表中的行),则补充Where中的ID条件,从Uri中提取ID。

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”


项目编译相关

如何编译项目

Github上的NotePad由于上传时间问题与最新的Android Studio编译环境存在差异,需修改相关的编译文件。
以下基于Android Studio3.5进行修改,更新的版本差异较大,应结合具体Android Studio版本进行修改。
在“工程视图”中,大致需要修改3个文件,如下图所示:

按照从上往下的顺序修改:
分别为应用程序模块的gradle脚本文件,gradle的Wrapper文件,以及项目的gradle脚本文件。

应用程序模块的gradle脚本文件修改

典型的修改之后的例子如下:

主要包括本地目标SDK版本,compileSdkVersion和targetSdkVersion,以及buildToolsVersion(最新的Android Studio可能无需指定)。
特别强调,如果需要添加一些依赖,应增加“dependencies”脚本模块。

gradle的Wrapper文件修改

主要修改distributionUrl值,根据本机实际情况进行修改,如下图:

项目的gradle脚本文件修改

主要增加了google()代码仓库,以及修改了classpath的值,如下图所示:

项目编译问题解决

参考文献

注:Android开发文档来自于developer.android.com,也可以参考相关的国内镜像

[1] Android开发文档,URI,
[2] Android开发文档,创建内容提供程序
[3] Android开发文档,创建数据库
[4] Android开发文档,菜单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值