Copy and Paste 版本:Android 4.0 r1
在本文中 ClipData、ClipDescription和ClipData.Item 將剪貼板中數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為文本 利用Content Provider復(fù)制復(fù)雜數(shù)據(jù) 關(guān)鍵類 相關(guān)示例 參閱 Android為復(fù)制和粘貼提供了強(qiáng)大的基于剪貼板的框架。該框架同時(shí)支持簡單和復(fù)雜數(shù)據(jù)類型,包括文本字符串、復(fù)雜數(shù)據(jù)結(jié)構(gòu)、文本和二進(jìn)制流數(shù)據(jù)、甚至程序asset。簡單文本數(shù)據(jù)直接存儲(chǔ)于剪貼板內(nèi),而復(fù)雜數(shù)據(jù)則保存為一個(gè)引用,粘貼應(yīng)用可利用內(nèi)容提供器(content provider)進(jìn)行解析。復(fù)制和粘貼可在應(yīng)用程序內(nèi)部或多個(gè)實(shí)現(xiàn)此框架的應(yīng)用程序之間進(jìn)行。 因?yàn)榭蚣艿囊徊糠钟玫搅?span style="font-family: Arial;">content provider,本文討論的內(nèi)容與Android Content Provider API有些類似,這些API已在Content Provider一章中描述。 使用剪貼板框架時(shí),可把數(shù)據(jù)放入剪輯(clip)對(duì)象,然后把該對(duì)象放入系統(tǒng)級(jí)剪貼板中。clip對(duì)象可以是以下三種形式: Text 文本串。可以直接把字符串放入clip對(duì)象,然后把clip對(duì)象放入剪貼板中。需要粘貼字符串時(shí),從剪貼板中獲取clip對(duì)象,然后把字符串拷貝到應(yīng)用程序的存儲(chǔ)中即可。 URI Uri對(duì)象表示任何形式的URI。這主要用于從content provider復(fù)制復(fù)雜數(shù)據(jù)。復(fù)制數(shù)據(jù)時(shí),先將Uri對(duì)象放入一個(gè)clip對(duì)象,再把clip對(duì)象放入剪貼板中。需要粘貼數(shù)據(jù)時(shí),先獲取clip對(duì)象,再獲取Uri對(duì)象,再把Uri解析到諸如content provider之類的數(shù)據(jù)源中,然后就能從此數(shù)據(jù)源中把數(shù)據(jù)拷貝到應(yīng)用程序的存儲(chǔ)中了。 Intent 一個(gè)Intent。這為復(fù)制應(yīng)用程序快捷方式提供了支持。復(fù)制數(shù)據(jù)時(shí),先創(chuàng)建一個(gè)Intent并把它放入一個(gè)clip對(duì)象,再把clip對(duì)象放入剪貼板。需要粘貼數(shù)據(jù)時(shí),可以獲取clip 對(duì)象并把Intent對(duì)象拷貝到應(yīng)用程序的內(nèi)存中。 剪貼板同時(shí)僅保存一個(gè)clip對(duì)象。當(dāng)應(yīng)用程序把一個(gè)clip對(duì)象放入剪貼板時(shí),前一個(gè)clip對(duì)象將會(huì)消失。 如果要允許用戶把數(shù)據(jù)粘貼到應(yīng)用程序中,沒必要對(duì)所有類型數(shù)據(jù)都進(jìn)行處理。在讓用戶選擇粘貼之前,可以先對(duì)剪貼板中的數(shù)據(jù)進(jìn)行檢測。除了包含指定格式的數(shù)據(jù)之外,clip對(duì)象還包含了元數(shù)據(jù),它能說明數(shù)據(jù)是屬于哪種格式或MIME類型。此元數(shù)據(jù)有助于應(yīng)用程序確定對(duì)剪貼板數(shù)據(jù)執(zhí)行合適的操作。比如,假定應(yīng)用程序主要是處理文本信息的,那就可以忽略包含URI和Intent的clip對(duì)象。 還有可能要允許用戶只粘貼文本,而不論剪貼板中的數(shù)據(jù)格式如何。要實(shí)現(xiàn)這個(gè)目標(biāo),可以把剪貼板數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為文本格式,然后粘貼這些文本。這將在將剪貼板內(nèi)數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為文本一節(jié)中描述。 本節(jié)描述類剪貼板框架中所用到的類。 在Android系統(tǒng)中,系統(tǒng)剪貼板由全局ClipboardManager類表示。此類不需要直接初始化,而是提交getSystemService(CLIPBOARD_SERVICE)來獲取一個(gè)引用。 ClipData、ClipData.Item和ClipDescription 要把數(shù)據(jù)加入剪貼板,可以創(chuàng)建一個(gè)ClipData對(duì)象,它包含了數(shù)據(jù)描述信息和數(shù)據(jù)本身。剪貼板每次只保存一個(gè)ClipData對(duì)象。一個(gè)ClipData包含了一個(gè)ClipDescription對(duì)象和一個(gè)以上的ClipData.Item對(duì)象。 ClipDescription對(duì)象包含了clip相關(guān)的元數(shù)據(jù)信息。特別重要的是,它包含了一個(gè)clip數(shù)據(jù)所對(duì)應(yīng)MIME類型的數(shù)組。把clip放入剪貼板后,粘貼應(yīng)用程序可以利用此數(shù)組,程序可以檢查此數(shù)組以確定其對(duì)這些MIME類型的處理能力。 一個(gè)ClipData.Item對(duì)象包含了文本、URI或Intent數(shù)據(jù): Text 一個(gè)CharSequence。 URI 一個(gè)Uri。雖然可以是任何URI值,但通常是包含一個(gè)content provider URI。提供數(shù)據(jù)的應(yīng)用程序把URI放入剪貼板。需要粘貼數(shù)據(jù)的應(yīng)用程序從剪貼板中獲取URI,并將它用于訪問content provider(或者其它數(shù)據(jù)源)并取回?cái)?shù)據(jù)。 Intent 一個(gè)Intent。本數(shù)據(jù)類型允許把應(yīng)用程序的快捷方式復(fù)制到剪貼板中。用戶可以在后續(xù)的使用中把快捷方式粘貼到其它應(yīng)用程序中。 可以在一個(gè)clip中加入多個(gè)ClipData.Item對(duì)象。這使得用戶可以把多個(gè)選中值復(fù)制為同一個(gè)clip。比如,如果有一個(gè)列表widget允許用戶一次選擇多個(gè)選項(xiàng),就可以把所有選中項(xiàng)一次復(fù)制到剪貼板中。要實(shí)現(xiàn)這一點(diǎn),為每個(gè)列表項(xiàng)創(chuàng)建一個(gè)ClipData.Item,然后把這些ClipData.Item對(duì)象加入ClipData對(duì)象即可。 ClipData常用方法 ClipData類提供了便捷的靜態(tài)方法來創(chuàng)建一個(gè)ClipData對(duì)象,附帶一個(gè)ClipData.Item對(duì)象和一個(gè)簡單的ClipDescription對(duì)象作為參數(shù): 返回包含了單個(gè)ClipData.Item對(duì)象的ClipData對(duì)象,此item對(duì)象內(nèi)含一個(gè)文本字符串。ClipDescription對(duì)象的標(biāo)簽設(shè)置為label。ClipDescription的MIME 類型是MIMETYPE_TEXT_PLAIN。 newPlainText()用于創(chuàng)建一個(gè)文本字符串clip。 返回一個(gè)包含單個(gè)ClipData.Item的ClipData對(duì)象,此item對(duì)象內(nèi)含一個(gè)URI。ClipDescription對(duì)象的標(biāo)簽設(shè)置為label。如果URI是一個(gè)content類型的URI (Uri.getScheme()返回content:),則該方法將用resolver的ContentResolver對(duì)象從content provider中獲取可用的MIME類型,并把這些類型保存到ClipDescription中。對(duì)于不是content:的URI ,該方法把MIME type設(shè)置為MIMETYPE_TEXT_URILIST。 newUri()用于創(chuàng)建一個(gè)URI的clip,特別是content: URI。 返回一個(gè)包含單個(gè)ClipData.Item的ClipData對(duì)象,此item對(duì)象內(nèi)含一個(gè)Intent。ClipDescription對(duì)象的標(biāo)簽設(shè)置為label。MIME類型置為MIMETYPE_TEXT_INTENT。 newIntent()用于創(chuàng)建一個(gè)Intent對(duì)象的clip。 將剪貼板內(nèi)數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為文本 如果應(yīng)用程序僅需處理文本,可用ClipData.Item.coerceToText()方法轉(zhuǎn)換一下,就可以從剪貼板復(fù)制非文本數(shù)據(jù)。 本方法將把ClipData.Item中的數(shù)據(jù)轉(zhuǎn)換為文本,并且返回一個(gè)CharSequence。ClipData.Item.coerceToText()的返回值根據(jù)ClipData.Item中的數(shù)據(jù)格式來確定: Text 如果ClipData.Item是文本(getText()不為null),則coerceToText()返回文本。 URI ClipData.Item是個(gè)URI(getUri()不為null),則coerceToText()會(huì)嘗試將其視為content URI: · · · Intent 如果ClipData.Item是個(gè)Intent(getIntent()不為null),則coerceToText()將其轉(zhuǎn)換為Intent URI后返回。該字符串表示形式與Intent.toUri(URI_INTENT_SCHEME)的返回值一致。 剪貼板的整體框架如圖1所示。在復(fù)制數(shù)據(jù)時(shí),應(yīng)用程序?qū)?span style="font-family: Courier new; color: #007000">ClipData對(duì)象放入全局的ClipboardManager剪貼板中。ClipData內(nèi)含了一個(gè)或多個(gè)ClipData.Item對(duì)象,以及一個(gè)ClipDescription對(duì)象。在粘貼數(shù)據(jù)時(shí),應(yīng)用程序先獲取ClipData,從ClipDescription中讀取MIME類型信息,再從ClipData.Item中或ClipData.Item指向的content provider中讀取數(shù)據(jù)。
圖1. Android剪貼板框架 如前所述,如果要把數(shù)據(jù)復(fù)制到剪貼板(剪貼板句柄指向全局的ClipboardManager對(duì)象),需要?jiǎng)?chuàng)建一個(gè)ClipData對(duì)象,再把一個(gè)ClipDescription和一個(gè)以上的ClipData.Item對(duì)象加入其中,最后把這個(gè)ClipData添加到ClipboardManager對(duì)象中去。詳細(xì)情況描述如下: 1. 2.
... // 如果用戶選中復(fù)制
case
R.id.menu_copy: // 獲取一個(gè)剪貼板服務(wù)的句柄
ClipboardManager
clipboard
=(ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 3.
4.
//
設(shè)置剪貼板的主clip. clipboard.setPrimaryClip(clip); 如上所述,要從剪貼板粘貼數(shù)據(jù),需先獲得全局剪貼板對(duì)象,再獲取clip對(duì)象,然后查找其中的數(shù)據(jù),最后從clip對(duì)象中把數(shù)據(jù)拷貝到自己的存儲(chǔ)中。本節(jié)詳細(xì)描述了如何針對(duì)三種剪貼板數(shù)據(jù)的格式進(jìn)行這些操作。 要粘貼普通文本,首先獲得全局剪貼板,并確認(rèn)能否返回普通文本,然后獲取clip對(duì)象,用getText()把其中文本拷貝到自己的存儲(chǔ)中,步驟如下: 1.
ClipboardManager
clipboard
=(ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE); String pasteData =""; 2. // 獲取“粘貼”菜單項(xiàng)的ID
MenuItem
mPasteItem
=
menu.findItem(R.id.menu_paste); // 如果剪貼板中沒有數(shù)據(jù),則禁用“粘貼”菜單項(xiàng) // 如果包含數(shù)據(jù),確定是否能夠處理數(shù)據(jù)
if(!(clipboard.hasPrimaryClip())){
} 3. // 對(duì)用戶選中“粘貼”作出響應(yīng)
case
R.id.menu_paste:
// 則表示clip項(xiàng)包含了文本。假定本程序一次僅處理一個(gè)數(shù)據(jù)項(xiàng)
pasteData
=
item.getText();
if(pasteData !=null){
}else{
} 從content URI粘貼數(shù)據(jù) 如果ClipData.Item對(duì)象包含了一個(gè)content URI,程序也確認(rèn)能處理其中的MIME 類型,則可創(chuàng)建一個(gè)ContentResolver并調(diào)用合適的content provider方法來獲取數(shù)據(jù)。 以下過程描述了如何根據(jù)剪貼板中的content URI從content provider獲取數(shù)據(jù)。程序會(huì)先檢查MIME類型,確認(rèn)能夠使用該provider提供的數(shù)據(jù): 1. // 聲明MIME類型常量,對(duì)應(yīng)provider所提供的MIME類型 publicstaticfinalString MIME_TYPE_CONTACT ="vnd.android.cursor.item/vnd.example.contact" 2.
//
獲取Clipboard
Manager的句柄
ClipboardManager
clipboard
=(ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
ContentResolver cr = getContentResolver(); 3. // 獲取剪貼板數(shù)據(jù)
ClipData
clip = clipboard.getPrimaryClip();
4.
5.
} 粘貼Intent 要粘貼一個(gè)意圖Intent,首先獲取全局剪貼板,再檢查ClipData.Item對(duì)象是否包含Intent。然后調(diào)用getIntent()來把Intent拷貝到程序的存儲(chǔ)中。以下代碼段演示了這一過程:
//
獲取Clipboard
Manager句柄
ClipboardManager
clipboard
=(ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
//
通過判斷getIntent()是否為null
Intent
pasteIntent
=
clipboard.getPrimaryClip().getItemAt(0).getIntent();
if(pasteIntent !=null){
}else{
} 利用Content Provider復(fù)制復(fù)雜數(shù)據(jù) Content provider支持對(duì)復(fù)雜數(shù)據(jù)的復(fù)制,比如數(shù)據(jù)庫記錄或文件流之類。在復(fù)制數(shù)據(jù)時(shí),把一個(gè)content URI放入剪貼板中。然后粘貼應(yīng)用程序從剪貼板中獲取此URI,并用它讀取數(shù)據(jù)庫數(shù)據(jù)或者文件流的描述符。 由于粘貼應(yīng)用程序只是將content URI作為數(shù)據(jù)讀取,因此還需要知道應(yīng)獲取數(shù)據(jù)的哪些部分。可以把所需數(shù)據(jù)的ID編入URI本身,或者讓URI只精確返回所需復(fù)制部分的數(shù)據(jù)。采用哪種方式取決于數(shù)據(jù)是如何組織在一起的。 以下章節(jié)描述了如何創(chuàng)建URI、如何提供復(fù)雜數(shù)據(jù)、如何提供文件流。以下假定開發(fā)人員已經(jīng)熟悉了content provider的一般設(shè)計(jì)規(guī)則。 將ID置入URI編碼 利用URI把數(shù)據(jù)復(fù)制到剪貼板時(shí),有一項(xiàng)實(shí)用技術(shù)就是把數(shù)據(jù)的ID置入URI編碼本身。然后content provider可以從URI得到ID,并用ID來讀取數(shù)據(jù)。粘貼應(yīng)用程序不必知道ID是否存在,要做的所有操作就是從剪貼板獲取“引用”(URI加ID),并把它交給content provider,然后讀取數(shù)據(jù)。 通常的編碼方式是把ID附在content URI后面。比如,假定已定義provider URI如下: "content://com.example.contacts" 如果需要把名稱置入URI,應(yīng)該使用如下代碼:
String
uriString
="content://com.example.contacts"+"/"+"Smith"
//
現(xiàn)在uriString包含了content://com.example.contacts/Smith // 根據(jù)字符串變量創(chuàng)建uri對(duì)象 Uri copyUri =Uri.parse(uriString); 如果程序中已經(jīng)用到了content provider,只需新增一個(gè)指示復(fù)制數(shù)據(jù)的URI路徑。比如,假設(shè)已存在以下URI路徑:
"content://com.example.contacts"/people
"content://com.example.contacts"/people/detail "content://com.example.contacts"/people/images 下面加入一個(gè)用于復(fù)制的URI: "content://com.example.contacts/copying" 可以利用模式匹配來檢測到"copy" URI,并用代碼進(jìn)行復(fù)制和粘貼處理。 如果是用content provider、內(nèi)部數(shù)據(jù)庫、內(nèi)部表來組織數(shù)據(jù),通常即可使用以上編碼技術(shù)。這種情況下會(huì)有多塊數(shù)據(jù)需要復(fù)制,很可能每塊數(shù)據(jù)都會(huì)有一個(gè)唯一ID。當(dāng)粘貼應(yīng)用程序查詢時(shí),可以用此ID查找并返回?cái)?shù)據(jù)。 如果沒有多塊數(shù)據(jù)需要復(fù)制,可能就不必把ID進(jìn)行編碼??梢院唵蔚厥褂媚軌蛭ㄒ粯?biāo)識(shí)provider的URI即可。查詢時(shí),provider將會(huì)返回它包含的數(shù)據(jù)。 Note Pad示例程序中就用ID獲取了單條記錄,以便從note列表中打開一條note。此示例使用了SQL數(shù)據(jù)庫中的_id字段,不過可以根據(jù)需要使用任何數(shù)字或字符ID。 為了復(fù)制和粘貼復(fù)雜數(shù)據(jù),應(yīng)該創(chuàng)建一個(gè)content provider,它是ContentProvider組件的子類。還應(yīng)該將編碼后的URI放入剪貼板,此URI指向了需提供的正確記錄。此外,還必須考慮應(yīng)用程序的現(xiàn)狀: · · · 在content provider中,至少會(huì)需要覆蓋以下方法: 粘貼應(yīng)用程序?qū)?huì)假定,通過此方法能夠獲取剪貼板中URI指定的數(shù)據(jù)。為了支持復(fù)制功能,應(yīng)該在本方法中對(duì)包含指定“復(fù)制”路徑的URI進(jìn)行檢測。然后,程序可以創(chuàng)建一個(gè)“復(fù)制”URI并放入剪貼板中,此URI包含了復(fù)制路徑和指向?qū)嶋H復(fù)制記錄的指針。 本方法應(yīng)該返回MIME類型或者需復(fù)制數(shù)據(jù)的類型。為了把MIME類型放入新建的ClipData對(duì)象,newUri()方法將會(huì)調(diào)用getType()。 復(fù)雜數(shù)據(jù)的MIME類型在Content Providers一節(jié)中描述。 注意,其它的content provider方法是沒必要用到的,比如insert()或update()。粘貼應(yīng)用程序只需要獲取所用的MIME類型并從provider拷貝數(shù)據(jù)。如果已經(jīng)實(shí)現(xiàn)了這些方法,那它們也不會(huì)影響復(fù)制操作。 以下代碼段演示了如何建立復(fù)制復(fù)雜數(shù)據(jù)的應(yīng)用程序: 1. // 聲明基本URI字符串
privatestaticfinalString
CONTACTS
="content://com.example.contacts"; // 聲明用于復(fù)制數(shù)據(jù)的URI路徑字符串
privatestaticfinalString
COPY_PATH
="/copy";
// 聲明需復(fù)制數(shù)據(jù)的MIME類型 publicstaticfinalString MIME_TYPE_CONTACT ="vnd.android.cursor.item/vnd.example.contact" 2.
publicclassMyCopyActivityextendsActivity{
// 用戶已經(jīng)選中了姓名并請(qǐng)求復(fù)制
case<="" span="">.id.menu_copy:
3.
publicclassMyCopyProviderextendsContentProvider{>{ privatestaticfinalUriMatcher sURIMatcher =newUriMatcher(UriMatcher.NO_MATCH););// 整數(shù),用于URI模式的開關(guān)變量。 privatestaticfinalint GET_SINGLE_CONTACT =>=0;.....// 為content URI加入匹配器。
//
匹配"content://com.example.contacts/copy/*" sUriMatcher.addURI(CONTACTS,"names/*", GET_SINGLE_CONTACT); 4. // 建立provider的query()方法
publicCursor
query(Uri uri,String[]
projection,String
selection,String[]
selectionArgs,
} 5. // 建立provider的getType()方法
publicString
getType(Uri uri){
從content URI粘貼數(shù)據(jù)一節(jié)描述了如何從剪貼板獲取content URI,并用它讀取并粘貼數(shù)據(jù)。 可以將大量的文本和二進(jìn)制數(shù)據(jù)以流的形式進(jìn)行復(fù)制和粘貼。數(shù)據(jù)可以具有如下格式: · · · 數(shù)據(jù)流content provider用文件描述符對(duì)象而不是Cursor< span="">AssetFileDescriptor。粘貼應(yīng)用程序利用此文件描述符來讀取數(shù)據(jù)流。流。 要建立用provider復(fù)制數(shù)據(jù)流的應(yīng)用程序,請(qǐng)按以下步驟進(jìn)行: 1. o o o 2. 3. 4. 要粘貼數(shù)據(jù)流時(shí),應(yīng)用程序先從剪貼板獲取clip,讀取URI,然后調(diào)用ContentResolver文件描述符方法打開流。ContentResolver方法將調(diào)用相應(yīng)的ContentProvider方法,把content URI傳入其中。provider把文件描述符返回給ContentResolver方法。這時(shí)粘貼程序就能讀取流中的數(shù)據(jù)了。 以下列表展示了對(duì)content provider而言最重要的文件描述符方法。每個(gè)方法都有后綴名為“Descriptor< span="">ContentResolver方法與之相對(duì)應(yīng)。比如,模擬openAssetFile()的ContentResolver是openAssetFileDescriptor():
openTypedAssetFile()
openAssetFile()
openFile() 可以選用openPipeHelper()方法作為文件描述符方法,這讓粘貼應(yīng)用可以用管道在后臺(tái)讀取流數(shù)據(jù)。使用此方法需要實(shí)現(xiàn)ContentProvider.PipeDataWriter接口。在Note Pad示例程序中有相關(guān)例程,位于NotePadProvider.java中的openTypedAssetFile()方法。 要為應(yīng)用程序設(shè)計(jì)高效的復(fù)制與粘貼功能,請(qǐng)記住以下幾點(diǎn): · · · · |
|