Text and Input:
使用文字服務來加上方便的功能到你的app,
像是複製/貼上,以及拼字檢查等。
你也可以開發你自己的文字服務來提供客製的IMEs(輸入法)、
字典,以及拼字檢查器,作為可以發布給使用者的apps。
Copy and Paste:
Android提供了一個強大的基於剪貼簿的複製/貼上的framework。
它同時提供了簡單和複雜兩種資料型態,
包括了文字strings、複雜的資料結構、
文字及二元串流資料,甚至是app assets。
簡單的文字資料是直接存在剪貼簿裡的,
而複雜的資料是以貼上的app用content provider來解析,
作為一個參照(reference)來儲存的。
複製和貼上可以在一個app或多個app之間來做操作,
只要那些apps有實作這個framework。
因為一部分的framework使用了content providers,
這個標題會假定您已經對於Android Content Provider API有所熟悉了。
(我就是要跳著看怎樣不行歐= =++++)
相關的資料是在
Content Providers裡所描述的。
The Clipboard Framework:
當你使用了剪貼簿的framework,你將資料放進了一個clip物件,
然後將這個clip物件放到系統廣域(system-wide)的剪貼簿。
clip物件可以選用下列三種格式之一:
Text:
一個文字string。你將string直接放進clip物件裡,
然後放到剪貼簿上。要貼上string時,從剪貼簿取得clip物件,
然後將string複製到你的app的儲存的地方。
URI:
一個
Uri物件代表任何格式的URI,這是主要作為
從content provider作為複製複雜的資料的用途的。
要複製資料,將一個
Uri物件放到clip物件,
然後將clip物件放到剪貼簿上。要貼上資料,
先取得clip物件,拿到
Uri物件,將其解析成資料source,
像是content provider,並且從source複製資料進你的app儲存空間。
Intent:
一個
Intent
。這支援了複製app捷徑。
要複製資料的話,新增一個Intent,將其放到clip物件,
然後將clip物件放到剪貼簿上。
要貼上資料的話,拿到clip物件後,
將Intent物件複製進app的記憶區域。
剪貼簿一個時間只保存一個clip物件。
當一個app將clip物件放到剪貼簿上時,
前一個clip物件會消失。
如果你要允許使用者在你的app裡貼上資料的話,
你不需要去處理所有種類的資料。
你可以在你給使用者選項做貼上前,
檢驗剪貼簿上的資料。
除了有特定的資料格式以外,clip物件也包含metadata,
它會告訴你是什麼MIME種類,或者哪些種類可用。
這個metadata幫你決定你的app,
是否可以用剪貼簿資料來做有用的事情。
舉例來說,如果你有一個app主要是處理文字的話,
你也許想要忽略那種含有URI或Intent的clip物件。
你也許也想要讓使用者貼上文字,
且忽略掉原本剪貼版上的資料格式。
要這麼做的話,你可以強迫剪貼簿資料轉成文字表達,
然後再貼上文字。
這個部分在下面的
Coercing the clipboard to text這個段落會說明。
Clipboard Classes:
這個段落描述了被剪貼簿的framework所使用的classes。
ClipboardManager:
在Android系統裡,
系統剪貼簿是由global
ClipboardManager class所表示的。
你不須直接實例化這個class;
取而代之的做法是,
藉由調用
getSystemService(CLIPBOARD_SERVICE)來得到reference。
ClipData, ClipData.Item, and ClipDescription:
要將資料加到剪貼簿,
製作一個含有資料描述和資料本身的
ClipData
物件。
剪貼簿一次只保留一個
ClipData
。
一個
ClipData
包含一個
ClipDescription物件,
以及一或多個
ClipData.Item
物件。
一個
ClipDescription物件包含了clip的metadata。
尤其是,它含有一個給clip的資料可用的MIME類別的陣列。
當你將clip放到剪貼簿上時,這個陣列就可以做為貼到app上所用。
可以透過檢驗它來確認是否它們可以處理任何可用的MIME種類。
一個
ClipData.Item物件含有text,URI,或者Intent data:
Text:
一個
CharSequence。
URI:
一個
Uri。這通常含有一個content provider URI,
儘管任何URI都是被允許的。提供資料來放URI到剪貼簿上的app。
想要將資料貼上的apps從剪貼簿拿到URI,
並且使用它來存取content provider(或者其他的資料source),
並且取得資料。
Intent:
一個
Intent。這個資料型態允許你去複製一個app的捷徑到剪貼簿上。
使用者接著就可以貼上捷徑到他們的apps,作為稍後的用途。
你可以將超過一個的
ClipData.Item物件到一個clip上。
這允許使用者來剪貼多重選擇區塊,以作為一個clip。
舉例來說,如果你有一個列表的widget允許使用者一次選取多個item,
你可以將所有items一次複製到剪貼簿上。
要這麼做的話,製作一個分開的
ClipData.Item給每一項列表的item,
接著將
ClipData.Item物件給
ClipData物件。
ClipData convenience methods:
ClipData class提供了方便的靜態methods,
給使用單一個
ClipData.Item物件,
及一個簡單的
ClipDescription
物件,
以製作
ClipData物件。
newPlainText(label, text)
回傳一個
ClipData
物件,當中單一個物件包含了一個文字string。
ClipDescription物件的label是被設為
label。
在
ClipDescription裡單一的MIME種類是
MIMETYPE_TEXT_PLAIN。
使用
newPlainText()來從文字string來製作clip。
newUri(resolver, label, URI)
回傳一個
ClipData物件
如果URI是一個content URI的話,
(
Uri.getScheme()回傳
content:)
method使用在
resolver裡提供的
ContentResolver物件,
來從content provider取得可用的MIME種類,
並且將它們存到
ClipDescription
裡。
對於不是
content:
URI的URI,
這個method設定MIME種類到
MIMETYPE_TEXT_URILIST
。
使用
newUri()來從URI製作一個clip,尤其是
content: URI。
newIntent(label, intent)
回傳一個
ClipData物件,
其單一的
ClipData.Item
物件包含一個
Intent
。
ClipDescription物件的標籤被設為
label。
MIME種類被設為
MIMETYPE_TEXT_INTENT
。
使用
newIntent()
來從一個Intent物件來製作一個clip。
Coercing the clipboard data to text:
即便你的app只處理文字,
你可以透過使用
ClipData.Item.coerceToText()
的method,
轉換從剪貼簿所複製的非文字的資料。
這個method將
ClipData.Item
裡的資料轉換成文字,
並且回傳一個
CharSequence
。
ClipData.Item.coerceToText()的回傳值是基於
ClipData.Item的資料格式:
Text
如果
ClipData.Item是文字的話(
getText()
不為null),
coerceToText()
回傳其文字。
URI:
如果
ClipData.Item是URI的話(
getUri()
不為null),
coerceToText()
嘗試使用它來當作一個content URI:
1. 如果URI是一個content URI,
且provider可以回傳一個文字串流(text stream),
coerceToText()
回傳一個文字串流。
2. 如果URI是一個content URI,
但provider並不提供文字串流,
coerceToText()回傳一個URI的表示式(representation)。
此表示式和由
Uri.toString()
所回傳的是相同的。
3. 如果URI不是一個content URI,
coerceToText()
回傳一個URI的表示式。
此表示式和由
Uri.toString()
所回傳的是相同的。
Intent:
如果
ClipData.Item是Intent的話(並非為null),
coerceToText()
將它轉換成一個Intent URI並回傳之。
這個表示和被
Intent.toUri(URI_INTENT_SCHEME)
回傳的是一樣的。
剪貼簿的framework可以由下圖來做總結。
要複製資料的時候,
一個app將
ClipData物件放上
ClipboardManager
的全域剪貼簿。
ClipData包含了一或多個
ClipData.Item
物件,
以及一個
ClipDescription
。
要貼上資料的時候,
一個app拿到
ClipData
,
從
ClipDescription取得它的MIME種類,
並且從
ClipData.Item
,
或者從
ClipData.Item
所指向的content provider取得資料。
Copying to the Clipboard:
就如前面所描述的,要將資料複製到剪貼簿的話,
你要處理global的
ClipboardManager
物件,
做出一個
ClipData
物件,加上一個
ClipDescription
,
以及一或多個
ClipData.Item
物件上去,
最後加上被完成的
ClipData
物件到
ClipboardManager
物件。
這在下面的程序中會詳盡的描述:
1. 如果你是用content URI來複製資料的話,設好一個content provider。
sample使用
Note Pad sample app,
是一個對於使用content provider來複製貼上的範例。
NotePadProvider的class實作了content provider。
NotePad的class定義了provider與其他app之間的契約(contract),
包含了支援的MIME種類。
2. 拿到系統的剪貼簿:
...
// if the user selects copy
case R.id.menu_copy:
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
3. 將資料複製到一個新的
ClipData物件:
對純文字而言:
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text","Hello, World!");
對URI而言:
這個片段藉由對record ID做編碼放到content URI上,
建構了一個URI給provider。
這個技術在
Encoding an identifier on the URI段落裡有更詳盡的敘述。
// Creates a Uri based on a base Uri and a record ID based on the contact's last name
// Declares the base URI string
private static final String CONTACTS = "content://com.example.contacts";
// Declares a path string for URIs that you use to copy data
private static final String COPY_PATH = "/copy";
// Declares the Uri to paste to the clipboard
Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
...
// Creates a new URI clip object. The system uses the anonymous getContentResolver() object to
// get MIME types from provider. The clip object's label is "URI", and its data is
// the Uri previously created.
ClipData clip = ClipData.newUri(getContentResolver(),"URI",copyUri);
對Intent而言:
這個片段建構一個Intent給app,
接著將其放進clip物件裡。
// Creates the Intent
Intent appIntent = new Intent(this, com.example.demo.myapplication.class);
...
// Creates a clip object with the Intent in it. Its label is "Intent" and its data is
// the Intent object created previously
ClipData clip = ClipData.newIntent("Intent",appIntent);
4. 將新的clip物件放上剪貼簿:
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Pasting from the Clipboard:
如前面所描述的,我們藉由從global的剪貼簿物件,
從剪貼簿取得資料,拿到clip物件,看看它的資料,
並且可能的話將其從clip物件複製到自己的儲存空間。
這個段落詳盡敘述了怎麼樣針對三種剪貼簿資料的格式來處理。
Pasting plain text:
想要貼上純文字,首先拿到global的剪貼簿,
接著拿到clip物件,
並且使用
getText()將文字複製到自己的儲存空間,
如下面的程序所述:
1. 使用
getSystemService(CLIPBOARD_SERVICE)
,
來取得global的
ClipboardManager物件。
並且也聲明一個全域變數來包含這個被貼上的文字:
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String pasteData = "";
2. 接著決定是否要在現在的Activity裡打開或關掉"貼上"的選項。
你必須驗證剪貼簿有一個clip讓你來藉此處理資料類別。
// Gets the ID of the "paste" menu item
MenuItem mPasteItem = menu.findItem(R.id.menu_paste);
// If the clipboard doesn't contain data, disable the paste menu item.
// If it does contain data, decide if you can handle the data.
if (!(clipboard.hasPrimaryClip())) {
mPasteItem.setEnabled(false);
} else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
// This disables the paste menu item, since the clipboard has data but it is not plain text
mPasteItem.setEnabled(false);
} else {
// This enables the paste menu item, since the clipboard contains plain text.
mPasteItem.setEnabled(true);
}
}
3. 從剪貼簿複製資料。這點只有在"貼上"的目錄選項有啟用時,
在程式裡才做得到,所以你可以預設剪貼簿含有純文字。
你不知道它是不是包含一個文字string,
或者一個指向純文字的URI。
下面的片段測試了這點,但它只展示了處理純文字的code。
// Responds to the user selecting "paste"
case R.id.menu_paste:
// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
// Gets the clipboard as text.
pasteData = item.getText();
// If the string contains data, then the paste operation is done
if (pasteData != null) {
return;
// The clipboard does not contain text. If it contains a URI, attempts to get data from it
} else {
Uri pasteUri = item.getUri();
// If the URI contains something, try to get text from it
if (pasteUri != null) {
// calls a routine to resolve the URI and get data from it. This routine is not
// presented here.
pasteData = resolveUri(Uri);
return;
} else {
// Something is wrong. The MIME type was plain text, but the clipboard does not contain either
// text or a Uri. Report an error.
Log.e("Clipboard contains an invalid data type");
return;
}
}
Pasting data from a content URI:
如果
ClipData.Item
物件包含了一個content URI,
且你已經決定了你可以處理其中一個MIME的種類,
做一個
ContentResolver
然後呼叫適合的content provider程序來取得資料。
下面的程序描述了,
如何從基於剪貼簿上的content URI的content provider取得資料。
它檢查了app可以使用的MIME類型是否能由provider取得。
1. 聲明一個全域變數來包含MIME種類。
// Declares a MIME type constant to match against the MIME types offered by the provider
public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
2. 取得全域的剪貼簿。並且也取得一個content provider,
藉此可以用來存取content provider。
// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// Gets a content resolver instance
ContentResolver cr = getContentResolver();
3. 從剪貼簿取得主要的clip,並且將其中的內容抓成URI:
// Gets the clipboard data from the clipboard
ClipData clip = clipboard.getPrimaryClip();
if (clip != null) {
// Gets the first item from the clipboard data
ClipData.Item item = clip.getItemAt(0);
// Tries to get the item's contents as a URI
Uri pasteUri = item.getUri();
4. 透過
getType(Uri)測試看看URI是否為content URI。
這個method回傳null,如果
Uri並沒有指到一個合法的(valid)content provider:
// If the clipboard contains a URI reference
if (pasteUri != null) {
// Is this a content URI?
String uriMimeType = cr.getType(pasteUri);
5. 測試看看content provider是否支援目前的app了解的MIME類型。
如果支援,就呼叫
ContentResolver.query()
來取得資料。
回傳值是一個
Cursor
:
// If the return value is not null, the Uri is a content Uri
if (uriMimeType != null) {
// Does the content provider offer a MIME type that the current application can use?
if (uriMimeType.equals(MIME_TYPE_CONTACT)) {
// Get the data from the content provider.
Cursor pasteCursor = cr.query(uri, null, null, null, null);
// If the Cursor contains data, move to the first record
if (pasteCursor != null) {
if (pasteCursor.moveToFirst()) {
// get the data from the Cursor here. The code will vary according to the
// format of the data model.
}
}
// close the Cursor
pasteCursor.close();
}
}
}
}
Pasting an Intent:
要貼上一個Intent,首先取得全域的剪貼簿。
檢查
ClipData.Item物件來看看它是否包含一個Intent。
接著呼叫
getIntent()來複製Intent到你的儲存空間。
下面的片段演示了作法:
// Gets a handle to the Clipboard Manager
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// Checks to see if the clip item contains an Intent, by testing to see if getIntent() returns null
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();
if (pasteIntent != null) {
// handle the Intent
} else {
// ignore the clipboard, or issue an error if your application was expecting an Intent to be
// on the clipboard
}
Using Content Providers to Copy Complex Data:
content providers支援了複製複雜的資料,
像是資料庫紀錄或檔案系統。
要複製資料,你可以放個content URI到剪貼簿上。
貼到apps上然後從剪貼簿取得URI,
並且使用它來取得資料庫資料或者檔案串流descriptors。
因為貼上的app只有你的資料的content URI,
它需要知道要取得哪塊資料。
你可以藉由將identifier編碼給URI上的資料,來提供這個資訊。
或者,你可以提供一個特定的URI來回傳你想複製的資料。
使用哪種技巧,取決於你的資料的組織。
下面的區塊描述了如何設好URIs,
如何提供複雜的資料,以及如何提供檔案串流。
這些描述假設你對於大體的content provider設計的principles熟悉。
Encoding an identifier on the URI:
用URI來複製資料到剪貼簿的一個好用的技巧,
是將資料的identifier紀錄到URI自己身上。
你的content provider接著可以從URI取得identifier,
並且使用它來取得資料。
貼上的app不會知道identifier存在;所有它所做的,
只是從剪貼簿拿到你的"參照"(URI及identifier),
給它你的content provider,並且取回資料。
你通常會將identifier透過串到URI的尾端,
來編碼到一個content URI上。
舉例來說,
假如你定義了你的provider URI如下面的字串:
"content://com.example.contacts"
如果你想要將一個名字編碼到這個URI的話,
使用下面的做法:
String uriString = "content://com.example.contacts" + "/" + "Smith"
// uriString now contains content://com.example.contacts/Smith.
// Generates a uri object from the string representation
Uri copyUri = Uri.parse(uriString);
如果你已經使用了一個content provider,
你可能想要加上一個新的URI path來指示作為複製的URI。
舉例來說,假設你已經有下面的URI paths:
"content://com.example.contacts"/people"content://com.example.contacts"/people/detail"content://com.example.contacts"/people/images
你可以加上另一個path作為特定用來複製URIs:
"content://com.example.contacts/copying"
接著你就能藉由pattern-matching偵測一個"複製"URI,
並且使用特定作為複製及貼上的code來處理了。
如果你已經在使用content provider、內部資料庫,
或者內部的table來管理你的資料,
你通常會用編碼技巧。
在這些狀況下,你有多塊資料要複製,
且想當然一個獨特的indentifier給每一塊。
要回應一個從貼上的app傳來的query,
你可以藉由資料的identifier來查找並回傳資料。
如果你沒有多塊的資料,那麼你大概不需要編碼一個identifier。
你可以簡單的使用一個對你的provider來說獨特的URI就行了。
要回應一個query的話,你的provider會回傳它現在所含的資料。
藉由ID取得單一一個record的方法,
在
Note Pad sample app裡被使用,
用來從notes list裡開啟一個note。
sample透過SQL資料庫,使用了
_id的field。
但是你可以用任何你想要的數字或字母的identifier。
Copying data structures:
我們將content provider設置好,做為將複製貼上複雜資料的準備,
寫成一個
ContentProvider
的subclass。
你也應該將你放上剪貼簿的URI做編碼,
如此一來,它會指向你所想提供的那個record。
除此以外,你必須要考慮目前存在的app狀態:
1. 如果已經有一個content provider,
你可以加到它的功能。你可能只需要去修改它的
query() method,
來處理從apps而來要貼上資料的URIs。
你大概會要修改method來處理"複製"URI的樣式。
2. 如果你的app維護一個內部的資料庫,
你也許要將資料庫移進一個content provider來方便從它作複製。
3. 如果你現在並不是在使用資料庫,你可以實作一個content provider,
唯一用途就是作為提供資料給從剪貼簿來貼上東西的app。
在content provider裡,你會要複寫最少下面的methods:
query()
做貼上工作的app,
會假定他們可以使用你放上剪貼簿的URI,
來利用這個method取得你的資料。
要支援複製的話,你必須要有這個method,
來偵測含有特別的"複製"路徑的URIs。
你的app接著就可以做出一個"複製"URI來放上剪貼簿,
當中包含了複製的路徑以及一個指標,用以指向你所要複製的紀錄。
getType()
這個method應該要將一或多個你想要複製的資料的MIME類型做回傳。
method
newUri()
會呼叫
getType(),
為了要將MIME類型放進新的
ClipData物件。
注意你不需要去有任何其他的content provider methods,
比如像是
insert()
或
update()
。
一個在做貼上的app,只需要去取得你支援的MIME類型,
並且從你的provider複製資料即可。
如果你已經有這些methods了,他們將不會干涉複製的操作。
下面的片段演示了如何將你的app設定來複製複雜的資料:
1. 在app全域變數的部分,聲明一個base URI字串,
以及一個路徑用以辨認你用來複製資料的URI字串。
同時也聲明一個MIME類型給被複製的資料:
// Declares the base URI string
private static final String CONTACTS = "content://com.example.contacts";
// Declares a path string for URIs that you use to copy data
private static final String COPY_PATH = "/copy";
// Declares a MIME type for the copied data
public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
2. 在使用者複製資料的Activity哩,
設好code來複製資料到剪貼簿。
作為對複製請求的回應,將URI放上剪貼簿:
public class MyCopyActivity extends Activity {
...
// The user has selected a name and is requesting a copy.
case R.id.menu_copy:
// Appends the last name to the base URI
// The name is stored in "lastName"
uriString = CONTACTS + COPY_PATH + "/" + lastName;
// Parses the string into a URI
Uri copyUri = Uri.parse(uriString);
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
3. 在content provider的全域範圍內,
做出一個URI matcher,
並且將一個符合你放上剪貼簿的URI pattern的URI加上去:
public class MyCopyProvider extends ContentProvider {
...
// A Uri Match object that simplifies matching content URIs to patterns.
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// An integer to use in switching based on the incoming URI pattern
private static final int GET_SINGLE_CONTACT = 0;
...
// Adds a matcher for the content URI. It matches
// "content://com.example.contacts/copy/*"
sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
4. 將
query()
的method設定好。這個method可以處理不同的URI樣式,
取決於你如何去寫code,但只有在剪貼簿上做複製的樣式會被顯示出來:
// Sets up your provider's query() method.
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
...
// Switch based on the incoming content URI
switch (sUriMatcher.match(uri)) {
case GET_SINGLE_CONTACT:
// query and return the contact for the requested name. Here you would decode
// the incoming URI, query the data model based on the last name, and return the result
// as a Cursor.
...
}
5. 將
getType() method設定好,
用以回傳對於複製的資料而言恰當的MIME種類:
// Sets up your provider's getType() method.
public String getType(Uri uri) {
...
switch (sUriMatcher.match(uri)) {
case GET_SINGLE_CONTACT:
return (MIME_TYPE_CONTACT);
Pasting data from a content URI描述了如何從剪貼簿拿到一個content URI,
並且用其來取得和貼上資料。
Copying data streams:
你可以複製貼上大量的文字和二進位資料作為串流。
資料可以有如下的格式:
1. 儲存在實際裝置的檔案。
2. 從sockets拿到的串流。
3. 大量的存在provider裡的相關的資料庫系統裡的資料。
一個給資料串流使用的content provider,
提供了使用file descriptor物件,
像是
AssetFileDescriptor instead of 一個
Cursor
物件。
貼上的app透過資料串流讀取資料串流。
要設定好你的app來從provider進行資料複製,
遵循下面的步驟:
1. 設好content URI給你要放上剪貼簿的資料串流。
這麼做的幾個選項包括如下所述:
a. 將identifier編碼給資料串流放上URI,
就如在段落
Encoding an identifier on the URI所描述的,
接著維護你含有identifiers及相對應的串流名稱的provider裡的table。
b. 將串流名稱直接在URI上編碼。
c. 使用一個獨特的URI,此URI總是回傳現在provider的串流。
如果你使用了這個選項,你必須記得去更新你的provider,
以指向不同的串流。
(只要你是透過URI從剪貼簿上的串流來複製的時候)
2. 提供一個MIME種類,給任何你想提供的資料種類。
貼上的app需要這個資訊,來決定他們能不能在剪貼簿上貼東西。
3. 實作其中一個
ContentProvider
methods來回傳一個file descriptor給串流。
如果你將content URI上的identifiers編碼,使用這個method來決定要開哪個串流。
4. 要複製資料串流到剪貼簿的話,建構content URI,
並且將其放上剪貼簿。
要貼上資料串流,一個app從剪貼簿拿到clip,
接著取得URI,
並且將其在呼叫
ContentResolver file descriptor method後使用。
ContentResolver
method呼叫了對應
ContentProvider
的 method。
貼上的app接著就有責任去從串流讀出資料。
下面的列表,
秀出一個content provider最重要的file descriptor methods列表。
每一個都有其相對應的
ContentResolver
method,
且有著字串"Descriptor"加在method名字後面;
舉例來說,
ContentResolver
裡
openAssetFile()
的analog,
是
openAssetFileDescriptor()
:
openTypedAssetFile()
這個method應該要回傳一個asset file descriptor,
但只有在提供的MIME種類有被provider支援的時候才會這麼做。
呼叫者(做貼上動作的app)提供了一個MIME種類的樣式。
content provider(有一個複製的URI到剪貼簿上的content provider)
會回傳一個
AssetFileDescriptor
檔案handle
(如果它可以提供那個MIME種類),或者拋出exception如果不行的話。
openAssetFile()
這個method是一個
openTypedAssetFile()
更普遍的的格式。
它並不會篩選允許的MIME種類,
但它可以讀檔案的subsections。
這是一個
openTypedAssetFile()
更普遍的格式。
它不能讀檔案的subsection。
你可以選擇性的使用
openPipeHelper()
這個method,
利用你的file descriptor method。這會允許貼上的app,
在背景的thread來讀串流資料,藉由使用pipw。
要使用這個method,
你需要實作
ContentProvider.PipeDataWriter
的介面。
一個做法的範例被放在
Note Pad samploe app裡面,
在
openTypedAssetFile()裡
NotePadProvider.java 的method。
Designing Effective Copy/Paste Functionality:
要為你的app設計有效率的複製和貼上的功能,
記得以下幾點:
1. 在任何時候,在剪貼簿上只有一個clip。
系統內新的app複製操作都會把前面的clip複寫。
既然使用者可能會導航(navigate)到別的地方去,
你不能假定剪貼簿有你前面使用過複製所留的資料。
2. 一個clip有多個
ClipData.Item
物件的意圖,
是要支援多重選擇的複製及貼上,
這樣會比每次選一段來的好。
你通常想要所有的
ClipData.Item
物件在相同一個cilp裡,
擁有同樣的格式,也就是說,
他們應該全都是純文字,content URI,
或者
Intent,但不是不同種類的組合。
3. 當你提供資料時,你可以提供不同的MIME表示法。
將你所支援的MIME種類加到
ClipDescription
,
然後將MIME種類實作在你的content provider。
4. 當你從剪貼簿取得資料,你的app就該負責檢查可用的MIME種類,
並且決定使用哪一個(如果有的話)。
即便是有一個clip在剪貼簿上,且使用者請求貼上,
你的app並不是非要做貼上不可。
你應該只在MIME種類能相容時才做貼上。
你可以選擇去逼迫剪貼簿上的資料轉為純文字,
使用
coerceToText()
。
如果你的app支援不只一個可用的MIME類型,
你可以允許使用者來選擇使用哪個。