diff options
Diffstat (limited to 'docs/html-intl/intl/zh-tw')
60 files changed, 23127 insertions, 4462 deletions
diff --git a/docs/html-intl/intl/zh-tw/design/get-started/principles.jd b/docs/html-intl/intl/zh-tw/design/get-started/principles.jd index 27cce81c7d2e..c5d40d4bdf86 100644 --- a/docs/html-intl/intl/zh-tw/design/get-started/principles.jd +++ b/docs/html-intl/intl/zh-tw/design/get-started/principles.jd @@ -9,13 +9,13 @@ page.title=Android 設計原則 <p> 當您套用您本身的創意與設計思維時,請考慮這些原則。 -偏離一般做法要帶有目的。 +除非有特定目的,否則請遵循這些原則。 </p> <h2 id="enchant-me">使人著迷</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="delight-me">以出乎意外的方式取悅人</h4> <p>漂亮的外觀、精心設置的動畫,或時機恰到好處的音效,都是令人感到喜悅的體驗。 @@ -23,7 +23,7 @@ page.title=Android 設計原則 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_delight.png"> @@ -32,15 +32,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> -<h4 id="real-objects-more-fun">真實的物件比按鈕和功能表更有趣</h4> -<p>讓人們可以在您應用程式中直接輕觸和操縱物件,這可減少執行某項工作所需的認知過程,同時在情緒上更令人感到滿足。 +<h4 id="real-objects-more-fun">真實的物件比生硬的按鈕和選單,更能夠引人入勝。</h4> +<p>您所設計的應用程式應該要能讓使用者可直接輕觸並操縱物件。這將可減少執行工作所需的認知準備,同時也能讓使用者在情緒上更為滿足。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_real_objects.png"> @@ -49,16 +49,16 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="make-it-mine">提供個人設定</h4> -<p>人們喜歡加上個人風格,因為這有助於他們感到自在並握有主控權。提供能令人感受及美觀的預設設定,但也可以考慮使用不會阻礙主要工作卻又好玩的可選用性自訂項目。 +<p>很多人喜歡加上個人風格,因為這有助於他們感到自在並握有主控權。提供實用且美觀的預設設定,但也可以考慮使用不會阻礙主要工作而且有趣的選用自訂項目。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_make_it_mine.png"> @@ -67,15 +67,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="get-to-know-me">設法了解使用者</h4> -<p>隨著時間而學習使用者的偏好。讓人們易於取得之前的選擇,而不是一再詢問他們會做出相同選擇的問題。 +<p>隨著時間而學習使用者的偏好。讓使用者容易取得之前的選擇,而不是一再請他們做出相同的選擇。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_get_to_know_me.png"> @@ -84,14 +84,14 @@ page.title=Android 設計原則 <h2 id="simplify-my-life">簡化生活</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="keep-it-brief">保持簡潔</h4> -<p>以簡單單字組成簡短語句。人們傾向於略過冗長的句子。</p> +<p>以簡單單字組成簡短語句。多數人傾向於略過冗長的句子。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_keep_it_brief.png"> @@ -100,15 +100,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="pictures-faster-than-words">圖片的傳達效果更勝於言語</h4> -<p>請考慮使用圖片來解釋想法。圖片能吸引人們的注意力,並比言語更有效率。 +<p>請考慮使用圖片來解釋想法。圖片能吸引目光,並比言語更有效率。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_pictures.png"> @@ -117,15 +117,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="decide-for-me">幫使用者決定,但使用者擁有最終決定權</h4> -<p>做出最好的猜測,先行動,而非先詢問。太多的選擇和決定會讓人們不高興。 +<p>做出最好的猜測,先行動,而非先詢問。太多的選擇和決定會讓使用者覺得很痛苦。 為防止錯誤,務必允許復原。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_decide_for_me.png"> @@ -134,15 +134,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> -<h4 id="only-show-when-i-need-it">必要時僅顯示使用者需要的東西</h4> -<p>人們無法承受一次看到太多東西。將工作和資訊細分成小型、易消化的區塊。 -隱藏當下不重要的選項,但在人們需要選擇時要明確指導。</p> +<h4 id="only-show-when-i-need-it">必要時僅顯示使用者需要的內容</h4> +<p>多數人無法承受一次看到太多東西。將工作和資訊細分成小型、易消化的區塊。 +隱藏當下不重要的選項,但在需要選擇時要明確指導。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_information_when_need_it.png"> @@ -151,15 +151,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> -<h4 id="always-know-where-i-am">使用者應該總是清楚所在位置</h4> -<p>給人們信心,知道自己沒有迷路。讓您應用程式中的各處看起來都有獨特性,並使用轉換來顯示畫面之間的關係。 +<h4 id="always-know-where-i-am">使用者應該永遠清楚所在位置</h4> +<p>給使用者信心,知道自己沒有迷路。讓您應用程式中的各個地方看起來都有獨特性,並使用轉換來顯示畫面之間的關係。 針對進行中的工作提供回饋。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_navigation.png"> @@ -168,8 +168,8 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="never-lose-my-stuff">別弄丟使用者的東西</h4> <p>儲存使用者花時間所建立的資訊,並且讓使用者可從任何地方存取。跨手機、平板電腦和電腦記住設定、個人風格和建立的資訊。 @@ -177,7 +177,7 @@ page.title=Android 設計原則 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_never_lose_stuff.png"> @@ -186,15 +186,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="looks-same-should-act-same">如果看起來一樣,就應該有相同的動作</h4> -<p>讓功能看起來就不一樣,而非變化微妙,這可以協助人們辨別功能差異。在相同輸入環境下,因為模式看起來很類似但卻有不同的動作,請盡量避免使用。 +<p>讓功能看起來就不一樣,而非變化微妙,這可以協助使用者辨別功能差異。在相同輸入環境下,請盡量避免使用看起來很類似但作用卻不同的模式。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_looks_same.png"> @@ -203,15 +203,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="interrupt-only-if-important">重要時才打斷</h4> -<p>就像一位好的私人助理,讓人們免於無關緊要的枝微末節。人們總想集中注意力,除非很重要且具時效性,不然貿然中斷會令人感到費力且沮喪。 +<p>就像一位好的私人助理,應該讓使用者免於無關緊要的枝微末節。使用者總想集中注意力,除非很重要且具時效性,否則貿然中斷會令人感到費力且沮喪。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_important_interruption.png"> @@ -220,16 +220,16 @@ page.title=Android 設計原則 <h2 id="make-me-amazing">讓使用者驚艷</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="give-me-tricks">提供使用者各處通用的訣竅</h4> -<p>當人們能自行弄清楚來龍去脈時是很棒的體驗。運用來自其他 Android 應用程式的視覺模式和肌肉記憶效應,讓使用者更易於學會您的應用程式。 +<p>當使用者能自行弄清楚來龍去脈時是很棒的體驗。運用來自其他 Android 應用程式的視覺模式和肌肉記憶效應,讓使用者更易於學會您的應用程式。 例如,擺動手勢可能是很好的導覽捷徑。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_tricks.png"> @@ -238,16 +238,16 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="its-not-my-fault">不是使用者的錯</h4> -<p>提示人們更正時要溫和。人們使用您的應用程式時,會想要感受到自已非常明智。如果有什麼不對,請提供明確的復原指示,但不需要使用者明瞭技術細節。如果可以,請盡量在幕後修正。 - +<p>提示使用者進行更正時要溫和。使用者使用您的應用程式時,不會自覺是笨蛋。 +若有地方出錯,請提供明確的復原指示,但不需要使用者明瞭技術細節。如果可以,請盡量在幕後修正。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_error.png"> @@ -256,15 +256,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="sprinkle-encouragement">分段鼓勵</h4> -<p>將複雜工作細分成更小的步驟,讓使用者可以輕鬆完成。對動作給予回饋,即使只是個微光效果。 +<p>將複雜工作細分成更小的步驟,讓使用者可以輕鬆完成。對動作給予回饋,即使只是細微的動作。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_sprinkle_encouragement.png"> @@ -273,16 +273,16 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="do-heavy-lifting-for-me">為使用者處理繁重的工作</h4> -<p>讓新手也能做出以前從未想像過可以辦到的事情,讓使用者有專家的感覺。例如,組合多個相片效果的捷徑,只要幾個步驟,即可讓業餘照片令人驚艷。 +<p>讓新手也能做到以前從未想像過可以完成的事情,讓使用者覺得自己是專家。例如,組合多個相片效果的捷徑,只要幾個步驟,即可讓業餘照片令人驚艷。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_heavy_lifting.png"> @@ -291,15 +291,15 @@ page.title=Android 設計原則 <div class="vspace size-2"> </div> -<div class="layout-content-row"> - <div class="layout-content-col span-7"> +<div class="cols"> + <div class="col-7"> <h4 id="make-important-things-fast">快速找到重要的功能</h4> -<p>並非所有的動作都一視同仁。決定應用程式中最重要的部分,並讓使用者易於找到並可迅速使用,例如相機的快門按鈕或音樂播放器的暫停按鈕。 +<p>並非所有的動作都平等。決定應用程式中最重要的部分,並讓使用者易於找到並可迅速使用,例如相機的快門按鈕或音樂播放器的暫停按鈕。 </p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/principles_make_important_fast.png"> diff --git a/docs/html-intl/intl/zh-tw/design/material/index.jd b/docs/html-intl/intl/zh-tw/design/material/index.jd index 620ee6ec2062..464dc2a1b216 100644 --- a/docs/html-intl/intl/zh-tw/design/material/index.jd +++ b/docs/html-intl/intl/zh-tw/design/material/index.jd @@ -1,7 +1,6 @@ -page.title=材料設計 -page.tags=Material, design -page.type=設計 -page.image=design/material/images/MaterialLight.png +page.title=Android 材料設計 +page.tags=Material,design, 設計 +page.image=images/cards/design-material-for-android_2x.jpg @jd:body @@ -41,25 +40,25 @@ page.image=design/material/images/MaterialLight.png <p itemprop="description">材料設計是一份內容廣泛的綜合性指南,引導您跨平台、跨裝置進行視覺、動態和互動的設計。 Android 現已納入對材料設計應用程式的支援。 -如果要在 Android 應用程式中使用材料設計,請依照<a href="http://www.google.com/design/spec">材料設計規格</a>中定義的指示,並使用 -Android -5.0 (API 層級 21) 或後續版本中的新元件和新功能。</p> +如果要在 Android 應用程式中使用材料設計,請依照<a href="http://www.google.com/design/spec">材料設計規格</a>中定義的指示,並使用 Android 5.0 (API 級別 21) 或後續版本中的新元件和新功能。 + +</p> <p>Android 提供下列元素,供您打造材料設計應用程式:</p> <ul> - <li>一個新的設計風格</li> - <li>用於複雜檢視的小工具</li> + <li>新的設計風格</li> + <li>用於複雜視圖的小工具</li> <li>自訂陰影和動畫的新 API</li> </ul> -<p>如需取得如何在 Android 上實作材料設計的詳細資訊,請詳見<a href="{@docRoot}training/material/index.html">使用材料設計建立應用程式</a>。 +<p>如需取得如何在 Android 上實作材料設計的詳細資訊,請參閱<a href="{@docRoot}training/material/index.html">使用材料設計建立應用程式</a>。 </p> <h3>材料設計風格</h3> -<p>材料設計風格提供您應用程式使用的新樣式、可以讓您設定色板的系統小工具,並針對輕觸回饋與操作行為轉換提供預設動畫。 +<p>材料設計風格提供您應用程式使用的新樣式、可以讓您設定色板的系統小工具,並針對輕觸回饋與操作 Activity 轉換提供預設動畫。 </p> <!-- two columns --> @@ -92,8 +91,8 @@ Android <div style="width:700px;margin-top:25px;margin-bottom:20px"> <div style="float:left;width:250px;margin-left:40px;margin-right:60px;"> <img src="{@docRoot}design/material/images/list_mail.png" width="500" height="426" /> - <p>新的 <code>RecyclerView</code> 小工具是 <code>ListView</code> -更容易插入的版本,支援不同的版面配置類型,並提供效能改善。</p> + <p>新的 <code>RecyclerView</code> 小工具是 <code>ListView</code> 更容易插入的版本,支援不同的版面配置類型,並提供效能改善。 +</p> </div> <div style="float:left;width:250px;margin-right:0px;"> <img src="{@docRoot}design/material/images/card_travel.png" width="500" height="426" /> @@ -109,12 +108,12 @@ Android <h3>檢視陰影</h3> -<p>除了 X 和 Y 屬性外,Android 中的檢視現在也有 Z 屬性。 -這個新屬性代表檢視的高度,這會決定:</p> +<p>除了 X 和 Y 屬性外,Android 中的視圖現在也有 Z 屬性。 +這個新屬性代表視圖的高度,這會決定:</p> <ul> -<li>陰影大小:帶有較高 Z 值的檢視會投射更大的陰影。</li> -<li>繪製順序:具有較高 Z 值的檢視會顯示在其他檢視之上。</li> +<li>陰影大小:Z 值較高的視圖會投射更大的陰影。</li> +<li>繪製順序:Z 值較高的視圖會顯示在其他視圖之上。</li> </ul> <div style="width:290px;margin-left:35px;float:right"> @@ -130,39 +129,38 @@ Android </div> </div> -<p>如需取得詳細資訊,請參閱<a href="{@docRoot}training/material/shadows-clipping.html">定義陰影和裁剪檢視</a>。 +<p>如需詳細資訊,請參閱<a href="{@docRoot}training/material/shadows-clipping.html">定義陰影和裁剪視圖</a>。 </p> <h3>動畫</h3> - -<p>新的動畫 API 可針對 UI 控制項的輕觸回饋、檢視狀態中的變更,以及行為轉換,讓您建立自訂動畫。 +<p>新的動畫 API 可針對 UI 控制項的輕觸回饋、視圖狀態中的變更,以及操作行為轉換,讓您建立自訂動畫。 </p> <p>這些 API 讓您可以:</p> <ul> <li style="margin-bottom:15px"> -回應您檢視中有<strong>輕觸回饋</strong>動畫的輕觸事件。 +回應您視圖中有<strong>輕觸回饋</strong>動畫的輕觸事件。 </li> <li style="margin-bottom:15px"> -隱藏和顯示有<strong>循環顯示</strong>動畫的檢視。 +隱藏和顯示有<strong>循環顯示</strong>動畫的視圖。 </li> <li style="margin-bottom:15px"> -在有自訂<strong>行為轉換</strong>動畫的行為間切換。 +在包含自訂<strong>操作行為轉換</strong>動畫的行為間切換。 </li> <li style="margin-bottom:15px"> 使用<strong>曲線動作</strong>建立更自然的動畫。 </li> <li style="margin-bottom:15px"> -在帶有<strong>檢視狀態變更</strong>動畫的一個或多個檢視屬性中變更動畫。 +在包含<strong>視圖狀態變更</strong>動畫的一個或多個視圖屬性中變更動畫。 </li> <li style="margin-bottom:15px"> -在檢視狀態變更間,顯示<strong>狀態清單可繪項目</strong>中的動畫。 +在視圖狀態變更之間,顯示<strong>狀態清單可繪項目</strong>中的動畫。 </li> </ul> -<p>輕觸回饋動畫會內建於數個標準檢視中,例如按鈕等。新 API 可讓您自訂這些動畫,並將其新增至您的自訂檢視中。 +<p>輕觸回饋動畫會內建於數個標準視圖中,例如按鈕等。新 API 可讓您自訂這些動畫,並將其新增至您的自訂視圖中。 </p> <p>如需詳細資訊,請參閱<a href="{@docRoot}training/material/animations.html">定義自訂動畫</a>。 @@ -176,7 +174,7 @@ Android <ul> <li><strong>矢量可繪項目</strong>可以調整大小,但又不會喪失定義,最適合於應用程式中的單色圖示。 </li> -<li><strong>可繪項目著色</strong>可讓您在執行階段將點陣圖定義為 Alpha 遮罩,並以一個顏色進行著色。 +<li><strong>繪製著色</strong>可讓您在執行階段將點陣圖定義為 Alpha 遮罩,並以一個顏色進行著色。 </li> <li><strong>顏色提取</strong>可讓您自動從點陣圖影像中提取顯著顏色。 </li> diff --git a/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd b/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd new file mode 100644 index 000000000000..b65b90be849c --- /dev/null +++ b/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=向下相容性 +page.tags="support" +page.metaDescription=有關 Android 4.x 改寫針對舊硬體和 OS 版本設計的 UI 之備註。 +@jd:body + +<a class="notice-developers" href="{@docRoot}training/basics/supporting-devices/index.html"> + <div> + <h3>開發人員文件</h3> + <p>支援不同裝置</p> + </div> +</a> + +<p>Android 3.0 主要更新包含:</p> +<ul> +<li>淘汰導覽硬體鍵 (返回、選單、搜尋、首頁),以虛擬控制按鍵取代實體導覽按鍵 (返回、首頁、最近使用記錄)。 +</li> +<li>健全的動作列選單使用模式。</li> +</ul> +<p>Android 4.0 版本將平板電腦的變更套用於手機平台。</p> + +<h2 id="older-hardware">針對舊版本的硬體及應用程式,改寫 Android 4.0</h2> + +<div class="cols"> + <div class="col-6"> + +<h4>使用虛擬導覽控制鍵的手機</h4> +<p>專為 Android 3.0 及之後版本設計的應用程式會在動作列中顯示動作。擠不進動作列或重要性不足的動作,會顯示在動作溢位中。 + +</p> +<p>使用者可輕觸動作列中的動作溢位以存取它。</p> + + </div> + <div class="col-7"> + + <img src="{@docRoot}design/media/compatibility_virtual_nav.png"> + + </div> +</div> + +<div class="cols"> + <div class="col-6"> + +<h4>使用實體導覽鍵的手機</h4> +<p>使用實體導覽鍵的 Android 手機不會在螢幕底部顯示虛擬導覽列。 +如要存取動作溢位,請使用選單硬體按鍵。所產生的動作彈出式選單的風格與先前範例相同,但會顯示在螢幕頂部。 +</p> + + </div> + <div class="col-7"> + + <img src="{@docRoot}design/media/compatibility_physical_buttons.png"> + + </div> +</div> + +<div class="cols"> + <div class="col-6"> + +<h4>在使用虛擬導覽控制鍵的手機上執行舊版應用程式</h4> +<p>當您在具備虛擬導覽控制鍵的手機上,執行為 Android 2.3 或之前版本所設計的應用程式時,動作溢位控制會顯示在虛擬導覽列的右側。 +您可以輕觸控制鍵,以顯示傳統 Android 選單風格的應用程式動作。 +</p> + + </div> + <div class="col-7"> + + <img src="{@docRoot}design/media/compatibility_legacy_apps.png"> + + </div> +</div> diff --git a/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd index ac8975f8092f..a92a017b32ff 100644 --- a/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd +++ b/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd @@ -2,14 +2,14 @@ page.title=確認及確認完成 page.tags=dialog,toast,notification @jd:body -<p>在某些情況下,當使用者在您應用程式中呼叫一個動作時,最好是透過文字來「確認」(confirm) <em></em>或「確認完成」<em></em>(acknowledge)。</p> +<p>在某些情況下,當使用者在您應用程式中呼叫一個動作時,最好是透過文字來「確認」<em></em>或「確認完成」<em></em>。</p> -<div class="layout-content-row"> - <div class="layout-content-col span-6"> +<div class="cols"> + <div class="col-6"> <img src="{@docRoot}design/media/confirm_ack_confirming.png"> <p><strong>確認</strong>是要求使用者確認真的要進行剛剛呼叫的動作。在某些情況下,確認訊息出現時會伴隨警告或需要使用者考量是否採取動作的相關重要資訊。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <img src="{@docRoot}design/media/confirm_ack_acknowledge.png"> <p><strong>確認完成</strong>是顯示文字,讓使用者知道已經完成剛剛呼叫的動作。這會排除系統正在採取之隱式作業的不確定性。在某些情況下,確認完成出現時會伴隨復原動作的選項。</p> </div> @@ -22,14 +22,14 @@ page.tags=dialog,toast,notification <img src="{@docRoot}design/media/confirm_ack_flowchart.png"> <h2>確認</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-6"> +<div class="cols"> + <div class="col-6"> <h4>範例:Google Play 書籍</h4> <img src="{@docRoot}design/media/confirm_ack_ex_books.png"> <p>在此範例中,使用者已要求從其 Google Play 媒體庫中刪除一本書籍。顯示<a href="{@docRoot}design/building-blocks/dialogs.html#alerts">警示</a>來確認此動作,因為使用者必須了解將不再針對任何裝置提供這本書籍。</p> <p>設計一個確認的對話方塊時,要讓標題具有意義就必須回應要求的動作。</p> </div> - <div class="layout-content-col span-7"> + <div class="col-7"> <h4>範例:Android Beam</h4> <img src="{@docRoot}design/media/confirm_ack_ex_beam.png"> <p>確認不一定要以具有兩個按鈕的警示來呈現。在啟動 Android Beam 之後,會提示使用者輕觸要共用的內容 (在此範例中是一張照片)。如果他們決定不進行,只要移開他們的電話即可。</p> @@ -37,15 +37,15 @@ page.tags=dialog,toast,notification </div> <h2>確認完成</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-6"> +<div class="cols"> + <div class="col-6"> <h4>範例:已儲存放棄的 Gmail 草稿</h4> <img src="{@docRoot}design/media/confirm_ack_ex_draftsave.png"> - <p>在此範例中,如果使用者從 Gmail 撰寫畫面返回,可能會發生預期外的狀況:會自動儲存目前的草稿。以快顯通知 (toast) 形式出現的確認完成,會讓使用者明瞭此情況。確認完成會在幾秒鐘後淡出。</p> + <p>在此範例中,如果使用者從 Gmail 撰寫畫面返回,可能會發生預期外的狀況:會自動儲存目前的草稿。以快顯通知形式出現的確認完成,會讓使用者明瞭此情況。確認完成會在幾秒鐘後淡出。</p> <p>在此並不合適使用復原功能,因為儲存動作是由應用程式發起,而非使用者。瀏覽至草稿清單,就可以方便且快速地繼續撰寫。</p> </div> - <div class="layout-content-col span-6"> + <div class="col-6"> <h4>範例:已刪除 Gmail 會話群組</h4> <img src="{@docRoot}design/media/confirm_ack_draft_deleted.png"> <p>使用者從 Gmail 清單中刪除一個會話群組後,會出現確認完成訊息,並提供一個復原選項。確認完成會持續出現,直到使用者採取不相關的動作,例如捲動清單。</p> @@ -53,14 +53,14 @@ page.tags=dialog,toast,notification </div> <h2>無「確認」或「確認完成」</h2> -<div class="layout-content-row"> - <div class="layout-content-col span-6"> +<div class="cols"> + <div class="col-6"> <h4>範例:+1 中</h4> <img style="padding: 33px 0 30px;" src="{@docRoot}design/media/confirm_ack_ex_plus1.png"> <p><strong>確認並非必要</strong>。如果使用者不小心按了 + 1 按鈕,這並不是什麼大問題。他們可以再次輕觸按鈕,復原此動作。</p> - <p><strong>確認完成並非必要</strong>。使用者將會看到 +1 彈起並變成紅色。這是個非常明確的訊號。</p> + <p><strong>確認完成並非必要</strong>。使用者將會看到 +1 按鈕彈起並變成紅色。這是個非常明確的訊號。</p> </div> - <div class="layout-content-col span-7"> + <div class="col-7"> <h4>範例:從主螢幕移除應用程式</h4> <img src="{@docRoot}design/media/confirm_ack_ex_removeapp.png"> <p><strong>確認並非必要</strong>。這是特意設計的動作:使用者必須拖曳項目放到相對較大且隔離的目標上。因此極不可能發生意外狀況。但如果使用者後悔所做的決定,只需幾秒鐘,就可以恢復原狀。</p> diff --git a/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd b/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd index db160c2516d2..a568cb1d6724 100644 --- a/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd +++ b/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd @@ -10,71 +10,71 @@ page.image=/design/media/navigation_between_siblings_gmail.png </div> </a> -<p itemprop="description">一致的導覽是整體使用者體驗的必備組成。基本導覽的行為若不一致又令人意外,是最令使用者感到更沮喪的狀況。 +<p itemprop="description">一致的導覽是整體使用者體驗的要件。以不一致且非預期方式運作的基本導覽行為最令使用者感到沮喪。 Android 3.0 已將重大變更導入全域的導覽行為中。 -完全遵循 [返回] 及 [上一層] 的方針,會讓使用者感到您的應用程式導覽既可靠又符合預期。 +仔細遵循以下指導方針原則以設定 [返回] 及 [上一層] 按鈕,讓您的應用程式更可靠且符合預期。 +</p> +<p>Android 2.3 及之前版本均使用 [返回]<em></em> 按鈕在應用程式中進行導覽。 +在 Android 3.0 導入導覽列功能,使用者有了另一種導覽方式:[上一層]<em></em> 按鈕,由應用程式圖示及左指插入號組成。 </p> -<p>Android 2.3 和更早版本依賴系統 [返回] -<em></em>按鈕,以支援應用程式內的導覽。在 Android 3.0 導入動作列之後,出現第二個導覽機制:[上一層] -<em></em>按鈕,由應用程式圖示和左指符號組成。</p> <img src="{@docRoot}design/media/navigation_with_back_and_up.png"> -<h2 id="up-vs-back">[上一層] vs.[返回]</h2> +<h2 id="up-vs-back">[返回] 及 [上一層]</h2> + +<p>[上一層] 按鈕用於在應用程式中,根據畫面之間的階層關係進行導覽。 +例如,如果畫面 A 顯示項目清單,然後選擇其中一個項目導致進入畫面 B (更詳細呈現該項目),那麼畫面 B 應該提供 [上一層] 按鈕,以便返回畫面 A。 -<p>[上一層] 按鈕用於在畫面間有階層關係的應用程式中導覽。 -例如,如果畫面 A -顯示項目清單,然後選擇其中一個項目導致進入畫面 B (更詳細呈現該項目),那麼畫面 B 應該提供 [上一層] 按鈕,以便返回畫面 A。 </p> -<p>如果畫面是在應用程式中的最頂端 (亦即應用程式的首頁),則不應該會有 [上一層]按鈕。 +<p>如果畫面是在應用程式中的最頂端 (亦即應用程式的首頁),則不應該會有 [上一層] 按鈕。 </p> <p>系統 [返回] 按鈕用於逆時間順序導覽,透過歷程記錄,可以經歷使用者最近使用過的畫面。 [返回] 通常基於畫面之間的暫時關係,而非應用程式的階層。 </p> -<p>當先前檢視的畫面也是目前畫面的階層父項時,按下 -[返回] 按鈕和按下 [上一層] 按鈕效果相同 — 這是常見的狀況。 -然而,與 [上一層] 按鈕不同的是 (該按鈕可以確保使用者仍停留在您的應用程式內):[返回] -按鈕可以讓使用者返回主畫面,或甚至是不同的應用程式。</p> +<p>當先前檢視的畫面也是目前畫面的階層父項時,按下 [返回] 按鈕和按下 [上一層] 按鈕效果相同 — 這是常見的狀況。 + +然而,與 [上一層] 按鈕不同的是 (該按鈕可以確保使用者仍停留在您的應用程式內):[返回] 按鈕可以讓使用者返回主螢幕,或甚至是不同的應用程式。 +</p> <img src="{@docRoot}design/media/navigation_up_vs_back_gmail.png"> <p>[返回] 按鈕還支援幾個間接關聯畫面對畫面導覽的行為: </p> <ul> -<li>關閉浮動視窗 (對話、快顯)</li> +<li>關閉浮動視窗 (對話框、快顯)</li> <li>關閉內容相關的動作列,並從選取項目移除醒目顯示</li> <li>隱藏畫面鍵盤 (IME)</li> </ul> <h2 id="within-app">在應用程式內導覽</h2> -<h4>導覽至具有多重入口的畫面</h4> -<p>有時候,一個畫面在應用程式的階層中並沒有嚴謹的位置,而且可以從多個入口存取 — 例如可以從您應用程式中任何其他畫面存取的設定畫面。在這種情況下,[上一層] -按鈕應該選擇返回導引至此畫面的前一畫面,這個行為與 [返回] 相同。 +<h4>導覽至具有多個進入點的畫面</h4> +<p>有時畫面在應用程式中的階層並不明確,可從多個入口點進入畫面 — 例如,可從應用程式中的任何其他畫面進入的設定畫面。 +在這種情況下,應按下 [上一層] 按鈕回到之前的參照畫面,操作方式與 [返回] 按鈕相同。 </p> -<h4>在畫面內變更檢視</h4> -<p>變更畫面的檢視選項並不會變更 [上一層] 或 [返回] 的行為:畫面仍維持在應用程式階層中的相同位置,並不會建立新的導覽歷程記錄。 +<h4>在畫面內變更視圖</h4> +<p>變更畫面的視圖選項並不會變更 [上一層] 或 [返回] 的行為:畫面仍維持在應用程式階層中的相同位置,並不會建立新的導覽歷程記錄。 </p> -<p>這類檢視變更的範例如下:</p> +<p>這類視圖變更的範例如下:</p> <ul> -<li>使用標籤和/或左與右滑動來切換檢視</li> -<li>使用下拉清單 (亦即折疊標籤) 切換檢視</li> +<li>使用標籤和/或左與右滑動來切換視圖</li> +<li>使用下拉清單 (亦即折疊標籤) 切換視圖</li> <li>篩選清單</li> <li>對清單排序</li> <li>變更顯示特性 (如縮放)</li> </ul> <h4>在同層級畫面間導覽</h4> -<p>當您的應用程式支援從項目清單導覽至項目之一的詳細檢視時,使用者通常會想使用方向導覽功能,以便從該項目導覽至清單中的前一個或後一個項目。 +<p>當您的應用程式支援從項目清單導覽至項目之一的詳細視圖時,使用者通常會想使用方向導覽功能,以便從該項目導覽至清單中的前一個或後一個項目。 例如在 Gmail 中,可以很容易從會話群組向左或右滑動,方便檢視相同「收件匣」中的較新或舊會話群組。 -就像在一個畫面中變更檢視時,這類導覽不會變更 [上一層] 或 [返回] 的行為。 +就像在一個畫面中變更視圖時,這類導覽不會變更 [上一層] 或 [返回] 的行為。 </p> <img src="{@docRoot}design/media/navigation_between_siblings_gmail.png"> -<p>然而有一個明顯的例外是,在不被引用清單綁在一起的相關詳細資料檢視之間瀏覽時 — 例如在 Play 商店中於相同開發者的不同應用程式之間瀏覽時,或是在相同演出者的專輯間瀏覽時。 +<p>然而有一個明顯的例外是,在未與引用清單繫結在一起的相關詳細資料視圖之間瀏覽時 — 例如在 Play 商店中於相同開發者的不同應用程式之間瀏覽時,或是在相同演出者的專輯間瀏覽時。 在這些情況下,瀏覽每個連結都會產生歷程記錄,這會造成 [返回] 按鈕會經歷每個先前檢視過的畫面。 [上一層] 應該會繼續略過這些相關的畫面,並導覽到最近檢視過的容器畫面。 @@ -82,16 +82,16 @@ Android 3.0 已將重大變更導入全域的導覽行為中。 <img src="{@docRoot}design/media/navigation_between_siblings_market1.png"> -<p>基於您對詳細資料檢視的瞭解,您有能力讓 [上一層] 行為甚至變得更聰明。 +<p>基於您對詳細資料視圖的瞭解,您有能力讓 [上一層] 行為甚至變得更聰明。 再延伸說明之前提及的 Play 商店範例,想像使用者已從最近檢視的「書籍」導覽至「電影」改編的詳細資料。 -在這種情況下,[上一層] -可以返回到使用者之前沒有導覽過的上層容器 (電影)。</p> +在這種情況下,[上一層] 可以返回到使用者之前沒有導覽過的上層容器 (電影)。 +</p> <img src="{@docRoot}design/media/navigation_between_siblings_market2.png"> -<h2 id="into-your-app">透過「主畫面小工具」和「通知」,導覽至您的應用程式</h2> +<h2 id="into-your-app">透過「主螢幕小工具」和「通知」,導覽至您的應用程式</h2> -<p>您可以使用主畫面小工具或通知,協助您直接導覽至深入您應用程式階層中的畫面。 +<p>您可以使用主螢幕小工具或通知,協助您直接導覽至深入您應用程式階層中的畫面。 例如,Gmail 的「收件匣」小工具和新郵件通知,都可以略過「收件匣」畫面,將使用者直接帶到會話群組檢視之中。 </p> @@ -100,7 +100,7 @@ Android 3.0 已將重大變更導入全域的導覽行為中。 <ul> <li>如果目的地畫面通常是透過您應用程式中的一個特定畫面到達,那麼 [上一層] 應該要導覽至該畫面。<em></em> </li> -<li>否則<em></em>, [上一層] 應該導覽至您應用程式的最頂端 (「主」) 畫面。</li> +<li>否則<em></em>, [上一層] 應該導覽至您應用程式的最頂端 (「主」) 螢幕。</li> </ul> <p>就 [返回] 按鈕而言,您應讓導覽更符合預期,方法是在工作的返回堆疊中,插入前往應用程式最頂端畫面的完整向上導覽路徑。 @@ -108,30 +108,30 @@ Android 3.0 已將重大變更導入全域的導覽行為中。 </p> -<p>舉例來說,Gmail 的主畫面小工具有一個按鈕,可以直接往下進入撰寫畫面。 -來自撰寫畫面的 [上一層] 或 [返回] 按鈕,會將使用者帶到「收件匣」中,而此處的 [返回] 按鈕則可繼續前往至「主畫面」。 +<p>舉例來說,Gmail 的主螢幕小工具有一個按鈕,可以直接往下進入撰寫畫面。 +來自撰寫畫面的 [上一層] 或 [返回] 按鈕,會將使用者帶到「收件匣」中,而此處的 [返回] 按鈕則可繼續前往至「主螢幕」。 </p> <img src="{@docRoot}design/media/navigation_from_outside_back.png"> <h4>間接通知</h4> -<p>當您的應用程式需要同時呈現多個事件的資訊時,可以使用單一通知,引導使用者進入一個插頁畫面。 -此畫面會摘要這些事件,並提供路徑,讓使用者可以深入應用程式之中。這種風格的通知稱為「間接通知」<em></em>。 - +<p>若應用程式必須同時呈現多個事件,可利用單一通知,引導使用者進入插頁畫面。 +插頁畫面會概述所有事件,並提供讓使用者可以深入應用程式之中的路徑。 +這種通知方式稱為<em>間接通知</em>。 </p> <p>與標準 (直接) 通知不同的是,從間接通知的插頁畫面按下 [返回],會讓使用者返回至通知觸發的起點 — 無其他畫面會插入至返回堆疊之中。 -一旦使用者從插頁畫面繼續進入應用程式之後,[上一層] -與 [返回] 會如上所述,其行為就像針對標準通知一樣:在應用程式內導覽,而非返回插頁畫面。 +一旦使用者從插頁畫面繼續進入應用程式之後,[上一層] 與 [返回] 會如上所述,其行為就像針對標準通知一樣:在應用程式內導覽,而非返回插頁畫面。 + </p> <p>例如,假設 Gmail 中的使用者收到來自「行事曆」的間接通知。輕觸這個通知會打開插頁畫面,而此畫面會顯示數個不同事件的提醒。 從插頁畫面輕觸 [返回],會讓使用者返回至 Gmail。輕觸特定事件會將使用者帶離插頁畫面,並進入完整的「行事曆」應用程式,顯示事件的詳細資料。 -從事件詳細資料中,[上一層] 和 [返回] 會導覽至「行事曆」的最頂層檢視。</p> +從事件詳細資料中,[上一層] 和 [返回] 會導覽至「行事曆」的最頂層視圖。</p> <img src="{@docRoot}design/media/navigation_indirect_notification.png"> @@ -139,75 +139,75 @@ Android 3.0 已將重大變更導入全域的導覽行為中。 <p>快顯通知<em></em>會略過通知匣,直接出現在使用者面前。 這不常使用,<strong>應該要保留在需要適時回應,以及必須中斷使用者前後關聯動作的時候</strong>。 -例如,Talk -就使用這種風格,用來提示使用者有朋友邀請加入視訊聊天,而且此邀請會在幾秒之後自動過期。 +例如,Talk 就使用這種風格,用來提示使用者有朋友邀請加入視訊聊天,而且此邀請會在幾秒之後自動過期。 + </p> <p>就導覽行為而言,快顯通知緊接著間接通知插頁畫面的行為。 -[返回] 會關閉快顯通知。如果使用者從快顯導覽進入通知應用程式,[上一層] -和 [返回] 會遵循標準通知的規則,只在應用程式內導覽。 +[返回] 會關閉快顯通知。如果使用者從快顯導覽進入通知應用程式,[上一層] 和 [返回] 會遵循標準通知的規則,只在應用程式內導覽。 + </p> <img src="{@docRoot}design/media/navigation_popup_notification.png"> <h2 id="between-apps">在應用程式間導覽</h2> -<p>Android 系統的基本優點之一是應用程式互相啟動的能力,讓使用者能夠直接從一個應用程式導覽至另一個應用程式。 -例如,需要擷取一張相片的應用程式可以啟動「相機」應用程式,而此應用程式會將相片傳回引用的應用程式。開發人員可以輕鬆利用其他應用程式的程式碼,而使用者在經常執行的動作中可以享受一致性的體驗,這對雙方都是一大好處。 - +<p>Android 系統的主要優勢在於應用程式之間可彼此互連,使用者可從一個應用程式中直接導覽至另一個應用程式。 +舉例來說,需要拍攝相片的的應用程式,可開啟「相機」應用程式,然後再返回原始參照應用程式中。 +這項功能不僅使開發人員可輕鬆地利用其他應用程式的程式碼,還可讓使用者透過一致的作業方式享受常用功能。 </p> -<p>要瞭解應用程式對應用程式的導覽,重要的是要瞭解以下討論的 Android 架構行為。 +<p>欲了解應用程式對應用程式的導覽行為,必須先了解以下介紹的 Android 架構行為。 </p> -<h4>活動、工作和意圖</h4> +<h4>Activity、工作和意圖</h4> -<p>在 Android 中,<strong>活動</strong>是一個應用程式元件,定義了資訊畫面,以及使用者可以執行的所有關聯動作。 -您的應用程式是個活動的集合,包括您可以建立及您能從其他應用程式重複使用的活動。 +<p>在 Android 中,<strong>Activity</strong> 是一個應用程式元件,定義了資訊畫面,以及使用者可以執行的所有關聯動作。 +您的應用程式是個 Activity 的集合,包括您可以建立及您能從其他應用程式重複使用的 Activity。 </p> -<p><strong>工作</strong>是使用者遵循以達到目標的一系列活動。單一工作可以利用來自單一應用程式的活動,或是汲取數個不同應用程式的活動。 +<p><strong>工作</strong>是使用者遵循以達到目標的一系列 Activity。單一工作可以利用來自單一應用程式的 Activity,或是汲取數個不同應用程式的 Activity。 </p> -<p><strong>意向</strong>是指應用程式所發出想要另一套應用程式協助執行某動作訊號的機制。 -應用程式的活動可指示其可回應哪些意向。 -針對如「共用」等常見意向,使用者可能已安裝許多可以滿足此要求的應用程式。 +<p><strong>意圖</strong>是指應用程式所發出想要另一套應用程式協助執行某動作訊號的機制。 +應用程式的 Activity 可指示其可回應哪些意圖。 +針對如「共用」等常見意圖,使用者可能已安裝許多可以滿足此要求的應用程式。 </p> -<h4>範例:在應用程式間導覽,以支援共用</h4> - -<p>要理解活動、工作和意向如何攜手合作,請思考應用程式如何讓使用者使用另一套應用程式來共用內容。例如,從「主」畫面啟動 Play 商店應用程式,開始新工作 A (見下圖)。 +<h4>例如:在多個應用程式中導覽以支援共用功能</h4> -在導覽經歷 Play 商店,並輕觸一本促銷的書籍以查看詳細資料後,使用者仍會停留在相同工作中,並透過新增行為延伸工作。 -觸發「共用」動作會提示使用者一個對話框,列出已註冊可用來處理「共用」意向的每個活動 (來自不同的應用程式)。 +<p>要了解使用活動、工作和意圖如何相互作用,必須先考慮一個應用程式如何讓使用者利用另一個應用程式共用內容。 +例如,在首頁中開啟 Play 商店應用程式,並開始工作 A (如下所示)。 +使用者在 Play 商店應用程式中結束導覽後,輕觸促銷的書籍以查看細節,這時仍會停留在相同的工作中,同時新增活動以延伸功能。 +觸發「共用」動作會提示使用者一個對話框,列出已註冊可用來處理「共用」意圖的每個 Activity (來自不同的應用程式)。 </p> <img src="{@docRoot}design/media/navigation_between_apps_inward.png"> -<p>當使用者選擇透過 Gmail 共用,Gmail -的撰寫活動會被新增為工作 A 的延續 — 而不會建立新工作。如果 Gmail 有本身的工作正在背景執行,這並不會影響該工作。 +<p>當使用者選擇透過 Gmail 共用,Gmail 的撰寫 Activity 會被新增為工作 A 的延續 — 而不會建立新工作。 +如果 Gmail 有本身的工作正在背景執行,這並不會影響該工作。 </p> -<p>從撰寫活動中,傳送訊息或輕觸 [返回] 按鈕,會讓使用者返回至書籍詳細資料的活動。 -繼續輕觸 [返回] 則會經由 Play 商店往回導覽,最終到達「主畫面」。 +<p>從撰寫 Activity 中,傳送訊息或輕觸 [返回] 按鈕,會讓使用者返回至書籍詳細資料的 Activity。 +繼續輕觸 [返回] 則會經由 Play 商店往回導覽,最終到達「主螢幕」。 </p> <img src="{@docRoot}design/media/navigation_between_apps_back.png"> -<p>然而,透過輕觸撰寫行為的 [上一層],代表使用者指明停留在 Gmail 的意願。 -Gmail 的會話群組清單活動會隨即出現,並針對該活動建立一個新的工作 B。新工作的最後根源都是「主畫面」,所以從會話群組輕觸 [返回] 會返回至該處。 +<p>然而,透過輕觸撰寫 Activity 的 [上一層],代表使用者指明停留在 Gmail 的意願。 +Gmail 的會話群組清單 Activity 會隨即出現,並針對該 Activity 建立一個新的工作 B。新工作的最後根源都是「主螢幕」,所以從會話群組輕觸 [返回] 會返回至該處。 </p> <img src="{@docRoot}design/media/navigation_between_apps_up.png"> -<p>工作 A 仍然存在背景之中,而使用者可能會在稍後返回 (例如,透過「最近」畫面)。 +<p>工作 A 仍然存在背景之中,而使用者可能會在稍後返回 (例如,透過「最近使用記錄」畫面)。 如果 Gmail 在背景正在執行自己的工作,則其會被工作 B 取代 — 並捨棄之前的前後關係,而就使用者的新目標。 </p> -<p>當您的應用程式註冊來處理具有深入應用程式階層活動的意向時,請參考<a href="#into-your-app">透過主畫面視窗小工具和通知,導覽至您的應用程式</a>,取得如何指定 [上一層] 導覽的指導方針。 +<p>當您的應用程式註冊來處理具有深入應用程式階層 Activity 的意圖時,請參考<a href="#into-your-app">透過主螢幕小工具和通知,導覽至您的應用程式</a>,取得如何指定 [上一層] 導覽的指導方針。 </p> diff --git a/docs/html-intl/intl/zh-tw/guide/components/activities.jd b/docs/html-intl/intl/zh-tw/guide/components/activities.jd new file mode 100644 index 000000000000..ea0934964fed --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Activity +page.tags=Activity,意圖 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>本文件內容</h2> +<ol> + <li><a href="#Creating">建立 Activity</a> + <ol> + <li><a href="#UI">實作使用者介面</a></li> + <li><a href="#Declaring">在宣示說明中宣告 Activity</a></li> + </ol> + </li> + <li><a href="#StartingAnActivity">啟動 Activity</a> + <ol> + <li><a href="#StartingAnActivityForResult">啟動 Activity 以取得結果</a></li> + </ol> + </li> + <li><a href="#ShuttingDown">關閉 Activity</a></li> + <li><a href="#Lifecycle">管理 Activity 生命週期</a> + <ol> + <li><a href="#ImplementingLifecycleCallbacks">實作生命週期回呼</a></li> + <li><a href="#SavingActivityState">儲存 Activity 狀態</a></li> + <li><a href="#ConfigurationChanges">處理設定變更</a></li> + <li><a href="#CoordinatingActivities">協調 Activity</a></li> + </ol> + </li> +</ol> + +<h2>重要類別</h2> +<ol> + <li>{@link android.app.Activity}</li> +</ol> + +<h2>另請參閱</h2> +<ol> + <li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">工作和返回堆疊</a> +</li> +</ol> + +</div> +</div> + + + +<p>{@link android.app.Activity} 是提供畫面的應用程式元件,使用者可以與此畫面互動以執行動作,例如撥號、拍照、傳送電子郵件或檢視地圖。 + +每個 Activity 都會有專屬視窗,用於繪製其使用者介面。視窗一般會佔滿螢幕,但也可能小於螢幕或在其他視窗上方浮動。 + +</p> + +<p> 應用程式通常由多個 Activity 組成,這些 Activity 之間的繫結鬆散。 +一般來說,應用程式中的某個 Activity 會指定為「主要」Activity。使用者第一次啟動應用程式時,會將此 Activity 向使用者顯示。 +然後,每個 Activity 可以啟動另一個 Activity,以執行不同的動作。 +每次啟動新的 Activity 時,之前的 Activity 會停止,但系統將該 Activity 保留在堆疊中(即「返回堆疊」)。 + +新的 Activity 啟動時會推送至返回堆疊,然後取得使用者焦點。 +返回堆疊遵循基本的「後進先出」堆疊機制,因此,使用者完成目前的 Activity,按下 [返回] 按鈕<em></em>時,目前的 Activity 會從堆疊被推出 (並終止),然後繼續之前的 Activity。 + +(返回堆疊在<a href="{@docRoot}guide/components/tasks-and-back-stack.html">工作和返回堆疊</a>文件中,有更詳細的說明)。 + +</p> + +<p>Activity 因啟動新的 Activity 而停止時,Activity 的生命週期回呼方法會通知此狀態的變更。由於 Activity 狀態的變更 — 可能是系統建立、停止、繼續或終止 Activity 時 — Activity 會收到數個回呼方法,而且每個回呼都提供您執行適合該次狀態變更的特定工作機會。 + + + + +例如,停止時,您的 Activity 應釋放任何大型物件,例如網路或資料庫連線。 +Activity 繼續時,您可以重新取得必要資源,並繼續之前中斷的動作。 +這些狀態轉換都是 Activity 生命週期的一部分。 +</p> + +<p>本文件的其他部分會討論建置和使用 Activity 的基本概念,包括完整討論 Activity 生命週期的運作方式,讓您可以正確地管理各種 Activity 狀態之間的轉換。 + +</p> + + + +<h2 id="Creating">建立 Activity</h2> + +<p>如要建立 Activity,您必須建立 {@link android.app.Activity} 的子類別 (或它現有的子類別)。 +在您的子類別中,您需要實作回呼方法,當 Activity 在其生命週期的各種狀態之間轉換時,系統可以呼叫此回呼方法。例如,Activity 建立、停止、繼續或終止時。 + +最重要的兩個回呼方法如下: +</p> + +<dl> + <dt>{@link android.app.Activity#onCreate onCreate()}</dt> + <dd>您必須實作此方法。系統建立您的 Activity 時會呼叫此方法。 +在您的實作中,應該初始化 Activity 的基本元件。 + + 最重要的是,您必須在這裡呼叫 {@link android.app.Activity#setContentView + setContentView()},才能定義 Activity 使用者介面的版面配置。</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>系統呼叫此方法做為使用者離開您的 Activity 的第一個指標 (但並非一定表示該 Activity 遭到終止)。 +您通常需要透過這個方法提交要在目前的使用者工作階段以外保留的任何變更 (原因在於使用者可能不會返回)。 + +</dd> +</dl> + +<p>還有一些您應使用的其他生命週期回呼方法,以便提供 Activity 之間流暢的使用者體驗,並處理讓您的 Activity 停止、甚至終止的非預期中斷。 + +如需所有生命週期回呼方法的相關資訊,請參閱<a href="#Lifecycle">管理 Activity 生命週期</a>。 +</p> + + + +<h3 id="UI">實作使用者介面</h3> + +<p> Activity 的使用者介面是由階層的檢視所提供 — 衍生自 {@link android.view.View} 類別的物件。 +每個檢視都控制 Activity 視窗內特定的長方形空間,並且可以回應使用者的互動。 +例如,檢視可能是按鈕,使用者觸碰此按鈕時會初始化一個動作。 +</p> + +<p>Android 提供許多現成的檢視,您可以用於設計並組織您的版面配置。 +「小工具」是在畫面上提供視覺和互動元素的檢視,例如按鈕、文字欄位、核取方塊或只是一張影像。 +「版面配置」是衍生自 {@link +android.view.ViewGroup} 的檢視,讓子檢視可具有獨特的版面配置模型,例如線性版面配置、網格版面配置或相對版面配置。 +您也可以製作 {@link android.view.View} 和 +{@link android.view.ViewGroup} 類別 (或現有子類別) 的子類別,以建立您自己的小工具和版面配置,然後套用到您的 Activity 版面配置。 +</p> + +<p>使用檢視定義版面配置的最常見方式,是使用 XML 版面配置檔案 (儲存於您的應用程式資源)。 +這樣一來,您可以分別維護使用者介面的設計和定義 Activity 行為的原始程式碼。 +您可以使用 {@link android.app.Activity#setContentView(int) setContentView()} 傳送版面配置的資源 ID,將版面配置設為 Activity 的 UI。 + +不過,您也可以透過將新的 {@link +android.view.View} 插入 {@link android.view.ViewGroup},然後藉由將根 +{@link android.view.ViewGroup} 傳送給 {@link android.app.Activity#setContentView(View) +setContentView()} 來使用該版面配置,以便在您的 Activity 程式碼中建立新的 {@link android.view.View},並且建置視圖層次。 +</p> + +<p>如需關於建立使用者介面的詳細資訊,請參閱<a href="{@docRoot}guide/topics/ui/index.html">使用者介面</a>。</p> + + + +<h3 id="Declaring">在宣示說明中宣告 Activity</h3> + +<p>您必須在宣示說明檔案中宣告 Activity,系統才能加以存取。 +如要宣告您的 Activity,請開啟宣示說明檔案,然後新增 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素做為 <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> 元素的下層物件。 + +範例:</p> + +<pre> +<manifest ... > + <application ... > + <activity android:name=".ExampleActivity" /> + ... + </application ... > + ... +</manifest > +</pre> + +<p>您可以包括此元素中的其他屬性 (attribute) 來定義屬性 (property),例如 Activity 的標籤、Activity 的圖示或 Activity UI 的設計風格。<a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">{@code android:name}</a> 屬性 (attribute) 是唯一必須的屬性 — 它會指定 Activity 的類別名稱。 + + +一旦發佈應用程式,您就不得變更此名稱,這是因為這樣做可能會破壞一些功能,例如應用程式捷徑 (閱讀部落格貼文:<a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">不能變更的事項</a>)。 + + +</p> + +<p>請參閱 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素參考文件,進一步瞭解如何在宣示說明中宣告 Activity。 +</p> + + +<h4>使用意圖篩選器</h4> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> 元素也可以指定各種意圖篩選器 — 使用 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> 元素 — 以便宣告其他應用程式元件啟動它的方式。 +</p> + +<p>使用 Android SDK 工具建立新的應用程式時,自動為您建立的 +虛設常式 Activity 會內含意圖篩選器。含意圖篩選器會宣告回應 +「主要」動作的 Activity,並且應放置在「啟動器」類別。意圖篩選器看起來會如下所示: +</p> + +<pre> +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 元素指出這是應用程式的「主要」進入點。<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 元素指出此 Activity 應列於系統的應用程式啟動器 (以允許使用者啟動此 Activity)。 +</p> + +<p>如果您本來就要讓應用程式獨自運作,不要讓其他應用程式啟動其 Activity,則不需要任何其他意圖篩選器。 +只有一個 Activity 可以有「主要」動作和「啟動器」類別,如同上一個範例所示。 +您不要讓其他應用程式使用的 Activity,則不應使用意圖篩選器,而且您可以使用明確的意圖自行加以啟動 (將於以下小節討論)。 + +</p> + +<p>不過,如果您的 Activity 需要回應從其他應用程式 (和您自己的應用程式) 傳送過來的隱含式意圖,則必須為您的 Activity 定義額外的意圖篩選器。 + +針對您要回應的意圖類型,您必須包括 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a>,其中內含 +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 元素和 <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 元素 (選用) 和/或 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> 元素。這些元素會指定您的 Activity 可以回應哪些類型的意圖。 +</p> + +<p>如需關於您的 Activity 如何回應意圖的詳細資訊,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 +</p> + + + +<h2 id="StartingAnActivity">啟動 Activity</h2> + +<p>透過呼叫 {@link android.app.Activity#startActivity + startActivity()}、將 {@link android.content.Intent} (描述要啟動的 Activity) 傳給它,您可以啟動另一個 Activity。 +意圖會指出您要啟動的那個 Activity,或描述您要執行的動作類型 (而讓系統為您選取適當的 Activity,甚至可以是來自不同應用程式的 Activity)。 + + +意圖也可以攜帶少量的資料給已啟動的 Activity 使用。 +</p> + +<p>在您自己的應用程式內運作時,通常只要啟動已知的 Activity。 + 建立意圖並明確定義您要啟動的 Activity (使用類別名稱),可以達成此目的。 +例如,以下示範 Activity 如何啟動另一個名為 {@code +SignInActivity} 的 Activity:</p> + +<pre> +Intent intent = new Intent(this, SignInActivity.class); +startActivity(intent); +</pre> + +<p>不過,您的應用程式也希望可以執行其他動作,例如使用您 Activity 中的資料以傳送電子郵件、文字訊息或狀態更新。 +在此情況下,您的應用程式可能就沒有專屬的 Activity 來執行這類動作。因此,您可以改為運用裝置上其他應用程式提供的 Activity。讓這些 Activity 為您執行所需的動作。 + +這正是意圖寶貴的地方 — 您可以建立意圖,在其中描述您要執行的動作,然後系統會從另一個應用程式啟動適當的 Activity。 + + +如果有多個 Activity 都可以處理意圖,則使用者可以選取要使用哪個 Activity。 +例如,如果要讓使用者傳送電子郵件訊息,您可以建立下列意圖: + +</p> + +<pre> +Intent intent = new Intent(Intent.ACTION_SEND); +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); +startActivity(intent); +</pre> + +<p>加入意圖的 {@link android.content.Intent#EXTRA_EMAIL} 額外值是一個字串陣列,由應該要寄送電子郵件的電子郵件地址所組成。 +電子郵件應用程式回應此意圖時,它會讀取額外值中提供的字串陣列,並將這些內容放置在編寫電子郵件表單的「收件人」欄位。 + +在此情況下,電子郵件應用程式的 Activity 會啟動,並且會在使用者完成後,繼續您的 Activity。 +</p> + + + + +<h3 id="StartingAnActivityForResult">啟動 Activity 以取得結果</h3> + +<p>有時候,您會想要收到由您啟動 Activity 的結果。如要接收結果,請透過呼叫 {@link android.app.Activity#startActivityForResult + startActivityForResult()} (而非 {@link android.app.Activity#startActivity + startActivity()}) 以啟動 Activity。 +如要接收後續 Activity 的結果,請實作 {@link android.app.Activity#onActivityResult onActivityResult()} 回呼方法。 + +後續 Activity 完成時,會將 {@link +android.content.Intent} 中的結果傳回到您的 {@link android.app.Activity#onActivityResult onActivityResult()} 方法。 +</p> + +<p>例如,您可能會讓使用者在聯絡人中挑選一位,讓您的 Activity 可以針對該聯絡人的資訊進行一些處理。 +以下示範如何建立這類意圖,並處理結果: +</p> + +<pre> +private void pickContact() { + // Create an intent to "pick" a contact, as defined by the content provider URI + Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + startActivityForResult(intent, PICK_CONTACT_REQUEST); +} + +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // If the request went well (OK) and the request was PICK_CONTACT_REQUEST + if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) { + // Perform a query to the contact's content provider for the contact's name + Cursor cursor = getContentResolver().query(data.getData(), + new String[] {Contacts.DISPLAY_NAME}, null, null, null); + if (cursor.moveToFirst()) { // True if the cursor is not empty + int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME); + String name = cursor.getString(columnIndex); + // Do something with the selected contact's name... + } + } +} +</pre> + +<p>此範例顯示您應該在您的 {@link +android.app.Activity#onActivityResult onActivityResult()} 方法中使用的基本邏輯,以便處理 Activity 結果。 +第一個條件會檢查要求是否成功 — 如果成功,則{@code resultCode} 會是 {@link android.app.Activity#RESULT_OK} —,以及此結果回應的要求是否為已知的要求 — 範例中的 {@code requestCode} 符合以 {@link android.app.Activity#startActivityForResult +startActivityForResult()} 傳送的第二個參數。 + + +程式碼在這裡透過查詢 {@link android.content.Intent} ({@code data} 參數) 中傳回的資料,來處理 Activity 結果。 +</p> + +<p>其中的過程是,{@link +android.content.ContentResolver} 針對內容供應程式執行查詢,所傳回的 +{@link android.database.Cursor} 可以讀取查詢到的資料。如需詳細資訊,請參閱<a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>。 +</p> + +<p>如需關於使用意圖的詳細資訊,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 +</p> + + +<h2 id="ShuttingDown">關閉 Activity</h2> + +<p>呼叫 Activity 的 {@link android.app.Activity#finish +finish()} 方法,可以關閉此 Activity。您也可以呼叫 +{@link android.app.Activity#finishActivity finishActivity()},關閉您之前啟動的個別 Activity。</p> + +<p class="note"><strong>注意:</strong>大多數情況,您不應使用這些方法明確地結束 Activity。 +如同下一節所討論的 Activity 生命週期,Android 系統會為您管理 Activity 的生命週期,所以您不需要結束您自己的 Activity。 + +呼叫這些方法對使用者體驗有負面的影響,只有在您十分確定不希望使用者返回 Activity 的此執行個體時,才加以呼叫。 + +</p> + + +<h2 id="Lifecycle">管理 Activity 生命週期</h2> + +<p>實作回呼方法來管理 Activity 的生命週期,對於開發強大且有彈性的應用程式來說,相當重要。 + +Activity 的生命週期受到相關其他 Activity、本身的工作以及返回堆疊的直接影響。 +</p> + +<p>Activity 基本上有以下三種狀態:</p> + +<dl> + <dt><i>已繼續</i></dt> + <dd>Activity 位於螢幕的前景,具有使用者焦點 (此狀態有時候也稱為「執行中」)。 +</dd> + + <dt><i>已暫停</i></dt> + <dd>前景中有其他具備焦點的 Activity,但系統仍然可以看到這個 Activity。也就是說,這個 Activity 上方有另一個,該 Activity 為半透明,或是未覆蓋整個螢幕。 + +已暫停的 Activity 是完全有效的 ({@link android.app.Activity} 物件會保留在記憶體中、維護所有狀態和成員資訊,以及繼續附加至視窗管理員),但在系統記憶體極低的情況下會遭到終止。 + +</dd> + + <dt><i>已停止</i></dt> + <dd>另一個 Activity 已完全遮蓋原本的 Activity (原本的 Activity 現在位於「背景」)。 +已停止的 Activity 仍然是有效的 ({@link android.app.Activity} 物件會保留在記憶體中、維護所有狀態和成員資訊,但「不會」<em></em>附加至視窗管理員)。 + +只是使用者不會再看到已停止的 Activity,而且其他地方需要記憶體時,系統會將它終止。 +</dd> +</dl> + +<p>如果 Activity 已暫停或已停止,系統可以透過要求它結束 (呼叫其 {@link android.app.Activity#finish finish()} 方法) 或直接終止其處理程序,將它從記憶體中刪除。 + +Activity 遭到結束或終止後,再次開啟時,必須全部重新建立。 +</p> + + + +<h3 id="ImplementingLifecycleCallbacks">實作生命週期回呼</h3> + +<p>Activity 在不同的狀態之間轉換時 (如上所述),會透過各種回呼方法進行通知。 +所有的回呼方法都是虛設,請加以覆寫,以便在 Activity 狀態變更時,執行適當的工作。 +下列主要 Activity 包括每個基礎生命週期方法: +</p> + + +<pre> +public class ExampleActivity extends Activity { + @Override + public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // The activity is being created. + } + @Override + protected void {@link android.app.Activity#onStart onStart()} { + super.onStart(); + // The activity is about to become visible. + } + @Override + protected void {@link android.app.Activity#onResume onResume()} { + super.onResume(); + // The activity has become visible (it is now "resumed"). + } + @Override + protected void {@link android.app.Activity#onPause onPause()} { + super.onPause(); + // Another activity is taking focus (this activity is about to be "paused"). + } + @Override + protected void {@link android.app.Activity#onStop onStop()} { + super.onStop(); + // The activity is no longer visible (it is now "stopped") + } + @Override + protected void {@link android.app.Activity#onDestroy onDestroy()} { + super.onDestroy(); + // The activity is about to be destroyed. + } +} +</pre> + +<p class="note"><strong>注意:</strong>實作這些生命週期方法時,一定要先呼叫超級類別實作,才能執行任何工作,如同以上範例所示。 +</p> + +<p>總而言之,這些方法定義了 Activity 的整個生命週期。透過實作這些方法,您可以監視 Activity 生命週期中的三個巢狀迴圈: + </p> + +<ul> +<li>Activity 的<b>整個生命週期</b>是介於 {@link +android.app.Activity#onCreate onCreate()} 呼叫和 {@link +android.app.Activity#onDestroy} 呼叫之間。您的 Activity 應在 {@link android.app.Activity#onCreate onCreate()} 中設定「全域」狀態 (例如定義版面配置),並且在 {@link android.app.Activity#onDestroy} 中釋放所有剩餘的資源。 + +例如,如果您的 Activity 有一個執行緒在背景執行,並從網路下載資料,那麼最好可以在 {@link android.app.Activity#onCreate onCreate()} 中建立該執行緒,然後在 {@link +android.app.Activity#onDestroy} 中將它停止。 + +</li> + +<li><p>Activity 的<b>可見生命週期</b>是介於 {@link +android.app.Activity#onStart onStart()} 呼叫和 {@link +android.app.Activity#onStop onStop()} 呼叫之間。在此期間,使用者可以在螢幕上看到該 Activity,並與之互動。 +例如,啟動新的 Activity,而這個 Activity 不再看得到時,會呼叫 {@link android.app.Activity#onStop onStop()}。 +在這兩個方法之間,您可以維護需要讓使用者看到的資源。 +例如,您可以在{@link +android.app.Activity#onStart onStart()} 中註冊 +{@link android.content.BroadcastReceiver},以監視影響到 UI 的變更,然後當使用者不再看到您顯示的內容時,在 {@link android.app.Activity#onStop onStop()} 中將它取消註冊。 + +系統可以在 Activity 的整個生命週期時,多次呼叫 {@link android.app.Activity#onStart onStart()} 和 {@link +android.app.Activity#onStop onStop()},因為 Activity 對使用者而言會一直在顯示和隱藏之間切換。 +</p></li> + +<li><p>Activity 的<b>前景生命週期</b>是介於 {@link +android.app.Activity#onResume onResume()} 呼叫和 {@link android.app.Activity#onPause +onPause()} 呼叫之間。在此期間,Activity 會在螢幕上所有其他 Activity 的前面,而且具有使用者輸入焦點。 +Activity 可以經常在前景和背景之間轉換 — 例如,裝置進入睡眠或顯示對話方塊時,會呼叫 {@link android.app.Activity#onPause onPause()}。 + +由於此狀態可能會經常轉換,因此這兩個方法中的程式碼應十分精簡,這樣可以避免使用者在轉換時等待。 +</p></li> +</ul> + +<p>圖 1 說明 Activity 在轉換狀態時的可能迴圈和路徑。長方形代表您可以實作的回呼方法,以便 Activity 在轉換狀態時執行操作。 + + <p> + +<img src="{@docRoot}images/activity_lifecycle.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>Activity 生命週期。</p> + +<p>表 1 列出相同的生命週期回呼方法,其中詳細描述每個回呼方法,並且指出每個回呼方法在 Activity 整個生命週期中的位置,包括系統是否可以在回呼方法完成後終止 Activity。 + + +</p> + +<p class="table-caption"><strong>表 1.</strong>Activity 生命週期回呼方法摘要。 +</p> + +<table border="2" width="85%" frame="hsides" rules="rows"> +<colgroup align="left" span="3"></colgroup> +<colgroup align="left"></colgroup> +<colgroup align="center"></colgroup> +<colgroup align="center"></colgroup> + +<thead> +<tr><th colspan="3">方法</th> <th>描述</th> <th>完成後是否可終止?</th> <th>下一個方法</th></tr> +</thead> + +<tbody> +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td> + <td>一開始建立 Activity 時呼叫。 + 您應該在這裡所有的一般靜態設定 — 建立檢視、將資料繫結至清單等等。 +「套件」物件會傳送給此方法,如果有擷取到狀態,此物件會內含 Activity 的上一個狀態 (請參閱下文的<a href="#actstate">儲存 Activity 狀態</a>)。 + + + + <p>後面一定會接著 {@code onStart()}。</p></td> + <td align="center">否</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td rowspan="5" style="border-left: none; border-right: none;"> </td> + <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart +onRestart()}</code></td> + <td>Activity 已停止後,即將再次啟動之前呼叫。 + + <p>後面一定會接著 {@code onStart()}。</p></td> + <td align="center">否</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td> + <td>Activity 即將要讓使用者看到之前呼叫。 + <p>如果 Activity 移到前景,後面會接著 {@code onResume()},如果變成隱藏,後面會接著 {@code onStop()}。 +</p></td> + <td align="center">否</td> + <td align="center">{@code onResume()} <br/>或<br/> {@code onStop()}</td> +</tr> + +<tr> + <td rowspan="2" style="border-left: none;"> </td> + <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td> + <td>Activity 即將與使用者開始互動之前呼叫。 +此時,Activity 位於 Activity 堆疊的最上方,接受使用的輸入。 + + <p>後面一定會接著 {@code onPause()}。</p></td> + <td align="center">否</td> + <td align="center">{@code onPause()}</td> +</tr> + +<tr> + <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td> + <td>系統即將開始繼續另一個 Activity 時呼叫。 +認可對永久資料的未儲存變更、停止動畫,以及會使用 CPU 等等資源的其他操作時,一般會使用此方法。 + +不論此操作會執行什麼動作,都要快速完成,這是因為此方法傳回後,才會繼續下一個 Activity。 + + <p>如果 Activity 返回前景,後面會接著 {@code onResume()},如果變成使用者看不到它,後面會接著 {@code onStop()}。 + +</td> + <td align="center"><strong style="color:#800000">是</strong></td> + <td align="center">{@code onResume()} <br/>或<br/> {@code onStop()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td> + <td>使用者看不到 Activity 時呼叫。Activity 遭到終止或另一個 Activity (不論是現有 Activity 或新的 Activity) 已經繼續,而且將它覆蓋住,就會發生此情形。 + + + <p>如果 Activity 回來與使用者互動,後面會接著 {@code onRestart()},如果 Activity 離開,後面會接著 + {@code onDestroy()}。 +</p></td> + <td align="center"><strong style="color:#800000">是</strong></td> + <td align="center">{@code onRestart()} <br/>或<br/> {@code onDestroy()}</td> +</tr> + +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy +onDestroy()}</code></td> + <td>在 Activity 終止前呼叫。Activity 會接收到的最後呼叫。 +Activity 正在完成 (有人在 Activity 上呼叫 <code>{@link android.app.Activity#finish + finish()}</code>),或系統正在暫時終止 Activity 的這個執行個體以節省空間時,會呼叫此方法。 + +您可以使用 <code>{@link + android.app.Activity#isFinishing isFinishing()}</code> 方法分辨這兩種情況。 +</td> + <td align="center"><strong style="color:#800000">是</strong></td> + <td align="center"><em>無</em></td> +</tr> +</tbody> +</table> + +<p>此欄標示為「完成後是否可終止?」表示系統是否可以「在方法傳回後」隨時終止代管 Activity 的處理程序<em></em>,而不需要執行另一行 Activity 的程式碼。 + +有三個方法標示為「是」:({@link +android.app.Activity#onPause +onPause()}、{@link android.app.Activity#onStop onStop()} 以及 {@link android.app.Activity#onDestroy +onDestroy()})。由於 {@link android.app.Activity#onPause onPause()} 是這三個方法中的第一個方法,Activity 建立後,{@link android.app.Activity#onPause onPause()} 是一定會呼叫的最後一個方法,之後處理程序才能加以終止<em></em> — 如果系統必須緊急收回記憶體,可能就不會呼叫 {@link +android.app.Activity#onStop onStop()} 和 {@link android.app.Activity#onDestroy onDestroy()}。 + + + +因此,您應該使用 {@link android.app.Activity#onPause onPause()} 將極為重要的永久資料 (例如使用者的編輯內容) 寫入儲存空間。 +不過,您應選擇哪些資訊必須在 {@link android.app.Activity#onPause onPause()} 期間加以保留,這是因為此方法中的任何程序遭到封鎖,都無法轉換到下一個 Activity,而且會讓使用者體驗變慢。 + + +</p> + +<p> 在「是否可終止」<b></b>欄標示為「否」的方法,從呼叫這些方法的那一刻起,會保護代管 Activity 的處理程序不會遭到終止。 +因此,Activity 從 {@link android.app.Activity#onPause onPause()} 傳回到呼叫 + {@link android.app.Activity#onResume onResume()} 的期間為可終止。 +再次呼叫和傳回 +{@link android.app.Activity#onPause onPause()} 之前,Activity 為不可終止。 </p> + +<p class="note"><strong>注意:</strong>表 1 中定義為不是「可終止」的 Activity 仍可遭到系統終止 — 但只會發生在無技可施的情況下。 + +Activity 可加以終止的時機,於<a href="{@docRoot}guide/components/processes-and-threads.html">處理和執行緒</a>文件中有更詳細的討論。 + +</p> + + +<h3 id="SavingActivityState">儲存 Activity 狀態</h3> + +<p><a href="#Lifecycle">管理 Activity 生命週期</a>的簡介中提到,當 Activity 暫停或停止時,會保留 Activity 的狀態。 + +這點是成立的,原因在於當 {@link android.app.Activity} 物件暫停或停止時,它仍然保留在記憶體中 — 關於它的成員和目前狀態的所有資訊,仍然為有效的。 + +因此,使用者在 Activity 內所做的任何變更,都會保留下來。所以,當 Activity 返回前景 (當它「繼續」時),那些變更仍然會在原地。 + +</p> + +<p>不過,當系統終止 Activity 以收回記憶體時,{@link +android.app.Activity} 物件會遭到終止,所以系統就無法將它及其狀態完好無缺地繼續。 +如果使用者瀏覽回 {@link android.app.Activity} 物件,系統必須加以重新建立。 +但是,使用者不會注意到系統已終止該 Activity 並加以重新建立,可能因此期待 Activity 就跟之前的狀態一樣。 + +如果是這樣,您可以實作額外的回呼方法,以確認 Activity 狀態相關的重要資訊會保留下來。此回呼方法讓您儲存關於 Activity 狀態的資訊:{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}。 + +</p> + +<p>系統會在終止 Activity 之前呼叫 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}。 +系統會將 {@link android.os.Bundle} 傳送給此方法,您可以使用 {@link +android.os.Bundle#putString putString()} 和 {@link +android.os.Bundle#putInt putInt()} 之類的方法,將 Activity 相關的狀態資訊以名稱-值組的方式儲存。 + +然後,如果系統終止應用程式處理程序,並且使用者瀏覽回您的 Activity,則系統會重新建立 Activity,然後將 {@link android.os.Bundle} 傳送給 {@link android.app.Activity#onCreate onCreate()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}。 + +您可以使用以上其中一種方法,從 {@link android.os.Bundle} 擷取已儲存的狀態,然後還原 Activity 狀態。 + +如果沒有狀態資訊可供還原,則傳送過來的 {@link +android.os.Bundle} 為空值 (null) (第一次建立 Activity 時,就是這種情況)。 +</p> + +<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" /> +<p class="img-caption"><strong>圖 2.</strong>Activity 返回使用者焦點,同時具備完整狀態的兩種方式:Activity 遭到終止,然後重新建立,Activity 必須還原之前儲存的狀態;或者Activity 已停止,然後繼續,Activity 狀態維持完整。 + + +</p> + +<p class="note"><strong>注意:</strong>不保證在您的 Activity 遭到終止之前,一定會呼叫 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},這是因為有時會發生不需要儲存狀態的情形 (例如,使用者使用「返回」按鈕離開您的 Activity 時<em></em>,原因在於使用者明確地關閉 Activity)。 + + + +如果系統要呼叫 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},會在 {@link +android.app.Activity#onStop onStop()} 之前呼叫,可能會在 {@link android.app.Activity#onPause +onPause()} 之前呼叫。</p> + +<p>不過,即使您沒有做任何事,沒有實作 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},有些 Activity 狀態會經由{@link android.app.Activity} 類別的預設實作 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 而還原。 +具體來說,預設實作會針對版面配置中的每一個 {@link +android.view.View} 呼叫對應的 {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} 方法,這樣可以讓每個檢視提供本身應該要儲存的相關資訊。 + +在 Android 架構中,幾乎每個小工具都適當地實作此方法,因此 UI 中可見的變更都會自動儲存,並於 Activity 重新建立時加以還原。 + +例如,{@link android.widget.EditText} 小工具會儲存使用者輸入的任何文字,而 {@link android.widget.CheckBox} 小工具則會儲存是否勾選。 + +您只要針對需要儲存狀態的每個小工具,提供唯一的 ID (使用 <a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a> 屬性) 即可。 +如果小工具沒有 ID,則系統無法儲存其狀態。 +</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p>您也可以明確地讓版面配置中的檢視停止儲存其狀態,只要將 +{@link android.R.attr#saveEnabled android:saveEnabled} 屬性設為 {@code "false"},或呼叫 {@link android.view.View#setSaveEnabled setSaveEnabled()} 方法即可。 +您通常不應停用儲存狀態的功能,不過,如果您想要還原不同的 Activity UI 狀態,則另當別論。 +</p> +</div> +</div> + +<p>儘管 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的預設實作會儲存 Activity UI 相關的實用資訊,您仍然需要加以覆寫,以儲存額外的資訊,例如,您需要儲存 Activity 生命期間變更的成員值 (此真可能與 UI 中要還原的值有關,但保留那些 UI 值的成員預設不會加以還原)。 + + + +</p> + +<p>由於 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的預設實作可協助儲存 UI 的狀態,因此,如果您覆寫此方法以儲存額外的狀態資訊,一定要再執行任何動作之前,呼叫 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的超級類別實作。 + + +同樣的情況,您也要呼叫 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 的超級類別實作 (如果您將它覆寫),讓預設實作可以還原檢視狀態。 +</p> + +<p class="note"><strong>注意:</strong>由於不保證一定會呼叫 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},您只能用它來記錄 Activity 的短暫狀態 (UI 的狀態) — 不應該用它儲存永久資料。 + +而是要在使用者離開 Activity 時,利用 {@link +android.app.Activity#onPause onPause()} 來儲存永內資料 (例如要儲存到資料庫的資料)。 +</p> + +<p>測試應用程式是否能夠還原其狀態的好方式,只要旋轉裝置,改變螢幕方向即可。 +螢幕方向改變時,系統會終止 Activity 並重新建立,以便套用針對新的螢幕設定而提供使用的替代資源。 + +單單就這一點而言,您的 Activity 在重新建立時可以完整還原,就非常重要了,這是因為使用者操作應用程式時會經常旋轉螢幕。 + +</p> + + +<h3 id="ConfigurationChanges">處理設定變更</h3> + +<p>有些裝置設定可以在執行階段期間進行變更 (例如,螢幕方向、鍵盤可用性和語言)。 +發生這類變更時,Android 會重新建立執行中的 Activity (系統呼叫 {@link android.app.Activity#onDestroy},然後立即呼叫 {@link +android.app.Activity#onCreate onCreate()})。 +此行為的設計透過自動重新載入應用程式與提供的替代資源 (例如針對不同的螢幕方向和大小的不同版面配置),可協助應用程式適應新的設定。 + + +</p> + +<p>如果您如上所述正確地設計 Activity,處理由於螢幕方向變更的重新啟動,然後還原 Activity 的狀態,您的應用程式對於 Activity 生命週期中的不可預期事件,會更具有抗性。 + +</p> + +<p>處理這類重新啟動的最佳方式,是使用 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (或 {@link +android.app.Activity#onCreate onCreate()}) 儲存並還原 Activity 的狀態,如同上一節所討論。 +</p> + +<p>如需關於執行階段發生的設定變更,以及如何加以處理的詳細資訊,請參閱<a href="{@docRoot}guide/topics/resources/runtime-changes.html">處理執行階段變更</a>指南。 + +</p> + + + +<h3 id="CoordinatingActivities">協調 Activity</h3> + + <p>Activity 啟動另一個 Activity 時,它們兩者都經歷生命週期轉換。第一個 Activity 暫停並停止 (雖然如果仍然可以在背景看到它,表示它並未真的「停止」),而另一個 Activity 建立起來。 + +若這些 Activity 共用儲存到磁碟或其他地方的資料,第一個 Activity 在第二個 Activity 已建立之前,不會完全停止,瞭解這件事很重要。然而,啟動第二個 Activity 的處理程序與停止第一個 Activity 的處理程序會重疊。 + + +</p> + +<p>生命週期回呼的順序定義的很好,尤其是當兩個 Activity 位於相同的處理程序,而其中一個 Activity 啟動另一個 Activity 時。 +Activity A 啟動 Activity B 時所發生的操作順利如下: + </p> + +<ol> +<li>Activity A 的 {@link android.app.Activity#onPause onPause()} 方法會執行。</li> + +<li>Activity B 按順序執行 {@link android.app.Activity#onCreate onCreate()}、{@link +android.app.Activity#onStart onStart()} 以及 {@link android.app.Activity#onResume onResume()} 方法。 +(Activity B 現在擁有使用者焦點)。</li> + +<li>然後,如果螢幕上已經看不到 Activity A,就會執行 Activity A 的 {@link +android.app.Activity#onStop onStop()} 方法。</li> +</ol> + + <p>這一段可預測的生命週期回呼,可以讓您管理 Activity 之間資訊的轉換。 +例如,如果第一個 Activity 停止時,您必須寫入資料庫,讓接下來的 Activity 可以讀取,那麼您應該在 {@link android.app.Activity#onPause onPause()} 期間寫入,而不是在 {@link +android.app.Activity#onStop onStop()} 期間寫入。 + +</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how Android maintains a history of activities and +enables user multitasking, continue with the <b><a +href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back +Stack</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd b/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd new file mode 100644 index 000000000000..da47634b894f --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=繫結服務 +parent.title=服務 +parent.link=services.html +@jd:body + + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>本文件內容</h2> +<ol> + <li><a href="#Basics">基本概念</a></li> + <li><a href="#Creating">建立繫結服務</a> + <ol> + <li><a href="#Binder">延伸 Binder 類別</a></li> + <li><a href="#Messenger">使用 Messenger</a></li> + </ol> + </li> + <li><a href="#Binding">繫結至服務</a></li> + <li><a href="#Lifecycle">管理繫結服務的週期</a></li> +</ol> + +<h2>重要類別</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.content.ServiceConnection}</li> + <li>{@link android.os.IBinder}</li> +</ol> + +<h2>範例</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code + RemoteService}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>另請參閱</h2> +<ol> + <li><a href="{@docRoot}guide/components/services.html">服務</a></li> +</ol> +</div> + + +<p>繫結服務是主從介面中的伺服器。繫結服務讓元件 (例如 Activity) 可以繫結至服務、傳送要求、接收回應,甚至執行處理程序間通訊 (IPC)。 + +繫結服務通常只會在服務另一個應用程式元件時才存在,而且不會在背景中一直執行。 +</p> + +<p>本文會告訴您如何建立繫結服務,包括如何從其他應用程式元件繫結至服務。 +不過,如需關於服務的一般其他資訊,可參閱<a href="{@docRoot}guide/components/services.html">服務</a>文件,例如如何從服務傳遞通知、設定服務在前景執行等等。 + +</p> + + +<h2 id="Basics">基本概念</h2> + +<p>繫結服務是 {@link android.app.Service} 類別的實作,允許其他應用程式繫結至此實作,並與之互動。 +若服務要要提供繫結功能,您必須實作 {@link android.app.Service#onBind onBind()} 回呼方法。 +此方法會傳回 {@link android.os.IBinder} 物件,其中定義程式設計介面。用戶端可以使用此介面與服務互動。 + +</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>繫結至已啟動的服務</h3> + +<p>我們在<a href="{@docRoot}guide/components/services.html">服務</a>文件中討論過,您可以建立已啟動且繫結的服務。 +也就是說,此服務可以透過呼叫 {@link android.content.Context#startService startService()} 加以啟動,讓此服務一直無限執行,也可以透過呼叫 {@link +android.content.Context#bindService bindService()},讓用戶端繫結至此服務。 + + + <p>如果您允許服務被啟動且繫結,當服務啟動後,系統「不會」<em></em>在所有用戶端都解除繫結時將此服務終結。 +您必須透過呼叫 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 明確地停止服務。 +</p> + +<p>雖然您通常只會實作 {@link android.app.Service#onBind onBind()} 或<em></em> {@link android.app.Service#onStartCommand onStartCommand()},但有時候需要實作兩者。 + +例如,此功能對於音樂播放器就很實用,除了可以讓服務無限期執行,也可以提供繫結功能。 +這樣一來,Activity 就可以啟用服務並播放音樂,然後即使使用者離開應用程式後,音樂仍然繼續播放。 +使用者後續回到此應用程式時,Activity 可以繫結至服務,以重新取得播放的控制權。 +</p> + +<p>務必閱讀<a href="#Lifecycle">管理繫結服務的週期</a>,以取得關於新增繫結至已啟動的服務時,此服務週期的詳細資訊。 + +</p> +</div> +</div> + +<p>用戶端可以透過呼叫 {@link android.content.Context#bindService +bindService()} 繫結至服務。這麼做時,用戶端必須實作 {@link +android.content.ServiceConnection} 以監視與服務之間的連線狀況。{@link +android.content.Context#bindService bindService()} 方法會立即傳回 (不含值),當 Android 系統在用戶端和服務之間建立連線時,會呼叫 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} (位於 {@link +android.content.ServiceConnection}) 以傳遞 {@link android.os.IBinder} (用戶端可以用來與服務溝通)。 + + +</p> + +<p>多個用戶端可以同時連線至該服務。不過,系統只會在用戶端第一次繫結時,呼叫服務的 +{@link android.app.Service#onBind onBind()} 方法,以擷取 {@link android.os.IBinder}。 +然後,系統會將同一個 {@link android.os.IBinder} 傳遞給其他任何繫結的用戶端,不會再次呼叫 {@link android.app.Service#onBind onBind()}。 +</p> + +<p>最後一個用戶端從服務解除繫結時,系統會終結服務 (除非服務也是由 {@link android.content.Context#startService startService()} 所啟動)。 +</p> + +<p>實作繫結服務時,最重要的部分是定義您的 {@link android.app.Service#onBind onBind()} 回呼方法傳回的介面。 +您可以使用幾種不同的方式來定義服務的 {@link android.os.IBinder} 介面,以下小節將討論其中的每一種技術。 + +</p> + + + +<h2 id="Creating">建立已繫結的服務</h2> + +<p>建立提供繫結功能的服務時,您必須提供 {@link android.os.IBinder} 程式設計介面。用戶端可以使用此介面與服務互動。 +定義介面有三種方式: +</p> + +<dl> + <dt><a href="#Binder">延伸 Binder 類別</a></dt> + <dd>如果您的服務只讓您自己的應用程式使用,而且跟用戶端執行在同一個處理程序 (此作法很常見),則應該要透過延伸 {@link android.os.Binder} 類別,並從 +{@link android.app.Service#onBind onBind()} 傳回此類別的執行個體來建立介面。 + +用戶端接收 {@link android.os.Binder} 後,可以用它直接存取 {@link android.os.Binder} 實作或 {@link android.app.Service} 提供的公用方法。 + + + <p>若您的服務只是您自己應用程式的背景工作者,建議使用此方式。 +除非您的服務是由其他應用程式或跨個別處理程序使用,才不用以此方式建立自己的介面。 +</dd> + + <dt><a href="#Messenger">使用 Messenger</a></dt> + <dd>如果您的介面需要跨不同處理程序運作,則可以建立內含 {@link android.os.Messenger} 服務的介面。 +此服務定義了回應不同類型 {@link +android.os.Message} 物件的 {@link android.os.Handler}。 +這個 {@link android.os.Handler} 是 {@link android.os.Messenger} 的基礎,之後可以與用戶端分享 {@link android.os.IBinder},讓用戶端使用 {@link +android.os.Message} 物件傳送命令給此服務。 + +此外,用戶端可以定義專屬的 {@link android.os.Messenger},服務就可以傳回訊息。 + + <p>這是處理程序間通訊 (IPC) 最簡單的執行方式,因為 {@link +android.os.Messenger} 會將所有要求都排列到單一個執行緒,因此,就不用將服務設計成執行緒安全的形式。 +</p> + </dd> + + <dt>使用 AIDL</dt> + <dd>AIDL (Android 介面定義語言) 的工作是將物件分解為作業系統瞭解的始類型,然後將這些原始類型在各個處理程序間進行封送,以執行 IPC。先前使用 {@link android.os.Messenger} 的技術,實際上就是以 AIDL 作為底層結構。 + + +如上所述,{@link android.os.Messenger} 會在單一執行緒中建立所有用戶端要求的佇列,所以服務一次會接收一個要求。 +不過,如果您要讓服務可以同時處理多個要求,則可以直接使用 AIDL。 + +在此情況下,您的服務必須具備多執行緒的功能,而且是以執行緒安全的形式建置。 + <p>如要直接使用 AIDL,您必須建立 {@code .aidl} 檔案,並在其中定義程式設計介面。 +Android SDK 工具會使用此檔案產生一個抽象類別,以便實作介面並處理 IPC。您就可以在服務內加以延伸。 + +</p> + </dd> +</dl> + + <p class="note"><strong>注意:</strong>大部分應用程式<strong>不應</strong>使用 AIDL 建立繫結服務,若自行建立的話,就需要實作多執行緒功能,可能會導致更複雜的實作。 + +因此,AIDL 不適用於大部分應用程式,而且本文不會討論如何在您的服務中使用 AIDL。 +如果您確定需要直接使用 AIDL,請參閱 <a href="{@docRoot}guide/components/aidl.html">AIDL</a> 文件。 + +</p> + + + + +<h3 id="Binder">延伸 Binder 類別</h3> + +<p>如果您的服務只會在本機應用程式使用,而且不需要跨處理程序運作,則可以實作您自己的 {@link android.os.Binder} 類別,讓用戶端直接存取服務中的公用方法。 + +</p> + +<p class="note"><strong>注意:</strong>用戶端和服務都位於相同應用程式和處理程序時才適用,這也是最常見的情況。 +例如,需要將 Activity 繫結到其專屬服務 (在背景播放音樂)的音樂應用程式,就很適合。 + +</p> + +<p>設定的方式如下:</p> +<ol> + <li>在您的服務中建立 {@link android.os.Binder} 的執行個體,以具備以下其中一種功用: + <ul> + <li>包含用戶端可以呼叫的公用方法</li> + <li>傳回目前的 {@link android.app.Service} 執行個體,其中含有用戶端可以呼叫的公用方法 +</li> + <li>傳回由服務所裝載另一個類別的執行個體,而此服務含有用戶端可以呼叫的公用方法 +</li> + </ul> + <li>從 {@link +android.app.Service#onBind onBind()} 回呼方法傳回此 {@link android.os.Binder} 的執行個體。</li> + <li>在用戶端方面,從 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼方法接收 {@link android.os.Binder},然後使用提供的方法呼叫繫結服務。 +</li> +</ol> + +<p class="note"><strong>注意:</strong>服務和用戶端必須位於相同的應用程式,是因為用戶端才可以轉換傳回的物件,然後正確地呼叫其 API。 +服務和用戶端也必須位於相同的處理程序,因為此技術不會執行任何跨處理程序間旳封送。 + +</p> + +<p>例如,以下的服務可以讓用戶端透過實作的 {@link android.os.Binder} 存取服務中的方法: +</p> + +<pre> +public class LocalService extends Service { + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + // Random number generator + private final Random mGenerator = new Random(); + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + // Return this instance of LocalService so clients can call public methods + return LocalService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** method for clients */ + public int getRandomNumber() { + return mGenerator.nextInt(100); + } +} +</pre> + +<p>{@code LocalBinder} 提供 {@code getService()} 方法,讓用戶端擷取 {@code LocalService} 目前的執行個體。 +這樣可以讓用戶端呼叫服務中的公用方法。 +例如,用戶端可以從服務呼叫 {@code getRandomNumber()}。</p> + +<p>當按一下按鈕時,會發生繫結至 {@code LocalService} 並呼叫 {@code getRandomNumber()}的 Activity: +</p> + +<pre> +public class BindingActivity extends Activity { + LocalService mService; + boolean mBound = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, LocalService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } + + /** Called when a button is clicked (the button in the layout file attaches to + * this method with the android:onClick attribute) */ + public void onButtonClick(View v) { + if (mBound) { + // Call a method from the LocalService. + // However, if this call were something that might hang, then this request should + // occur in a separate thread to avoid slowing down the activity performance. + int num = mService.getRandomNumber(); + Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); + } + } + + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; +} +</pre> + +<p>上述範例顯示:用戶端如何使用 +{@link android.content.ServiceConnection} 的實作和 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼,繫結至服務。下一節提供關於繫結至服務處理程序的詳細資訊。 +</p> + +<p class="note"><strong>注意:</strong>上述範例未明確從服務解除繫結,但所有用戶端都應該在適當時間解除繫結 (例如,Activity 暫停時)。 +</p> + +<p>如要取得更多範例程式碼,請參閱 <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a> 中的 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code +LocalService.java}</a> 類別和 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code +LocalServiceActivities.java}</a> 類別。</p> + + + + + +<h3 id="Messenger">使用 Messenger</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>與 AIDL 的比較</h4> + <p>需要執行 IPC 時,使用 {@link android.os.Messenger} 作為介面較簡單 (與使用 AIDL 實作介面相比),因為 {@link android.os.Messenger} 佇列都會呼叫服務,但是單純的 AIDL 介面會同時將要求傳送給服務。因此,服務必須具備處理多執行緒功能。 + + +</p> + <p>對於大部分應用程式而言,服務不需要執行多執行緒,所以使用 {@link +android.os.Messenger} 讓服務一次處理一個呼叫。如果您的服務必須處理多執行緒,則要使用 <a href="{@docRoot}guide/components/aidl.html">AIDL</a> 定義您的介面。 +</p> +</div> +</div> + +<p>如果服務要和遠端處理程序溝通,則可以使用 +{@link android.os.Messenger} 為您的服務提供介面。此技術讓您不需要使用 AIDL,就可以執行處理程序間通訊 (IPC)。 +</p> + +<p>以下是使用 {@link android.os.Messenger} 的摘要:</p> + +<ul> + <li>此服務實作 {@link android.os.Handler},可以從用戶端接收每個呼叫的回呼。 +</li> + <li>{@link android.os.Handler} 用於建立 {@link android.os.Messenger} 物件 (這是 {@link android.os.Handler} 的參照)。 +</li> + <li>{@link android.os.Messenger} 會建立 {@link android.os.IBinder},服務會從 {@link android.app.Service#onBind onBind()} 傳回給用戶端。 +</li> + <li>用戶端使用 {@link android.os.IBinder} 將 {@link android.os.Messenger} (參照服務的 {@link android.os.Handler}) 具現化,用戶端就可以用來將 +{@link android.os.Message} 物件傳送給服務。 +</li> + <li>服務會在其 {@link +android.os.Handler} 中接收每個 {@link android.os.Message} — 更明確地說,就是在 {@link android.os.Handler#handleMessage +handleMessage()} 方法中加以接收。</li> +</ul> + + +<p>這樣一來,用戶端就不需要呼叫服務的任何「方法」。用戶端只要傳遞「訊息」({@link android.os.Message} 物件),服務就會在其 {@link android.os.Handler} 中接收。 + +</p> + +<p>以下是使用 {@link android.os.Messenger} 介面的簡單範例服務:</p> + +<pre> +public class MessengerService extends Service { + /** Command to the service to display a message */ + static final int MSG_SAY_HELLO = 1; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAY_HELLO: + Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); + return mMessenger.getBinder(); + } +} +</pre> + +<p>請注意, +{@link android.os.Handler} 中的 {@link android.os.Handler#handleMessage handleMessage()} 方法是服務接收傳入 {@link android.os.Message} 的位置,也是根據 {@link android.os.Message#what} 成員,決定後續執行動作的位置。 +</p> + +<p>用戶端只需要根據服務傳回的 {@link +android.os.IBinder},建立 {@link android.os.Messenger},然後使用 {@link +android.os.Messenger#send send()} 傳送訊息。例如,以下的簡單 Activity 會繫結至服務,然後將 {@code MSG_SAY_HELLO} 訊息傳遞給服務: +</p> + +<pre> +public class ActivityMessenger extends Activity { + /** Messenger for communicating with the service. */ + Messenger mService = null; + + /** Flag indicating whether we have called bind on the service. */ + boolean mBound; + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + mService = new Messenger(service); + mBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mBound = false; + } + }; + + public void sayHello(View v) { + if (!mBound) return; + // Create and send a message to the service, using a supported 'what' value + Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to the service + bindService(new Intent(this, MessengerService.class), mConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } +} +</pre> + +<p>注意,此範例並未顯示服務回應用戶端的方式。如果您希望 +服務有所回應,則同時需要在用戶端建立 {@link android.os.Messenger}。之後,用戶端接收 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 回呼時,會傳送 {@link android.os.Message} 給服務,其中包括用戶端的 {@link android.os.Messenger} (位於 {@link android.os.Messenger#send send()} 方法的 {@link android.os.Message#replyTo} 參數中)。 + + +</p> + +<p>您可以在 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code +MessengerService.java}</a> (服務) 和 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code +MessengerServiceActivities.java}</a> (用戶端) 的範例中看到如何進行雙向傳訊的例子。</p> + + + + + +<h2 id="Binding">繫結至服務</h2> + +<p>應用程式元件 (用戶端) 可以透過呼叫 +{@link android.content.Context#bindService bindService()},繫結至服務。然後,Android 系統會呼叫服務的 {@link android.app.Service#onBind +onBind()} 方法 (此方法傳回 {@link android.os.IBinder} 以便與服務互動)。 +</p> + +<p>繫結為非同步。{@link android.content.Context#bindService +bindService()} 會立即傳回,但「不會」將<em></em> {@link android.os.IBinder} 傳回給用戶端。 +如要接收 {@link android.os.IBinder},用戶端必須建立 {@link +android.content.ServiceConnection} 的執行個體,然後將此執行個體傳送給 {@link android.content.Context#bindService +bindService()}。{@link android.content.ServiceConnection} 內含回呼方法,系統會呼叫此方法以傳遞 {@link android.os.IBinder}。 +</p> + +<p class="note"><strong>注意:</strong>只有 Activity、服務以及內容提供者可以繫結至服務 — 您<strong>不能</strong>從廣播接收者繫結至服務。 +</p> + +<p>因此,如要從用戶端繫結至服務,必須符合下列條件: </p> +<ol> + <li>實作 {@link android.content.ServiceConnection}。 + <p>實作中必須覆寫兩個回呼方法:</p> + <dl> + <dt>{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}</dt> + <dd>系統會呼叫此方法來傳遞 {@link android.os.IBinder} (由服務的 {@link android.app.Service#onBind onBind()} 方法所傳回)。 +</dd> + <dt>{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}</dt> + <dd>與服務之間的連線突然遺失時 (例如服務當機或遭到終止時),Android 系統會呼收此方法。 +用戶端解除繫結時,「不會」<em></em>呼叫此方法。 +</dd> + </dl> + </li> + <li>呼叫會傳送 {@link +android.content.ServiceConnection} 實作的 {@link +android.content.Context#bindService bindService()}。 </li> + <li>系統呼叫 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 回呼方法時,您就可以使用介面定義的方法,開始呼叫服務。 +</li> + <li>如要與服務中斷連線,請呼叫 {@link +android.content.Context#unbindService unbindService()}。 + <p>用戶端遭到終結時,會與服務解除繫結。不過,您應該要在與服務完成互動時,或者 Activity 暫停,要讓服務未使用時可以加以關閉的情況下,一定要解除繫結。 + +(以下將有繫結和解除繫結適當時機的詳細討論)。 +</p> + </li> +</ol> + +<p>例如,下列程式碼片段會透過<a href="#Binder">延伸 Binder 類別</a>,將用戶端連線到上述建立的服務,因此用戶端只要將傳回的 +{@link android.os.IBinder} 轉換為 {@code LocalService} 類別,然後要求 {@code +LocalService} 執行個體: +</p> + +<pre> +LocalService mService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + mBound = false; + } +}; +</pre> + +<p>使用此 {@link android.content.ServiceConnection},用戶端可以透過將它傳送給 {@link android.content.Context#bindService bindService()} 而繫結至服務。 +例如:</p> + +<pre> +Intent intent = new Intent(this, LocalService.class); +bindService(intent, mConnection, Context.BIND_AUTO_CREATE); +</pre> + +<ul> + <li>{@link android.content.Context#bindService bindService()} 的第一個參數是 +{@link android.content.Intent},明確地指定服務進行繫結 (儘管意圖是隱含的)。 +</li> +<li>第二個參數是 {@link android.content.ServiceConnection} 物件。</li> +<li>第三個參數是旗標,用來指出繫結的選項。如果服務尚未存在,通常是使用 {@link +android.content.Context#BIND_AUTO_CREATE} 以建立服務。其他可能的值為 {@link android.content.Context#BIND_DEBUG_UNBIND}和 {@link android.content.Context#BIND_NOT_FOREGROUND},或者 {@code 0} 代表無。 + +</li> +</ul> + + +<h3>其他注意事項</h3> + +<p>以下是關於繫結至服務的一些重要注意事項:</p> +<ul> + <li>您一定要設陷 {@link android.os.DeadObjectException} 例外狀況 (連線中斷時會擲回此例外狀況)。 +遠端方法只會擲回這個例外狀況。</li> + <li>物件會跨處理程序計算參照。 </li> + <li>繫結和解除繫結通常應成對使用,以符合用戶端的開始和結束週期。 +例如: + <ul> + <li>如果您只要在 Activity 可見時與服務互動,則要在 {@link android.app.Activity#onStart onStart()} 時繫結,並於 {@link +android.app.Activity#onStop onStop()} 時解除繫結。 +</li> + <li>如果您希望 Activity 即使在背景中停止時,仍然會接收回應,則可以在 {@link android.app.Activity#onCreate onCreate()} 時繫結,並於{@link android.app.Activity#onDestroy onDestroy()} 時解除繫結。 + +請注意,這表示您的 Activity 在整個執行期間 (即使是在背景執行也一樣) 都需要使用服務,因此,如果服務位於另一個處理程序,您要增加該處理程序的權重。但系統很可能因而將它終止。 + + +</li> + </ul> + <p class="note"><strong>注意:</strong>Activity 的 {@link android.app.Activity#onResume onResume()} 和 {@link +android.app.Activity#onPause onPause()} 期間,通常<strong>不要</strong>繫結和解除繫結,因為這些回呼會在每個週期轉換時發生,而且您應該要讓這些轉換期間所發生的處理動作保持在最少狀態。 + +另外,如果您的應用程式中有多個 Activity 繫結至同一個服務,而這兩個 Activity 之間會進行轉換,則服務會在目前的 Activity 解除繫結(暫停) 時,而下一個 Activity 繫結之前 (繼續時),先終結後再重新建立 + + +(此 Activity 轉換如何在 Activity 之間協調其週期的資訊,於 <a href="{@docRoot}guide/components/activities.html#CoordinatingActivities">Activity</a> 文件中說明)。 + +</p> +</ul> + +<p>如要取得如何繫結至服務的更多範例程式碼,請參閱 <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a> 中的 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a> 類別。</p> + + + + + +<h2 id="Lifecycle">管理繫結服務的週期</h2> + +<p>服務與所有用戶端解除繫結時,Android 系統會將服務終結 (除非服務是和 {@link android.app.Service#onStartCommand onStartCommand()} 一起啟動的)。 +如果您的服務純粹是繫結服務,就不用管理它的週期— Android 系統會根據服務是否繫結至任何用戶端,為您管理服務。 + +</p> + +<p>不過,如果您選擇實作 {@link android.app.Service#onStartCommand +onStartCommand()} 回呼方法,則必須明確停止服務,因為服務現在會視為「已啟動」<em></em>。 +如果是此情形,除非服務本身使用 {@link android.app.Service#stopSelf()} 自行停止,或另一個元件呼叫 {@link +android.content.Context#stopService stopService()} 加以停止,否則服務會持續執行,不論它是否繫結至任何用戶端。 + +</p> + +<p>此外,如果您的服務已啟動並且接受繫結,當系統呼叫您的 {@link android.app.Service#onUnbind onUnbind()} 方法時,可以選擇傳回 +{@code true} (如果您希望用戶端下次繫結至服務時,可以接收 {@link android.app.Service#onRebind +onRebind()} 呼叫,而不是接收 {@link +android.app.Service#onBind onBind()} 的呼叫)。{@link android.app.Service#onRebind +onRebind()} 會傳回空值,但用戶端仍然會在其 +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼中接收到 {@link android.os.IBinder}。以下「圖 1」說明這類週期的邏輯。 + +</p> + + +<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>服務的週期開始後,就允許繫結行為。 +</p> + + +<p>如需關於已啟動服務週期的詳細資訊,請參閱<a href="{@docRoot}guide/components/services.html#Lifecycle">服務</a>文件。</p> + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/fragments.jd b/docs/html-intl/intl/zh-tw/guide/components/fragments.jd new file mode 100644 index 000000000000..e54769b0c88b --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=片段 +parent.title=Activity +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> + <ol> + <li><a href="#Design">設計概念</a></li> + <li><a href="#Creating">建立片段</a> + <ol> + <li><a href="#UI">新增使用者介面</a></li> + <li><a href="#Adding">將片段新增到 Activity 中</a></li> + </ol> + </li> + <li><a href="#Managing">管理片段</a></li> + <li><a href="#Transactions">進行片段交易</a></li> + <li><a href="#CommunicatingWithActivity">與 Activity 通訊</a> + <ol> + <li><a href="#EventCallbacks">為 Activity 建立事件回呼</a></li> + <li><a href="#ActionBar">將項目新增到動作列中</a></li> + </ol> + </li> + <li><a href="#Lifecycle">處理片段生命週期</a> + <ol> + <li><a href="#CoordinatingWithActivity">調整 Activity 生命週期</a></li> + </ol> + </li> + <li><a href="#Example">範例說明</a></li> + </ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.app.Fragment}</li> + <li>{@link android.app.FragmentManager}</li> + <li>{@link android.app.FragmentTransaction}</li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="{@docRoot}training/basics/fragments/index.html">使用片段建置動態 UI</a></li> + <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">支援平板電腦和手機</a> +</li> + </ol> +</div> +</div> + +<p>{@link android.app.Fragment} 代表一種行為或 +{@link android.app.Activity} 中的一部分使用者介面。您可以合併單一 Activity 中的多個片段,藉此建置 +多窗格 UI 以及在多個 Activity 中重複使用片段。您可以將片段想成是 Activity 的模組化區段,片段擁有自己的生命週期、接收自己的輸入事件,而且您可以在 Activity 執行時新增或移除片段 (有點像是您可以在不同 Activity 中重複使用的「子 Activity」)。 + + +</p> + +<p>片段必須一律嵌入 Activity 中,而主要 Activity 的生命週期會直接影響片段的生命週期。 +例如,當 Activity 暫停時,其中的所有片段也會一併暫停;而當 Activity 遭到刪除時,所有片段也會一併刪除。 +不過,當 Activity 執行時 (該 Activity 會處於繼續進行<em></em><a href="{@docRoot}guide/components/activities.html#Lifecycle">生命週期狀態</a>),您可以個別操縱所有片段,例如新增或移除片段。 + +當您進行片段交易這類操作時,您也可以將片段加到 Activity 所管理的返回堆疊中 — Activity 中的所有返回堆疊項目均為所發生片段交易的記錄。 + + +返回堆疊可讓使用者復原片段交易 (往回瀏覽),只要按下 [返回]<em></em> 按鈕即可。 +</p> + +<p>當您將片段新增為 Activity 版面配置的一部分後,片段就會位於 Activity 檢視階層中的 {@link +android.view.ViewGroup},而且片段會自行定義專屬的版面配置。您可以宣告 Activity 版面配置檔案中的片段,或是在應用程式的程式碼中將片段加到現有的 {@link android.view.ViewGroup} 中,藉此在 Activity 版面配置中將片段插入為 {@code <fragment>} 元素。 + + + +不過,片段未必要成為 Activity 版面配置的一部分;您也可以選擇不透過其 UI,以隱形工作人員的身分使用 Activity 的片段。 + +</p> + +<p>本文說明如何建置應用程式以使用片段,包括片段如何在加到 Activity 返回堆疊時保持自身狀態、如何與 Activity 和 Activity 中的其他片段共用活動、如何製作 Activity 欄等等。 + + +</p> + + +<h2 id="Design">設計概念</h2> + +<p>我們在 Android 3.0 (API 級別 11) 中導入了片段,主要目的是為了在大型螢幕 (例如平板電腦) 上支援更多動態和彈性 UI 設計。 +由於平板電腦的螢幕比手機大上許多,因此有更多空間可結合及交換 UI 元件。 + +片段可實現這種介面設計,而不必讓您管理複雜的檢視階層變更。 +將 Activity 的版面配置劃分成片段後,您就可以修改 Activity 在執行階段的外觀,以及保留 Activity 所管理返回堆疊的相關變更。 + +</p> + +<p>例如,某個新聞應用程式可使用單一片段在畫面左側顯示文章清單,並且使用另一個片段在畫面右側顯示某篇文章 — 這兩個片段是以並排方式出現在某個 Activity 中,而每個片段都有自己的一組生命週期回呼方法,可自行處理其使用者輸入事件。 + + +因此,使用者可以在相同 Activity 中選取並閱讀某篇文章 (如圖 1 中的平板電腦版面配置所示),而不必使用不同 Activity 選取及閱讀文章。 + +</p> + +<p>請務必將每個片段設計成模組化和可重複使用的 Activity 元件。這是因為每個片段會根據其生命週期回呼,定義專屬版面配置和行為,而您可將單一片段加到多個 Activity 中,故請將其設計成可重複使用的元件,同時避免直接操縱個別片段。 + + +由於模組化片段可讓您針對不同螢幕大小變更片段組合,因此請務必這麼做。 +設計您的應用程式以支援平板電腦和手機時,您可以在不同版面配置設定中重複使用片段,藉此根據可用的螢幕空間提供最佳的使用者體驗。 + +以手機為例說明,如果相同 Activity 中有多個片段不相符,則只要分割片段即可提供單一面板式的 UI。 + +</p> + +<img src="{@docRoot}images/fundamentals/fragments.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>片段所定義的兩個 UI 模組如何針對平板電腦設計合併成單一 Activity、如何針對手機設計分割成個別 Activity。 + +</p> + +<p>例如 — 延續新聞應用程式範例加以說明 — 在平板電腦大小的裝置上執行的應用程式可將兩個片段嵌入「Activity A」<em></em>。 +不過,在手機大小的螢幕上,由於螢幕空間不足以容納兩個片段,因此「Activity A」<em></em>只會包含文章清單的片段,而當使用者選取文章後,系統就會啟動內含第二個片段的「Activity B」<em></em>,讓使用者閱讀文章。 + + +因此,應用程式可透過重複使用不同片段組合的方式,同時支援平板電腦和手機 (如圖 1 所示)。 + +</p> + +<p>如要進一步瞭解如何使用不同片段組合針對各種螢幕設定設計應用程式,請參閱<a href="{@docRoot}guide/practices/tablets-and-handsets.html">支援平板電腦和手機</a>指南。 +</p> + + + +<h2 id="Creating">建立片段</h2> + +<div class="figure" style="width:327px"> +<img src="{@docRoot}images/fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>圖 2.</strong>片段的生命週期 (當其中的 Activity 處於執行狀態時)。 +</p> +</div> + +<p>如要建立片段,您必須建立 {@link android.app.Fragment} 的子類別 (或是其現有的子類別)。 +{@link android.app.Fragment} 類別內含與{@link android.app.Activity} 十分雷同的程式碼。 +該程式碼包括與 Activity 類似的回呼方法,例如 {@link android.app.Fragment#onCreate onCreate()}、{@link android.app.Fragment#onStart onStart()}、 +{@link android.app.Fragment#onPause onPause()} 和 {@link android.app.Fragment#onStop onStop()}。 +事實上,如果您是設定現有 Android 應用程式改用片段,只要將 Activity 的回呼方法中的程式碼移到片段的個別回呼方法即可。 + + +</p> + +<p>一般來說,您至少必須實作下列生命週期方法:</p> + +<dl> + <dt>{@link android.app.Fragment#onCreate onCreate()}</dt> + <dd>系統會在建立片段時呼叫這個方法。在實作這個方法時,您必須初始化您想保留的必要片段元件,以便恢復已暫停或停止的片段。 + +</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>系統會在片段初次顯示其使用者介面時呼叫這個方法。 +您必須透過這個方法傳回 {@link android.view.View} (片段版面配置的根目錄),才能顯示片段的 UI。 +如果片段並未提供 UI 的話,則可以傳回空值。 +</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>系統會在使用者初次離開片段時呼叫這個方法 (即使使用者這麼做未必會刪除片段)。 +您通常需要透過這個方法提交要在目前的使用者工作階段以外保留的任何變更 (原因在於使用者可能不會返回)。 + +</dd> +</dl> + +<p>大多數應用程式都至少必須針對每個片段實作這三個方法,不過您也必須使用幾個其他回呼方法來控制片段生命週期的各種狀態。 + +如要進一步瞭解所有回呼方法,請參閱<a href="#Lifecycle">處理片段生命週期</a>。 +</p> + + +<p>以下列出幾個您可能會想擴充的子類別 (基礎 {@link +android.app.Fragment} 類別除外):</p> + +<dl> + <dt>{@link android.app.DialogFragment}</dt> + <dd>顯示浮動對話方塊。使用這個類別建立對話方塊是使用 {@link android.app.Activity} 類別中的對話方塊協助程式方法的推薦替代方法,這是因為使用此類別可將片段對話方塊納入 Activity 所管理的片段堆疊,讓使用者得已返回已關閉的片段。 + + +</dd> + + <dt>{@link android.app.ListFragment}</dt> + <dd>顯示配接器 (例如 {@link +android.widget.SimpleCursorAdapter}) 所管理的項目清單;與 {@link android.app.ListActivity} 方法相似。這個方法可提供數種管理清單檢視畫面的方法,例如可處理點擊事件的 {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()}回呼。 + +</dd> + + <dt>{@link android.preference.PreferenceFragment}</dt> + <dd>列出 {@link android.preference.Preference} 物件的階層;與 +{@link android.preference.PreferenceActivity} 方法相似。為應用程式建立「設定」Activity 時,這個方法就非常實用。 +</dd> +</dl> + + +<h3 id="UI">新增使用者介面</h3> + +<p>片段通常是當作某 Activity 的使用者介面使用,而且可將自身的版面配置提供給 Activity。 +</p> + +<p>如要提供片段的版面配置,您必須實作 {@link +android.app.Fragment#onCreateView onCreateView()} 回呼方法,讓 Android 系統呼叫片段顯示其版面配置。 +實作這個方法時,您必須傳回 +{@link android.view.View} (片段版面配置的根目錄)。</p> + +<p class="note"><strong>注意:</strong>如果您的片段是 {@link +android.app.ListFragment} 的子類別,則實作完畢後系統預設會傳回 {@link android.app.Fragment#onCreateView onCreateView()} 的 +{@link android.widget.ListView},因此您不必加以實作。</p> + +<p>如要從 {@link +android.app.Fragment#onCreateView onCreateView()} 傳回版面配置,您可以從 XML 中定義的<a href="{@docRoot}guide/topics/resources/layout-resource.html">l版面配置資源</a>擴大它。為協助您完成這項作業,{@link android.app.Fragment#onCreateView onCreateView()} 提供了 +{@link android.view.LayoutInflater} 物件。 +</p> + +<p>例如,以下是 {@link android.app.Fragment} 的子類別,可從 +{@code example_fragment.xml} 檔案載入版面配置:</p> + +<pre> +public static class ExampleFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.example_fragment, container, false); + } +} +</pre> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>建立版面配置</h3> + <p>在上方範例中,{@code R.layout.example_fragment} 是應用程式中儲存的 +「{@code example_fragment.xml}」版面配置資源的參照資料。如要瞭解如何在 XML 中建立版面配置,請參閱<a href="{@docRoot}guide/topics/ui/index.html">使用者介面</a>。 + +</p> +</div> +</div> + +<p>傳入 {@link android.app.Fragment#onCreateView +onCreateView()} 的 {@code container} 參數是上層 {@link android.view.ViewGroup} (來自 Activity 的版面配置),系統會將您的片段版面配置插入其中。 + +{@code savedInstanceState} 參數是 {@link android.os.Bundle},當片段即將恢復時 (如要進一步瞭解還原狀態,請參閱<a href="#Lifecycle">處理片段生命週期</a>),這個參數就會提供先前的片段執行個體的相關資料。 + + +</p> + +<p>{@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} 方法採用三種引數: +</p> +<ul> + <li>您想要擴大的版面配置的資源 ID。</li> + <li>要設為擴大過後版面配置的上層檢視的 {@link android.view.ViewGroup}。請務必傳遞 {@code +container},以便讓系統將版面配置參數套用至擴大過後版面配置的根檢視 (由將做為其目標的父檢視所指定)。 +</li> + <li>用於指示系統是否要在擴大期間將擴大過後的版面配置附加到 {@link +android.view.ViewGroup} (第二個參數) 的布林值 (由於系統已將擴大過後的版面配置插入 {@code +container},因此布林值應為 false — 如果您傳送 true,會導致系統在最終版面配置中建立多餘的檢視群組)。 +</li> +</ul> + +<p>您現在已瞭解如何建立可提供版面配置的片段了。接著,請將建立好的片段新增至 Activity。 +</p> + + + +<h3 id="Adding">將片段新增到 Activity 中</h3> + +<p>片段通常會將一部分 UI 嵌入主要 Activity 的整體檢視階層中。 +您有兩種方式可將片段新增到 Activity 版面配置: +</p> + +<ul> + <li><b>宣告 Activity 版面配置檔案內含的片段。</b> +<p>選用這種方式時,您可以將片段視為檢視,為其指定版面配置屬性。 +例如,以下是內含兩個片段的 Activity 版面配置檔案: +</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <fragment android:name="com.example.news.ArticleListFragment" + android:id="@+id/list" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent" /> + <fragment android:name="com.example.news.ArticleReaderFragment" + android:id="@+id/viewer" + android:layout_weight="2" + android:layout_width="0dp" + android:layout_height="match_parent" /> +</LinearLayout> +</pre> + <p>{@code <fragment>} 中的 {@code android:name} 屬性可指定系統呼叫版面配置中的 {@link +android.app.Fragment} 類別。</p> + +<p>系統建立這個 Activity 版面配置後,就會呼叫版面配置中指定的任何片段,並為每個片段呼叫 {@link android.app.Fragment#onCreateView onCreateView()} 方法,藉此擷取所有片段的版面配置。 + +系統會插入片段所傳回的 {@link android.view.View} 來取代 {@code <fragment>} 元素。 +</p> + +<div class="note"> + <p><strong>注意:</strong>您必須為每個片段提供專屬識別碼,以便系統在 Activity 重新開始時復原片段 (您也可以使用此識別碼擷取要交易的片段,例如移除片段)。 + +您有三種方式可提供片段的 ID: +</p> + <ul> + <li>提供內含專屬 ID 的 {@code android:id} 屬性。</li> + <li>提供內含不重複字串的 {@code android:tag} 屬性。</li> + <li>如果您未提供上述兩項屬性,系統會採用容器檢視的 ID。 +</li> + </ul> +</div> + </li> + + <li><b>或者,利用程式將片段新增至現有的 {@link android.view.ViewGroup}。</b> +<p>只要 Activity 處於執行狀態,您都可以將片段新增至 Activity 版面配置。方法很簡單,只要指定您想在其中加入片段的 {@link +android.view.ViewGroup} 即可。 +</p> + <p>如要在 Activity 中進行片段交易 (例如新增、移除或替換片段),請使用 {@link android.app.FragmentTransaction} 中的 API 進行。 +您可以透過以下方式取得 {@link android.app.Activity} 的 {@link android.app.FragmentTransaction} 執行個體: +</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()} +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>接著,您就可以使用 {@link +android.app.FragmentTransaction#add(int,Fragment) add()} 方法指定要新增的片段,以及要插入片段的目標檢視。 +例如:</p> + +<pre> +ExampleFragment fragment = new ExampleFragment(); +fragmentTransaction.add(R.id.fragment_container, fragment); +fragmentTransaction.commit(); +</pre> + + <p>第一個傳入 {@link android.app.FragmentTransaction#add(int,Fragment) add()} 的引數是要在其中插入片段的 {@link android.view.ViewGroup} (使用資源 ID 加以指定),而第二個參數則是要新增的引數。 + +</p> + <p>透過 +{@link android.app.FragmentTransaction} 完成變更後,請呼叫 {@link android.app.FragmentTransaction#commit} 以便讓變更生效。 +</p> + </li> +</ul> + + +<h4 id="AddingWithoutUI">新增不顯示 UI 的片段</h4> + +<p>上述範例說明如何將片段新增至 Activity,以提供 UI。但事實上,您也可以使用片段為 Activity 提供背景行為,避免顯示額外的 UI。 + +</p> + +<p>如要新增不顯示使 UI 的片段,請使用 {@link +android.app.FragmentTransaction#add(Fragment,String)} 從 Activity 新增片段 (請提供片段的不重複字串「標記」,而不是檢視 ID)。 +這樣即可新增片段,但由於該片段並未與 Activity 版面配置中的檢視相關聯,因此不會接收 {@link +android.app.Fragment#onCreateView onCreateView()} 的呼叫。 +如此一來,您就不必實作該方法。</p> + +<p>提供片段的字串標記並不是採用非 UI 片段時的必要步驟 — 您也可以提供沒有 UI 的片段的字串標記 — 不過,如果片段沒有 UI,則字串標記將成為識別片段的唯一途徑。 + +如果您想之後再從 Activity 中取得片段,請使用 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}。 +</p> + +<p>如需使用沒有 UI 的片段做為背景工作者的 Activity 範例,請參閱 SDK 範例中位於以下路徑的 {@code +FragmentRetainInstance.java} 範例 (可透過 Android SDK Manager 存取)<code><sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java</code>。 + +</p> + + + +<h2 id="Managing">管理片段</h2> + +<p>如要管理 Activity 中的片段,請使用 {@link android.app.FragmentManager}。如要取得這些片段,請呼叫 Activity 中的 {@link android.app.Activity#getFragmentManager()}。 +</p> + +<p>您可透過 {@link android.app.FragmentManager} 執行下列操作:</p> + +<ul> + <li>使用 {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (針對在 Activity 版面配置中提供 UI 的片段) 或 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (針對未提供 UI 的片段) 取得 Activity 中的現有片段。 +</li> + <li>使用 {@link +android.app.FragmentManager#popBackStack()} (模擬使用者的「返回」<em></em>命令) 將片段從返回堆疊中推出。</li> + <li>使用 {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()} 針對返回堆疊的變更項目註冊監聽器。</li> +</ul> + +<p>如要進一步瞭解上述方法以及其他方法,請參閱 {@link +android.app.FragmentManager} 類別說明文件。</p> + +<p>如上一節所述,您也可以使用 {@link android.app.FragmentManager} 開啟 {@link android.app.FragmentTransaction},以便進行片段交易 (例如新增及移除片段)。 + +</p> + + +<h2 id="Transactions">進行片段交易</h2> + +<p>使用 Activity 中片段的一項實用功能,就是新增、移除、替換片段以及對它們執行其他動作,藉此反映使用者互動。 +您針對 Activity 提交的每組變更稱為交易,而您可以使用 {@link +android.app.FragmentTransaction} 中的進行這種交易。 +此外,您也可以儲存對 Activity 所管理的返回堆疊進行的交易,讓使用者能夠往回瀏覽片段變更 (如同往回瀏覽 Activity)。 + +</p> + +<p>您可以從 {@link +android.app.FragmentManager} 中取得如下所示的 {@link android.app.FragmentTransaction}:</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}; +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>每次交易都是您想同時進行的一組變更。您可以使用 {@link +android.app.FragmentTransaction#add add()}、{@link android.app.FragmentTransaction#remove remove()}和 {@link android.app.FragmentTransaction#replace replace()} 等方法設定您想針對特定交易進行的變更。 + +接著,只要呼叫 {@link android.app.FragmentTransaction#commit()},就能將該交易套用至 Activity。 +</p> +</dl> + +<p>不過,您可能會為了新增交易至片段交易返回堆疊,先呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},然後再呼叫 {@link +android.app.FragmentTransaction#commit()}。 +返回堆疊是由 Activity 所管理,可讓使用者透過按下 [返回]<em></em> 按鈕的方式,返回先前的片段狀態。 +</p> + +<p>以下範例可讓您替換片段,並且保留先前的返回堆疊狀態: +</p> + +<pre> +// Create new fragment and transaction +Fragment newFragment = new ExampleFragment(); +FragmentTransaction transaction = getFragmentManager().beginTransaction(); + +// Replace whatever is in the fragment_container view with this fragment, +// and add the transaction to the back stack +transaction.replace(R.id.fragment_container, newFragment); +transaction.addToBackStack(null); + +// Commit the transaction +transaction.commit(); +</pre> + +<p>在這個範例中,{@code newFragment} 會針對依據 {@code R.id.fragment_container} ID 識別的版面配置容器, +替換其中的任何現有片段 (如果有的話)。系統會呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},將替換交易儲存到返回堆疊,以便使用者按下 [返回]<em></em> 按鈕來復原交易以及返回上一個片段。 + +</p> + +<p>如果您將多項變更新增至交易 (例如新增另一個 {@link +android.app.FragmentTransaction#add add()} 或 {@link android.app.FragmentTransaction#remove +remove()}),並且呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},那麼您在呼叫 {@link android.app.FragmentTransaction#commit commit()} 之前套用的所有變更都會新增至返回堆疊做為單次交易,在這種情況下,按下 [返回]<em></em> 按鈕就能一次復原所有變更。 + +</p> + +<p>您將變更新增至 {@link android.app.FragmentTransaction} 的順序並不會造成任何影響,但請注意以下例外狀況: +</p> +<ul> + <li>務必最後才呼叫 {@link android.app.FragmentTransaction#commit()}</li> + <li>如果您是將多個片段新增至同一容器,那麼您新增片段的順序將決定這些片段在檢視階層中的顯示順序 +</li> +</ul> + +<p>如果您並未在進行移除片段的交易時呼叫 {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()},該片段會在您提交交易後遭到刪除,而且使用者無法往回瀏覽至該片段。 +不過,如果您在移除片段時呼叫 {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()},則該片段將會遭到「停止」<em></em>,而且會在使用者往回瀏覽時繼續進行。 + + +</p> + +<p class="note"><strong>提示:</strong>您可以在進行每次片段交易時套用交易動畫,方法是在提交交易前呼叫 {@link android.app.FragmentTransaction#setTransition setTransition()}。 + +</p> + +<p>呼叫 {@link android.app.FragmentTransaction#commit()} 並不會立即進行交易, +而是會讓系統排定 UI 執行緒 (「主要」執行緒) 可執行這個方法時,立即加以執行。 +不過,您可以視需要透過 UI 執行緒呼叫 {@link +android.app.FragmentManager#executePendingTransactions()},立即執行 {@link android.app.FragmentTransaction#commit()} 所提交的交易。 +您通常不必這樣做,除非該交易是其他執行緒的工作的必要元件。 +</p> + +<p class="caution"><strong>注意:</strong>您可以使用 {@link +android.app.FragmentTransaction#commit commit()} 來提交交易,但僅限於 Activity <a href="{@docRoot}guide/components/activities.html#SavingActivityState">儲存其狀態</a>之前 (也就是使用者離開 Activity 之前)。 +如果您在這個時間點之後嘗試提交交易,就會發生例外狀況, +這是因為狀態會在交易提交後遺失 (如果需要復原 Activity 的話)。 +如果想確保狀態遺失不會造成任何影響,請使用 {@link +android.app.FragmentTransaction#commitAllowingStateLoss()} 提交交易。</p> + + + + +<h2 id="CommunicatingWithActivity">與 Activity 通訊</h2> + +<p>雖然 {@link android.app.Fragment} 是實作成不同於 +{@link android.app.Activity} 的物件,而且可在多個 Activity 中使用,特定片段執行個體仍會直接與含有該物件的 Activity 建立關聯。 +</p> + +<p>因此,片段可存取內含{@link +android.app.Fragment#getActivity()} 的 {@link android.app.Activity} 執行個體,以及輕鬆進行在 Activity 版面配置中尋找檢視等工作: +</p> + +<pre> +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list); +</pre> + +<p>相同地,您的 Activity 可利用 {@link +android.app.FragmentManager#findFragmentById findFragmentById()} 或 {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()} 從 {@link android.app.FragmentManager} 中取得 {@link android.app.Fragment} 參照資料,以呼叫片段中的方法。 +例如:</p> + +<pre> +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); +</pre> + + +<h3 id="EventCallbacks">為 Activity 建立事件回呼</h3> + +<p>在某些情況下,您可能需要可用來與 Activity 分享事件的片段。如果您需要這種片段,建議您在片段內定義回呼介面,然後要求主要 Activity 實作該片段。 + +當 Activity 透過介面接收回呼後,即可視需要與版面配置中的其他片段分享這項資訊。 +</p> + +<p>例如,假設新聞應用程式的 Activity 中有兩個片段 — 一個用於顯示文章清單 (片段 A),另一個用於顯示文章 (片段 B) — 其中的片段 A 必須告知 Activity 使用者選取清單項目的時間點,以便通知片段 B 顯示文章。 + +在這個範例中,{@code OnArticleSelectedListener} 介面是在片段 A 中完成宣告: +</p> + +<pre> +public static class FragmentA extends ListFragment { + ... + // Container Activity must implement this interface + public interface OnArticleSelectedListener { + public void onArticleSelected(Uri articleUri); + } + ... +} +</pre> + +<p>接著,代管片段的 Activity 會實作 {@code OnArticleSelectedListener} 介面,並且覆寫 {@code onArticleSelected()} 以便將片段 A 的事件告知片段 B。為了確保主要 Activity 可實作該介面,片段 A 的 {@link +android.app.Fragment#onAttach onAttach()} 回呼方法 (系統將片段新增至 Activity 時會呼叫這種方法) 轉換傳入 {@link android.app.Fragment#onAttach +onAttach()} 的 {@link android.app.Activity},藉此呼叫 {@code OnArticleSelectedListener} 執行個體: + + + + +</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnArticleSelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); + } + } + ... +} +</pre> + +<p>如果 Activity 並未實作介面,那麼片段會擲回 +{@link java.lang.ClassCastException}。成功擲回時,{@code mListener} 成員會保留 Activity 所實作 +{@code OnArticleSelectedListener} 的參照資料,以便片段 A 呼叫 {@code OnArticleSelectedListener} 介面定義的方法與 Activity 分享事件。 + +例如,假設片段 A 是 +{@link android.app.ListFragment} 的延伸,則每當使用者點擊清單項目時,系統就會呼叫片段中的 {@link android.app.ListFragment#onListItemClick +onListItemClick()},讓該方法呼叫 {@code onArticleSelected()} 以便與 Activity 分享事件: + +</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + // Append the clicked item's row ID with the content provider Uri + Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id); + // Send the event and Uri to the host activity + mListener.onArticleSelected(noteUri); + } + ... +} +</pre> + +<p>傳入 {@link +android.app.ListFragment#onListItemClick onListItemClick()} 的 {@code id} 參數是使用者所點擊項目的資料列 ID,可讓 Activity (或其他片段) 用來從應用程式的 {@link +android.content.ContentProvider} 擷取文章。 +</p> + +<p><!--To see a complete implementation of this kind of callback interface, see the <a +href="{@docRoot}resources/samples/NotePad/index.html">NotePad sample</a>. -->如要進一步瞭解如何使用內容供應程式,請參閱<a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>。 +</p> + + + +<h3 id="ActionBar">將項目新增到動作列中</h3> + +<p>片段可實作 +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()} 來為 Activity 的<a href="{@docRoot}guide/topics/ui/menus.html#options-menu">選項選單</a> (以及<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>) 提供選單項目。不過,您必須在呼叫 {@link +android.app.Fragment#onCreate(Bundle) onCreate()} 時呼叫 {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()},告知系統該片段會新增項目到「選項選單」,這個方法才能接收呼叫 (否則該片段將無法接收 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} 的呼叫)。 + +</p> + +<p>您之後透過片段新增到「選項選單」的任何物件都會附加到現有的選單項目。 +該片段也會在使用者選取選單項目時接收 {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} 的回呼。 +</p> + +<p>此外,您也可以在片段版面配置中註冊檢視來提供內容選單,方法是呼叫 {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}。當使用者開啟內容選單時,片段就會接收 {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()} 的呼叫。 +而當使用者選取某個項目時,片段則會接收 {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()} 的呼叫。</p> + +<p class="note"><strong>注意:</strong>雖然片段會在使用者選取項目時,針對所有新增的選單項目接收回呼,不過最先在使用者選取選單項目時接收個別回呼的是 Activity。 + +如果 Activity 在使用者選取項目時所實作的回呼無法處理所選項目,則系統會將該事件傳送到片段的回呼中。 +這種情況會發生在「選項選單」和內容選單。 +</p> + +<p>如要進一步瞭解選單,請參閱<a href="{@docRoot}guide/topics/ui/menus.html">選單</a>和<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>開發人員指南。</p> + + + + +<h2 id="Lifecycle">處理片段生命週期</h2> + +<div class="figure" style="width:350px"> +<img src="{@docRoot}images/activity_fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>圖 3.</strong>Activity 生命週期對片段生命週期造成的影響。 +</p> +</div> + +<p>管理片段生命週期的方式與管理 Activity 生命週期十分雷同。與 Activity 相同,片段有以下三種狀態: +</p> + +<dl> + <dt><i>已繼續</i></dt> + <dd>系統會在執行中的 Activity 內顯示片段。</dd> + + <dt><i>已暫停</i></dt> + <dd>前景中有其他具備焦點的 Activity,但系統仍會顯示含有這個片段的 Activity (前景 Activity 處於半透明狀態,或是未覆蓋整個螢幕)。 + +</dd> + + <dt><i>已停止</i></dt> + <dd>系統不會顯示片段。這可能是因為主要 Activity 已停止,或是加到返回堆疊的片段已從 Activity 中移除。 +已停止的片段仍處於有效狀態 (系統會保留所有狀態和成員資訊), +但使用者無法看到這類片段,而且當 Activity 遭到刪除時,這些片段也會一併刪除。 +</dd> +</dl> + +<p>與 Activity 相同,您可以使用 {@link +android.os.Bundle} 保留片段的狀態,以便在 Activity 的處理程序遭到刪除後想重新建立 Activity 時,還原片段狀態。 +您可以在片段的 {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} 回呼期間儲存狀態,並且在 +{@link android.app.Fragment#onCreate onCreate()}、{@link +android.app.Fragment#onCreateView onCreateView()} 或 {@link +android.app.Fragment#onActivityCreated onActivityCreated()} 時還原狀態。如要進一步瞭解如何儲存狀態,請參閱 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Activity</a>。 + +</p> + +<p>Activity 與片段生命週期之間最明顯的差異是,生命週期儲存在個別返回堆疊的方式。 +在預設情況下,Activity 停止後會插入系統所管理的 Activity 堆疊,方便使用者按下 [返回]<em></em> 按鈕來返回該 Activity (如<a href="{@docRoot}guide/components/tasks-and-back-stack.html">工作和返回堆疊</a>所述)。不過,片段只會在您進行移除片段的交易期間,呼叫 {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} 來要求系統儲存執行個體時,插入主要 Activity 所管理的返回堆疊。 + + + + +</p> + +<p>在其他情況下,管理片段生命週期的方式與管理 Activity 生命週期十分雷同。 +因此,<a href="{@docRoot}guide/components/activities.html#Lifecycle">管理 Activity 生命週期</a>的做法同樣適用於片段。 +不過,建議您參考相關資源,瞭解 Activity 生命週期對片段生命週期造成的影響。 +</p> + +<p class="caution"><strong>注意:</strong>如果您需要 {@link android.app.Fragment} 的 +{@link android.content.Context} 物件,請呼叫 {@link android.app.Fragment#getActivity()}。不過,請務必只在確定片段是附加到 Activity 的情況下,再呼叫 {@link android.app.Fragment#getActivity()}。 + +如果片段未附加到 Activity,或是片段因超過生命週期而遭到卸除,則 {@link android.app.Fragment#getActivity()} 將傳回空值。 +</p> + + +<h3 id="CoordinatingWithActivity">調整 Activity 生命週期</h3> + +<p>內含片段的 Activity 的生命週期會直接影響片段的生命週期,這樣一來,Activity 的每次生命週期回呼會針對每個片段產生相似的回呼。 + +例如,當 Activity 收到 {@link android.app.Activity#onPause} 後,Activity 中的每個片段都會收到 {@link android.app.Fragment#onPause}。 +</p> + +<p>不過,片段有幾個額外的生命週期回呼,可用於處理與 Activity 之間的特殊互動,以執行建置或刪除片段 UI 等動作。以下是這些額外的回呼方法: + +</p> + +<dl> + <dt>{@link android.app.Fragment#onAttach onAttach()}</dt> + <dd>當片段與 Activity 建立關聯時,系統就會呼叫這個方法 ({@link +android.app.Activity} 會傳入與片段相關聯的 Activity)。</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>系統會呼叫這個方法來建立與片段相關聯的檢視階層。</dd> + <dt>{@link android.app.Fragment#onActivityCreated onActivityCreated()}</dt> + <dd>當 Activity 的 {@link android.app.Activity#onCreate +onCreate()} 方法成功傳回時,系統就會呼叫這個方法。</dd> + <dt>{@link android.app.Fragment#onDestroyView onDestroyView()}</dt> + <dd>當使用者移除與片段相關聯的檢視階層時,系統就會呼叫這個方法。</dd> + <dt>{@link android.app.Fragment#onDetach onDetach()}</dt> + <dd>當使用者將片段與 Activity 解除關聯時,系統就會呼叫這個方法。</dd> +</dl> + +<p>如圖 3 所述,片段生命週期的流程會受到主要 Activity 的影響。 +您可以透過該圖片瞭解 Activity 的連續狀態如何決定片段要接收的回呼方法。 +例如,當 Activity 收到 {@link +android.app.Activity#onCreate onCreate()} 回呼後,Activity 中的片段就不會收到 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} 以外的回呼。</p> + +<p>Activity 一旦進入已恢復狀態,您便可視需要在 Activity 中新增或移除片段。 +因此,只有處於已恢復狀態的 Activity 會影響片段的生命週期。 +</p> + +<p>不過,當 Activity 不再處於已恢復狀態後,Activity 就會再次推送片段的生命週期。 +</p> + + + + +<h2 id="Example">範例說明</h2> + +<p>以下提供使用兩個片段建立兩個面板的版面配置的 Activity 範例,藉此綜合說明本文所述內容。 +下方 Activity 包含一個用於顯示莎士比亞劇作清單的片段,以及另一個用於在使用者選取清單項目時顯示劇作摘要的片段。 + +這個 Activity 範例同時示範了如何根據螢幕設定提供不同的片段設定。 +</p> + +<p class="note"><strong>注意:</strong>如需這個 Activity 的完整原始碼,請查閱 +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code +FragmentLayout.java}</a>。</p> + +<p>主要 Activity 會在 {@link +android.app.Activity#onCreate onCreate()} 時以常見的方式套用版面配置:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +<p>Activity 套用的版面配置為 {@code fragment_layout.xml}:</p> + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +<p>使用這個版面配置可讓系統在 Activity 載入版面配置時,呼叫 {@code TitlesFragment} (這個類別會列出劇本名稱),而 {@link android.widget.FrameLayout} (顯示劇本摘要的片段將納入的目標類別) 則會佔用螢幕右側的空間,但在一開始會保持空白狀態。 + + +如下方所示,系統只會在使用者從清單中選取項目後,才將片段插入 {@link android.widget.FrameLayout}。 +</p> + +<p>不過,並非所有螢幕設定都有足夠的空間同時並排顯示劇作清單以及劇作摘要。 +因此,上方版面配置只適用於橫向螢幕設定,系統會將它儲存在 {@code res/layout-land/fragment_layout.xml} 中。 +</p> + +<p>而在螢幕為直向的情況下,系統會套用儲存在 {@code res/layout/fragment_layout.xml} 中的以下版面配置: +</p> + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +<p>這個版面配置只包含 {@code TitlesFragment}。也就是說,如果裝置採用直向螢幕設定,系統只會顯示劇作名稱清單。 +因此,當使用者在採用這種設定的裝置上點擊清單項目後,應用程式就會啟動新 Activity 來顯示劇作摘要,而不是載入第二個片段。 + +</p> + +<p>接著,請看看片段類別如何達到以上目標。首先是用於顯示莎士比亞劇作名稱清單的 {@code +TitlesFragment}。這個片段會延伸 {@link +android.app.ListFragment},並且依據該類別控制大多數的清單檢視工作。</p> + +<p>如果您檢查這個程式碼,將會發現使用者點擊清單項目後會觸發兩種行為:視採用的版面配置而定,系統會建立並呈現新的片段,以便在同一 Activity 中顯示詳細資料 (將片段加到 {@link +android.widget.FrameLayout}),或是啟動新的 Activity (藉此顯示片段)。 + +</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +<p>第二個片段 {@code DetailsFragment} 則會針對 +{@code TitlesFragment} 中,使用者所選清單項目的劇本摘要:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +<p>針對 {@code TitlesFragment} 類別發出的回呼,如果使用者點擊清單項目,而且目前的版面配置「並未」<em></em>包含 {@code R.id.details} 檢視 ({@code DetailsFragment} 所屬的檢視),則應用程式會執行 {@code DetailsActivity} Activity 來顯示項目內容。 + + +</p> + +<p>以下是會在螢幕採用橫向版面設定時,嵌入 {@code DetailsFragment} 以顯示所選劇本摘要的 {@code DetailsActivity}: +</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +<p>請注意,這個 Activity 會在螢幕採用橫向版面配置的情況下自行結束,因此主要 Activity 會接續顯示 {@code TitlesFragment} 旁的 {@code DetailsFragment}。如果使用者在採用直向版面配置的裝置上執行 {@code DetailsActivity},然後將該裝置旋轉成橫向 (這會重新執行目前的 Activity),就可能會發生這種情況。 + + +</p> + + +<p>如需更多使用片段的範例 (以及本範例的原始檔案),請參閱<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment">ApiDemos</a> 所提供的 API Demos 範例應用程式 (可透過 <a href="{@docRoot}resources/samples/get.html">SDK 元件範本</a>下載)。 + +</p> + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd b/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd new file mode 100644 index 000000000000..d3b3c2899614 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=應用程式基礎知識 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> +<li><a href="#Components">應用程式元件</a> + <ol> + <li><a href="#ActivatingComponents">啟用元件</a></li> + </ol> +</li> +<li><a href="#Manifest">宣示說明檔案</a> + <ol> + <li><a href="#DeclaringComponents">宣告元件</a></li> + <li><a href="#DeclaringRequirements">宣告應用程式需求</a></li> + </ol> +</li> +<li><a href="#Resources">應用程式資源</a></li> +</ol> +</div> +</div> + +<p>Android 應用程式是以 Java 程式語言編寫而成。Android SDK 工具可將您的程式碼 — 連同任何相關資料和資源檔案 — 編入 APK ( + <i>Android 套件,</i>使用 {@code .apk} 後綴字詞的封存檔)。 +APK 檔案包含 Android 應用程式的所有內容,搭載 Android 作業系統的裝置會使用這種檔案來安裝應用程式。 +</p> + +<p>Android 應用程式安裝到裝置之後,便可在專屬的安全性沙箱中執行: </p> + +<ul> + <li>Android 作業系統是一種支援多位使用者的 Linux 系統;在這種系統中,每款應用程式即代表不同的使用者。 +</li> + +<li>在預設情況下,系統會為每款應用程式指派一個不重複 Linux 使用者 ID (只有系統可使用這個 ID,應用程式無法取得這項資訊)。 +系統會為應用程式中的所有檔案設定權限,因此只有該應用程式指派的使用者 ID 可存取這些檔案。 + </li> + +<li>所有處理程序都有專屬的虛擬機器 (VM),供系統在獨立環境中執行應用程式的程式碼。 +</li> + +<li>在預設情況下,每款應用程式會在專屬的 Linux 處理程序中執行。Android 會在需要執行應用程式的任何元件時啟動處理程序,並且在不必執行應用程式元件,或系統必須復原記憶體供其他應用程式使用時關閉處理程序。 + +</li> +</ul> + +<p>如此一來,Android 系統就會實作「最低權限原則」<em></em>。換句話說,在預設情況下,所有應用程式只能存取執行工作時所需的元件。 + +這樣一來,應用程式便無法存取系統的某些部分,藉此建立十分安全的執行環境。 +</p> + +<p>不過,應用程式可透過一些方式與其他應用程式分享資料,以及存取系統服務: +</p> + +<ul> + <li>兩款應用程式可共用相同的 Linux 使用者 ID,以便存取彼此的檔案。 +為了節省系統資源,共用相同使用者 ID 的應用程式可在相同 Linux 處理程序中執行,以及共用相同的 VM (前提是應用程式必須使用相同的憑證進行簽署)。 + +</li> + <li>應用程式可要求存取裝置資料 (例如使用者的聯絡人資料、簡訊、掛載式儲存空間 (SD 卡)、相機、藍牙等)。 +使用者必須在安裝期間授予所有應用程式權限。 +</li> +</ul> + +<p>本文提供有關 Android 應用程式如何在系統中運作的基本概念,其餘部分則說明以下幾點: +</p> +<ul> + <li>定義應用程式的核心架構元件。</li> + <li>用於宣告應用程式所需元件和裝置功能的宣示說明檔案。 +</li> + <li>應用程式的程式碼以外的資源;這些資源可讓您的應用程式針對各種裝置設定最佳化本身的行為。 +</li> +</ul> + + + +<h2 id="Components">應用程式元件</h2> + +<p>應用程式元件是 Android 應用程式的重要設計模組。每個元件 都是系統進入您應用程式的不同要點。並非所有元件都是使用者的實際進入點;某些元件的定義取決於其他元件,但所有元件都是獨立的個體,扮演著特定角色 — 換句話說,每個元件都是獨特的設計模組,可協助定義您應用程式的整體行為。 + + + +</p> + +<p>應用程式元件可分為 4 種不同類型。每種類型的用途和生命週期均不相同,可定義元件的建立及刪除方式。 +</p> + +<p>以下是應用程式元件的 4 種類型:</p> + +<dl> + +<dt><b>Activity</b></dt> + +<dd>單一 <i>Activity</i> 代表顯示使用者介面的一個畫面。例如,電子郵件應用程式可包含一個用於顯示新郵件清單的 Activity、一個用於撰寫郵件的 Activity,以及一個用於閱讀郵件的 Activity。 + +雖然電子郵件應用程式的 Activity 會共同運作,以提供豐富的使用者體驗,不過每個 Activity 都是不同的個體。 + +因此,其他應用程式可執行其中任何一項 Activity (如果電子郵件應用程式允許的話)。 +例如,相機應用程式可在電子郵件應用程式中,執行用於撰寫新郵件的 Activity,以便讓使用者分享相片。 + + +<p>Activity 是實作成 {@link android.app.Activity} 的子類別;詳情請參閱 <a href="{@docRoot}guide/components/activities.html">Activity</a> 開發人員指南。 + +</p> +</dd> + + +<dt><b>服務</b></dt> + +<dd>單一 <i>服務</i> 是在背景執行的元件,用於進行長期作業或遠端處理程序工作。 +服務並不會提供使用者介面。 +例如,服務可在使用者位於其他應用程式時在背景撥放音樂,或是透過網路擷取資料,同時允許使用者與 Activity 進行互動。 + +其他元件 (例如 Activity) 可啟動並讓服務執行,或是繫結至 Activity 以便與其進行互動。 + + +<p>服務是實作成 {@link android.app.Service} 的子類別;詳情請參閱<a href="{@docRoot}guide/components/services.html">服務</a>開發人員指南。 + +</p> +</dd> + + +<dt><b>內容供應程式</b></dt> + +<dd>單一 <i>內容供應程式</i> 可管理一組已分享的應用程式資料。您可以將資料儲存在檔案系統、SQLite 資料庫、網路上,或是您應用程式可存取的任何其他永久儲存空間。 + +其他應用程式可透過內容供應程式查詢或甚至修改資料 (如果內容供應程式允許這麼做的話)。 +例如,Android 系統會提供用於管理使用者聯絡資訊的內容供應程式。 +因此,任何具備適當權限的應用程式均可查詢內容供應程式的一部分 (例如 {@link +android.provider.ContactsContract.Data}),以便讀取及寫入有關特定使用者的相關資訊。 + + +<p>此外,內容供應程式也可用於讀取及寫入只有您應用程式能存取的不公開資料。 +例如,<a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> 範例應用程式可使用內容供應程式儲存記事。 +</p> + +<p>內容供應程式是實作成 {@link android.content.ContentProvider} 的子類別,而且必須實作一組標準 API 以便讓其他應用程式執行交易。 + +如需詳細資訊,請參閱<a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>開發人員指南。 +</p> +</dd> + + +<dt><b>廣播接收器</b></dt> + +<dd>單一 <i>廣播接收器</i> 是一種元件,可回應整個系統的廣播通知。 +大多數廣播都是由系統所發出 — 例如,系統會發出廣播來通知使用者螢幕已關閉、電池電量不足,或相片已拍攝完成。此外,應用程式也可發出廣播 — 例如說發出廣播來通知其他應用程式特定資料已下載到裝置,可供它們使用。 + + +雖然廣播接收器無法顯示使用者介面,但它們可<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">建立狀態列通知</a>,告訴使用者發生了廣播事件。 + +具體而言,廣播接收器只是其他元件的「閘道」,用於執行極少量的工作。 +例如,廣播接收器可啟動服務依據事件執行特定工作。 + + +<p>廣播接收器是實作為成 {@link android.content.BroadcastReceiver} 的子類別,而每個廣播都是由 {@link android.content.Intent} 物件所發出。 +如需詳細資訊,請參閱 {@link android.content.BroadcastReceiver} 類別。 +</p> +</dd> + +</dl> + + + +<p>Android 系統設計的一項特色是,任何應用程式都可啟動其他應用程式的元件。 +例如,假設您想讓使用者透過裝置相機拍攝相片,您的應用程式可利用其他具備相關功能的應用程式 (如果有的話) 以達到這個目標,這樣您就不必自行建立用於拍攝相片的 Activity。 + +您不需要納入或連結相機應用程式的程式碼,只要啟動相機應用程式中用於拍攝像片的 Activity 即可。 + + +啟動相關 Activity 後,系統就會將相片傳回您的應用程式供您使用。對於使用者而言,相機就宛如是您應用程式的一部分。 +</p> + +<p>當系統啟動某個元件後,就會啟動該應用程式的處理程序 (如果該應用程式目前並非處於執行中狀態) 並且呼叫該元件所需的類別。 +例如,假設您的應用程式啟動相機應用程式中用於拍攝相片的 Activity,則該 Activity 會在隸屬於相機應用程式的處理程序中執行,而不是您應用程式的處理程序。因此,與大多數其他系統的應用程式不同,Android 應用程式沒有單一進入點 (例如沒有 {@code main()} 函式)。 + + + +</p> + +<p>系統是在個別處理程序 (具備檔案權限,可限制其他應用程式存取) 中執行每款應用程式,因此您的應用程式無法直接啟動其他應用程式的元件,不過 Android 系統可以。 + +基於這個原因,如要啟動其他應用程式的元件,您必須向指定「意圖」<em></em>的系統發送訊息,以啟動特定元件。 + +系統隨後會為您啟用所需的元件。</p> + + +<h3 id="ActivatingComponents">啟用元件</h3> + +<p>4 種元件類型的其中 3 種 — Activity、服務和廣播接收器 — 是透過「意圖」<em></em>這種非同步訊息啟用。意圖會在執行階段將元件與彼此繫結 (您可以意圖想成要求其他元件進行動作的傳令員),不論元件是否屬於您的應用程式。 + + + +</p> + +<p>意圖是使用 {@link android.content.Intent} 物件建立而成,該物件可定義訊息來啟用特定元件或特定「類型」<em></em>的元件 — 意圖可以採取明確或隱含設定。 + +</p> + +<p>針對 Activity 和服務,意圖會定義要執行的動作 (例如「查看」或「傳送」某項目),並且可能會指定執行動作的目標資料 URI (以及通知要啟用的元件)。 + +例如,意圖可傳達某 Activity 的要求,顯示圖片或開啟網頁。 +在某些情況下,您可以啟動 Activity 來接收結果,此時該 Activity 也會傳回{@link android.content.Intent} 的結果 (例如,您可以發出意圖讓使用者挑選聯絡人資料,並將該資訊傳回給您 — 傳回意圖會包含指向所選聯絡人的 URI )。 + + + +</p> + +<p>針對廣播接收器,意圖只會定義要廣播的通知 (例如,用於通知裝置電量不足的廣播只會包含指出「電池電量不足」的已知動作字串)。 + +</p> + +<p>其他元件類型和內容供應程式並非由意圖所啟用,而是在受 {@link android.content.ContentResolver} 發出的要求所指定時由系統啟用。 +內容解析程式可處理內容供應程式的所有直接交易,因此與供應程式進行交易的元件不必呼叫 {@link +android.content.ContentResolver} 物件的方法。 + +這樣會在內容供應程式與要求資訊 (基於安全目的) 之間保留抽象層。 +</p> + +<p>用於啟用各種元件的方法有以下幾種:</p> +<ul> + <li>將 {@link android.content.Intent} 傳送到 {@link android.content.Context#startActivity +startActivity()} 或 {@link android.app.Activity#startActivityForResult startActivityForResult()} (如果您想讓 Activity 傳回結果的話) 即可啟動 Activity (或是指派新工作給 Activity)。 + +</li> + <li>將 {@link android.content.Intent} 傳送到 {@link android.content.Context#startService +startService()} 即可啟動服務 (或是指派新指示給正在執行的服務)。 +或者,您也可以將 {@link android.content.Intent} 傳送到 +{@link android.content.Context#bindService bindService()} 來繫結至服務。</li> + <li>將 {@link android.content.Intent} 傳送到 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、{@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} 或 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()} 等方法即可啟用廣播。</li> + <li>對 {@link android.content.ContentResolver} 呼叫{@link +android.content.ContentProvider#query query()} 即可查詢內容供應程式。</li> +</ul> + +<p>如要進一步瞭解如何使用意圖,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 +如要進一步瞭解如何啟用特定元件,請參閱下列說明文件: +<a href="{@docRoot}guide/components/activities.html">Activity</a>、<a href="{@docRoot}guide/components/services.html">服務</a>、{@link +android.content.BroadcastReceiver} 和<a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>。</p> + + +<h2 id="Manifest">宣示說明檔案</h2> + +<p>Android 系統必須先讀取應用程式的 {@code AndroidManifest.xml} 檔案 (「宣示說明」檔案) 確認應用程式元件確實存在,才能啟動該元件。 + +您的應用程式必須在這個檔案中宣告本身的所有元件,而該檔案必須位於應用程式專案目錄的根目錄。 +</p> + +<p>除了宣告應用程式的元件以外,宣示說明還可以進行許多工作,包括: +</p> +<ul> + <li>識別應用程式所需的任何使用者權限,例如網際網路存取權或使用者合約的讀取存取權。 +</li> + <li>根據應用程式使用的 API,宣告應用程式所需的最低 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API 級別</a>。 +</li> + <li>宣告應用程式所使用或所需的硬體和軟體,例如相機、藍牙服務或多點觸控螢幕。 +</li> + <li>應用程式需要連結的 API 程式庫 (除了 Android 架構 API 以外),例如 <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google 地圖程式庫</a>。 + +</li> + <li>還有其他工作</li> +</ul> + + +<h3 id="DeclaringComponents">宣告元件</h3> + +<p>宣告說明的主要工作是將應用程式的元件告知系統。例如,宣告說明檔案可用如下方式宣告 Activity: + </p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<manifest ... > + <application android:icon="@drawable/app_icon.png" ... > + <activity android:name="com.example.project.ExampleActivity" + android:label="@string/example_label" ... > + </activity> + ... + </application> +</manifest></pre> + +<p>在 <code><a +href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code>元素中,{@code android:icon} 屬性會指向識別應用程式的圖示資源。 + +</p> + +<p>而在 <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素中, +{@code android:name} 屬性會指定 {@link +android.app.Activity} 子類別的完整類別名稱,{@code android:label} 屬性則會指定要當作 Activity 的使用者可見標籤使用的字串。 +</p> + +<p>您必須用以下方式宣告所有應用程式元件:</p> +<ul> + <li><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>:Activity 適用的元素 +</li> + <li><code><a +href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code>:服務適用的元素 +</li> + <li><code><a +href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code>:廣播接收器適用的元素 +</li> + <li><code><a +href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code>:內容供應程式適用的元素 +</li> +</ul> + +<p>系統看不到您納入來源但未在宣示說明中宣告的 Activity、服務和內容供應程式,因此系統無法執行這些項目。 +不過,您可在宣示說明宣告廣播接收器,或是透過程式碼以動態方式建立廣播接收器 (將廣播接收器建立為 +{@link android.content.BroadcastReceiver} 物件),然後呼叫 {@link android.content.Context#registerReceiver registerReceiver()} +向系統註冊廣播接收器。 + +</p> + +<p>如要進一步瞭解如何為應用程式建立宣示說明檔案,請參閱 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml 檔案</a>。 + </p> + + + +<h3 id="DeclaringComponentCapabilities">宣告元件功能</h3> + +<p>如<a href="#ActivatingComponents">啟用元件</a>所述,您可以使用 +{@link android.content.Intent} 來啟動 Activity、服務和廣播接收器。如要這麼做,請在意圖中明確指定目標元件 (使用元件類別名稱)。 +不過,意圖最大的功能在於「隱含意圖」<em></em>的概念。 +隱含意圖可簡單描述要執行的動作類型 (或是執行動作的資料依據) 以及讓系統在裝置中找出可執行動作的元件,然後加以啟動。 + + +如果意圖指出有多個元件可執行動作,則使用者可選取要使用的元件。 +</p> + +<p>系統會比對接受到的意圖與裝置上其他應用程式的宣示說明檔案中提供的意圖篩選器,藉此識別可回應意圖的元件。 +<i></i> +</p> + +<p>在應用程式的宣示說明中宣告 Activity 時,您可視需要納入宣告 Activity 功能的意圖篩選器,以便讓 Activity 可回應其他應用程式的意圖。 + +您可以為元件宣告意圖篩選器,方法是將 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> 元素新增為元件宣告元素的子元素。 +</p> + +<p>例如,假設您以用於撰寫新郵件的 Activity 建置電子郵件應用程式,您可以下列方式宣告意圖篩選器來回應「傳送」意圖 (藉此傳送新郵件): +</p> +<pre> +<manifest ... > + ... + <application ... > + <activity android:name="com.example.project.ComposeEmailActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <data android:type="*/*" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> +</manifest> +</pre> + +<p>接著,如果其他應用程式透過 {@link +android.content.Intent#ACTION_SEND} 動作建立了意圖並傳送到 {@link android.app.Activity#startActivity +startActivity()},系統就可能會啟動您的 Activity 讓使用者撰寫及傳送郵件。 +</p> + +<p>如要進一不瞭解如何建立意圖篩選器,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 +</p> + + + +<h3 id="DeclaringRequirements">宣告應用程式需求</h3> + +<p>並非所有搭載 Android 作業系統的裝置都能提供完整功能。 +為了避免使用者在缺少應用程式所需功能的裝置上安裝您的應用程式,請務必在宣示說明檔案中宣告裝置和軟體需求,清楚定義您的應用程式支援的裝置類型。 + + +大多數宣告僅供使用者參考,系統無法讀取,但 Google Play 等外部服務可讀取這些宣示,以便在使用者透過自己的裝置搜尋應用程式時提供篩選功能。 + +</p> + +<p>例如,假設您的應用程式需要相機且採用 Android 2.1 (<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API 級別</a> 7) 導入的 API,建議您用下列方式在宣示說明檔案中宣告這些需求: +</p> + +<pre> +<manifest ... > + <uses-feature android:name="android.hardware.camera.any" + android:required="true" /> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" /> + ... +</manifest> +</pre> + +<p>如此一來,「沒有」<em></em>相機且搭載 Android 2.1「以下版本」<em></em>的裝置就無法從 Google Play 安裝您的應用程式。 +</p> + +<p>不過,您也可以宣告您的應用程式會使用相機,但相機並非應用程式的「必要」<em></em>配備。 +在這種情況下,應用程式的 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#required">{@code required}</a>屬性必須設為 {@code "false"},而且應用程式必須在執行階段檢查裝置是否具備相機,並且視需要停用任何相機功能。 + +</p> + +<p>如要進一步瞭解如何管理應用程式與不同裝置的相容性,請參閱<a href="{@docRoot}guide/practices/compatibility.html">裝置相容性</a>。 + +</p> + + + +<h2 id="Resources">應用程式資源</h2> + +<p>Android 應用程式是以程式碼等其他要素開發而成 — 例如圖片、音訊檔案以及與應用程式視覺效果相關的其他資源。例如,您必須使用 XML 檔案定義 Activity 使用者介面的動畫、選單、樣式、顏色以及版面配置。 + + +使用應用程式資源可協助更新應用程式的各種特性,而不必修改程式碼 — 或是提供多組替代資源 — 藉此針對各種裝置設定 (例如不同的語言和螢幕大小) 最佳化您的應用程式。 + + +</p> + +<p>針對您在 Android 專案中加入的所有資源,SDK 建置工具會定義一個整數 ID,讓您用於從應用程式的程式碼或 XML 中定義的其他資源參照特定資源。 + +例如,假設您的應用程式含有名為 {@code +logo.png} 的圖片檔案 (儲存在 {@code res/drawable/} 目錄中),SDK 工具會產生名為 {@code R.drawable.logo} 的資源 ID,讓您用於參照圖片並將其插入使用者介面。 + +</p> + +<p>提供原始碼以外資源的一個重點是針對不同的裝置設定提供替代資源。 + +例如,您可以在 XML 中定義使用者介面字串,藉此將字串翻譯成其他語言,以及將這些字串儲存成個別檔案。 +接著,視您附加到資源目錄名稱的語言「修飾語」<em></em> (例如代表法文字串值的 {@code res/values-fr/}),以及使用者的語言設定而定,Android 系統會為您的 UI 套用適當的語言字串。 + + +</p> + +<p>Android 針對替代資源支援各種「修飾語」<em></em>。修飾語是一個簡短字串;您可在資源目錄名稱中加入修飾語,藉此定義應使用這些資源的裝置設定。 + +例如,您通常需要為 Activity 建立多種版面配置 (視裝置螢幕的方向和大小而定)。 + +例如,假設裝置螢幕的方向為縱向 (直版),版面配置的按鈕就必須以垂直方向排列;假設裝置螢幕的方向為橫向 (寬版),則版面配置的按鈕就必須以水平方向排列。 + +如要根據螢幕方向變更版面配置,請建立兩種版面配置,然後為每個版面配置目錄名稱套用適當的修飾語。 + +如此系統就會根據目前的裝置方向,自動套用適當的版面配置。 +</p> + +<p>如要進一步瞭解您可在應用程式中加入的資源類型,以及如何針對不同的裝置設定建立替代資源,請詳閱<a href="{@docRoot}guide/topics/resources/providing-resources.html">提供資源</a>。 +</p> + + + +<div class="next-docs"> +<div class="col-6"> + <h2 class="norule">繼續閱讀有關下列主題的說明文章:</h2> + <dl> + <dt><a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a> + </dt> + <dd>說明如何使用 {@link android.content.Intent} API 來啟用應用程式元件 (例如 Activity 和服務),以及如何將應用程式元件提供給其他應用程式使用。 + +</dd> + <dt><a href="{@docRoot}guide/components/activities.html">Activity</a></dt> + <dd>說明如何建立 {@link android.app.Activity} 類別執行個體,以便讓應用程式的使用者介面提供不同內容。 +</dd> + <dt><a href="{@docRoot}guide/topics/resources/providing-resources.html">提供資源</a></dt> + <dd>說明 Android 應用程式如何區別應用程式資源與程式碼,包括如何針對特定裝置設定供替代資源。 + + + </dd> + </dl> +</div> +<div class="col-6"> + <h2 class="norule">您可能也會想瞭解下列主題:</h2> + <dl> + <dt><a href="{@docRoot}guide/practices/compatibility.html">裝置相容性</a></dt> + <dd>說明 Android 如何在各種裝置上運作,以及如何針對各個裝置最佳化您的應用程式,或針對不同裝置限制應用程式提供的功能。 + +</dd> + <dt><a href="{@docRoot}guide/topics/security/permissions.html">系統權限</a></dt> + <dd>說明 Android 如何運用系統權限規定應用程式必須取得使用者同意才能使用特定 API。 +</dd> + </dl> +</div> +</div> + diff --git a/docs/html-intl/intl/zh-tw/guide/components/index.jd b/docs/html-intl/intl/zh-tw/guide/components/index.jd new file mode 100644 index 000000000000..f34c7120e8ad --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=應用程式元件 +page.landing=true +page.landing.intro=Android 的應用程式架構可讓您使用一系列可重複使用的元件,建立內容豐富的新穎應用程式。本節說明如何建置元件來定義應用程式的設計模組,以及如何使用意圖連結這些元件。 +page.metaDescription=Android 的應用程式架構可讓您使用一系列可重複使用的元件,建立內容豐富的新穎應用程式。本節說明如何建置元件來定義應用程式的設計模組,以及如何使用意圖連結這些元件。 +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +<div class="landing-docs"> + + <div class="col-6"> + <h3>部落格文章</h3> + + <a href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html"> + <h4>使用 DialogFragments</h4> + <p>本文示範如何搭配 v4 支援程式庫 (可針對 Honeycomb 以下版本裝置提供向下相容性支援) 使用 DialogFragments 來顯示簡易的編輯對話方塊,以及透過介面將結果回傳給呼叫「Activity」。</p> + </a> + + <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + <h4>適用於各種裝置的片段</h4> + <p>我們於今日推出的靜態程式庫可列出相同的 Fragments API (以及新的 LoaderManager 和些許其他類別),讓與 Android 1.6 以下版本相容的應用程式能夠使用片段來建立相容於平板電腦的使用者介面。 </p> + </a> + + <a href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html"> + <h4>運用多個執行緒來提升效能</h4> + <p>建立回應式應用程式的建議做法是,最小化您的主要 UI 執行緒所執行的工作數。 +方法是讓應用程式透過其他執行緒處理任何所需執行時間偏長的工作。 +</p> + </a> + </div> + + <div class="col-6"> + <h3>培訓</h3> + + <a href="http://developer.android.com/training/basics/activity-lifecycle/index.html"> + <h4>管理 Activity 生命週期</h4> + <p>本課程說明每個「Activity」執行個體都會接收的生命週期重要回呼方法,並且說明如何使用這些方法,讓 Activity 的運作能符合使用者的預期,以及讓 Activity 在不需要系統資源時不耗用這類資源。 + +</p> + </a> + + <a href="http://developer.android.com/training/basics/fragments/index.html"> + <h4>使用片段建置動態 UI</h4> + <p>本課程示範如何使用片段來設計動態使用者體驗,以及如何針對各種螢幕大小的裝置最佳化您的應用程式使用者體驗,同時讓應用程式繼續支援搭載 Android 1.6 等舊版作業系統的裝置。 + +</p> + </a> + + <a href="http://developer.android.com/training/sharing/index.html"> + <h4>分享內容</h4> + <p>本課程說明幾個使用 Intent API 和 Action Provider 物件在不同應用程式間傳送及接收內容的常見方法。 +</p> + </a> + </div> + +</div> diff --git a/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd b/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd new file mode 100644 index 000000000000..d3edac3f817a --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=意圖和意圖篩選器 +page.tags="IntentFilter" +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> + <li><a href="#Types">意圖類型</a></li> + <li><a href="#Building">建置意圖</a> + <ol> + <li><a href="#ExampleExplicit">明確意圖範例</a></li> + <li><a href="#ExampleSend">隱含意圖範例</a></li> + <li><a href="#ForceChooser">強制顯示應用程式選擇器</a></li> + </ol> + </li> + <li><a href="#Receiving">接收隱含意圖</a> + <ol> + <li><a href="#ExampleFilters">篩選器範例</a></li> + </ol> + </li> + <li><a href="#PendingIntent">使用待處理意圖</a></li> + <li><a href="#Resolution">意圖解析</a> + <ol> + <li><a href="#ActionTest">動作測試</a></li> + <li><a href="#CategoryTest">類別測試</a></li> + <li><a href="#DataTest">資料測試</a></li> + <li><a href="#imatch">意圖比對</a></li> + </ol> + </li> +</ol> + +<h2>另請參閱</h2> +<ol> +<li><a href="{@docRoot}training/basics/intents/index.html">與其他應用程式互動</a></li> +<li><a href="{@docRoot}training/sharing/index.html">分享內容</a></li> +</ol> + +</div> +</div> + + + + +<p>{@link android.content.Intent} 是可用來向另一個<a href="{@docRoot}guide/components/fundamentals.html#Components">應用程式元件</a>要求動作的傳訊物件。 + +雖然意圖有幾種方式可加速元件間的通訊,但共有三種基本使用案例: +</p> + +<ul> +<li><b>如何啟動 Activity:</b> +<p>{@link android.app.Activity} 代表應用程式中的單一畫面。您可以將 {@link android.content.Intent} 傳送至 {@link android.content.Context#startActivity startActivity()} 來啟動 +{@link android.app.Activity} 的新執行個體。 +{@link android.content.Intent} 可描述要啟動的 Activity 並攜帶任何必要資料。 +</p> + +<p>如果您想要在 Activity 完成時收到結果, +請呼叫 {@link android.app.Activity#startActivityForResult +startActivityForResult()}。Activity 的 {@link android.app.Activity#onActivityResult onActivityResult()}回呼中的個別 {@link android.content.Intent} 物件,就是 Activity 收到的結果。 + +如需詳細資訊,請參閱 <a href="{@docRoot}guide/components/activities.html">Activity</a> 指南。 +</p></li> + +<li><b>如何啟動服務:</b> +<p>{@link android.app.Service} 是可以在背景中執行操作的元件,但沒有使用者介面。 +您可以將 {@link android.content.Intent} 傳送至 +{@link android.content.Context#startService startService()} 來啟動服務以執行一次性操作 (例如下載檔案)。 +{@link android.content.Intent} 可描述要啟動的服務並攜帶任何必要資料。 +</p> + +<p>如果服務是採用主從介面設計,您可以將 {@link android.content.Intent} 傳送至 {@link +android.content.Context#bindService bindService()} 來繫結至另一個元件的服務。</code> +如需詳細資訊,請參閱<a href="{@docRoot}guide/components/services.html">服務</a>指南。</p></li> + +<li><b>如何傳送廣播:</b> +<p>廣播是指任何應用程式都可接收的訊息。系統會傳送各種系統事件廣播,例如系統開機或裝置開始充電。 +您可以將 {@link android.content.Intent} 傳送至 {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、 +{@link android.content.Context#sendOrderedBroadcast(Intent, String) 或{@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()},以向其他應用程式傳送廣播。 + + +</p> +</li> +</ul> + + + + +<h2 id="Types">意圖類型</h2> + +<p>意圖類型分為兩種:</p> + +<ul> +<li><b>明確意圖</b>:可依名稱 (完整類別名稱) 指定要啟動的元件。 +一般情況下,您會使用明確意圖啟動您應用程式中的元件,這是因為您知道 Activity 的類別名稱或您想要啟動的服務。 +例如,為回應使用者動作而啟動新的 Activity,或啟動服務以在背景下載檔案。 + +</li> + +<li><b>隱含意圖</b>:不會指定特定元件,而會宣告要執行的一般動作,讓另一個應用程式的元件來處理它。 +例如,如果您想要向使用者顯示地圖上的某個位置,可以使用隱含意圖,要求另一個支援應用程式在地圖上顯示指定的位置。 + +</li> +</ul> + +<p>當您建立明確意圖以啟動 Activity 或服務時,系統會立即啟動 +{@link android.content.Intent} 物件中指定的應用程式元件。</p> + +<div class="figure" style="width:446px"> +<img src="{@docRoot}images/components/intent-filters@2x.png" width="446" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>說明如何透過系統傳送隱含意圖以啟動另一個 Activity:<b>[1]</b> Activity A <em></em> 會建立含有動作描述的{@link android.content.Intent} 並傳送至 {@link +android.content.Context#startActivity startActivity()}。<b>[2]</b> Android 系統會搜尋所有應用程式,以找出符合該意圖的意圖篩選器。 + + +找到相符項目時,<b>[3]</b> 系統會呼叫其 {@link android.app.Activity#onCreate onCreate()} 方法,並將 {@link android.content.Intent} 傳送給它來啟動相符的 Activity (Activity B<em></em>)。 + + +</p> +</div> + +<p>當您建立隱含意圖時,Android 系統會比較意圖內容和裝置上其他應用程式的<a href="{@docRoot}guide/topics/manifest/manifest-intro.html">宣示說明檔案</a>中宣告的「意圖篩選器」<em></em>,以找出要啟動的適當元件。 + +如果意圖和意圖篩選器相符,系統會啟動該元件,並將 {@link android.content.Intent} 物件傳送給它。 +如果有多個意圖篩選器符合意圖,系統會顯示對話方塊,供使用者挑選要使用的應用程式。 +</p> + +<p>意圖篩選器是應用程式宣示說明檔案中的運算式,可指定元件要接收的意圖類型。 + +例如,藉由宣告 Activity 的意圖篩選器,可讓其他應用程式使用特定意圖類型直接啟動您的 Activity。 +同樣地,如果您「不」<em></em>為 Activity 宣告任何意圖篩選器,就只能以明確意圖啟動它。 + +</p> + +<p class="caution"><strong>注意:</strong>為了確保您的應用程式安全,請一律使用明確意圖啟動 {@link android.app.Service},並且不要宣告服務的意圖篩選器。 + +使用隱含意圖啟動服務會危害安全性,原因在於您無法確定哪個服務會回應意圖,而且使用者無法得知系統會啟動哪項服務。 + +從 Android 5.0 (API 級別 21) 開始,如果您使用隱含意圖呼叫 {@link android.content.Context#bindService bindService()},系統都會擲回例外狀況。 + +</p> + + + + + +<h2 id="Building">建置意圖</h2> + +<p>{@link android.content.Intent} 物件攜帶 Android 系統用來判斷要啟動哪個元件的資訊 (例如應接收意圖的確切元件名稱或元件類別),再加上接收者元件用以適當執行動作的資訊 (例如要執行的動作和據以執行的資料)。 + + +</p> + + +<p>{@link android.content.Intent} 包含的主要資訊如下:</p> + +<dl> + +<dt><b>元件名稱</b></dt> +<dd>要啟動元件的名稱。 + +<p>雖可選擇是否使用,但這卻是讓意圖「明確」<b></b>的重要資訊,表示意圖只能傳送至元件名稱所定義的應用程式元件。 + +如果不使用元件名稱,意圖會是「隱含」<b></b>的,因此系統會根據其他意圖資訊來決定哪個元件應接收意圖 (例如動作、資料及類別 — 如下所述)。 + +如果您需要啟動應用程式中的特定元件,就應該指定元件名稱。 +</p> + +<p class="note"><strong>注意:</strong>啟動 {@link android.app.Service} 時,請務必指定元件名稱。 +<strong></strong>否則,您無法確定哪個服務會回應意圖,而且使用者無法得知系統會啟動哪項服務。 +</p> + +<p>{@link android.content.Intent} 的這個欄位是 +{@link android.content.ComponentName} 物件,您可以使用目標元件的完整類別名稱加以指定,包括應用程式的封裝名稱。例如, + +{@code com.example.ExampleActivity}。您可以使用 {@link +android.content.Intent#setComponent setComponent()}、{@link android.content.Intent#setClass +setClass()}、{@link android.content.Intent#setClassName(String, String) setClassName()} 或 +{@link android.content.Intent} 建構函式來設定元件名稱。</p> + +</dd> + +<p><dt><b>動作</b></dt> +<dd>以字串指定要執行的一般動作 (例如「檢視」<em></em>或「挑選」<em></em>)。 + +<p>就廣播意圖而言,這是指已發生且系統回報的動作。動作大半決定其餘意圖的建構方式 — 特別是資料與額外資料中包含的項目。 + + + +<p>您可以在應用程式內指定自己的動作以供意圖使用 (或由其他應用程式用來呼叫應用程式中的元件),但您應使用 {@link android.content.Intent} 類別或其他架構類別定義的動作常數。 + +以下是可啟動 Activity 的一些常見動作: +</p> + +<dl> +<dt>{@link android.content.Intent#ACTION_VIEW}</dt> + <dd>當您有一些資訊可讓 Activity 向使用者顯示時,例如要在圖庫應用程式檢視的相片或要在地圖應用程式檢視的地址,就可以透過 {@link android.content.Context#startActivity startActivity()} 使用意圖中的這個動作。 + + +</dd> + +<dt>{@link android.content.Intent#ACTION_SEND}</dt> + <dd>也稱為「分享」意圖,當您有一些資料可供使用者透過其他應用程式分享時,例如電子郵件應用程式或社交分享應用程式,才應透過 {@link android.content.Context#startActivity startActivity()} 使用意圖中的這個動作。 + +</dd> +</dl> + +<p>如要進一步瞭解定義一般動作的常數,請參閱 {@link android.content.Intent} 類別參考文件。 +其他動作則是在 Android 架構的其他位置完成定義,例如可在系統設定應用程式中開啟特定畫面的動作位在 {@link android.provider.Settings} 中。 + +</p> + +<p>您可以透過 {@link android.content.Intent#setAction +setAction()} 或 {@link android.content.Intent} 建構函式來指定意圖的動作。</p> + +<p>如果您定義自己的動作,請務必加入您應用程式的封裝名稱做為前置詞。 +例如:</p> +<pre>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</pre> +</dd> + +<dt><b>資料</b></dt> +<dd>URI ({@link android.net.Uri} 物件) 可參考據以執行的資料和/或該資料的 MIME 類型。 +提供的資料類型通常是由意圖的動作控制。例如,如果動作是 {@link android.content.Intent#ACTION_EDIT},資料就應包含欲編輯文件的 URI。 + + + +<p>建立意圖時,除了意圖的 URI 以外,請務必指定資料類型 (其 MIME 類型)。 + + +例如,能夠顯示影像的 Activity 可能無法播放音訊檔案,即使有類似的 URI 格式。 +因此,指定資料的 MIME 格式可協助 Android 系統找出最適合接收意圖的元件。 + +不過 — 尤其是當資料指出資料位在裝置何處且受 +{@link android.content.ContentProvider} 控制讓系統看見資料 MIME 類型的 +{@code content:} URI 時,有時能夠從 URI 推論出 MIME 類型。</p> + +<p>如果您只想設定資料 URI,請呼叫 {@link android.content.Intent#setData setData()}。 +如要設定 MIME 類型,請呼叫 {@link android.content.Intent#setType setType()}。您還可以視需要利用 +{@link +android.content.Intent#setDataAndType setDataAndType()} 明確設定兩者。</p> + +<p class="caution"><strong>注意:</strong>如果您想同時設定 URI 與 MIME 類型,「請勿」<strong></strong>呼叫 {@link android.content.Intent#setData setData()} 和 +{@link android.content.Intent#setType setType()},原因是這兩者會抵銷彼此的值。 +請務必使用 {@link android.content.Intent#setDataAndType setDataAndType()} 來設定 URI 與 MIME 類型。 + +</p> +</dd> + +<p><dt><b>類別</b></dt> +<dd>字串當中包含哪種元件應處理意圖的其他相關資訊。 +意圖中可放置的類別描述數目並沒有限制,但大多數意圖都不需要類別。 +以下是一些常見類別: + + +<dl> +<dt>{@link android.content.Intent#CATEGORY_BROWSABLE}</dt> + <dd>目標 Activity 允許自己由網頁瀏覽器啟動,以顯示連結所參照的資料 — 例如影像或電子郵件訊息。 + + </dd> +<dt>{@link android.content.Intent#CATEGORY_LAUNCHER}</dt> + <dd>Activity 是工作的初始 Activity,而且列在系統的應用程式啟動器中。 + + </dd> +</dl> + +<p>如需類別的完整清單,請參閱 {@link android.content.Intent} 類別描述。 +</p> + +<p>您可以使用 {@link android.content.Intent#addCategory addCategory()} 來指定類別。</p> +</dd> +</dl> + + +<p>以上列出的屬性 (元件名稱、動作、資料及類別) 代表意圖的定義特性。 +藉由讀取這些屬性,Android 系統就能分析出應啟動的應用程式元件。 +</p> + +<p>不過,意圖還能攜帶其他不影響如何將它解析成應用程式元件的資訊。 +意圖還能提供:</p> + +<dl> +<dt><b>額外資料</b></dt> +<dd>鍵值對當中攜帶完成要求動作所需的其他資訊。 +和有些動作會使用特定種類的資料 URI 一樣,有些動作也會使用特定的額外資料。 + +<p>您可以使用各種 {@link android.content.Intent#putExtra putExtra()}方法來新增額外資料,每種方法都接受兩個參數:索引鍵名稱與值。 + + +您也可以使用所有的額外資料建立 {@link android.os.Bundle} 物件,再透過 {@link +android.content.Intent#putExtras putExtras()} 將 {@link android.content.Intent} 插入 {@link android.os.Bundle}。</p> + +<p>例如,建立意圖以使用 +{@link android.content.Intent#ACTION_SEND} 來傳送電子郵件時,您可以利用 +{@link android.content.Intent#EXTRA_EMAIL} 索引鍵指定「收件者」,並使用 +{@link android.content.Intent#EXTRA_SUBJECT} 索引鍵指定「主旨」。</p> + +<p>{@link android.content.Intent} 類別指定許多用於標準化資料類型的 +{@code EXTRA_*} 常數。如果您需要宣告自己的額外資料索引鍵 (用於您應用程式接收的意圖),請務必加入您應用程式的封裝名稱做為前置詞。 + +例如:</p> +<pre>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</pre> +</dd> + +<dt><b>旗標</b></dt> +<dd>{@link android.content.Intent} 類別中定義的旗標,可當成意圖的中繼資料使用。 +旗標可指示 Android 系統如何啟動 Activity (例如,Activity 屬於哪個 +<a href="{@docRoot}guide/components/tasks-and-back-stack.html">工作</a>) 以及如何處理已啟動的 Activity (例如,它是否屬於最近 Activity 清單)。 + + + +<p>如需詳細資訊,請參閱 {@link android.content.Intent#setFlags setFlags()} 方法。</p> +</dd> + +</dl> + + + + +<h3 id="ExampleExplicit">明確意圖範例</h3> + +<p>您用來啟動特定應用程式元件的就是明確意圖,例如應用程式中的特定 Activity 或服務。如要建立明確意圖,請定義 + +{@link android.content.Intent}物件的元件名稱 — 其他意圖屬性均為選用性質。 +</p> + +<p>例如,您在應用程式中建置稱為 {@code DownloadService} 的服務,其設計為從網頁下載檔案,您可以使用下列程式碼來啟動該服務: +</p> + +<pre> +// Executed in an Activity, so 'this' is the {@link android.content.Context} +// The fileUrl is a string URL, such as "http://www.example.com/image.png" +Intent downloadIntent = new Intent(this, DownloadService.class); +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl)); +startService(downloadIntent); +</pre> + +<p>{@link android.content.Intent#Intent(Context,Class)} 建構函式提供應用程式 {@link android.content.Context} 與元件 ({@link java.lang.Class} 物件)。 + +因此,這個意圖會明確啟動應用程式中的 +{@code DownloadService} 類別。</p> + +<p>如要進一步瞭解如何建置及啟動服務,請參閱<a href="{@docRoot}guide/components/services.html">服務</a>指南。 +</p> + + + + +<h3 id="ExampleSend">隱含意圖範例</h3> + +<p>隱藏意圖指定的動作會呼叫裝置上任何能執行該動作的應用程式。 +當您的應用程式無法執行該動作,但其他應用程式或許能執行,而您想讓使用者挑選要使用的應用程式時,使用隱含意圖相當有用。 +</p> + +<p>例如,您有想讓使用者與其他人分享的內容,可使用 +{@link android.content.Intent#ACTION_SEND} 動作來建立意圖,以及新增可指定要分享內容的額外資料。 +當您使用該意圖呼叫 +{@link android.content.Context#startActivity startActivity()} 時,使用者就能挑選要用以分享內容的應用程式。 +</p> + +<p class="caution"><strong>注意:</strong>使用者可能會沒有「任何」<em></em> +應用程式可處理您傳送至 {@link android.content.Context#startActivity +startActivity()} 的隱含意圖。如果發生這種情況,呼叫會失敗且應用程式將會當機。如要確認 Activity 將可收到意圖,請在 +{@link android.content.Intent} 物件上呼叫 {@link android.content.Intent#resolveActivity +resolveActivity()}。如果結果不是 null,表示至少有一個應用程式能夠處理該意圖,可以放心呼叫 + +{@link android.content.Context#startActivity startActivity()}。如果結果為 null,表示您不應使用該意圖,可以的話您還必須將發出該意圖的功能停用。 + +</p> + + +<pre> +// Create the text message with a string +Intent sendIntent = new Intent(); +sendIntent.setAction(Intent.ACTION_SEND); +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); +sendIntent.setType("text/plain"); + +// Verify that the intent will resolve to an activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(sendIntent); +} +</pre> + +<p class="note"><strong>注意:</strong>在這種情況下,不會使用 URI,但會宣告意圖的資料類型,以指定額外資料所攜帶的內容。 +</p> + + +<p>呼叫 {@link android.content.Context#startActivity startActivity()} 時,系統會檢查所有安裝的應用程式,判斷哪些可以處理這種意圖 (含有 {@link android.content.Intent#ACTION_SEND}動作和攜帶「純文字」資料的意圖)。 + + +如果只有一個應用程式能夠處理,該應用程式會立即開啟並獲派該意圖。 +如果有多個 Activity 接受該意圖,系統會顯示對話方塊,供使用者挑選要使用的應用程式。 +</p> + + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt=""> + <p class="img-caption"><strong>圖 2.</strong>選擇器對話方塊。</p> +</div> + +<h3 id="ForceChooser">強制顯示應用程式選擇器</h3> + +<p>有多個應用程式均回應您的隱含意圖時,使用者可以選擇要使用的應用程式,並將該應用程式當成動作的預設選擇。 + +如果在執行動作時使用者希望之後都使用同一應用程式 (例如使用者偏好使用某個特定網頁瀏覽器開啟網頁,這項功能就非常實用。 + +</p> + +<p>不過,如果有多個應用程式能回應該意圖,而使用者每次都想使用不同的應用程式,您應該明確顯示選擇器對話方塊。 +選擇器對話方塊每次都會要求使用者選取要用於該動作的應用程式 (使用者無法為該動作選取預設應用程式)。 + +例如,當您的應用程式使用 {@link +android.content.Intent#ACTION_SEND} 動作執行「分享」時,使用者可能希望根據當下的情況使用不同的應用程式來分享,此時您應該一律使用選擇器對話方塊,如圖 2 所示。 +</p> + + + + +<p>如要顯示選擇器,請使用 {@link +android.content.Intent#createChooser createChooser()} 建立 {@link android.content.Intent},並將其傳送至 {@link +android.app.Activity#startActivity startActivity()}。例如:</p> + +<pre> +Intent sendIntent = new Intent(Intent.ACTION_SEND); +... + +// Always use string resources for UI text. +// This says something like "Share this photo with" +String title = getResources().getString(R.string.chooser_title); +// Create intent to show the chooser dialog +Intent chooser = Intent.createChooser(sendIntent, title); + +// Verify the original intent will resolve to at least one activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>以上範例會顯示對話方塊 (將回應意圖的應用程式清單傳送至 {@link +android.content.Intent#createChooser createChooser()} 方法),並使用提供的文字做為對話方塊的標題。 +</p> + + + + + + + + + +<h2 id="Receiving">接收隱含意圖</h2> + +<p>如要通知應用程式可接收的隱含內容,請在<a href="{@docRoot}guide/topics/manifest/manifest-intro.html">宣告說明檔案</a>中利用 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> 元素,針對您的每個應用程式元件宣告一或多個意圖篩選器。每個意圖篩選器都會根據意圖的動作、資料和類別,指定其接受的意圖類型。 + + + +只有在意圖通過您的其中一個意圖篩選器時,系統才會將隱含意圖傳送至您的應用程式元件。 +</p> + +<p class="note"><strong>注意:</strong>不論元件宣告的意圖篩選器為何,明確意圖一律會傳送至其目標。 +</p> + +<p>應用程式元件應為其所能執行的各個獨特工作宣告不同的篩選器。例如,圖片庫應用程式中的一個 Activity 可能會有兩個篩選器:一個用於檢視圖片的篩選器,以及另一個用於編輯圖片的篩選器。 + +當 Activity 啟動時,它會檢查 +{@link android.content.Intent} 並根據 +{@link android.content.Intent} 中的資訊決定如何運作 (例如,是否要檢視編輯器控制項)。</p> + +<p>每個意圖篩選器都是由 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> 元素定義在應用程式的宣示說明檔案中,以巢狀方式置於對應的應用程式元件 (例如,<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素)。 + + +在 <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> 內,您可以使用以下三個元素當中的一或多個元素,指定要接受的意圖類型: + +</p> + +<dl> +<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt> + <dd>在 {@code name} 屬性中,宣告接受的意圖動作。值必須是動作的常值字串值,不得為類別常數。 +</dd> +<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt> + <dd>使用一或多項屬性指定資料 URI ( +<code>scheme</code>、<code>host</code>、<code>port</code>、 +<code>path</code> 等) 與 MIME 類型的各層面,以宣告接受的資料類型。</dd> +<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt> + <dd>在 {@code name} 屬性中,宣告接受的意圖類別。值必須是動作的常值字串值,不得為類別常數。 + + + <p class="note"><strong>注意:</strong>如要接收隱含意圖,您「必須」<strong></strong>在意圖篩選器中納入 {@link android.content.Intent#CATEGORY_DEFAULT} 類別。 + + +{@link android.app.Activity#startActivity startActivity()} 與 +{@link android.app.Activity#startActivityForResult startActivityForResult()} 方法對所有意圖進行處理時,就像已宣告 +{@link android.content.Intent#CATEGORY_DEFAULT} 類別一樣。 + 如果您未在意圖篩選器中宣告此類別,任何隱含意圖都不會解析為 Activity。 +</p> + </dd> +</dl> + +<p>例如,假設資料類型為文字,以下 Activity 宣告的意圖篩選器都可接受 +{@link android.content.Intent#ACTION_SEND} 意圖:</p> + +<pre> +<activity android:name="ShareActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p>想要建立包含多個 +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>、 +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 或 +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a> 執行個體的篩選器也沒關係。 +如果您這樣做,只需要確定該元件能處理這些篩選器元素的任何組合。 +</p> + +<p>當您想要處理多種意圖,但只限特定的動作、資料及類別類型組合時,您必須建立多個意圖篩選器。 +</p> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>限制存取元件</h2> +<p>使用意圖篩選器並無法安全地防止其他應用程式啟動您的元件。 +雖然意圖篩選器限制元件只能回應特定的隱含意圖,如果開發人員決定您的元件名稱,另一款應用程式還是有可能使用明確意圖來啟動您的應用程式元件。 + + +如果必須「限定只有您自己的應用程式」<em></em>才能啟動您的元件,請將該元件的 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#exported">{@code +exported}</a> 屬性設定為 {@code "false"}。 +</p> +</div> +</div> + +<p>藉由將意圖與三個元素個別比較,針對篩選器來測試隱含意圖。 +如要傳送至元件,意圖必須通過共三個測試。 + +如果無一相符,Android 系統就不會將意圖傳送至該元件。不過,由於元件可能會有多個意圖篩選器,未通過其中一個元件篩選器的意圖可能會通過另一個篩選器。 + + +如要進一步瞭解系統如何解析意圖,請參閱下文的<a href="#Resolution">意圖解析</a>。</p> + +<p class="caution"><strong>注意:</strong>如要避免一時疏忽而執行不同應用程式的 +{@link android.app.Service},請一律使用明確意圖啟動您自己的服務,同時不要宣告服務的意圖篩選器。 +</p> + +<p class="note"><strong>注意:</strong>對於所有 Activity,您都必須在宣示說明檔案中宣告意圖篩選器。 + +不過,廣播接收器的篩選器可以藉由呼叫 +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()} 進行動態註冊。您之後可以利用 {@link +android.content.Context#unregisterReceiver unregisterReceiver()} 來取消註冊接收器。這樣做可在您的應用程式執行時,讓應用程式只能在指定期間內接聽特定廣播。 + +</p> + + + + + + + +<h3 id="ExampleFilters">篩選器範例</h3> + +<p>如要進一步瞭解意圖篩選器行為,可以看看社交分享應用程式宣示說明檔案中的以下程式碼片段。 +</p> + +<pre> +<activity android:name="MainActivity"> + <!-- This activity is the main entry, should appear in app launcher --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> + +<activity android:name="ShareActivity"> + <!-- This activity handles "SEND" actions with text data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> + <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <action android:name="android.intent.action.SEND_MULTIPLE"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="application/vnd.google.panorama360+jpg"/> + <data android:mimeType="image/*"/> + <data android:mimeType="video/*"/> + </intent-filter> +</activity> +</pre> + +<p>第一個 Activity {@code MainActivity} 是應用程式的主要進入點 — 這個 Activity 會在使用者使用啟動器圖示初次啟動應用程式時開啟: +</p> +<ul> + <li>{@link android.content.Intent#ACTION_MAIN} 動作可指出這是主要進入點且預期沒有任何意圖資料。 +</li> + <li>{@link android.content.Intent#CATEGORY_LAUNCHER} 類別可指出此 Activity 的圖示應該放在系統的應用程式啟動器中。 +如果 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>元素未以 {@code icon} 指定圖示,系統會使用 <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> 元素中的圖示。 + +</li> +</ul> +<p>上述兩項必須成對,Activity 才會顯示在應用程式啟動器中。</p> + +<p>第二個 Activity {@code ShareActivity} 是用來加速分享文字與媒體內容。 +雖然使用者可能藉由從 {@code MainActivity} 來瀏覽它而進入此 Activity,但它們也能從發出隱含意圖 (符合兩個意圖篩選器之一) 的另一款應用程式直接進入 {@code ShareActivity}。 + +</p> + +<p class="note"><strong>注意:</strong> +<a href="https://developers.google.com/panorama/android/">{@code +application/vnd.google.panorama360+jpg}</a> MIME 類型是指定全景相片的特殊資料類型,您可以透過 +<a href="{@docRoot}reference/com/google/android/gms/panorama/package-summary.html">Google 全景</a> API 來處理。 +</p> + + + + + + + + + + + + + +<h2 id="PendingIntent">使用待處理意圖</h2> + +<p>{@link android.app.PendingIntent} 物件是環繞 {@link +android.content.Intent} 物件的包裝函式。{@link android.app.PendingIntent} 的主要用途是將權限授予外部應用程式,以便使用包含的 {@link android.content.Intent},有如從應用程式自己的程序執行一般。 + + +</p> + +<p>待處理意圖的主要使用案例包括:</p> +<ul> + <li>宣告當使用者透過您的<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">通知</a>執行動作時要執行的意圖 (Android 系統的 {@link android.app.NotificationManager} 會執行 {@link android.content.Intent})。 + + + <li>宣告當使用者透過您的<a href="{@docRoot}guide/topics/appwidgets/index.html">應用程式小工具</a>執行動作時要執行的意圖 (主螢幕應用程式會執行 {@link android.content.Intent})。 + + + <li>宣告要在未來的指定時間內執行的意圖 (Android 系統的 {@link android.app.AlarmManager} 會執行 {@link android.content.Intent})。 + +</ul> + +<p>由於每個 {@link android.content.Intent} 物件都設計為要由特定的應用程式元件類型來處理 ({@link android.app.Activity}、{@link android.app.Service} 或 {@link android.content.BroadcastReceiver}),因此請務必以相同的考量建立 {@link android.app.PendingIntent}。 + + +使用待處理意圖時,您的應用程式將不會透過像是 +{@link android.content.Context#startActivity +startActivity()} 的呼叫來執行意圖。當您藉由呼叫以下的個別建立者方法建立 +{@link android.app.PendingIntent} 時,您必須改為宣告意圖元件類型:</p> + +<ul> + <li>{@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} 適用於啟動 {@link android.app.Activity} 的 {@link android.content.Intent}。 +</li> + <li>{@link android.app.PendingIntent#getService PendingIntent.getService()} 適用於啟動 {@link android.app.Service} 的 {@link android.content.Intent}。 +</li> + <li>{@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} 適用於啟動 {@link android.content.BroadcastReceiver} 的 {@link android.content.Intent}。 +</li> +</ul> + +<p>除非您的應用程式「收到」<em></em>來自其他應用程式的待處理意圖,否則您可能只需要上述建立 +{@link android.app.PendingIntent} 的 +{@link android.app.PendingIntent} 方法。</p> + +<p>每個方法會採用目前的應用程式 {@link android.content.Context}、您要包裝的 +{@link android.content.Intent} 以及一或多個指定應如何使用意圖的旗標 (例如該意圖是否能使用多次)。 +</p> + +<p>如要進一步瞭解如何使用待處理意圖,請參閱個別使用案例的參考文件,例如<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">通知</a>以及<a href="{@docRoot}guide/topics/appwidgets/index.html">應用程式小工具</a> API 指南。 + +</p> + + + + + + + +<h2 id="Resolution">意圖解析</h2> + + +<p>當系統收到要啟動 Activity 的隱含意圖時,會根據三個層面來比較意圖與意圖篩選器,以便為該意圖搜尋最適合的 Activity。 +</p> + +<ul> + <li>意圖動作 + <li>意圖資料 (URI 與資料類型) + <li>意圖類別 +</ul> + +<p>下列各節就如何在應用程式宣示說明檔案中宣告意圖篩選器這點,說明如何比對意圖以找出適當的元件。 +</p> + + +<h3 id="ActionTest">動作測試</h3> + +<p>如要指定接受的意圖動作,意圖篩選器可以宣告零或多個 +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> 元素。例如:</p> + +<pre> +<intent-filter> + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.VIEW" /> + ... +</intent-filter> +</pre> + +<p>如要通過此篩選器,{@link android.content.Intent} 中指定的動作必須與篩選器中列出的其中一個動作相符。 +</p> + +<p>如果篩選器未列出任何可供意圖比對的動作,所有意圖都無法通過測試。 +不過,如果 {@link android.content.Intent} 未指定任何動作,它就會通過測試 (只要篩選器至少包含一個動作即可)。 + +</p> + + + +<h3 id="CategoryTest">類別測試</h3> + +<p>如要指定接受的意圖類別,意圖篩選器可以宣告零或多個 +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> 元素。例如:</p> + +<pre> +<intent-filter> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + ... +</intent-filter> +</pre> + +<p>{@link android.content.Intent} 中的每個類別都必須與篩選器中的類別相符,意圖才會通過類別測試。 +不需要反向進行 — 意圖篩選器宣告的類別可以比 +{@link android.content.Intent} 中指定的類別多,而 +{@link android.content.Intent} 仍可通過測試。因此,不論篩選器中宣告的類別為何,未包含類別的意圖一律可通過此測試。 +</p> + +<p class="note"><strong>注意:</strong>Android 會將 {@link android.content.Intent#CATEGORY_DEFAULT} 類別自動套用到所有傳送至 {@link +android.content.Context#startActivity startActivity()} 與 {@link +android.app.Activity#startActivityForResult startActivityForResult()} 的隱含意圖。因此,如果您希望 Activity 收到隱含意圖,Activity 的意圖篩選器就必須包含 +{@code "android.intent.category.DEFAULT"} 的類別 (如先前的 {@code <intent-filter>} 範例中所示)。 + + + +</p> + + + +<h3 id="DataTest">資料測試</h3> + +<p>如要指定接受的意圖資料,意圖篩選器可以宣告零或多個 +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> 元素。例如:</p> + +<pre> +<intent-filter> + <data android:mimeType="video/mpeg" android:scheme="http" ... /> + <data android:mimeType="audio/mpeg" android:scheme="http" ... /> + ... +</intent-filter> +</pre> + +<p>每個 <code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code> + 元素都能指定 URI 結構與資料類型 (MIME 媒體類型)。URI 的每個部分都有個別的屬性: +{@code scheme}、{@code host}、{@code port} 和 {@code path}。 + +</p> + +<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p> + +<p> +例如: +</p> + +<p style="margin-left: 2em">{@code content://com.example.project:200/folder/subfolder/etc}</p> + +<p>在這個 URI 中,配置為 {@code content}、主機為 {@code com.example.project}、連接埠為 {@code 200},而路徑為 {@code folder/subfolder/etc}。 + +</p> + +<p>在 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 元素中,上述所有屬性均為選用性質,但具有線性相依性: +</p> +<ul> + <li>如果未指定配置,就會忽略主機。</li> + <li>如果未指定主機,就會忽略連接埠。</li> + <li>如果配置與主機均未指定,就會忽略路徑。</li> +</ul> + +<p>將意圖中的 URI 與篩選器中的 URI 規格比較時,只會比較篩選器中所包含的 URI 部分。 +例如:</p> +<ul> + <li>如果篩選器只指定配置,含有該配置的所有 URI 都會與篩選器相符。 +</li> + <li>如果篩選器指定了配置與授權,但未指定路徑,不論其路徑為何,含有相同配置與授權的所有 URI 都會通過篩選器。 +</li> + <li>如果篩選器指定了配置、授權和路徑,則只有包含相同配置、授權和路徑的 URI 才會通過篩選器。 +</li> +</ul> + +<p class="note"><strong>注意:</strong>路徑規格可以包含萬用字元星號 (*),以要求僅部分相符的路徑名稱。 +</p> + +<p>資料測試會比較意圖中的 URI 與 MIME 類型,以及篩選器中指定的 URI 與 MIME 類型。 +以下說明規則: +</p> + +<ol type="a"> +<li>只有在篩選器未指定任何 URI 或 MIME 類型時,未包含 URI 或 MIME 類型的意圖才會通過測試。 +</li> + +<li>只有在其 URI 與篩選器的 URI 格式相符,而且篩選器同樣未指定 MIME 類型時,包含 URI 但沒有 MIME 類型 (既非明確也無法從 URI 推測得出) 的意圖才會通過測試。 + +</li> + +<li>只有在篩選器列出相同的 MIME 類型且未指定 URI 格式時,包含 MIME 類型但沒有 URI 的意圖才會通過測試。 +</li> + +<li>只有在 MIME 類型與篩選器中列出的類型相符時,包含 URI 與 MIME 類型 (明確或可從 URI 推測得出) 的意圖才會通過 MIME 類型部分的測試。 + +如果它的 URI 與篩選器中的 URI 相符,或如果它有 +{@code content:} 或 {@code file:} URI,而且篩選器未指定 URI 時,就會通過 URI 部分的測試。換句話說,如果它的篩選器「只」<em></em>列出 MIME 類型,就會假設元件支援 {@code content:} 與 {@code file:} 資料。 + + +</p></li> +</ol> + +<p> +最後一項規則 (規則 (d)) 可反映出希望元件能夠從檔案或內容供應程式取得本機資料的期望。 + +因此,其篩選器可以只列出資料類型,同時不需要明確命名 +{@code content:} 與 {@code file:} 配置。 +此為典型案例。例如,如下所示的 <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> 元素可以告訴 Android,元件能夠從內容供應程式取得影像資料並加以顯示: + + +</p> + +<pre> +<intent-filter> + <data android:mimeType="image/*" /> + ... +</intent-filter></pre> + +<p> +由於大部分可用資料都是由內容供應程式分配,因此指定資料類型但未指定 URI 的篩選器也許會最常見。 + +</p> + +<p> +另一個常見的設定是含有配置與資料類型的篩選器。例如,如下所示的 +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>元素可以告訴 Android,元件能夠從網路擷取影片資料以執行動作: + + +</p> + +<pre> +<intent-filter> + <data android:scheme="http" android:type="video/*" /> + ... +</intent-filter></pre> + + + +<h3 id="imatch">意圖比對</h3> + +<p>和意圖篩選器比對的意圖不只可探尋要啟動的目標元件,還能探尋裝置上元件集合的相關資料。 + +例如,主畫面應用程式會利用指定 +{@link android.content.Intent#ACTION_MAIN} 動作與 +{@link android.content.Intent#CATEGORY_LAUNCHER} 類別的意圖篩選器來尋找所有 Activity,以填入應用程式啟動器。 +</p> + +<p>您的應用程式能以類似的方式使用意圖比對。 +{@link android.content.pm.PackageManager} 有一組 {@code query...()}方法可將接受特定意圖的所有元件傳回,還有一系列類似的 +{@code resolve...()} 方法可決定回應意圖的最佳元件。 + +例如, +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} 會傳回所有 Activity 清單,上述的 Activity 都可以執行當成引數傳送的意圖,以及 +{@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} 可傳回類似的服務清單。 + +這些方法不會啟動元件,只會列出可以回應的元件。類似的方法 +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()} 可用於廣播接收器。 +</p> + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/loaders.jd b/docs/html-intl/intl/zh-tw/guide/components/loaders.jd new file mode 100644 index 000000000000..89bfc80744d9 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=載入器 +parent.title=Activity +parent.link=activities.html +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> + <ol> + <li><a href="#summary">載入器 API 摘要</a></li> + <li><a href="#app">在應用程式中使用載入器</a> + <ol> + <li><a href="#requirements"></a></li> + <li><a href="#starting">啟動載入器</a></li> + <li><a href="#restarting">重新啟動載入器</a></li> + <li><a href="#callback">使用 LoaderManager 回呼</a></li> + </ol> + </li> + <li><a href="#example">範例說明</a> + <ol> + <li><a href="#more_examples">其他範例</a></li> + </ol> + </li> + </ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.app.LoaderManager}</li> + <li>{@link android.content.Loader}</li> + + </ol> + + <h2>相關範例</h2> + <ol> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">LoaderCursor +</a></li> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html">LoaderThrottle +</a></li> + </ol> + </div> +</div> + +<p>在 Android 3.0 導入的載入器可輕鬆在 Activity 或片段中,以非同步方式載入資料。 +載入器的特性如下:</p> + <ul> + <li>適用於所有 {@link android.app.Activity} 和 {@link +android.app.Fragment} 。</li> + <li>提供以非同步方式載入資料。</li> + <li>監視其資料來源,並在內容變更時傳送新的結果。 +</li> + <li>可在設定變更後重新建立時,自動重新連接到上次載入器的游標, +因此不需要重新查詢資料。 +</li> + </ul> + +<h2 id="summary">載入器 API 摘要</h2> + +<p>有多個類別和介面可能與在應用程式中使用載入器有關。 +請參閱下表的摘要說明:</p> + +<table> + <tr> + <th>類別/介面</th> + <th>說明</th> + </tr> + <tr> + <td>{@link android.app.LoaderManager}</td> + <td>與 {@link android.app.Activity} 或 +{@link android.app.Fragment} 相關聯的抽象類別,可用於管理一或多個 {@link +android.content.Loader} 執行個體。這可以協助應用程式管理所需執行時間較長的操作與 {@link android.app.Activity} 或 {@link android.app.Fragment} 生命週期搭配使用,這最常與 +{@link android.content.CursorLoader} 搭配使用,不過應用程式能夠撰寫自己的載入器來載入其他類型的資料。 + + + + <br /> + <br /> + 每個 Activity 或片段只能有一個 {@link android.app.LoaderManager},但 {@link android.app.LoaderManager} 可以有多個載入器。 +</td> + </tr> + <tr> + <td>{@link android.app.LoaderManager.LoaderCallbacks}</td> + <td>可供用戶端與 {@link +android.app.LoaderManager} 互動的回呼介面。例如,您使用 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +回呼方法來建立新的載入器。</td> + </tr> + <tr> + <td>{@link android.content.Loader}</td> + <td>以非同步方式載入資料的抽象類別。這是載入器的基本類別。 +您通常會使用 {@link +android.content.CursorLoader},但您也可以實作自己的子類別。載入器處於使用中時,應會監視其資料來源,並在內容有變更時傳送新的結果。 + + </td> + </tr> + <tr> + <td>{@link android.content.AsyncTaskLoader}</td> + <td>提供 {@link android.os.AsyncTask} 以執行工作的抽象載入器。</td> + </tr> + <tr> + <td>{@link android.content.CursorLoader}</td> + <td>查詢 +{@link android.content.ContentResolver} 並傳回 {@link +android.database.Cursor} 的 {@link android.content.AsyncTaskLoader} 子類別。此類別會以標準方式實作 {@link +android.content.Loader} 通訊協定,用來查詢建置於 {@link android.content.AsyncTaskLoader} 的游標,以便在背景執行緒中執行游標查詢,藉此避免封鎖應用程式的 UI。 + + +以非同步方式從 {@link +android.content.ContentProvider} 載入資料,而不是透過片段或 Activity 的 API 來管理查詢,最好的方法就是使用此載入器。 +</td> + </tr> +</table> + +<p>上表中的類別和介面就是您將用來在應用程式中實作載入器的基本元件。 +上述元件不需要在您建立載入器時全部使用,但您必須一律參照到 {@link +android.app.LoaderManager},才能初始化載入器並實作 +{@link android.content.Loader} 類別,例如 {@link +android.content.CursorLoader}。 +以下各節說明如何在應用程式中使用這些類別和介面。 +</p> + +<h2 id ="app">在應用程式中使用載入器</h2> +<p>本節說明如何在 Android 應用程式中使用載入器。使用載入器的應用程式通常包括下列物件: +</p> +<ul> + <li>{@link android.app.Activity} 或 {@link android.app.Fragment}。</li> + <li>{@link android.app.LoaderManager} 的執行個體。</li> + <li>可載入 {@link +android.content.ContentProvider} 所備份資料的 {@link android.content.CursorLoader}。或者,您可以實作自己的 +{@link android.content.Loader} 子類別或 {@link android.content.AsyncTaskLoader},從其他來源載入資料。 +</li> + <li>{@link android.app.LoaderManager.LoaderCallbacks} 的實作。 +您可以在這裡建立新的載入器和管理對現有載入器的參照。 +</li> +<li>顯示載入器資料的一種方式,例如 {@link +android.widget.SimpleCursorAdapter}。</li> + <li>使用 {@link android.content.CursorLoader} 時的資料來源,例如 {@link android.content.ContentProvider}。 +</li> +</ul> +<h3 id="starting">啟動載入器</h3> + +<p>{@link android.app.LoaderManager} 可在 {@link android.app.Activity} 或 {@link android.app.Fragment} 內管理一或多個 {@link +android.content.Loader} 執行個體。 +每個 Activity 或片段只能有一個 {@link +android.app.LoaderManager}。</p> + +<p>您通常會在 Activity 的 {@link +android.app.Activity#onCreate onCreate()} 方法內或在片段的 {@link android.app.Fragment#onActivityCreated onActivityCreated()} 方法內,初始化 {@link android.content.Loader}, + +如下所示: +</p> + +<pre>// Prepare the loader. Either re-connect with an existing one, +// or start a new one. +getLoaderManager().initLoader(0, null, this);</pre> + +<p>{@link android.app.LoaderManager#initLoader initLoader()} 方法採用下列參數: +</p> +<ul> + <li>可識別載入器的不重複 ID。在本範例中,此 ID 為 0。</li> +<li>可在建構時提供給載入器的選用引數 (在本範例中為<code>null</code>)。 +</li> + +<li>{@link android.app.LoaderManager.LoaderCallbacks} 實作, +{@link android.app.LoaderManager} 會呼叫此實作來回報載入器事件。在本範例中,本機類別會實作 {@link +android.app.LoaderManager.LoaderCallbacks} 執行個體,以將參照傳送給它自己 ({@code this})。 + +</li> +</ul> +<p>{@link android.app.LoaderManager#initLoader initLoader()} 呼叫可確保載入器已初始化且處於有效狀態。 +可能會有兩種結果:</p> +<ul> + <li>如果 ID 所指定的載入器已經存在,就會重複使用上次建立的載入器。 +</li> + <li>如果 ID 所指定的載入器「不存在」<em></em>, +{@link android.app.LoaderManager#initLoader initLoader()} 會觸發 +{@link android.app.LoaderManager.LoaderCallbacks} 方法 {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。 +您可以在這裡實作程式碼以具現化及傳回新的載入器。 +如需詳細資訊,請參閱 <a href="#onCreateLoader">onCreateLoader</a>。</li> +</ul> +<p>在任一情況下,指定的 {@link android.app.LoaderManager.LoaderCallbacks}實作會與載入器建立關聯且會在載入器狀態變更時呼叫。 + +如果進行此呼叫時,呼叫器處於已啟動狀態,而要求的載入器已經存在並產生自己的資料,那麼系統會立即呼叫 {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} (在 {@link android.app.LoaderManager#initLoader initLoader()} 期間),請務必做好發生這種情況的準備。 + + + +如要進一步瞭解此回呼,請參閱 <a href="#onLoadFinished">onLoadFinished +</a>。</p> + +<p>請注意,{@link android.app.LoaderManager#initLoader initLoader()}方法會傳回建立的 {@link android.content.Loader},但您不需要擷取它的參照。 + +{@link android.app.LoaderManager} 會自動管理載入器的生命週期。 +{@link android.app.LoaderManager} 會在必要時啟動及停止載入,並維護載入器的狀態與其相關內容。 + +顧名思義,您鮮少會直接與載入器互動 (但如需使用載入器方法微調載入器行為的範例,請參閱 <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> 範例)。 + +當發生特定事件時,您最常會使用 {@link +android.app.LoaderManager.LoaderCallbacks} 方法來介入載入程序。 + +如要進一步瞭解此主題,請參閱<a href="#callback">使用 LoaderManager 回呼</a>。</p> + +<h3 id="restarting">重新啟動載入器</h3> + +<p>當您使用 {@link android.app.LoaderManager#initLoader initLoader()} (如上所示),它會使用已指定 ID 的現有載入器 (如果有的話)。 + +如果沒有,就會自行建立載入器。不過,有時候您會想捨棄舊資料並從頭開始。 +</p> + +<p>如要捨棄舊資料,請使用 {@link +android.app.LoaderManager#restartLoader restartLoader()}。例如,當使用者的查詢改變時,實作 {@link android.widget.SearchView.OnQueryTextListener} 會重新啟動載入器。 + +您必須重新啟動載入器,才能使用修訂後的搜尋篩選器執行新的查詢: +</p> + +<pre> +public boolean onQueryTextChanged(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; +}</pre> + +<h3 id="callback">使用 LoaderManager 回呼</h3> + +<p>{@link android.app.LoaderManager.LoaderCallbacks} 是回呼介面,可讓用戶端與 {@link android.app.LoaderManager} 互動。 + </p> +<p>載入器 (特別是 {@link android.content.CursorLoader}) 在停止之後,應該會保留它們的資料。 +這可讓應用程式保留其在 Activity 或片段的 {@link android.app.Activity#onStop +onStop()} 與 {@link android.app.Activity#onStart onStart()} 方法間的資料,好讓使用者回到應用程式時,不必枯等資料重新載入。 + + +您可以使用 {@link android.app.LoaderManager.LoaderCallbacks} 方法,藉以得知何時該建立新的載入器,以及指示應用程式何時該停止使用載入器的資料。 + +</p> + +<p>{@link android.app.LoaderManager.LoaderCallbacks} 包含以下方法: +</p> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — 具現化及傳回指定 ID 的新 {@link android.content.Loader}。 + +</li></ul> +<ul> + <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} — 當先前建立的載入器已完成其載入工作時呼叫。 + +</li></ul> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} — 正要重設先前建立的載入器時呼叫,使其資料無法使用。 + + +</li> +</ul> +<p>以上方法在下列幾節有更詳細的說明。</p> + +<h4 id ="onCreateLoader">onCreateLoader</h4> + +<p>當您嘗試存取載入器 (例如,透過 {@link +android.app.LoaderManager#initLoader initLoader()}) 時,系統會檢查根據 ID 指定的載入器是否已存在。 +如果不存在,就會觸發 {@link +android.app.LoaderManager.LoaderCallbacks} 方法 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。您可以在這裡建立新的載入器。 +這通常會是 {@link +android.content.CursorLoader},但您也可以實作自己的 {@link +android.content.Loader} 子類別。 </p> + +<p>在此範例中,{@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} 回呼方法會建立 {@link android.content.CursorLoader}。 +您必須使用其建構函式方法來建置 +{@link android.content.CursorLoader},其需要一組完整的資訊才能對 {@link +android.content.ContentProvider} 執行查詢。 +具體來說,建構函式方法需要以下項目:</p> +<ul> + <li>uri<em></em> - 要擷取內容的 URI。 </li> + <li>projection<em></em> - 要傳回的欄清單。傳送 +<code>null</code> 將會傳回無效的所有欄。 </li> + <li>selection<em></em> - 篩選器會採用 SQL WHERE 子句的格式 (WHERE 本身除外) 宣告要傳回的列。 +傳送 +<code>null</code> 將會傳回指定 URI 的所有列。 </li> + <li>selectionArgs<em></em> - 您可能會在選取項目中包含 ?s,而其會由來自 selectionArgs<em></em> 的值按照其出現在選取項目中的順序所取代。 + +值將會繫結為字串。 </li> + <li>sortOrder<em></em> - 如何採用 SQL ORDER BY 子句的格式 (ORDER BY 本身除外) 來排列各列的順序。 +傳遞 <code>null</code> 將會使用預設的排序順序,其可能是無排序順序。 +</li> +</ul> +<p>例如:</p> +<pre> + // If non-null, this is the current filter the user has provided. +String mCurFilter; +... +public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); +}</pre> +<h4 id="onLoadFinished">onLoadFinished</h4> + +<p>這個方法是在先前建立的載入器已完成其載入工作時呼叫。 +此方法一律是在提供給此載入器的最後資料發佈之前呼叫。 +此時,您應要移除所有使用的舊資料 (由於資料即將發佈),但不應自行發佈,因為載入器擁有該資料且將會負責處理。 + +</p> + + +<p>一旦應用程式不再使用資料時,載入器就會立即發佈資料。 +例如,如果資料是來自 {@link +android.content.CursorLoader} 的游標,您不應該自行對它呼叫 {@link +android.database.Cursor#close close()}。如果正要將該游標放入 +{@link android.widget.CursorAdapter},您應該使用 {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} 方法,如此才不會關閉舊的 +{@link android.database.Cursor}。例如:</p> + +<pre> +// This is the Adapter being used to display the list's data.<br +/>SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}</pre> + +<h4 id="onLoaderReset">onLoaderReset</h4> + +<p>這個方法是在正要重設先前建立的載入器時呼叫,以便使其資料無法使用。 +此回呼方法可讓您知道即將要發佈資料,而能先行將其參照移除。 + </p> +<p>此實作會以 <code>null</code> 的值呼叫 +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}: +</p> + +<pre> +// This is the Adapter being used to display the list's data. +SimpleCursorAdapter mAdapter; +... + +public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); +}</pre> + + +<h2 id="example">範例說明</h2> + +<p>例如,以下是 {@link +android.app.Fragment} 的完整實作, +其顯示的 {@link android.widget.ListView} 包含聯絡人內容供應程式的查詢結果。它使用 {@link +android.content.CursorLoader} 管理對供應程式的查詢。</p> + +<p>如本範例所示,針對要存取使用者聯絡人的應用程式,它的宣示說明必須包含 {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} 權限。 + +</p> + +<pre> +public static class CursorLoaderListFragment extends ListFragment + implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_2, null, + new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, + new int[] { android.R.id.text1, android.R.id.text2 }, 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // Place an action bar item for searching. + MenuItem item = menu.add("Search"); + item.setIcon(android.R.drawable.ic_menu_search); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + SearchView sv = new SearchView(getActivity()); + sv.setOnQueryTextListener(this); + item.setActionView(sv); + } + + public boolean onQueryTextChange(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override public boolean onQueryTextSubmit(String query) { + // Don't care about this. + return true; + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("FragmentComplexList", "Item clicked: " + id); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { + Contacts._ID, + Contacts.DISPLAY_NAME, + Contacts.CONTACT_STATUS, + Contacts.CONTACT_PRESENCE, + Contacts.PHOTO_ID, + Contacts.LOOKUP_KEY, + }; + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + } + + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +}</pre> +<h3 id="more_examples">其他範例</h3> + +<p><strong>ApiDemos</strong> 中有數個不同的範例,示範如何使用載入器: +</p> +<ul> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html">LoaderCursor</a> - 上述程式碼片段的完整版本在此。 + +</li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> - 以範例說明如何使用節流功能在其資料變更時降低內容供應程式執行的查詢數目。 +</li> +</ul> + +<p>如要進一步瞭解如何下載及安裝 SDK 範例,請參閱<a href="http://developer.android.com/resources/samples/get.html">取得範例</a>。 + </p> + diff --git a/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd b/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd new file mode 100644 index 000000000000..74dbb8ebb3f1 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=處理程序和執行緒 +page.tags=生命週期、背景 + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> +<li><a href="#Processes">處理程序</a> + <ol> + <li><a href="#Lifecycle">處理程序生命週期</a></li> + </ol> +</li> +<li><a href="#Threads">執行緒</a> + <ol> + <li><a href="#WorkerThreads">工作者執行緒</a></li> + <li><a href="#ThreadSafe">安全執行緒方法</a></li> + </ol> +</li> +<li><a href="#IPC">處理程序間通訊</a></li> +</ol> + +</div> +</div> + +<p>當應用程式元件啟動且該應用程式未執行任何其他元件時,Android 系統會以執行單一執行緒的方式,為該應用程式啟動新的 Linux 處理程序。 + +預設會以相同的處理程序和執行緒 (稱為「主要」執行緒) 執行相同應用程式的所有元件。 +如果應用程式元件啟動且已有該應用程式的處理程序存在 (由於應用程式還有另一個元件存在),那麼元件會在該處理程序中啟動,並使用相同的執行緒執行。 + +不過,您可以安排應用程式中的不同元件以個別處理程序執行,還可以為任何處理程序建立額外的執行緒。 + +</p> + +<p>本文件說明處理程序和執行緒如何在 Android 應用程式中運作。</p> + + +<h2 id="Processes">處理程序</h2> + +<p>在預設情況下,系統會以相同的處理程序執行相同應用程式的所有元件,而且大部分應用程式都是如此。 +不過,如果您需要控制特定元件所屬的處理程序,可以在宣示說明檔案中這麼做。 +</p> + +<p>每種元件元素 — <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a>、<a href="{@docRoot}guide/topics/manifest/service-element.html">{@code +<service>}</a>、<a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code +<receiver>}</a> 和 <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code +<provider>}</a> — 的宣示說明項目都支援 {@code android:process} 屬性,這項屬性能指定元件應在哪個處理程序執行。 +您可以設定此屬性讓每個元件都以自己的處理程序執行,或只讓當中的部分元件共用同一處理程序。 +您也可以設定 +{@code android:process},讓不同應用程式的元件以相同的處理程序執行,只要這些應用程式分享相同的 Linux 使用者 ID 並以相同的憑證簽署。 + +</p> + +<p><a href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</a> 元素也支援 {@code android:process} 屬性,以設定要套用到所有元件的預設值。 +</p> + +<p>Android 可能會在記憶體不足且需要其他處理程序更立即為使用者提供服務時,決定關閉處理程序。 +使用已終止的處理程序執行的應用程式元件會因此而終結。 +當再次有工作需要執行時,就會為這些元件再次啟動處理程序。 +</p> + +<p>Android 系統會將處理程序對使用者的相對重要性加權,以決定要終止的處理程序。 +例如,相較於代管可見 Activity 的處理程序,系統較容易關閉代管已不在螢幕上顯示的 Activity 的處理程序。 +因此,是否要終止處理程序,取決於以該處理程序執行中的元件狀態。 +如要瞭解用於決定是否終止處理程序的規則,請參閱下文。 + </p> + + +<h3 id="Lifecycle">處理程序生命週期</h3> + +<p>Android 系統會儘可能持續維護處理程序,但最終仍必須移除舊的處理程序,以便回收記憶體供新的或更重要的處理程序使用。 +系統會根據以該處理程序執行的元件和那些元件的狀態,將每個處理程序放入「重要性階層」,藉此決定要保留以及要終止的處理程序。 + + +重要性最低的處理程序會最先遭到終止,接著是重要性次低的處理程序,依此類推,視需要收回系統資源。 + +</p> + +<p>重要性階層共有五個層級。下方清單依照重要性的順序列出不同類型的處理程序 (第一個處理程序為「最重要」<em></em>且會「最後終止」<em></em>): + +</p> + +<ol> + <li><b>前景處理程序</b> + <p>這種處理程序是指使用者目前執行的工作所需的處理程序。針對下列任一情況,系統或將處理程序視為位於前景中: +</p> + + <ul> + <li>使用者正與其代管的 {@link android.app.Activity} 互動 (已呼叫 {@link +android.app.Activity} 的 {@link android.app.Activity#onResume onResume()} 方法)。 +</li> + + <li>其代管的 {@link android.app.Service} 已繫結至正在與使用者互動的 Activity。 +</li> + + <li>其代管的 {@link android.app.Service} 正「在前景」執行中 (服務已呼叫 {@link android.app.Service#startForeground startForeground()})。 + + + <li>其代管的 {@link android.app.Service} 正在執行本身的其中一個生命週期回呼 ({@link android.app.Service#onCreate onCreate()}、{@link android.app.Service#onStart onStart()} 或 {@link android.app.Service#onDestroy onDestroy()})。 + +</li> + + <li>其代管的 {@link android.content.BroadcastReceiver} 正在執行本身的 {@link +android.content.BroadcastReceiver#onReceive onReceive()} 方法。</li> + </ul> + + <p>一般來說,在任何指定時間內只會有幾個前景處理程序存在。只有在記憶體過低而全都無法繼續執行時,才會採取這最後的手段來終止它們。 +在這種情況下,裝置通常已達到記憶體分頁處理狀態,因此必須終止一些前景處理程序,才能讓使用者介面保持回應。 + +</p></li> + + <li><b>可見處理程序</b> + <p>這種處理程序是指沒有任何前景元件的處理程序,但仍會影響使用者在螢幕上看見的內容。 +針對下列任一情況,系統會將處理程序視為可見: +</p> + + <ul> + <li>其代管的 {@link android.app.Activity} 不在前景中,但使用者仍可看見 (已呼叫它的 {@link android.app.Activity#onPause onPause()} 方法)。 +例如,如果前景 Activity 啟動的對話方塊允許在它身後看見先前的 Activity,就會發生這種情況。 + +</li> + + <li>其代管的 {@link android.app.Service} 已繫結至可見 (或前景) Activity。 +</li> + </ul> + + <p>可見處理程序相當重要而且不會遭到終止,除非系統為了讓所有前景處理程序維持執行而必須終止這類處理程序。 + </p> + </li> + + <li><b>服務處理程序</b> + <p>這種處理程序是指正在執行已使用 {@link +android.content.Context#startService startService()} 方法啟動的服務的處理程序;此處理程序不屬於上述兩種較重要的類別。 +雖然服務處理程序是間接繫結至使用者所見內容,但通常會執行使用者重視的工作 (例如,在背景中播放音樂,或下載網路上的資料),因此除非記憶體不足,無法讓所有前景與可見處理程序保持執行,否則系統會讓這類處理程序繼續執行。 + + + </p> + </li> + + <li><b>背景處理程序</b> + <p>這種處理程序會保留使用者目前看不見的 Activity (已呼叫 Activity 的 +{@link android.app.Activity#onStop onStop()} 方法)。這些處理程序會間接影響使用者體驗,且系統能隨時將其終止,藉此回收記憶體以供前景、可見或服務處理程序使用。 + + +通常會有許多背景處理程序處於執行中,因此會將它們放在 LRU (最近最少使用) 清單中,以確保在最後才將包含使用者最近最常見 Activity 的處理程序終止。 + +如果 Activity 正確實作其生命週期方法並儲存其目前狀態,終止其處理程序不會對使用者體驗造成任何可察覺的影響,原因是當使用者瀏覽回 Activity 時,該 Activity 會還原它的所有可見狀態。 + + +如要進一步瞭解如何儲存及還原狀態,請參閱 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Activity</a>。 +</p> + </li> + + <li><b>空白處理程序</b> + <p>這種處理程序是指未保留任何使用中應用程式元件的處理程序。讓這類處理程序保持有效的唯一目的是將其用於快取,以改善元件下次執行時所需的啟動時間。 + +系統通常會終止這些處理程序,以平衡處理程序快取與底層核心快取之間的整體系統資源。 +</p> + </li> +</ol> + + + <p>Android 會根據在處理程序中目前處於使用中的元件重要性,將處理程序盡量排在最高層級。 +例如,如果處理程序代管一項服務和一個可見 Activity,此處理程序的會排為可見處理程序,而不是服務處理程序。 +</p> + + <p>此外,還可能因為它有其他相依處理程序,而導致處理程序的排名提升:為另一個處理程序提供服務的處理程序,其排名絕不能低於為其提供服務的處理程序。 + +例如,如果處理程序 A 的內容供應程式為處理程序 B 中的用戶端提供服務,或處理程序 A 中的服務繫結至處理程序 B 中的元件,則系統至少會將處理程序 A 視為和處理程序 B 一樣重要。 + +</p> + + <p>由於執行服務的處理程序排名會比包含背景 Activity 的處理程序排名高,因此初始化長時間執行操作的 Activity 可能適合啟動該操作的<a href="{@docRoot}guide/components/services.html">服務</a>,而不只是建立工作者執行緒 — 特別是該操作可能會比 Activity 持久。例如,將圖片上傳至網站 Activity 應該啟動要執行上傳的服務,如果使用者離開 Activity,上傳處理程序也能在背景中繼續進行。如果使用服務,不論 Activity 發生什麼情況,都可保證該操作的優先順序至少會是「服務處理程序」。 + + + + + +廣播接收器應該採用服務,而不是只在執行緒中放置時間耗用操作,也是相同的理由。 +</p> + + + + +<h2 id="Threads">執行緒</h2> + +<p>當應用程式啟動時,系統會為執行該應用程式建立一個稱為「主要」的執行緒。 +這個執行緒非常重要,原因是它負責將事件分配給適當的使用者介面小工具,包括繪製事件。 +您的應用程式與 Android UI 工具組中的元件 ({@link +android.widget} 和 {@link android.view} 中的元件) 互動時,也需要使用這個執行緒。 +因此,主要執行緒有時也稱為 UI 執行緒。 +</p> + +<p>系統「不會」<em></em>為每個元件執行個體建立個別的執行緒。以相同處理程序執行的所有元件都是利用 UI 執行緒來具現化,而且都是由該執行緒分配系統呼叫的每個元件。 + +因此,回應系統回呼的方法 (例如報告使用者動作的 {@link android.view.View#onKeyDown onKeyDown()} 或生命週期回呼方法) 一律會以該處理程序的 UI 執行緒執行。 + +</p> + +<p>例如,當使用者輕觸畫面上的按鈕時,您應用程式的 UI 執行緒會將輕觸事件分配給小工具,接著設定其按下狀態並對事件佇列張貼失效要求。 + +UI 執行緒會將要求從佇列中移除,然後通知小工具應重新繪製本身。 +</p> + +<p>當您的應用程式密集執行作業以回應使用者互動時,除非您適當實作應用程式,否則這種單一執行緒模型會降低效能。 +具體來說,假設一切都在 UI 執行緒中進行,如果執行像是網路存取或資料庫查詢的長時間操作,將會封鎖整個 UI。當執行緒遭到封鎖時,會無法分配任何事件 (包括繪製事件)。 + + +從使用者的觀點來看,應用程式似乎閒置不動。 +更糟的是,如果 UI 執行緒遭到封鎖長達數秒 (目前約為 5 秒),就會向使用者顯示的「<a href="http://developer.android.com/guide/practices/responsiveness.html">應用程式沒有回應</a>」(ANR) 對話方塊。 + +使用者接著會決定結束您的應用程式,並可能因感到不滿而將其解除安裝。 +</p> + +<p>此外,Andoid UI 工具組「並非」<em></em>安全執行緒,因此請勿透過工作者執行緒操縱 UI — 使用者介面的所有操縱作業都必須從 UI 執行緒來執行。 + +基於上述原因,Android 的單一執行緒模型只有兩項簡單規則:</p> + +<ol> +<li>不要封鎖 UI 執行緒 +<li>不要從 UI 執行緒以外的位置存取 Android UI 工具組 +</ol> + +<h3 id="WorkerThreads">工作者執行緒</h3> + +<p>因為上述的單一執行緒模型,所以不封鎖 UI 執行緒對於應用程式 UI 的回應能力至關重要。 +如果您有不會立即執行的操作,請務必以不同的執行緒 (「背景」或「工作者」執行緒) 執行這類操作。 + +</p> + +<p>例如,以下是從個別執行緒下載圖片並顯示在 {@link android.widget.ImageView} 的部分點擊接聽器程式碼: +</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + Bitmap b = loadImageFromNetwork("http://example.com/image.png"); + mImageView.setImageBitmap(b); + } + }).start(); +} +</pre> + +<p>首先,由於這會建立新的執行緒來處理網路操作,所以看起來似乎可以正常運作。 +不過,它違反單一執行緒模型的第二項規則:「不要從 UI 執行緒以外的位置存取 Android UI 工具組」<em></em>— 這個範例修改工作者執行緒中的 {@link +android.widget.ImageView},而不是 UI 執行緒。 +這樣會產生未定義且預期外的行為,不但難以追蹤且耗費時間。 +</p> + +<p>為修正這個問題,Android 提供數種可從其他執行緒存取 UI 執行緒的方法。 +以下是可協助修正此問題的方法清單:</p> + +<ul> +<li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}</li> +<li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li> +<li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}</li> +</ul> + +<p>例如,您可以使用 {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)} 方法來修正上述程式碼:</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); + mImageView.post(new Runnable() { + public void run() { + mImageView.setImageBitmap(bitmap); + } + }); + } + }).start(); +} +</pre> + +<p>現在這個實作才是安全執行緒:雖然從不同的執行緒完成網路操作,但卻是從 UI 執行緒操縱 {@link android.widget.ImageView}。 +</p> + +<p>不過,隨著操作複雜度日益增加,這種程式碼也會變得複雜且難以維護。 +如要利用工作者執行緒處理更複雜的互動,您可能要考慮在工作者執行緒中使用 {@link android.os.Handler},以處理從 UI 執行緒傳送的訊息。 + +最佳解決方案也許是擴充 {@link android.os.AsyncTask} 類別,將必須與 UI 互動的工作者執行緒工作執行簡化。 +</p> + + +<h4 id="AsyncTask">使用 AsyncTask</h4> + +<p>{@link android.os.AsyncTask} 可讓您透過使用者介面執行非同步工作。 +它會以工作者執行緒執行封鎖操作,然後將結果發行在 UI 執行緒,完全不需要您自行處理執行緒和/或處理常式。 +</p> + +<p>使用方法是您必須要有子類別 {@link android.os.AsyncTask},並實作以背景執行緒集區執行的 {@link +android.os.AsyncTask#doInBackground doInBackground()} 回呼方法。 +如要更新您的 UI,請實作 {@link +android.os.AsyncTask#onPostExecute onPostExecute()} 來傳送 {@link +android.os.AsyncTask#doInBackground doInBackground()} 的結果,然後以 UI 執行緒執行,如此您才能安全地更新 UI。 +接著,您可以從 UI 執行緒呼叫 {@link android.os.AsyncTask#execute execute()} 來執行該工作。 +</p> + +<p>例如,您可以使用 {@link android.os.AsyncTask} 這種方法實作先前的範例: +</p> + +<pre> +public void onClick(View v) { + new DownloadImageTask().execute("http://example.com/image.png"); +} + +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { + /** The system calls this to perform work in a worker thread and + * delivers it the parameters given to AsyncTask.execute() */ + protected Bitmap doInBackground(String... urls) { + return loadImageFromNetwork(urls[0]); + } + + /** The system calls this to perform work in the UI thread and delivers + * the result from doInBackground() */ + protected void onPostExecute(Bitmap result) { + mImageView.setImageBitmap(result); + } +} +</pre> + +<p>現在 UI 很安全,而且程式碼變得更簡單,這是因為它將工作分成兩部分,一部分應在工作者執行緒上完成,而另一部分應在 UI 執行緒上完成。 +</p> + +<p>建議您參閱 {@link android.os.AsyncTask} 參考資料,以全面瞭解如何使用此類別;以下是其如何運作的快速總覽: +</p> + +<ul> +<li>您可以使用泛型來指定參數類型、進度值以及工作的最終值 +</li> +<li>{@link android.os.AsyncTask#doInBackground doInBackground()} 方法會在工作者執行緒中自動執行 +</li> +<li>{@link android.os.AsyncTask#onPreExecute onPreExecute()}、{@link +android.os.AsyncTask#onPostExecute onPostExecute()} 和 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} 全都是透過 UI 執行緒呼叫。</li> +<li>{@link android.os.AsyncTask#doInBackground doInBackground()} 傳回的值會傳送至 +{@link android.os.AsyncTask#onPostExecute onPostExecute()}</li> +<li>您隨時都可用 {@link +android.os.AsyncTask#doInBackground doInBackground()} 呼叫 {@link android.os.AsyncTask#publishProgress publishProgress()},以便透過 UI 執行緒執行 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()}。</li> +<li>您可以從任何執行緒隨時取消工作</li> +</ul> + +<p class="caution"><strong>注意:</strong>使用工作者執行緒可能會遇到的另一個問題是,由於<a href="{@docRoot}guide/topics/resources/runtime-changes.html">執行階段設定變更</a> (例如使用者變更螢幕方向時) 使您的 Activity 意外重新啟動,而可能會終止您的工作者站執行緒。 + +請參閱 <a href="http://code.google.com/p/shelves/">Shelves</a> 範例應用程式的原始程式碼,以瞭解如何在遇到其中一種重新啟動情況時保留您的工作,以及如何在 Activity 遭到終止時適當取消該工作。 + +</p> + + +<h3 id="ThreadSafe">安全執行緒方法</h3> + +<p> 在某些情況下,您實作的方法可能是從多個執行緒呼叫,因此務必要撰寫成安全執行緒。 + </p> + +<p>這種情況主要發生在可從遠端呼叫的方法,例如<a href="{@docRoot}guide/components/bound-services.html">已繫結服務</a>中的方法。當在源自執行 {@link android.os.IBinder IBinder} 的相同處理程序中實作 {@link android.os.IBinder} 方法上的呼叫時,該方法是以呼叫端的執行緒執行。 + + + +不過,當呼叫源自另一個處理程序時,會從系統在相同處理程序中當成 {@link android.os.IBinder +IBinder} (未以處理程序的 UI 執行緒執行) 維護的執行緒集區,以選擇的執行緒來執行該方法。例如,雖然服務的 +{@link android.app.Service#onBind onBind()} 方法可從服務處理程序的 UI 執行緒呼叫,但以 {@link android.app.Service#onBind +onBind()} 傳回的物件實作的方法會從集區中的執行緒呼叫。 + +由於服務能有多個用戶端,同時也能有多個集區執行緒採用相同的 +{@link android.os.IBinder IBinder} 方法。因此 {@link android.os.IBinder +IBinder} 方法必須實作為安全執行緒。</p> + +<p> 同樣地,內容供應程式能接收源自其他處理程序的資料要求。 +雖然 {@link android.content.ContentResolver} 與 {@link android.content.ContentProvider} +類別會隱藏如何管理處理程序間通訊的詳細資料,但回應這些要求的 {@link +android.content.ContentProvider} 方法 — {@link +android.content.ContentProvider#query query()}、{@link android.content.ContentProvider#insert +insert()}、{@link android.content.ContentProvider#delete delete()}、{@link +android.content.ContentProvider#update update()} 和 {@link android.content.ContentProvider#getType +getType()} — 是在內容供應程式的處理程序中從執行緒集區呼叫,而不是該處理程序的 UI 執行緒。 +由於可能會同時有任意數目的執行緒呼叫這些方法,因此它們也要實作為安全執行緒。 + </p> + + +<h2 id="IPC">處理程序間通訊</h2> + +<p>Android 提供一項使用遠端程序呼叫 (RPC) 進行處理程序間通訊 (IPC) 的機制,RPC 是指 (以另一個處理程序) 從遠端執行由 Activity 或其他應用程式元件呼叫的方法,再加上要傳回給呼叫端的任何結果。 + + +這需要將方法呼叫與其資料分解成作業系統能夠瞭解的程度,將它從本機處理程序與位址空間傳輸到遠端處理程序與位址空間,然後再重新組合和重新實作呼叫。 + +接著,再以相反的方向傳輸傳回值。 +Android 提供進行這些 IPC 交易的所有程式碼,讓您可以專心定義及實作 RPC 程式設計介面。 + </p> + +<p>如要執行 IPC,您的應用程式必須使用 {@link +android.content.Context#bindService bindService()} 來繫結至服務。如需詳細資訊,請參閱<a href="{@docRoot}guide/components/services.html">服務</a>開發人員指南。</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>For information about how to perform work in the background for an indefinite period of time +(without a user interface), continue with the <b><a +href="{@docRoot}guide/components/services.html">Services</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/zh-tw/guide/components/recents.jd b/docs/html-intl/intl/zh-tw/guide/components/recents.jd new file mode 100644 index 000000000000..d56c12c0e87b --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=總覽畫面 +page.tags="recents","overview" + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>本文件內容</h2> + <ol> + <li><a href="#adding">新增工作到總覽畫面</a> + <ol> + <li><a href="#flag-new-doc">使用意圖旗標來新增工作</a></li> + <li><a href="#attr-doclaunch">使用 Activity 屬性來新增工作</a></li> + </ol> + </li> + <li><a href="#removing">移除工作</a> + <ol> + <li><a href="#apptask-remove">使用 AppTask 類別來移除工作</a></li> + <li><a href="#retain-finished">保留結束的工作</a></li> + </ol> + </li> + </ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.app.ActivityManager.AppTask}</li> + <li>{@link android.content.Intent}</li> + </ol> + + <h2>程式碼範例</h2> + <ol> + <li><a href="{@docRoot}samples/DocumentCentricApps/index.html">以文件為中心的應用程式</a></li> + </ol> + +</div> +</div> + +<p>總覽畫面 (也被稱為最近畫面、最近工作清單,或最近的應用程式) 是系統層級的 UI,可以列出最近存取的 <a href="{@docRoot}guide/components/activities.html">Activity</a> 與<a href="{@docRoot}guide/components/tasks-and-back-stack.html">工作</a>。 + +使用者可以透過清單導覽並選擇要繼續的工作,或是使用者可以滑動的方式從清單移除工作。 + +使用 Android 5.0 版本 (API 級別 21),相同 Activity (包含不同文件) 的多個執行個體可以在總覽畫面中顯示為工作。 +例如:對多份 Google 文件,Google 雲端硬碟能讓每份文件都對應一個工作。 +每份文件在總覽畫面中都會顯示為工作。 +</p> + +<img src="{@docRoot}images/components/recents.png" alt="" width="284" /> +<p class="img-caption"><strong>圖 1.</strong>總覽畫面會顯示三份 Google 雲端硬碟文件,每份都分別顯示為一項工作。 +</p> + +<p>一般而言,您應該允許系統定義如何在總覽畫面中呈現工作與 Activity,而且不需要修改此行為。 +然而,您的應用程式可以決定要如何與在何時於總覽畫面中顯示 Activity。 + +{@link android.app.ActivityManager.AppTask} 類別讓您可以管理工作,而 {@link android.content.Intent} 類別的 Activity 旗標可以讓您指定何時從總覽畫面新增或移除 Activity。 + +此外,<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> 屬性可以讓您設定宣示說明中的行為。</p> + +<h2 id="adding">新增工作到總覽畫面</h2> + +<p>使用 {@link android.content.Intent} 類別的旗標可以新增工作,該工作針對何時與如何在總覽畫面中開啟或重新開啟文件,可給予更多控制權。 +當您使用 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>屬性時,您可以選擇永遠在新工作開啟文件或對文件重複使用現有工作。 + +</p> + +<h3 id="flag-new-doc">使用意圖旗標來新增工作</h3> + +<p>當您建立 Activity 的新文件時,您可以呼叫 + {@link android.app.ActivityManager.AppTask} 類別的 {@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()}方法,傳送啟動 Activity 的意圖至新文件。 + +如要插入邏輯中斷,讓系統可以將您的 Activity 當作總覽視窗中的新工作,傳送啟動 Activity 的 {@link android.content.Intent}其 {@link android.content.Intent#addFlags(int) addFlags()} 方法中的 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標。 + + +</p> + +<p class="note"><strong>注意:</strong>{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標會取代 {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} 旗標,後者已從 Android 5.0 (API 級別 21) 起失效。 + +</p> + +<p>如果您在建立新文件時設定 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,則系統永遠會在建立新工作時也在根目錄建立目標 Activity。此設定允許可以在一個以上的工作中開啟相同的文件。 + +下列程式碼示範主要 Activity 如何處理: +</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +public void createNewDocument(View view) { + final Intent newDocumentIntent = newDocumentIntent(); + if (useMultipleTasks) { + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + startActivity(newDocumentIntent); + } + + private Intent newDocumentIntent() { + boolean useMultipleTasks = mCheckbox.isChecked(); + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; + } + + private static int incrementAndGet() { + Log.d(TAG, "incrementAndGet(): " + mDocumentCounter); + return mDocumentCounter++; + } +} +</pre> + +<p class="note"><strong>注意:</strong>與 {@code FLAG_ACTIVITY_NEW_DOCUMENT} 旗標一起啟動的 Activity 務必要在宣示說明中設定 {@code android:launchMode="standard"} 屬性值 (預設)。 + +</p> + +<p>當主要 Activity 啟動新的 Activity 時,系統會透過現有工作搜尋其意圖和 Activity 意圖元件名稱及意圖資料相同的 Activity。 +如果找不到工作,或意圖已包含 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,則會建立新的工作並使用 Activity 做為其根目錄。 + +如果找到工作,則會將該工作帶到前面並傳送新的意圖到 {@link android.app.Activity#onNewIntent onNewIntent()}。 +新的 Activity 會取得意圖並在總覽視窗中建立新的文件,如下列範例所示: + +</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html">NewDocumentActivity.java +</a></p> +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_new_document); + mDocumentCount = getIntent() + .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0); + mDocumentCounterTextView = (TextView) findViewById( + R.id.hello_new_document_text_view); + setDocumentCounterText(R.string.hello_new_document_counter); +} + +@Override +protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity + is reused to create a new document. + */ + setDocumentCounterText(R.string.reusing_document_counter); +} +</pre> + + +<h3 id="#attr-doclaunch">使用 Activity 屬性來新增工作</h3> + +<p>Activity 也可以在宣示說明中指定為永遠啟動新工作,這可透過使用<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 屬性的 <a href="{@docRoot}guide/topics/manifest/activity-element.html#dlmode"> +{@code android:documentLaunchMode}</a> 達成。 + +此屬性有四個值,在使用者使用應用程式開啟文件時,會產生下列效果: +</p> + +<dl> + <dt>"{@code intoExisting}"</dt> + <dd>Activity 會對文件重複使用現有的工作。設定 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標,但「不」<em></em>設定 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,會與上述<a href="#flag-new-doc">使用意圖旗標來新增工作</a>達到相同效果。 +</dd> + + <dt>"{@code always}"</dt> + <dd>Activity 會為文件建立新的工作,就算文件已開始也會建立新的工作。使用此值與設定 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 與 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標會達到相同效果。 + +</dd> + + <dt>"{@code none”}"</dt> + <dd>Activity 不會為文件建立新的工作。總覽視窗會將 Activity 當作預設:會顯示應用程式的單一工作,該工作會從使用者最後呼叫的 Activity 繼續。 + +</dd> + + <dt>"{@code never}"</dt> + <dd>Activity 不會為文件建立新的工作。設定此值會覆寫 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 與 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標的行為,如果任一個已於意圖中設定,總覽視窗會顯示應用程式的單一工作,該工作會從使用者最後呼叫的 Activity 繼續。 + + + +</dd> +</dl> + +<p class="note"><strong>注意:</strong>如果值不是 {@code none} 與 {@code never},則 Activity 必須使用 {@code launchMode="standard"} 定義。 +如果沒有指定此屬性,則會使用 +{@code documentLaunchMode="none"}。</p> + +<h2 id="removing">移除工作</h2> + +<p>依照預設,當 Activity 結束時,會自動將文件工作從總覽畫面移除。 +您可以使用 {@link android.app.ActivityManager.AppTask} 類別,搭配 {@link android.content.Intent} 旗標或 <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> 屬性,來覆寫此行為。 +</p> + +<p>您可以完全從總覽畫面排除工作,設定方式為將 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 屬性的<a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude"> +{@code android:excludeFromRecents}</a> 設為 {@code true}。 +</p> + +<p>您可以設定應用程式可以納入總覽視窗的最大工作數目,方法是對 <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 屬性 <a href="{@docRoot}guide/topics/manifest/activity-element.html#maxrecents">{@code android:maxRecents}</a> 設定整數值。 + + +預設值為 16。當達到工作的最大值時,會從總覽視窗移除最近最少使用的工作。 +{@code android:maxRecents} 的最大值是 50 (在低記憶體裝置上是 25);數值不可以小於 1。 +</p> + +<h3 id="#apptask-remove">使用 AppTask 類別來移除工作</h3> + +<p>在總覽畫面建立新工作的 Activity 中,您可以指定何時移除工作與結束所有相關 Activity,方法為呼叫{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 方法。 + +</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html">NewDocumentActivity.java +</a></p> +<pre> +public void onRemoveFromRecents(View view) { + // The document is no longer needed; remove its task. + finishAndRemoveTask(); +} +</pre> + +<p class="note"><strong>注意:</strong>使用 +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 方法覆寫 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 標籤的使用,會在下面討論。 + +</p> + +<h3 id="#retain-finished">保留結束的工作</h3> + +<p>如果您要保留總覽畫面中的工作 (就算其 Activity 已結束), 方法為傳送啟動 Activity 的意圖其 {@link android.content.Intent#addFlags(int) addFlags()} 方法中的 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 旗標。 +</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html">DocumentCentricActivity.java +</a></p> +<pre> +private Intent newDocumentIntent() { + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; +} +</pre> + +<p>如要達到相同效果,可以設定 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 屬性的 <a href="{@docRoot}guide/topics/manifest/activity-element.html#autoremrecents"> +{@code android:autoRemoveFromRecents}</a> 為 {@code false}。 +對文件 Activity 的預設值為 {@code true},對定期 Activity 的預設值則為 {@code false}。 +如同之前的討論,使用此屬性可以覆寫 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 旗標。 +</p> + + + + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/services.jd b/docs/html-intl/intl/zh-tw/guide/components/services.jd new file mode 100644 index 000000000000..d6a440d88dd6 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=服務 +@jd:body + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>本文件內容</h2> +<ol> +<li><a href="#Basics">基本概念</a></li> +<ol> + <li><a href="#Declaring">在宣示說明中宣告服務</a></li> +</ol> +<li><a href="#CreatingAService">建立已啟動的服務</a> + <ol> + <li><a href="#ExtendingIntentService">延伸 IntentService 類別</a></li> + <li><a href="#ExtendingService">延伸服務類別</a></li> + <li><a href="#StartingAService">啟動服務</a></li> + <li><a href="#Stopping">停止服務</a></li> + </ol> +</li> +<li><a href="#CreatingBoundService">建立已繫結的服務</a></li> +<li><a href="#Notifications">傳送通知給使用者</a></li> +<li><a href="#Foreground">在前景中執行服務</a></li> +<li><a href="#Lifecycle">管理服務的生命週期</a> +<ol> + <li><a href="#LifecycleCallbacks">實作生命週期回呼</a></li> +</ol> +</li> +</ol> + +<h2>重要類別</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.app.IntentService}</li> +</ol> + +<h2>範例</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.html">{@code + ServiceStartArguments}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>另請參閱</h2> +<ol> +<li><a href="{@docRoot}guide/components/bound-services.html">已繫結的服務</a></li> +</ol> + +</div> + + +<p>{@link android.app.Service} 是可以在背景中長時間執行操作的應用程式元件,且不提供使用者介面。 +也是另一個可以啟動服務的應用程式元件,就算使用者切換至其他應用程式,也會繼續在背景中執行。 + +此外,元件也可以繫結至服務,以便與其互動,甚至執行處理程序間通訊 (IPC)。 +例如,服務可能處理網路交易、播放音樂、執行檔案輸入/輸出或與內容供應程式互動,這些都可以從背景執行。 + +</p> + +<p>服務基本上可以採取兩種形式:</p> + +<dl> + <dt>啟動</dt> + <dd>服務「啟動」表示應用程式元件 (例如 Activity) 透過呼叫 +{@link android.content.Context#startService startService()} 來啟動服務。一旦啟動,服務可以無限次數地在背景中執行,就算啟動服務的元件已終結也不會影響。 +通常,已啟動的服務會執行單一操作且不會將結果傳回呼叫端。例如,服務可能會透過網路下載或上傳檔案。 + +當操作完成時,服務應該會自行終結。 +</dd> + <dt>繫結</dt> + <dd>服務「繫結」表示應用程式元件透過呼叫 {@link +android.content.Context#bindService bindService()} 來繫結至服務。已繫結的服務提供主從式介面,讓元件可以與服務互動、傳送要求、取得結果,甚至可以使用處理程序間通訊 (IPC) 來跨程序達到目的。 + +已繫結的服務伴隨另一個繫結至服務的應用程式元件執行。 +多重元件可以一次繫結至服務,但是當所有元件都取消繫結時,服務就會被終結。 +</dd> +</dl> + +<p>雖然此文件通常分別討論服務的這兩種類別,但您的服務兩種方式都可以執行 — 可以啟動服務 (無限次數地執行) 也允許繫結。 +這僅與您是否實作兩種回呼方法而定:{@link +android.app.Service#onStartCommand onStartCommand()} 允許元件啟動服務,而 {@link +android.app.Service#onBind onBind()} 則允許繫結。 +</p> + +<p>不論您的應用程式是否啟動、繫結或兩者都有,任何應用程式元件可以使用服務 (就算來自不同的應用程式),與任何元件可以使用 Activity 的方式相同 — 使用 {@link android.content.Intent} 啟動服務。 + +然而,您可以宣告服務為私用、位於宣示說明檔案之中,與封鎖從其他應用程式存取。 +如需更多討論資訊,請詳見<a href="#Declaring">在宣示說明中宣告服務</a>。 + +</p> + +<p class="caution"><strong>注意:</strong>服務會在其託管程序的主執行緒中執行 — 服務<strong>不會</strong>建立自己的執行緒且<strong>不會</strong>在另外的程序中執行 (除非您另行指定)。 + +這代表如果您的服務即將從事任何 CPU 密集的作業或封鎖操作 (如播放 MP3 或連線網路),您應該在服務中建立新的執行緒來執行這些工作。 + +透過使用另外的執行緒,您會降低應用程式不回應 (ANR) 錯誤的風險,且應用程式的主執行緒仍可以專務於使用者與您的 Activity 互動。 + +</p> + + +<h2 id="Basics">基本概念</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>應該使用服務或執行緒?</h3> + <p>服務簡單而言就是可以在背景中執行的元件,就算使用者不與您的應用程式互動也不會影響。 +所以,您應該只在需要時才建立服務。 +</p> + <p>如果您必須在主執行緒外執行作業,但只有在使用者與您應用程式互動時才需要,則您可能應該建立新的執行緒而非服務。 +例如,如果您想要播放一些音樂,但只在執行您的 Activity 時播放,您可能在 +{@link android.app.Activity#onCreate onCreate()} 中建立執行緒,在 {@link +android.app.Activity#onStart onStart()} 中開始執行該執行緒,然後在 {@link android.app.Activity#onStop +onStop()} 中停止。 +也可以考慮使用 {@link android.os.AsyncTask} 或 {@link android.os.HandlerThread},來代替傳統的 {@link java.lang.Thread} 類別。 +如需更多有關執行緒的資訊,請參閱<a href="{@docRoot}guide/components/processes-and-threads.html#Threads">程序與執行緒</a>文件。 +</p> + <p>請記得如果您確實使用服務,依照預設會在您應用程式的主執行緒中執行該服務,所以,如果服務執行密集的操作或封鎖操作,則您仍應在服務中建立新的執行緒。 + +</p> +</div> +</div> + +<p>如要建立服務,您必須建立 {@link android.app.Service} 的子類別 (或是其現有的子類別之一)。 +在您的實作中,如果可以,您必須覆寫某些回呼方法,這些方法負責處理服務生命週期的關鍵層面,並提供元件繫結至服務的機制。 + +您應該覆寫的最重要回呼方法為:</p> + +<dl> + <dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt> + <dd>當另一個元件 (如 Activity) 透過呼叫 {@link android.content.Context#startService +startService()} 來要求啟動服務時,系統會呼叫此方法。 +一旦執行此方法,服務會被啟動且可以無限次數地在背景中執行。 +如果您實作此方法,當作業完成時,您必須負責停止服務,方式為呼叫 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()}。 +(如果您只想要提供繫結功能,您不需要實作此方法)。 +</dd> + <dt>{@link android.app.Service#onBind onBind()}</dt> + <dd>當其他元件想要與服務 (如執行 RPC) 繫結時,系統會透過呼叫 {@link android.content.Context#bindService +bindService()} 來呼叫此方法。 +在您實作此方法時,透過傳回 {@link android.os.IBinder},您必須提供用戶端可用來與服務通訊的介面。 +您必須永遠實作此方法,但如果您不想允許繫結,則您應該傳回 null。 +</dd> + <dt>{@link android.app.Service#onCreate()}</dt> + <dd>當第一次建立服務時,系統會呼叫此方法來執行一次性的設定程序 (在其呼叫 {@link android.app.Service#onStartCommand onStartCommand()} 或 +{@link android.app.Service#onBind onBind()} 之前)。 +如果已經執行服務,則不會呼叫此方法。 +</dd> + <dt>{@link android.app.Service#onDestroy()}</dt> + <dd>當不再使用服務且正在終結服務時,系統會呼叫此方法。您的服務應該實作此方法來清除任何如執行緒、註冊的接聽器,接收器等資源。 + +這會是服務接收到的最後呼叫。</dd> +</dl> + +<p>如果透過呼叫 {@link +android.content.Context#startService startService()} (這是呼叫至 {@link +android.app.Service#onStartCommand onStartCommand()} 的結果) 讓元件啟動服務,則服務仍會保持執行狀態,直到使用 {@link android.app.Service#stopSelf()} 讓服務自行停止或透過呼叫 +{@link android.content.Context#stopService stopService()} 由其他元件停止服務。 +</p> + +<p>如果元件呼叫 +{@link android.content.Context#bindService bindService()} 來建立服務 (且「沒有」呼叫 {@link +android.app.Service#onStartCommand onStartCommand()}<em></em>),則服務會伴隨繫結至服務的元件執行。 +一旦服務與所有用戶端解除繫結時,系統會終結服務。 +</p> + +<p>只有在記憶體不足且必須復原擁有使用者焦點的 Activity 其系統資源時,Android 系統才會強制停止服務。 +如果服務已繫結至擁有使用者焦點的 Activity,則該服務不太容易被終止,且如果服務被宣告為<a href="#Foreground">在前景中執行</a> (稍後會討論),則該服務幾乎不會被終止。 +否則,如果長時間執行於前景中啟動的服務,則系統會隨時間降低該服務在背景工作清單中的位置,且該服務會很容易被終止 — 如果已啟動您的服務,則您必須設計讓系統可以完美地處理重新啟動。 + + + +如果系統終止服務,則系統會在可以再度取得資源時立即重新啟動服務 (雖然這也會依據您從 {@link +android.app.Service#onStartCommand onStartCommand()} 傳回的值而有所不同,稍後會討論)。 +如需更多有關系統何時可能終結服務的資訊,請參閱<a href="{@docRoot}guide/components/processes-and-threads.html">程序與執行緒</a>文件。 + +</p> + +<p>在下列小節,您將看到您如何可以建立每種類型的服務與如何從不同應用程式元件使用。 +</p> + + + +<h3 id="Declaring">在宣示說明中宣告服務</h3> + +<p>就如同 Activity (與其他元件),您必須在您應用程式的宣示說明檔案中宣告所有服務。 +</p> + +<p>如要宣告您的服務,可以新增 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> 元素為 <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>元素的子項。 + +例如:</p> + +<pre> +<manifest ... > + ... + <application ... > + <service android:name=".ExampleService" /> + ... + </application> +</manifest> +</pre> + +<p>如需更多有關在宣示說明中宣告您服務的行系資訊,請參閱 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> 元素。 +</p> + +<p>您還可以在 <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> 元素中包含其他屬性,用來定義如啟動服務所需權限的屬性,與服務應該執行的程序。 + +<a href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a> 屬性是唯一必備的屬性 — 用來指定服務的類別名稱。 +一旦您發佈應用程式,您就不應該變更此名稱,因為如果這樣做,會由於依賴明確的意圖來啟動或繫結服務 (閱讀部落格貼文、<a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">不能變更的事項</a>),造成程式碼中斷的風險。 + + + + +<p>如要確認您的應用程式是安全的,<strong>當啟動或繫結您的 {@link android.app.Service} 時,永遠使用明確意圖</strong>,且不要宣告服務的意圖篩選器。 +如果允許一定程度的模糊來決定啟動的服務是重要的,您可以提供您的服務意圖篩選器,並從 {@link +android.content.Intent} 排除元件名稱,但您仍需使用 {@link +android.content.Intent#setPackage setPackage()} 設定意圖的封裝,這會提供目標服務足夠明確、避免含糊的指示。 + + +</p> + +<p>此外,您可以確保只有您的應用程式可以使用您的服務,方法為包含 <a href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a> 屬性並將其設定為 {@code "false"}。 + +這可以有效地避免其他應用程式啟動您的服務,就算使用明確意圖時也是如此。 +</p> + + + + +<h2 id="CreatingStartedService">建立已啟動的服務</h2> + +<p>已啟動的服務是由其他應用程式呼叫 {@link +android.content.Context#startService startService()} 所啟動的,由呼叫至服務的 +{@link android.app.Service#onStartCommand onStartCommand()} 方法所導致。</p> + +<p>當服務啟動,其生命週期與啟動服務的元件無關,且服務可以無限次數地在背景中執行,就算啟動服務的元件已終結也不會影響。 + +因此,當服務的工作藉由呼叫 {@link android.app.Service#stopSelf stopSelf()} 而完成時,服務應該會自行停止,或藉由呼叫 {@link android.content.Context#stopService stopService()},其他元件也可以停止服務。 + +</p> + +<p>如 Activity 等應用程式元件可以透過呼叫 {@link +android.content.Context#startService startService()} 與傳送用來指定服務及包含服務所要使用任何資料的 +{@link android.content.Intent},來啟動服務。服務會接收 {@link android.app.Service#onStartCommand +onStartCommand()} 方法中的此 {@link android.content.Intent}。 +</p> + +<p>例如,假設 Activity 需要一些資料到線上資料庫。藉由傳送意圖至 {@link +android.content.Context#startService startService()},Activity 可以啟動伴隨服務並傳送要儲存的資料。 +服務會接收 {@link +android.app.Service#onStartCommand onStartCommand()} 中的意圖,連線到網際網路,並執行資料庫交易。 +當操作完成時,服務應該會自行終結。 +</p> + +<p class="caution"><strong>注意:</strong>服務會在與應用程式相同的程序中執行,服務會在該程序中被宣告,且依照預設,會位於該應用程式的主執行緒之中。 +所以,在使用者與來自相同應用程式的 Activity 互動時,如果您的服務執行密集的操作或封鎖操作,則該服務將會降低 Activity 的效能。 + +要避免影響應用程式的效能,您應該在服務之中啟動新的執行緒。 +</p> + +<p>傳統上,有兩種類別可以延伸為建立已啟動的服務:</p> +<dl> + <dt>{@link android.app.Service}</dt> + <dd>這是所有服務的基本類別。當您延伸此類別時,建立用來執行所有服務工作的新執行緒是非常重要的,因為依照預設,服務會使用您應用程式的主執行緒,這會降低任何您應用程式所執行 Activity 的效能。 + + +</dd> + <dt>{@link android.app.IntentService}</dt> + <dd>這是 {@link android.app.Service} 的子類別,會使用 worker 執行緒來處理所有的啟動要求,一次一個。 +如果您不需要您的服務同時處理多個要求,則這是最佳的選項。 +您唯一要做的就是實作 {@link +android.app.IntentService#onHandleIntent onHandleIntent()},這會接收每個起始要求的意圖 ,所以您可以在背景執行。 +</dd> +</dl> + +<p>下列小節說明如何可以使用這些類別之一來實作您的服務。 +</p> + + +<h3 id="ExtendingIntentService">延伸 IntentService 類別</h3> + +<p>因為大多數的已啟動服務不需要同時處理多個要求(多重執行緒策略事實上是危險的),如果您使用 {@link android.app.IntentService} 類別來實作您的服務可能是最佳的方法。 + +</p> + +<p>{@link android.app.IntentService} 會達到下列目的:</p> + +<ul> + <li>建立預設的 worker 執行緒,該執行緒會執行所有傳送至 {@link +android.app.Service#onStartCommand onStartCommand()} 的意圖,並與您應用程式的主執行緒有所分別。 +</li> + <li>建立工作佇列,該佇列會一次傳送一個意圖到您的 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 實作,所以您絕對不需要擔心多重執行緒的問題。 +</li> + <li>在所有啟動要求都已處理後,停止服務,這樣您永遠不需要呼叫 +{@link android.app.Service#stopSelf}。</li> + <li>提供傳回 null 的 {@link android.app.IntentService#onBind onBind()} 其預設實作。 +</li> + <li>提供傳送意圖至工作佇列的 {@link android.app.IntentService#onStartCommand +onStartCommand()} 其預設實作,然後傳送至您的 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 實作。</li> +</ul> + +<p>所有這一切都說明了您要做的就是實作 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 來完成用戶端提供的工作。 +(雖然,您也需要提供小型建構函式給服務)。</p> + +<p>下列是 {@link android.app.IntentService} 實作的範例:</p> + +<pre> +public class HelloIntentService extends IntentService { + + /** + * A constructor is required, and must call the super {@link android.app.IntentService#IntentService} + * constructor with a name for the worker thread. + */ + public HelloIntentService() { + super("HelloIntentService"); + } + + /** + * The IntentService calls this method from the default worker thread with + * the intent that started the service. When this method returns, IntentService + * stops the service, as appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + } +} +</pre> + +<p>您需要的是:{@link +android.app.IntentService#onHandleIntent onHandleIntent()} 的建構函式與實作。</p> + +<p>如果您決定也要覆寫其他回呼方法,如 {@link +android.app.IntentService#onCreate onCreate()}、{@link +android.app.IntentService#onStartCommand onStartCommand()} 或 {@link +android.app.IntentService#onDestroy onDestroy()},請確認呼叫超級實作,這樣 +{@link android.app.IntentService} 才可以適當處理 worker 執行緒的生命。</p> + +<p>例如,{@link android.app.IntentService#onStartCommand onStartCommand()} 必須傳回預設的實作 (這就是意圖如何被傳送至 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}): +</p> + +<pre> +@Override +public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + return super.onStartCommand(intent,flags,startId); +} +</pre> + +<p>除了 {@link android.app.IntentService#onHandleIntent onHandleIntent()},您不需要呼叫超級類別的唯一方法就是 {@link android.app.IntentService#onBind +onBind()} (但只有在您的服務允許繫結時才需要實作)。 +</p> + +<p>在下一節,您會看到在延伸基本 {@link android.app.Service} 類別時,如何實作同類的服務,這需要寫更多程式碼,但是如果您需要同時處理多個啟動要求時,這可能是比較合適的處理方式。 + +</p> + + +<h3 id="ExtendingService">延伸服務類別</h3> + +<p>如同您在前一節看到的,使用 {@link android.app.IntentService} 可讓您已啟動服務的實作變得非常容易。 +然而,如果您要求您的服務執行多重執行緒 (取代透過工作佇列處理啟動要求),則您可以延伸 {@link android.app.Service} 類別來處理每個意圖。 + +</p> + +<p>為了對比之用,下列程式碼範例是 {@link +android.app.Service} 類別的實作,該類別執行與上述使用 {@link +android.app.IntentService} 範例相同的工作。也就是,對每個啟動要求,會使用 worker 執行緒來執行工作,且一次只處理一個要求。 +</p> + +<pre> +public class HelloService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>如同您看到的,比起使用 {@link android.app.IntentService},這需要花更多功夫。</p> + +<p>然而,因為您自行處理每個對 {@link android.app.Service#onStartCommand +onStartCommand()} 的呼叫,您可以同時執行多個要求。那不是這個範例要做的,但如果那是您想要的,那麼您可以針對每個要求建立新的執行緒,並立即執行 (代替等待之前的要求結束)。 + +</p> + +<p>請注意:{@link android.app.Service#onStartCommand onStartCommand()} 方法必須傳回整數。 +整數是一個數值,說明在系統終止服務的事件中,系統該如何繼續服務 (如上述的討論,雖然您可以修改,但 {@link +android.app.IntentService} 的預設實作會替您處理)。 +從 +{@link android.app.Service#onStartCommand onStartCommand()} 傳回的值必須是下列常數之一: +</p> + +<dl> + <dt>{@link android.app.Service#START_NOT_STICKY}</dt> + <dd>如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,除非有待決的意圖要傳送,否則請「不要」<em></em>建立服務。 +在非必要時與應用程式可以簡單地重新啟動任何未完成的工作時,這是避免執行服務的最安全方式。 +</dd> + <dt>{@link android.app.Service#START_STICKY}</dt> + <dd>如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,請重新建立服務並呼叫 {@link +android.app.Service#onStartCommand onStartCommand()},但「不要」<em></em>重新傳送最後的意圖。 +相反地,除非有待決的意圖要啟動服務傳送,否則系統會使用 null 意圖呼叫 {@link android.app.Service#onStartCommand onStartCommand()},如果有待決的意圖要啟動服務,則會傳送那些意圖。 + +這適用於媒體播放程式 (或類似服務) 這類不執行命令,但可以無次數限制執行與等待工作的服務。 +</dd> + <dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt> + <dd>如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,請重新建立服務並使用傳送至服務的最後意圖呼叫 {@link +android.app.Service#onStartCommand onStartCommand()}。 +任何待決的意圖會反過來由後往前傳送。這適用的服務為主動執行如下載檔案等應該立即繼續的工作。 +</dd> +</dl> +<p>如需有關這些傳回值的詳細資訊,請參閱每個常數的連結參考文件。 +</p> + + + +<h3 id="StartingAService">啟動服務</h3> + +<p>您可以透過傳送 +{@link android.content.Intent} (指定要啟動的服務) 到 {@link +android.content.Context#startService startService()},從 Activity 或其他應用程式元件來啟動服務。Android 系統會呼叫服務的 {@link +android.app.Service#onStartCommand onStartCommand()} 方法並傳送 {@link +android.content.Intent} 給該方法。(絕對不要直接呼叫 {@link android.app.Service#onStartCommand +onStartCommand()})。</p> + +<p>例如,使用明確意圖並搭配 {@link android.content.Context#startService +startService()},Activity 可以啟動前小節範例中的服務 ({@code +HelloSevice}):</p> + +<pre> +Intent intent = new Intent(this, HelloService.class); +startService(intent); +</pre> + +<p>{@link android.content.Context#startService startService()} 方法會立即回傳且Android 系統會呼叫服務的 {@link android.app.Service#onStartCommand +onStartCommand()} 方法。 +如果尚未開始執行服務,系統會先呼叫 {@link +android.app.Service#onCreate onCreate()},然後呼叫 {@link android.app.Service#onStartCommand +onStartCommand()}。</p> + +<p>如果服務也不提供繫結,則與意圖一同傳送的 {@link +android.content.Context#startService startService()} 是在應用程式元件與服務間通訊的唯一模式。 +然而,如果您想要服務將結果送回,則啟動服務的用戶端會針對廣播建立 {@link android.app.PendingIntent}(搭配 {@link android.app.PendingIntent#getBroadcast getBroadcast()}) 並將其傳送至服務,該服務在啟動服務的 {@link android.content.Intent} 之中。 + + +然後服務就可使用廣播來傳送結果。 +</p> + +<p>多個啟動服務的要求會導致多個相關呼叫至服務的 +{@link android.app.Service#onStartCommand onStartCommand()}。然而,只允許一個要求可以停止服務 (使用 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()})。 +</p> + + +<h3 id="Stopping">停止服務</h3> + +<p>已啟動的服務必須管理本身的生命週期。也就是,系統不會停止或終結服務,除非系統必須復原系統記憶體,而服務會在 {@link android.app.Service#onStartCommand onStartCommand()} 回傳後繼續執行。 + +所以,服務務必自我停止,可透過呼叫 {@link android.app.Service#stopSelf stopSelf()} 達成,或透過呼叫 {@link android.content.Context#stopService stopService()} 讓其他元件可以停止服務。 + +</p> + +<p>一旦要求使用 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 停止,系統會盡快終結服務。 +</p> + +<p>然而,如果您的服務同時處理多個對 {@link +android.app.Service#onStartCommand onStartCommand()} 的要求,則在您開始處理啟動要求時,您不應該停止服務,因為您仍可能會收到新的啟動要求 (在第一個要求結束時停止可能會終止第二個要求)。 + +如要避免發生此問題,您可以使用 {@link android.app.Service#stopSelf(int)} 來確保您停止服務的要求會總是基於最新的啟動要求。 + +也就是,當您呼叫 {@link +android.app.Service#stopSelf(int)} 時,會傳送啟動要求的 ID (<code>startId</code> 已傳送至 {@link android.app.Service#onStartCommand onStartCommand()}) 至您相關的停止要求。 + +然而,如果服務在您可以呼叫 {@link +android.app.Service#stopSelf(int)} 之前,就收到新的啟動要求,則 ID 將不會相符,服務也不會停止。</p> + +<p class="caution"><strong>注意:</strong>在您的應用程式完成工作時,應用程式停止其服務是非常重要的,這可以避免浪費系統資源與消耗電池電力。 +如果必要,透過呼叫 {@link +android.content.Context#stopService stopService()},其他元件可以停止服務。 +就算您啟動服務的繫結,如果曾接收對 {@link +android.app.Service#onStartCommand onStartCommand()} 的呼叫,您永遠仍必須自行停止服務。 +</p> + +<p>如需有關服務生命週期的詳細資訊,請參閱下節<a href="#Lifecycle">管理服務的生命週期</a>。</p> + + + +<h2 id="CreatingBoundService">建立已繫結的服務</h2> + +<p>已繫結的服務是允許應用程式元件透過呼叫 {@link +android.content.Context#bindService bindService()} 來建立繫結的服務,這是為了建立長期的連線 (一般而言,並不允許元件透過呼叫 {@link +android.content.Context#startService startService()} 來「啟動」<em></em> )。 +</p> + +<p>當您想要透過處理程序間通訊 (IPC),與來自 Activity 的服務及您應用程式中的其他元件互動時,或是想要揭露您應用程式的特定功能給其他應用程式時,您應該建立已繫結的服務。 + +</p> + +<p>如要建立已繫結的服務,您必須實作 {@link +android.app.Service#onBind onBind()} 回呼方法並傳回 +{@link android.os.IBinder} (這用來定義與服務溝通的介面)。接著其他應用程式元件可以呼叫 +{@link android.content.Context#bindService bindService()} 來擷取介面並開始服務上的呼叫方法。 +服務只會為了服務所繫結應用程式元件才存在, +所以當沒有繫結服務的元件時,系統會終結服務 (當服務透過 +{@link android.app.Service#onStartCommand onStartCommand()} 啟動時,您「不」<em></em>需要停止已繫結的服務)。 +</p> + +<p>如要建立已繫結的服務,您必須做的第一件事就是定義用來指定用戶端如何與服務通訊的介面。 +介於服務與用戶端間的介面必須是 {@link android.os.IBinder} 的實作,也是您服務必須從 {@link android.app.Service#onBind +onBind()} 回呼方法傳回的。 + +一旦用戶端收到 {@link android.os.IBinder},可以透過介面開始與服務互動。 +</p> + +<p>服務一次可以繫結多個用戶端。當用戶端完成與服務互動時,會呼叫 {@link android.content.Context#unbindService unbindService()} 來取消繫結。 +一旦沒有用戶端與服務繫結,系統就會終結服務。 +</p> + +<p>有多個方法可以實作已繫結的服務,且實作比已啟動服務更加複雜,所以會在另一份有關<a href="{@docRoot}guide/components/bound-services.html">已繫結的服務</a>文件中討論已繫結的服務。 + +</p> + + + +<h2 id="Notifications">傳送通知給使用者</h2> + +<p>一旦執行,服務可以使用<a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">快顯通知</a>或<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">狀態列通知</a>來通知事件的使用者。</p> + +<p>快顯通知是一個會出現在目前視窗表面上短暫時間然後消失的訊息,此時狀態列通知會在狀態列提供一個圖示與訊息,使用者可以選擇然後採取行動 (例如啟動 Activity)。 + +</p> + +<p>通常,當已完成某些背景作業(如完成檔案下載) 且使用者現在可以採取行動時,狀態列通知是最佳的方法。 + +當使用者從擴展的檢視選取通知時,通知可以啟動 Activity (如檢視已下載檔案)。 +</p> + +<p>如需更多資訊,請參閱<a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">快顯通知</a>或<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">狀態列通知</a>開發人員指南。 +</p> + + + +<h2 id="Foreground">在前景中執行服務</h2> + +<p>前景服務是被認為使用者已主動意識到、就算在記憶體不足時系統也不會終結的服務。 +前景服務必須提供通知給狀態列,狀態列會放置在「進行中」標題之下,這表示除非服務已被停止或從前景移除,否則無法解除通知。 + + +</p> + +<p>例如,播放來自某服務音樂的音樂播放器應該被設為在前景中執行,因為使用者明確意識到這項操作。 + +狀態列中的通知可能會指出目前播放的歌曲,並允許使用者啟動 Activity 來與音樂播放器互動。 +</p> + +<p>如要要求您的服務在前景中執行,可以呼叫 {@link +android.app.Service#startForeground startForeground()}。此方法有兩個參數:一個整數用來唯一識別通知,以及 {@link +android.app.Notification} 供狀態列使用。 +例如:</p> + +<pre> +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), + System.currentTimeMillis()); +Intent notificationIntent = new Intent(this, ExampleActivity.class); +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); +notification.setLatestEventInfo(this, getText(R.string.notification_title), + getText(R.string.notification_message), pendingIntent); +startForeground(ONGOING_NOTIFICATION_ID, notification); +</pre> + +<p class="caution"><strong>注意:</strong>您給 {@link +android.app.Service#startForeground startForeground()} 的整數 ID 不能為 0。</p> + + +<p>如要從前景移除服務,可以呼叫 {@link +android.app.Service#stopForeground stopForeground()}。此方法有一個布林數,表示是否同時移除狀態列通知。 +此方法「不會」<em></em>停止服務。 +然而,如果您在服務仍於前景執行時停止服務,則也會移除通知。 +</p> + +<p>如需更多有關通知的資訊,請參閱<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">建立狀態列通知</a>。 +</p> + + + +<h2 id="Lifecycle">管理服務的生命週期</h2> + +<p>服務的生命週期比 Activity 的生命週期要簡單多了。然而,密切關注如何建立與終結服務就更重要了,因為在使用者沒有意識到的狀況下可在背景中執行服務。 + +</p> + +<p>服務生命週期 — 從何時建立到何時終結 — 可以遵循兩種不同的路徑: +</p> + +<ul> +<li>已啟動的服務 + <p>當其他元件呼叫 {@link +android.content.Context#startService startService()} 時,會建立服務。然後服務可無限次數執行,且必須自行停止,方法是呼叫 {@link +android.app.Service#stopSelf() stopSelf()}。 +透過呼叫 {@link android.content.Context#stopService +stopService()},其他元件可以停止服務。 +當服務停止時,系統會終結服務。</p></li> + +<li>已繫結的服務 + <p>當其他元件 (一個用戶端) 呼叫 {@link +android.content.Context#bindService bindService()} 時,會建立服務。然後用戶端會透過 +{@link android.os.IBinder} 介面與服務通訊。用戶端也可以呼叫 +{@link android.content.Context#unbindService unbindService()} 來切斷連線。多重用戶端可以繫結至相同的服務,但是當所有用戶端都取消繫結時,系統就會終結服務。 +(服務「不」<em></em>需要自行停止)。 +</p></li> +</ul> + +<p>這兩種路徑不是完全獨立的。也就是,您可以繫結至已經使用 +{@link android.content.Context#startService startService()} 啟動的服務。例如,可以啟動背景音樂服務,方法是呼叫 {@link android.content.Context#startService +startService()} 並搭配可識別要播放音樂的 {@link android.content.Intent}。 +之後,使用者可能會想要透過播放器試試某些控制或取得關於目前歌曲的資訊,可以將 Activity 繫結至服務,方法為呼叫 {@link +android.content.Context#bindService bindService()}。 + +就這個的案子而言,除非所有的用戶端都取消繫結,否則 {@link +android.content.Context#stopService stopService()} 或 {@link android.app.Service#stopSelf +stopSelf()} 不會實際上停止服務。 </p> + + +<h3 id="LifecycleCallbacks">實作生命週期回呼</h3> + +<p>就如同 Activity,服務有您可以實作來監控服務狀態變更與在適合時段執行作業的生命週期回呼方法。 +下列服務會示範每個生命週期方法: +</p> + +<pre> +public class ExampleService extends Service { + int mStartMode; // indicates how to behave if the service is killed + IBinder mBinder; // interface for clients that bind + boolean mAllowRebind; // indicates whether onRebind should be used + + @Override + public void {@link android.app.Service#onCreate onCreate}() { + // The service is being created + } + @Override + public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) { + // The service is starting, due to a call to {@link android.content.Context#startService startService()} + return <em>mStartMode</em>; + } + @Override + public IBinder {@link android.app.Service#onBind onBind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()} + return <em>mBinder</em>; + } + @Override + public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) { + // All clients have unbound with {@link android.content.Context#unbindService unbindService()} + return <em>mAllowRebind</em>; + } + @Override + public void {@link android.app.Service#onRebind onRebind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()}, + // after onUnbind() has already been called + } + @Override + public void {@link android.app.Service#onDestroy onDestroy}() { + // The service is no longer used and is being destroyed + } +} +</pre> + +<p class="note"><strong>注意:</strong>不像 Activity 生命週期回呼方法,您「不」<em></em>需要呼叫這些回呼方法的超級類別實作。 +</p> + +<img src="{@docRoot}images/service_lifecycle.png" alt="" /> +<p class="img-caption"><strong>圖 2.</strong>服務生命週期。左圖顯示使用 {@link android.content.Context#startService +startService()} 建立服務的生命週期,右圖顯示使用 +{@link android.content.Context#bindService bindService()} 建立服務的生命週期。 +</p> + +<p>透過實作這些方法,您可以監控服務生命週期中的兩個巢狀迴圈: </p> + +<ul> +<li>服務的<strong>整個生命</strong>發生在 {@link +android.app.Service#onCreate onCreate()} 被呼叫的時間與 {@link +android.app.Service#onDestroy} 回傳的時間之間。就像 Activity,服務於 +{@link android.app.Service#onCreate onCreate()} 中開始其初始設定,並於 {@link +android.app.Service#onDestroy onDestroy()} 中釋出所有剩餘的資訊。例如,音樂播放服務可以建立執行緒,且音樂會於 {@link +android.app.Service#onCreate onCreate()} 中播放,然後在 {@link +android.app.Service#onDestroy onDestroy()} 中停止執行緒。 + + +<p>{@link android.app.Service#onCreate onCreate()} 與 {@link android.app.Service#onDestroy +onDestroy()} 方法可供所有服務呼叫,不論服務是否由 {@link android.content.Context#startService startService()} 或 {@link +android.content.Context#bindService bindService()} 所建立。 +</p></li> + +<li>服務的<strong>使用生命</strong>從對 {@link +android.app.Service#onStartCommand onStartCommand()} 或 {@link android.app.Service#onBind onBind()} 的呼叫開始。 +每個方法都被遞交 {@link +android.content.Intent},也會被分別傳送至 {@link android.content.Context#startService +startService()} 或 {@link android.content.Context#bindService bindService()}。 +<p>如果服務已啟動,使用生命的結束時間與整個生命結束的時間相同 (就算在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後,服務仍在作用中)。 +如果服務已繫結,使用生命的會於 {@link +android.app.Service#onUnbind onUnbind()} 回傳時結束。</p> +</li> +</ul> + +<p class="note"><strong>注意:</strong>雖然已啟動的服務可藉由對 + {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 的呼叫來停止,但沒有服務的相對應回呼 (沒有 {@code onStop()} 回呼)。 +所有,除非服務已繫結至用戶端,系統會在服務停止時終結服務 — {@link +android.app.Service#onDestroy onDestroy()} 是唯一收到的回呼。 +</p> + +<p>圖 2 說明服務的回呼方法。雖然圖示區隔由 +{@link android.content.Context#startService startService()} 所建立的服務與由 +{@link android.content.Context#bindService bindService()} 所建立的服務,但請記住,任何服務不論是如何建立的,都可能可以允許使用者繫結。 + +所以,一開始使用 {@link android.app.Service#onStartCommand +onStartCommand()} 啟動的服務 (由用戶端呼叫 {@link android.content.Context#startService startService()})仍可以接收對 {@link android.app.Service#onBind onBind()} 的呼叫 (當用戶端呼叫 +{@link android.content.Context#bindService bindService()} 時)。 +</p> + +<p>針對建立提供已繫結的服務,如需更多詳細資訊,請參閱<a href="{@docRoot}guide/components/bound-services.html">已繫結的服務</a>文件,其中在<a href="{@docRoot}guide/components/bound-services.html#Lifecycle">管理已繫結服務的生命週期</a>包含更多有關 {@link android.app.Service#onRebind onRebind()}回呼方法的詳細資訊。 + + +</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>To learn how to query data from the system or other applications (such as contacts or media +stored on the device), continue with the <b><a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></b> +document.</p> +--> diff --git a/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd new file mode 100644 index 000000000000..e23301d641bc --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=工作和返回堆疊 +parent.title=Activity +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> +<li><a href="#ActivityState">儲存 Activity 狀態</a></li></li> +<li><a href="#ManagingTasks">管理工作</a> + <ol> + <li><a href="#TaskLaunchModes">定義啟動模式</a></li> + <li><a href="#Affinities">處理親和性</a></li> + <li><a href="#Clearing">清除返回堆疊</a></li> + <li><a href="#Starting">開始工作</a></li> + </ol> +</li> +</ol> + +<h2>文章</h2> +<ol> + <li><a href="http://android-developers.blogspot.com/2010/04/multitasking-android-way.html"> Android 的多工作業方式 +</a></li> +</ol> + +<h2>另請參閱</h2> +<ol> + <li><a href="{@docRoot}design/patterns/navigation.html">Android 設計:導覽 +</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>} 宣示說明元素 +</a></li> + <li><a href="{@docRoot}guide/components/recents.html">總覽畫面</a></li> +</ol> +</div> +</div> + + +<p>一個應用程式通常包含多個 <a href="{@docRoot}guide/components/activities.html">Activity</a>。每個 Activity 應根據使用者可執行的特定動作類型加以設計,且要能啟動其他 Activity。 + +例如,電子郵件應用程式可能會有一個可顯示新訊息清單的 Activity。 +當使用者選擇一則訊息,會開啟新的 Activity 以檢視該訊息。</p> + +<p>Activity 甚至可啟動裝置上其他應用程式的 Activity。例如,如果您的應用程式想要傳送一封電子郵件訊息,您可以定義一個意圖以執行「傳送」動作並包含一些資料,像是電子郵件地址和訊息。 + +其他應用程式中宣告處理此意圖類型的 Activity 就會開啟。 +在這種情況下,意圖就是要傳送電子郵件,因此電子郵件應用程式會啟動「撰寫」Activity (如果有多個 Activity 支援相同的意圖,則系統會讓使用者選擇要使用的 Activity)。 + +電子郵件傳送後,您的 Activity 就會繼續,並將電子郵件 Activity 視為您應用程式的一部分。 +雖然 Activity 可能來自不同的應用程式,但 Android 會將兩個 Activity 放在相同的工作中,以維護使用者體驗的流暢性。 + +<em></em></p> + +<p>工作是執行特定工作時,與使用者互動的 Activity 集合。 +Activity 會在堆疊 (返回堆疊<em></em>) 中依照每個 Activity 開啟的順序加以排列。 +</p> + +<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Adding fragments to a task's back stack</h3> + +<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example, +suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the +other being a layout to display an item from the list (fragment B). When the user selects an item +from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be +desireable for the user to navigate back to reveal fragment B, using the <em>Back</em> button.</p> +<p>In order to add fragment B to the back stack so that this is possible, you must call {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link +android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment +C.</p> +<p>For more information about using fragments and adding them to the back stack, see the {@link +android.app.Fragment} class documentation.</p> + +</div> +</div> +--> + +<p>裝置主螢幕是大多數工作開始的地方。當使用者輕觸應用程式啟動組件上的某個圖示 (或主螢幕上的捷徑 ) 時,應用程式工作會移到前景。 + +如果應用程式沒有工作 (最近未使用應用程式),則會建立新的工作,且該應用程式的「主要」Activity 會以堆疊中的根 Activity 形式開啟。 + +</p> + +<p>當目前的 Activity 啟動另一個 Activity 時,會將新的 Activity 推到堆疊的頂端並取得焦點。 +之前的 Activity 會留在堆疊中,但已停止。Activity 停止後,系統會保留其使用者介面的目前狀態。 +當使用者按下 [返回] 按鈕<em></em>,會將目前的 Activity 從堆疊頂端推出 (Activity 已終結),並繼續進行之前的 Activity (還原其 UI 之前的狀態)。 + + +堆疊中的 Activity 不會重新整理,只會從堆疊推入和推出 — 由目前 Activity 啟動時推入堆疊,而當使用者使用 [返回] 按鈕離開時推出堆疊。<em></em> + +因此,返回堆疊會以「後進先出」的物件結構進行運作。 + +圖 1 透過時間軸將此行為視覺化,以時間軸顯示 Activity 間的進度以及每個時間點的目前返回堆疊。 + +</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>顯示工作中每個新 Activity 如何將項目新增到返回堆疊。 +當使用者按下 [返回] 按鈕,目前的 Activity 將會終結,而之前的 Activity 則會繼續進行。<em></em> + +</p> + + +<p>如果使用者持續按下 [返回]<em></em>,則會持續推出堆疊中的每個 Activity 以顯示之前的 Activity,直到使用者返回主螢幕 (或到工作開始時執行的 Activity)。 + + +當堆疊中的 Activity 全部移除後,工作將不再存在。</p> + +<div class="figure" style="width:287px"> +<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p +class="img-caption"><strong>圖 2.</strong>兩個工作:工作 B 在前景收到使用者互動,而工作 A 在背景等待繼續。 +</p> +</div> +<div class="figure" style="width:215px"> + <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p +class="img-caption"><strong>圖 3.</strong>單一 Activity 會具現化很多次。</p> +</div> + +<p>工作是一個緊密結合的單位,當使用者開始新的工作時可以移到「背景」,或透過 [首頁] 按鈕前往主螢幕。<em></em> +在背景時,工作中的所有 Activity 都會停止,但該工作的返回堆疊會保留下來 — 該工作純粹失去焦點,由另一個工作取而代之,如圖 2 所示。 + + +之後,工作可以返回「前景」,讓使用者繼續未完成的工作。 +例如,假設目前的工作 (工作 A) 的堆疊中有三個 Activity — 兩個位於目前的 Activity 下。 +使用者按下 [首頁] 按鈕,<em></em>然後從應用程式啟動新的應用程式。 + +當主螢幕出現時,工作 A 會移到背景。 +新的應用程式啟動時,系統會啟動該應用程式的工作 (工作 B),該應用程式會有自己的 Activity 堆疊。 +與該應用程式互動之後,使用者會再次回到首頁,並選取原來啟動工作 A 的應用程式。現在,工作 A 移到了前景 — 堆疊中的三個 Activity 全部保持不變,而堆疊中最頂端的 Activity 則會繼續進行。 + + + +此時,使用者也能切換回工作 B,前往首頁並選取啟動該工作的應用程式圖示 (或從<a href="{@docRoot}guide/components/recents.html">總覽畫面</a>選取應用程式工作)。這是在 Android 執行多工作業的範例。 + + + +</p> + +<p class="note"><strong>注意:</strong>背景可以一次執行多個工作。 +不過,如果使用者同時執行多個背景工作,系統可能會開始終結背景 Activity 以復原記憶體,導致 Activity 狀態遺失。 +請參閱下列有關 <a href="#ActivityState">Activity 狀態</a>的章節。 +</p> + +<p>由於返回堆疊中的 Activity 不會重新整理,如果您的應用程式允許使用者從一個以上的 Activity 中啟動特定 Activity,則會建立該 Activity 的新執行個體並推入堆疊 (而不會將 Activity 任何之前的執行個體移到最頂端)。 + + +因此,您應用程式中的一個 Activity 可能會具現化很多次 (甚至來自不同的工作),如圖 3 所示。 +也因為這樣,如果使用者使用 [返回] 按鈕瀏覽之前的資訊,Activity 的每個執行個體會依開啟的順序顯示<em></em> (每個會有自己的 UI 狀態)。 + + +不過,如果您不希望 Activity 具現化一次以上,則可以修改這個行為。 +如需詳細步驟,請參閱下文的<a href="#ManagingTasks">管理工作</a>。</p> + + +<p>摘要說明 Activity 和工作的預設行為:</p> + +<ul> + <li>當 Activity A 啟動 Activity B,Activity A 會停止,但系統會保留其狀態(例如捲軸位置和輸入表單的文字)。 + +如果使用者在 Activity B 按下 [返回] 按鈕,<em></em>Activity A 的狀態會復原並繼續執行。 +</li> + <li>當使用者按下 [首頁] 按鈕離開工作,<em></em>目前的 Activity 會停止且其工作會移到背景。 + +系統會保留工作中所有 Activity 的狀態。如果使用者稍後選取啟動工作的啟動組件圖示繼續執行工作,工作會移到前景並在堆疊頂端繼續執行 Activity。 + +</li> + <li>如果使用者按下 [返回] 按鈕<em></em>,會將目前的 Activity 從堆疊推出並終結。 + +堆疊中之前的 Activity 會繼續進行。Activity 終結後,系統將不會保留 Activity 的狀態。 +<em></em></li> + <li>Activity 可以具現化很多次,即使來自其他工作也一樣。</li> +</ul> + + +<div class="note design"> +<p><strong>導覽設計</strong></p> + <p>如需應用程式導覽如何在 Android 運作的詳細資訊,請參閱 Android 設計的<a href="{@docRoot}design/patterns/navigation.html">導覽</a>指南。</p> +</div> + + +<h2 id="ActivityState">儲存 Activity 狀態</h2> + +<p>如上所述,系統的預設行為會在 Activity 停止時保留 Activity 的狀態。 +如此一來,當使用者瀏覽之前的 Activity 時,其使用者介面的顯示方式將與離開時一樣。 +不過,您可以 — 也<strong>應該</strong> — 使用回呼方法主動保留 Activity 的狀態,以防止 Activity 遭到終結且必須重新建立的情況。 + +</p> + +<p>如果系統停止您其中一個 Activity (例如,當新的 Activity 開始或工作移到背景),當系統需要復原系統記憶體時,可能會完全終結該 Activity。 + +發生這種情況時,與 Activity 狀態相關的資訊都將遺失。如果發生這種情況,系統仍然知道該 Activity 位於返回堆疊中,但是當 Activity 移到堆疊頂端時,系統必須重新建立該 Activity (而不是繼續執行)。 + + +如果不想讓使用者的工作遺失,您應該在 Activity 中實作 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 回呼方法,主動保留該工作。 + + +</p> + +<p>如需儲存 Activity 狀態的詳細資訊,請參閱 <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Activity</a> 文件。 +</p> + + + +<h2 id="ManagingTasks">管理工作</h2> + +<p>如上所述,Android 管理工作和返回堆疊的方式 — 將連續啟動的所有 Activity 放在相同的工作及「後進先出」堆疊中 — 對大多數應用程式而言非常好用,而且您不需擔心 Activity 與工作關聯的方式或它們如何存在於返回堆疊中。 + + +不過,您也許會想中斷一般的行為。 +您或許會希望應用程式的 Activity 可以在啟動時開始一個新的工作 (而不是放入目前的工作中);或者當您啟動一個 Activity 時,您可能會想使用其現有的執行個體 (而不是在返回堆疊頂端建立新的執行個體);又或者當使用者離開工作時,您想要清除返回堆疊中的所有 Activity,只留下根 Activity。 + + + +</p> + +<p>如要執行這些工作及更多工作,您可以使用 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 宣示說明元素中的屬性,以及您傳送到 {@link android.app.Activity#startActivity startActivity()} 之意圖中的旗標。 + + +</p> + +<p>就這個情況而言,您可以使用的主要 <a href="{@docRoot}guide/topics/manifest/activity-element.html"> +{@code <activity>}</a> 屬性包括:</p> + +<ul class="nolist"> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff"> + {@code taskAffinity}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode"> + {@code launchMode}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> + {@code allowTaskReparenting}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear"> + {@code clearTaskOnLaunch}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> + {@code alwaysRetainTaskState}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish"> + {@code finishOnTaskLaunch}</a></li> +</ul> + +<p>而您可以使用的主要意圖旗標包括:</p> + +<ul class="nolist"> + <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li> +</ul> + +<p>在以下各節中,您將瞭解如何使用這些宣示說明屬性和意圖旗標,定義 Activity 與工作關聯的方式以及它們在返回堆疊中的行為。 +</p> + +<p>同時,還會分開討論工作與 Activity 如何在總覽畫面表示和進行管理。 +請參閱<a href="{@docRoot}guide/components/recents.html">總覽畫面</a>以取得詳細資訊。 +一般而言,您應該允許系統定義如何在總覽畫面中呈現工作與 Activity,而且不需要修改此行為。 +</p> + +<p class="caution"><strong>注意:</strong>大多數應用程式不應中斷 Activity 和工作的預設行為: +如果您判斷修改 Activity 的預設行為是必要的,請謹慎小心並記得測試啟動期間 Activity 的可用性,以及使用 [返回] 按鈕從其他 Activity 和工作瀏覽到此 Activity 的情況。請記得測試可能會與使用者預期的行為衝突的瀏覽行為。<em></em> + + +</p> + + +<h3 id="TaskLaunchModes">定義啟動模式</h3> + +<p>啟動模式可讓您定義 Activity 的新執行個體與目前工作關聯的方式。 +您可用兩種方法定義不同的啟動模式:</p> +<ul class="nolist"> + <li><a href="#ManifestForTasks">使用宣示說明檔案</a> + <p>當您在宣示說明檔案中宣告 Activity 時,您可以指定 Activity 啟動時該如何與工作關聯。 +</li> + <li><a href="#IntentFlagsForTasks">使用意圖旗標</a> + <p>當您呼叫 {@link android.app.Activity#startActivity startActivity()} 時,您可以在 {@link android.content.Intent} 包含一個旗標,宣告新 Activity 應如何 (或是否) 與目前的工作關聯。 + +</p></li> +</ul> + +<p>因此,如果 Activity A 啟動 Activity B,Activity B 可以在其宣示說明中定義它應如何與目前的工作 (如果有) 關聯,而且 Activity A 也能要求 Activity B 應如何與目前的工作關聯。 + +如果這兩個 Activity 皆定義 Activity B 應如何與工作關聯,相較於 Activity B 的要求 (如宣示說明中所定義),會優先採用 Activity A 的要求 (如意圖中所定義)。 + +</p> + +<p class="note"><strong>注意:</strong>某些宣示說明檔案中提供的啟動模式可能沒有對應的意圖旗標,同樣地,某些意圖旗標提供的啟動模式無法在宣示說明中定義。 + +</p> + + +<h4 id="ManifestForTasks">使用宣示說明檔案</h4> + +<p>當您在宣示說明檔案中宣告 Activity 時,您可以使用 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素的 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> 屬性,指定 Activity 應如何與工作關聯。 + +</p> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> 屬性可指定應如何將 Activity 啟動至工作內的指示。 +您可以為 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code> 屬性指定四種不同的啟動模式: +</p> + +<dl> +<dt>{@code "standard"} (預設模式)</dt> + <dd>預設。系統在啟動 Activity 的工作中建立新的執行個體,並將意圖路由至該處。 +Activity 可以具現化很多次,每個執行個體可屬於不同的工作,而且一個工作可以有多個執行個體。 +</dd> +<dt>{@code "singleTop"}</dt> + <dd>如果 Activity 的執行個體已經出現在目前工作的頂端,系統會透過呼叫其 {@link +android.app.Activity#onNewIntent onNewIntent()} 方法,將意圖路由至該執行個體,而不是建立新的 Activity 執行個體。 + +Activity 可以具現化很多次,每個執行個體可屬於不同的工作,而且一個工作可以有多個執行個體 (但僅限於返回堆疊頂端的 Activity 不是現有的 Activity 執行個體時<em></em>)。 + + + <p>例如,假設工作的返回堆疊包含根 Activity A 及 Activity B、C 及在最頂端的 D (堆疊為 A-B-C-D;D 在最頂端)。 +類型 D Activity 的意圖抵達。 +如果 D 有預設的 {@code "standard"} 啟動模式,則會啟動新的類別執行個體,且堆疊會變成 A-B-C-D-D。不過,如果 D 啟動模式為 {@code "singleTop"},D 的現有執行個體會透過 {@link +android.app.Activity#onNewIntent onNewIntent()} 接收意圖,這是因為它位於堆疊的最頂端 — 堆疊會維持 A-B-C-D。不過,如果類型 B Activity 的意圖抵達,則 B 的新執行個體會新增到堆疊中,即使其啟動模式為 {@code "singleTop"} 也是如此。 + + + +</p> + <p class="note"><strong>注意:</strong>建立新的 Activity 執行個體之後,使用者可按下 [返回]<em></em> 按鈕,返回之前的 Activity。 +但是,如果處理新意圖的是現有的 Activity 執行個體,則使用者無法按下 [返回]<em></em> 按鈕回到新意圖抵達 {@link android.app.Activity#onNewIntent +onNewIntent()} 之前的 Activity 狀態。 + + + +</p> +</dd> + +<dt>{@code "singleTask"}</dt> + <dd>系統會建立新的工作並在新工作的根目錄將 Activity 具現化。不過,如果 Activity 的執行個體已經出現在其他工作中,系統會透過呼叫其 {@link +android.app.Activity#onNewIntent onNewIntent()} 方法,將意圖路由至現有的執行個體,而不是建立新的執行個體。 + +一次只能有一個 Activity 執行個體。 + + <p class="note"><strong>注意:</strong>雖然 Activity 是在新工作中啟動,使用者仍可使用 [返回] 按鈕返回之前的 Activity。 +<em></em></p></dd> +<dt>{@code "singleInstance"}。</dt> + <dd>與 {@code "singleTask"} 一樣,差別在於系統不會將任何其他 Activity 啟動至保留執行個體的工作中。 +Activity 一律是其工作的唯一成員;使用此項目啟動的任何 Activity 會在個別的工作中開啟。 +</dd> +</dl> + + +<p>另外一個例子,Android 瀏覽器應用程式宣告網頁瀏覽器 Activity 應永遠在自己的工作中開啟 — 透過在 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素指定 {@code singleTask} 啟動模式。 +這表示如果您的應用程式發出開啟 Android 瀏覽器的意圖,其 Activity 不會與您的應用程式放在同一個工作中。<em></em> + + +而是會為瀏覽器啟動新的工作,或者如果瀏覽器已經有工作在背景執行,會將該工作帶出來處理新的意圖。 + +</p> + +<p>無論 Activity 在新工作啟動或與啟動該 Activity 之 Activity 的相同工作中啟動,使用者都能使用 [返回] 按鈕返回之前的 Activity。<em></em> +不過,如果您啟動指定 {@code singleTask} 啟動模式的 Activity,如果該 Activity 的執行個體存在於背景工作中,則該工作會整個移到前景。 + +此時,返回堆疊現在包含已帶出且位於堆疊頂端之工作的所有 Activity。 + +圖 4 說明這種類型的情況。</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" /> +<p class="img-caption"><strong>圖 4.</strong>顯示含有啟動模式 "singleTask" 的 Activity 如何新增到返回堆疊。 +如果 Activity 已經是背景工作的一部份且有自己的返回堆疊,則整個返回堆疊都會帶出來,位於目前工作的最頂端。 + +</p> + +<p>如要進一步瞭解如何使用宣示說明檔案中的啟動模式,請參閱 <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素,其中會有 {@code launchMode} 屬性和可接受值的詳細說明。 + + +</p> + +<p class="note"><strong>注意:</strong>您使用 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 屬性指定的 Activity 行為可被啟動 Activity 之意圖所含的旗標所覆寫,如下一節所述。 + +</p> + + + +<h4 id="#IntentFlagsForTasks">使用意圖旗標</h4> + +<p>啟動 Activity 時,您可以在傳送到 {@link +android.app.Activity#startActivity startActivity()} 的意圖中包含旗標,以修改 Activity 及其工作的預設關聯。 +您可以用來修改預設行為的旗標包括: +</p> + +<p> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt> + <dd>在新工作中啟動 Activity。如果工作已為您目前啟動的 Activity 執行,該工作會移到前景並復原至上個狀態,而且 Activity 會在 {@link android.app.Activity#onNewIntent onNewIntent()} 收到新的意圖。 + + + <p>這會產生與 {@code "singleTask"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 值相同的行為,如上節所述。 +</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt> + <dd>如果現在正在啟動的 Activity 是目前的 Activity (位於返回堆疊的頂端),則現有執行個體會收到 {@link android.app.Activity#onNewIntent onNewIntent()} 呼叫,而不會建立新的 Activity 執行個體。 + + + <p>這會產生與 {@code "singleTop"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 值相同的行為,如上節所述。 +</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt> + <dd>如果正在啟動的 Activity 已在目前的工作中執行,則不會啟動新的 Activity 執行個體,而是會終結位於其上方的所有其他 Activity,且此意圖會透過 {@link android.app.Activity#onNewIntent onNewIntent()} 傳送到繼續執行的 Activity 執行個體 (現在位於頂端)。 + + + + <p>沒有任何 <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> 屬性值可以產生此行為。 +</p> + <p>{@code FLAG_ACTIVITY_CLEAR_TOP} 最常與 {@code FLAG_ACTIVITY_NEW_TASK} 搭配使用。 +一起使用時,這些旗標可以找出位於其他工作中的現有 Activity,然後將它放置於可以回應意圖的地方。 + + </p> + <p class="note"><strong>注意:</strong>如果指定 Activity 的啟動模式為 {@code "standard"},它也會從堆疊中移除,改為啟動新的執行個體處理傳入的意圖。 + + +這是因為當啟動模式為 {@code "standard"} 時,一律會為新的意圖建立新的執行個體。 + </p> +</dd> +</dl> + + + + + +<h3 id="Affinities">處理親和性</h3> + +<p>親和性<em></em>可指出 Activity 偏好屬於哪個工作。根據預設,相同應用程式的所有 Activity 間互相都有親和性。 +因此,根據預設,相同應用程式的所有 Activity 都偏好位於相同的工作。 +不過,您可以修改 Activity 的預設親和性。 +不同應用程式中定義的 Activity 可以共用親和性,或者相同應用程式中定義的 Activity 可以指派不同的工作親和性。 + +</p> + +<p>您可以使用 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素的 <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> 屬性修改任何指定 Activity 的親和性。 + +</p> + +<p><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> 屬性使用字串值,但必須與 <a href="{@docRoot}guide/topics/manifest/manifest-element.html"> +{@code <manifest>}</a> 元素中宣告的預設封裝名稱不同,因為系統使用該名稱來識別應用程式的預設工作親和性。 + + + +</p> + +<p>在兩種情況下會用到親和性:</p> +<ul> + <li>當啟動 Activity 的意圖包含 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} 旗標。 + + +<p>根據預設,新的 Activity 會啟動至 Activity (名為 {@link android.app.Activity#startActivity startActivity()}) 的工作中。 +系統會將它推入至與呼叫端相同的返回堆疊。 +不過,如果傳送至 +{@link android.app.Activity#startActivity startActivity()} 的意圖包含 {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} 旗標,則系統會找尋不同的工作來放置新的 Activity。 + +這通常是新工作。 +不過,它不一定要是新工作。如果現有工作中有與新 Activity 相同的親和性,Activity 會啟動至該工作中。 +如果沒有,會開始新的工作。</p> + +<p>如果此旗標導致 Activity 開始新的工作,而使用者按 [首頁] 按鈕離開它,必須要有方法可以讓使用者回來瀏覽這個工作。<em></em> + +有些實體 (例如,通知管理員) 總是從外部工作開始 Activity,從來不使用自己的工作,因此他們都會將 {@code FLAG_ACTIVITY_NEW_TASK} 放入傳送到 +{@link android.app.Activity#startActivity startActivity()} 的意圖。 + +如果您的 Activity 可以由外部實體呼叫且可能使用此旗標,記得要提供使用者獨立的方法回到啟動的工作,例如,透過啟動組件圖示 (工作的根 Activity 有一個 {@link android.content.Intent#CATEGORY_LAUNCHER} 意圖篩選器;請參閱下方的<a href="#Starting">開始工作</a>)。 + + + +</p> +</li> + + <li>當 Activity 的<a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> +{@code allowTaskReparenting}</a> 屬性設為 {@code "true"}。 + <p>在這種情況下,當工作移到前景時,Activity 可以從其啟動的工作移到與其有親和性的工作。 +</p> + <p>例如,假設將報告所選城市天氣狀況的 Activity 定義為旅遊應用程式的一部份。 +它與相同應用程式中的其他 Activity 有相同的親和性 (預設的應用程式親和性),而且它允許與此屬性重設父代。 +當您的其中一個 Activity 開始氣象報告程式 Activity,它一開始屬於與您 Activity 相同的工作。 + +不過,當旅遊應用程式工作移到前景,氣象報告程式 Activity 就會重新指派給該工作,並在其中顯示。 +</p> +</li> +</ul> + +<p class="note"><strong>提示:</strong>如果從使用者的角度來看 {@code .apk} 檔案包含一個以上的「應用程式」,您可能會想要使用 <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> 屬性對與每個「應用程式」關聯的 Activity 指派不同的親和性。 + +</p> + + + +<h3 id="Clearing">清除返回堆疊</h3> + +<p>如果使用者離開工作一段很長的時間,系統會清除根 Activity 以外所有 Activity 的工作 +。當使用者再次回到工作,只會復原根 Activity。 +系統會有這樣的行為是因為在一段很長的時間後,使用者很可能會放棄他們之前在做的工作,並回到工作開始其他新的工作。 + </p> + +<p>您可以使用下列一些 Activity 屬性來修改這個行為: </p> + +<dl> +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code> +</dt> +<dd>如果這項屬性在工作的根 Activity 中設為 {@code "true"},則剛描述的預設行為不會發生。 +即使過了很長的一段時間,工作仍然會在堆疊保留所有的 Activity。 +</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt> +<dd>如果這項屬性在工作的根 Activity 中設為 {@code "true"},則剛描述的預設行為不會發生。 +即使過了很長的一段時間,工作仍然會在堆疊保留所有的 Activity。 +換句話說,它與 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> +{@code alwaysRetainTaskState}</a> 相反。即便使用者只離開工作一小段時間,使用者還是會回到工作的起始狀態。 +</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code> +</dt> +<dd>這項屬性與 <a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a> 相似,但它在單一 Activity 上作業,而不是在整個工作。 + +它也會導致任何 Activity 離開,包含根 Activity。 +如果設成 {@code "true"},Activity 只會在目前的工作階段留在此工作中。 +如果使用者離開後再回到工作,該工作將不再存在。 +</dd> +</dl> + + + + +<h3 id="Starting">開始工作</h3> + +<p>您可以給予 Activity 一個意圖篩選器,將 +{@code "android.intent.action.MAIN"} 設定為指定的動作, +{@code "android.intent.category.LAUNCHER"} 設定為指定的類別,以便將該 Activity 設定為工作的進入點。 +例如:</p> + +<pre> +<activity ... > + <intent-filter ... > + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + ... +</activity> +</pre> + +<p>這類意圖篩選器可在應用程式啟動組件顯示 Activity 的圖示和標籤,讓使用者啟動 Activity 並回到 Activity 啟動後任何時間建立的工作。 + + +</p> + +<p>第二項功能很重要:使用者必須能夠在離開工作後,使用此 Activity 啟動組件回到此工作。 +由於這個原因,兩個將 Activity 標示為一律啟動工作的<a href="#LaunchModes">啟動模式</a> {@code "singleTask"} 和 +{@code "singleInstance"},應只能在 Activity 有 {@link android.content.Intent#ACTION_MAIN} 和 {@link android.content.Intent#CATEGORY_LAUNCHER} 篩選器時才能使用。 + + +例如,試想如果缺少篩選器會發生什麼情況: +意圖會啟動 {@code "singleTask"} Activity、起始新工作,然後使用者會花一些時間在該工作進行作業。 +之後,使用者按下 [首頁]<em></em> 按鈕。 +此工作現在會傳送到背景而且不會顯示。現在,使用者沒有辦法回到工作,這是因為應用程式啟動組件沒有代表此工作的項目。 +</p> + +<p>在您不希望使用者返回 Activity 的情況下,將 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素的 +<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code finishOnTaskLaunch}</a> 設定為 {@code "true"} (請參閱<a href="#Clearing">清除堆疊</a>)。 + +</p> + +<p>如要進一步瞭解工作和 Activity 在總覽畫面中的顯示及管理方式,請參閱<a href="{@docRoot}guide/components/recents.html">總覽畫面</a>。 + +</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how to use intents to +activate other application components and publish the intents to which your components +respond, continue with the <b><a +href="{@docRoot}guide/components/intents-filters.html">Intents and Intent +Filters</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/zh-tw/guide/index.jd b/docs/html-intl/intl/zh-tw/guide/index.jd new file mode 100644 index 000000000000..f7ad966d73fa --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Android 簡介 + +@jd:body + + +<div class="sidebox" style="width:220px"><!-- width to match col-4 below --> +<p>如要瞭解應用程式的運作方式,請參閱<a href="{@docRoot}guide/components/fundamentals.html">應用程式基礎知識</a>。 +</p> +<p>如果想立即編寫程式碼,請參閱<a href="{@docRoot}training/basics/firstapp/index.html">建立您的第一個應用程式</a>。</p> +</div> + +<p>Android 提供內容豐富的應用程式架構,可讓您在 Java 語言環境中建置適用於行動裝置的新穎應用程式和遊戲。 +您可以參閱左側導覽區所列的文件,進一步瞭解如何使用 Android 的各種 API 建置應用程式。 +</p> + +<p>如果您是剛開始接觸 Android 開發環境,請務必詳閱下列有關 Android 應用程式架構的基本概念: +</p> + + +<div class="landing-banner"> + +<div class="col-6"> + +<h4>應用程式可提供多個進入點</h4> + +<p>Android 應用程式是由許多不同元件建置而成,應用程式可個別呼叫每個元件。 +例如,「Activity」<em></em>可在單一畫面中顯示使用者介面,而「服務」<em></em>則個別可在背景中執行作業。 + +</p> + +<p>您可以透過某個元件使用「意圖」<em></em>啟動另一個元件。您甚至可以啟動其他應用程式中的元件,例如啟動地圖應用程式的 Activity 來顯示地址。 +這個模型可為單一應用程式提供多個進入點,還能讓任何應用程式針對其他應用程式可能呼叫的動作,以使用者設定的「預設值」運作。 + +</p> + + +<p><b>瞭解詳情:</b></p> +<ul class="nolist"> +<li><a href="{@docRoot}guide/components/fundamentals.html">應用程式基礎知識</a> +<li><a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a> +<li><a href="{@docRoot}guide/components/activities.html">Activity</a> +</ul> + +</div> + + +<div class="col-6"> + +<h4>應用程式會針對不同裝置進行調整</h4> + +<p>Android 提供的應用程式架構可視情況進行調整,讓您能夠針對不同的裝置設定提供專屬資源。 +例如,您可以針對不同的螢幕大小建立各種 XML 版面配置檔案,藉此讓系統根據目前裝置的螢幕大小決定要套用的版面配置設定。 + +</p> + +<p>如果有應用程式功能需要特定硬體 (例如相機) 才能運作,您可以在執行階段查詢裝置功能的可用性。 +此外,您還可以視需要宣告您的應用程式所需的功能,以便讓 Google Play 商店等應用程式市集禁止使用者在不支援相關功能的裝置上安裝您的應用程式。 + +</p> + + +<p><b>瞭解詳情:</b></p> +<ul class="nolist"> +<li><a href="{@docRoot}guide/practices/compatibility.html">裝置相容性</a> +<li><a href="{@docRoot}guide/topics/resources/overview.html">資源總覽</a> +<li><a href="{@docRoot}guide/topics/ui/overview.html">使用者介面總覽</a> +</ul> + +</div> + +</div><!-- end landing-banner --> + + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 000000000000..5e42e37d91fc --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=應用程式宣示說明 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> +<li><a href="#filestruct">宣示說明檔案結構</a></li> +<li><a href="#filec">檔案轉換</a> +<li><a href="#filef">檔案功能</a> + <ol> + <li><a href="#ifs">意圖篩選器</a></li> + <li><a href="#iconlabel">圖示和標籤</a></li> + <li><a href="#perms">權限</a></li> + <li><a href="#libs">程式庫</a></li> + </ol></li> +</ol> +</div> +</div> + +<p> + 每個應用程式的根目錄都必須包含 AndroidManifest.xml 檔案 (名稱要一字不差)。 + <span itemprop="description">宣示說明檔案可向 Android 系統顯示應用程式的基本資訊,也就是系統在執行該應用程式的任何程式碼之前必須具備的資訊。 + + +</span> 宣示說明可執行下列動作: +</p> + +<ul> +<li>為應用程式的 Java 封裝命名。 +封裝名稱可當成應用程式的唯一識別碼使用。</li> + +<li>描述應用程式的元件 — 組成應用程式的 Activity、服務、廣播接收器和內容供應程式。 + +為實作每個元件的類別命名以及發佈類別的功能 (例如,類別可處理的 {@link android.content.Intent +Intent} 訊息)。 +這些宣告可讓 Android 系統瞭解元件為何以及可在哪些情況下啟動。 +</li> + +<li>決定代管應用程式元件的程序。</li> + +<li>宣告應用程式必須擁有哪些權限,才能存取 API 受保護的部分以及與其他應用程式互動。 +</li> + +<li>宣示說明亦可宣告其他項目必須擁有哪些權限,才能與應用程式的元件互動。 +</li> + +<li>列出可在應用程式執行時提供分析和其他資訊的 {@link android.app.Instrumentation} 類別。 +只有在應用程式開發及測試完成的情況下,宣示說明中才會顯示這些宣告;這些宣告會在應用程式發佈之前移除。 + +</li> + +<li>宣告應用程式要求的最低 Android API 級別。 +</li> + +<li>列出應用程式必須連結的程式庫。</li> +</ul> + + +<h2 id="filestruct">宣示說明檔案結構</h2> + +<p> +下圖顯示宣示說明檔案的一般結構和可納入其中的元素。 +每個元素和其所有屬性都會完全記錄在個別檔案中。 +如要查看任一元素的詳細資訊,只要按一下圖表中的元素名稱、圖表後方按字母順序列出的元素清單,或在他處提及的任何元素名稱。 + + + +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> + +<a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a> + + <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission /></a> + <a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission /></a> + <a href="{@docRoot}guide/topics/manifest/permission-tree-element.html"><permission-tree /></a> + <a href="{@docRoot}guide/topics/manifest/permission-group-element.html"><permission-group /></a> + <a href="{@docRoot}guide/topics/manifest/instrumentation-element.html"><instrumentation /></a> + <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk /></a> + <a href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><uses-configuration /></a> <!-- ##api level 3## --> + <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><uses-feature /></a> <!-- ##api level 4## --> + <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><supports-screens /></a> <!-- ##api level 4## --> + <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html"><compatible-screens /></a> <!-- ##api level 9## --> + <a href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html"><supports-gl-texture /></a> <!-- ##api level 11## --> + + <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a> + + <a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a> + <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a> + <a href="{@docRoot}guide/topics/manifest/action-element.html"><action /></a> + <a href="{@docRoot}guide/topics/manifest/category-element.html"><category /></a> + <a href="{@docRoot}guide/topics/manifest/data-element.html"><data /></a> + <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"></intent-filter></a> + <a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data /></a> + <a href="{@docRoot}guide/topics/manifest/activity-element.html"></activity></a> + + <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"><activity-alias></a> + <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a> . . . <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"></intent-filter></a> + <a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data /></a> + <a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"></activity-alias></a> + + <a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a> + <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a> . . . <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"></intent-filter></a> + <a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data/></a> + <a href="{@docRoot}guide/topics/manifest/service-element.html"></service></a> + + <a href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a> + <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a> . . . <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"></intent-filter></a> + <a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data /></a> + <a href="{@docRoot}guide/topics/manifest/receiver-element.html"></receiver></a> + + <a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a> + <a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission /></a> + <a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data /></a> + <a href="{@docRoot}guide/topics/manifest/path-permission-element.html"><path-permission /></a> + <a href="{@docRoot}guide/topics/manifest/provider-element.html"></provider></a> + + <a href="{@docRoot}guide/topics/manifest/uses-library-element.html"><uses-library /></a> + + <a href="{@docRoot}guide/topics/manifest/application-element.html"></application></a> + +<a href="{@docRoot}guide/topics/manifest/manifest-element.html"></manifest></a> +</pre> + +<p> +下方按字母順序列出可出現在宣示說明檔案中的所有元素。 +只有這些才是符合資格的元素,您無法新增自己的元素或屬性。 + +</p> + +<p style="margin-left: 2em"> +<code><a href="{@docRoot}guide/topics/manifest/action-element.html"><action></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"><activity-alias></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/category-element.html"><category></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/instrumentation-element.html"><instrumentation></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/permission-group-element.html"><permission-group></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/permission-tree-element.html"><permission-tree></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/supports-screens-element.html"><supports-screens></a></code> <!-- ##api level 4## --> +<br/><code><a href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><uses-configuration></a></code> <!-- ##api level 3## --> +<br/><code><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><uses-feature></a></code> <!-- ##api level 4## --> +<br/><code><a href="{@docRoot}guide/topics/manifest/uses-library-element.html"><uses-library></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> +<br/><code><a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk></a></code> +</p> + + + + +<h2 id="filec">檔案轉換</h2> + +<p> +某些轉換和規則通常適用於宣示說明中的所有元素與屬性: + +</p> + +<dl> +<dt><b>元素</b></dt> +<dd>只有 +<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> 與 +<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 是必要元素,務必顯示兩者且這些元素只能出現一次。 +雖然您至少必須顯示當中的一些元素,才能完成有意義的作業,但大部分其他元素可以出現數次或完全不出現。 + + + + +<p> +如果可以的話,元素還可以包含其他元素。所有值並非當成元素內的字元資料使用,而是透過屬性設定。 + +</p> + +<p> +系統通常不會將位於相同層級的元素排序。例如, +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>、 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> 和 +<code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> 元素能以任何順序排列組合。 +( +<code><a href="{@docRoot}guide/topics/manifest/activity-alias-element.html"><activity-alias></a></code> 元素是這項規則的例外狀況: +由於它是 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 的別名,因此必須跟在該元素的後面)。 + +</p></dd> + +<dt><b>屬性</b></dt> +<dd>形式上而言,所有屬性均為選用性質。不過,您必須為元素指定某些屬性,才能達到其目的。 +請使用本文件當成參考指南。 +真正的選用屬性會提及在缺少規格時要使用的預設值或狀態。 + + +<p>除了 +<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> 根元素的某些屬性以外,所有屬性名稱都是以前置詞 {@code android:}為開頭,例如 {@code android:alwaysRetainTaskState}。 + +由於前置詞是通用的,因此按名稱參照屬性時,文件通常會加以省略。 + +</p></dd> + +<dt><b>宣告類別名稱</b></dt> +<dd>許多元素都會對應到 Java 物件,包括應用程式本身的元素 ( +<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 元素) 與其主要元件:Activity (<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>)、服務 (<code><a href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code>)、廣播接收器 (<code><a href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code>) 以及內容供應程式 (<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code>)。 + + + + + + + + + + +<p> +如果您一如往常定義元件類別 ({@link android.app.Activity}、{@link android.app.Service}、 +{@link android.content.BroadcastReceiver} 及 {@link android.content.ContentProvider}) 般定義子類別,就會透過 {@code name} 屬性宣告子類別。 + +該名稱必須包含完整的封裝指定名稱。 +例如,{@link android.app.Service} 子類別可能會以下列格式宣告: + +</p> + +<pre><manifest . . . > + <application . . . > + <service android:name="com.example.project.SecretService" . . . > + . . . + </service> + . . . + </application> +</manifest></pre> + +<p> +不過,採用速記法時,如果字串的第一個字元是句點,就會將字串附加到應用程式的封裝名稱 (如同由 +<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html"><manifest></a></code> 元素的 +<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html#package">package</a></code> 屬性指定)。 + + +下列的指派結果會和上述相同: +</p> + +<pre><manifest package="com.example.project" . . . > + <application . . . > + <service android:name=".SecretService" . . . > + . . . + </service> + . . . + </application> +</manifest></pre> + +<p> +啟動元件時,Android 會建立具名子類別的執行個體。如果未指定子類別,就會建立基本類別的執行個體。 + +</p></dd> + +<dt><b>多個值</b></dt> +<dd>如果可以指定多個值,該元素幾乎會一直重複,而不是在單一元素內列出多個值。 +例如,意圖篩選器能列出數種動作: + + +<pre><intent-filter . . . > + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.INSERT" /> + <action android:name="android.intent.action.DELETE" /> + . . . +</intent-filter></pre></dd> + +<dt><b>資源值</b></dt> +<dd>有些屬性的值可以供使用者查看 — 例如 Activity 的標籤和圖示。 +您必須將這些屬性的值本地化,以便從資源或主題設定這些值。 +資源值是採用下列格式表示: +</p> + +<p style="margin-left: 2em">{@code @[<i>package</i>:]<i>type</i>:<i>name</i>}</p> + +<p> +其中的<i>package</i> 名稱可以省略 (如果資源所在的封裝和應用程式相同的話), + <i>type</i> 是指資源類型 — 例如「字串」或「可繪項目」 — 而 + <i>name</i> 則是可識別特定資源的名稱。範例: + +</p> + +<pre><activity android:icon="@drawable/smallPic" . . . ></pre> + +<p> +主題中的值會以類似的方式表示,但字首會是 '{@code ?}',而不是 '{@code @}': + +</p> + +<p style="margin-left: 2em">{@code ?[<i>package</i>:]<i>type</i>:<i>name</i>} +</p></dd> + +<dt><b>字串值</b></dt> +<dd>如果屬性值為字串,必須使用雙反斜線 ('{@code \\}')來溢出字元,例如 '{@code \\n}'表示換行字元,或 '{@code \\uxxxx}' 表示 Unicode 字元。 + +</dd> +</dl> + + +<h2 id="filef">檔案功能</h2> + +<p> +下列各節說明如何在宣示說明檔案中反映部分 Android 功能。 + +</p> + + +<h3 id="ifs">意圖篩選器</h3> + +<p> +應用程式的核心元件 (即應用程式的 Activity、服務和廣播接收器) 是由 + <i>意圖</i>啟動。意圖是一組資訊組合 ({@link android.content.Intent} 物件),用於說明要採取的動作 — 包括執行依據的資料、應執行動作的元件類別,以及其他相關的指示。 + + +Android 會找出適當的元件來回應意圖、視需要啟動元件的新執行個體,以及將意圖物件傳送給它。 + + + +</p> + +<p> +元件會通知其功能 (元件可回應的意圖類型),而通知途徑是 + <i>意圖篩選器</i>。由於 Android 系統必須先瞭解元件能夠處理哪些意圖,才能啟動該元件,因此意圖篩選器在宣示說明中會指定為 +<code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code> 元素。 + + +元件可包含的篩選器數目不拘,每個篩選器描述的功能各不相同。 + +</p> + +<p> +明確命名目標元件的意圖會啟動該元件,而不必使用篩選器。 +但未指定目標名稱的意圖,只有在其通過其中一個元件的篩選器後,才能啟動元件。 + + +</p> + +<p> +如要瞭解意圖物件測試意圖篩選器的方式,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 + + + +</p> + + +<h3 id="iconlabel">圖示和標籤</h3> + +<p> +許多元素都有可供小型圖示與文字標籤使用的 {@code icon} 和 {@code label} 屬性,而使用者可看到這些圖示和標籤。 +有些元素還包含可供較長說明文字使用的 +{@code description} 屬性,這個說明文字亦可顯示在螢幕上。 +例如,假設 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素含有上述三種屬性,當系統詢問使用者是否將權限授予發出要求的應用程式時,可以將代表權限的圖示、該權限的名稱以及所需的描述全都向使用者顯示。 + + + + +</p> + +<p> +在各種情況下,元件中設定的圖示和標籤會成為所有容器下層元素的預設 +{@code icon} 與 {@code label} 設定因此, +<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 元素中設定的圖示和標籤會是應用程式各元件的預設圖示和標籤。 + +同樣地,為元件 (例如 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素) 設定的圖示和標籤會是元件的 +<code><a href="{@docRoot}guide/topics/manifest/intent-filter-element.html"><intent-filter></a></code> 元素預設值。 + + +如果 +<code><a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 元素設有一個標籤,但 Activity 與其意圖篩選器並未設定該標籤,系統會將應用程式標籤視為 Activity 和意圖篩選器的標籤。 + + + +</p> + +<p> +每當執行篩選器通告的功能,要向使用者顯示元件時,就會將為意圖篩選器設定的圖示和標籤用來代表元件。 + +例如,包含 +"{@code android.intent.action.MAIN}" 與 +"{@code android.intent.category.LAUNCHER}" 設定的篩選器會將某 Activity 宣告為啟動應用程式的 Activity,也就是應顯示在應用程式啟動器中的 Activity。 + +因此,顯示在啟動器中的會是篩選器中設定的圖示和標籤。 + +</p> + + +<h3 id="perms">權限</h3> + +<p> +單一 <i>權限</i> 是指一項限制,可限制某部分程式碼或裝置上資料的存取權。 + 系統會強制實施這項限制,以保護會遭到誤用而扭曲或損害使用者體驗的重要資料與程式碼。 + +</p> + +<p> +各項權限都是用不重複的標籤加以辨識。該標籤通常會指出受到限制的動作。 +例如,以下是 Android 定義的一些權限: + +</p> + +<p style="margin-left: 2em">{@code android.permission.CALL_EMERGENCY_NUMBERS} +<br/>{@code android.permission.READ_OWNER_DATA} +<br/>{@code android.permission.SET_WALLPAPER} +<br/>{@code android.permission.DEVICE_POWER}</p> + +<p> +單一功能最多只能利用一項權限來加以保護。 +</p> + +<p> +如果應用程式需要存取受權限保護的功能,它必須在宣示說明中利用 +<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素來宣告其需要該項權限。 + +接著,要在裝置上安裝該應用程式時,安裝程式會檢查簽署該應用程式憑證的授權單位 (在某些情況下還會詢問使用者),然後決定是否授予要求的權限。 + + +如果授予權限,該應用程式就能夠使用受保護的功能。 + +如果不授予權限,存取相關功能的嘗試就會失敗,但使用者不會收到任何通知。 + +</p> + +<p> +應用程式也能利用權限來保護自己的元件 (Activity、服務、廣播接收器和內容供應程式)。 +它能使用 Android 定義的任何權限 (列於 +{@link android.Manifest.permission android.Manifest.permission}) 或其他應用程式宣告的任何權限。 + +此外,應用程式也能自行定義權限。新的權限是以 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素宣告。 + +例如,您可以利用下列權限保護 Activity: +</p> + +<pre> +<manifest . . . > + <permission android:name="com.example.project.DEBIT_ACCT" . . . /> + <uses-permission android:name="com.example.project.DEBIT_ACCT" /> + . . . + <application . . .> + <activity android:name="com.example.project.FreneticActivity" + android:permission="com.example.project.DEBIT_ACCT" + . . . > + . . . + </activity> + </application> +</manifest> +</pre> + +<p> +請注意,在本範例中,不只以 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素宣告 {@code DEBIT_ACCT} 權限,還使用 +<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素來要求使用此權限。 + + +即使保護是由應用程式本身強制施行,還是必須要求使用該權限,應用程式的其他元件才能啟動受保護的 Activity。 + + +</p> + +<p> +在相同的範例中,如果 {@code permission} 屬性設定為在別處宣告的權限 (例如 {@code android.permission.CALL_EMERGENCY_NUMBERS}),就不必再次使用 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素來宣告。 + + + +不過,還是必須利用 +<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 來要求使用。 +</p> + +<p> + +<code><a href="{@docRoot}guide/topics/manifest/permission-tree-element.html"><permission-tree></a></code> 元素可為程式碼將定義的一組權限宣告命名空間。 + +此外, +<code><a href="{@docRoot}guide/topics/manifest/permission-group-element.html"><permission-group></a></code> 可為一組權限定義標籤 (以 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素在宣示說明中宣告的權限或在別處宣告的權限)。 + +它只會影響在向使用者呈現權限時的分組方式。 + +<code><a href="{@docRoot}guide/topics/manifest/permission-group-element.html"><permission-group></a></code> 元素不會指定各權限所屬的群組,只會指定群組的名稱。 + +只要將群組名稱指派給 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html"><permission></a></code> 元素 +<code><a href="{@docRoot}guide/topics/manifest/permission-element.html#pgroup">permissionGroup</a></code> 的屬性,就能將權限放入群組中。 + + + +</p> + + +<h3 id="libs">程式庫</h3> + +<p> +每款應用程式都會與預設的 Android 程式庫連結,該程式庫中包含的基本封裝可用於建置應用程式 (使用 Activity、Service、Intent、View、Button、Application、ContentProvider 等一般類別)。 + + + +</p> + +<p> +不過,有些封裝是存放在其專屬的程式庫中。如果您的應用程式使用來自這類封裝的程式碼,您必須明確要求與其建立連結。 + +宣示說明必須包含個別的 +<code><a href="{@docRoot}guide/topics/manifest/uses-library-element.html"><uses-library></a></code> 元素,才能命名各個程式庫。 +(您可以在封裝的說明文件中找到程式庫名稱)。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd new file mode 100644 index 000000000000..42434e4b30e8 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=日曆供應程式 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> + <ol> + <li><a href="#overview">基本概念</a></li> + <li><a href="#manifest">使用者權限</a></li> + <li><a href="#calendar">日曆表格</a> +<ol> + <li><a href="#query">查詢日曆</a></li> + <li><a href="#modify-calendar">修改日曆</a></li> + <li><a href="#insert-calendar">插入日曆</a></li> + </ol> + </li> + <li><a href="#events">活動表格</a> +<ol> + <li><a href="#add-event">新增活動</a></li> + <li><a href="#update-event">更新活動</a></li> + <li><a href="#delete-event">刪除活動</a></li> + </ol> + </li> + <li><a href="#attendees">參與者表格</a> +<ol> + <li><a href="#add-attendees">新增參與者</a></li> + </ol> + </li> + <li><a href="#reminders">提醒表格</a> +<ol> + <li><a href="#add-reminders">新增提醒</a></li> + </ol> + </li> + <li><a href="#instances">執行個體表格</a> + <ol> + <li><a href="#query-instances">查詢執行個體表格</a></li> + </ol></li> + <li><a href="#intents">日曆意圖</a> + <ol> + <li><a href="#intent-insert">使用意圖插入活動</a></li> + <li><a href="#intent-edit">使用意圖編輯活動</a></li> + <li><a href="#intent-view">使用意圖檢視日曆資料</a></li> + </ol> + </li> + + <li><a href="#sync-adapter">同步配接器</a></li> +</ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.provider.CalendarContract.Calendars}</li> + <li>{@link android.provider.CalendarContract.Events}</li> + <li>{@link android.provider.CalendarContract.Attendees}</li> + <li>{@link android.provider.CalendarContract.Reminders}</li> + </ol> +</div> +</div> + +<p>「日曆供應程式」是使用者日曆活動的存放庫。「日曆供應程式」API 允許您在日曆、活動、參與者、提醒等等項目中執行查詢、插入、更新以及刪除操作。 + +</p> + + +<p>「日曆供應程式」API 可以由應用程式和同步配接器使用。規則取決於發出呼叫的程式類型。 +本文件主要著重於將「日曆供應程式」API 作為應用程式使用。 +如要瞭解同步配接器不一樣的資訊,請參閱<a href="#sync-adapter">同步配接器</a>。 + +</p> + + +<p>一般而言,如要讀取或寫入日曆資料,應用程式的宣示說明必須含有適當的權限 (於<a href="#manifest">使用者權限</a>中描述)。 + +如要更輕鬆執行常見操作,「日曆供應程式」提供一組意圖 (於<a href="#intents">日曆意圖</a>中描述)。 + +這些意圖會將使用者帶往「日曆」應用程式,以插入、檢視以及編輯活動。 +使用者與「日曆」應用程式互動後,回到原來的應用程式。 +因此,您的應用程式不需要要求權限,也不需要提供檢視或建立活動的使用者介面。 +</p> + +<h2 id="overview">基本概念</h2> + +<p><a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>會儲存資料,讓應用程式可以存取這些資料。 +內容供應程式是由 Android 平台所提供 (包括「日曆供應程式」),通常會根據關聯式資料庫模型,以一組表格的形式公開資料。表格中的每一列都是一筆記錄,而每一欄則是特定類型和意義的資料。 + +透過「日曆供應程式」API,應用程式和同步配接器可以取得資料庫表格 (此資料庫表格含有使用者日曆資料) 的讀取/寫入存取權。 + +</p> + +<p>每個內容供應程式都會公開一個公用 URI (包裝為 +{@link android.net.Uri} 物件),可唯一識別其資料集。 +控制多個資料集 (多個表格) 的內容供應程式會為每個資料集公開個別的 URI。 +供應程式所有 URI 的開頭字串為「content://」。 +這表示資料是受到內容供應程式的控制。 +「日曆供應程式」會為每個類別 (表格) 的 URI 定義常數。 +這些 URI 的格式為 <code><em><class></em>.CONTENT_URI</code>。 +例如,{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}。 +</p> + +<p>「圖 1」顯示「日曆供應程式」資料模型的圖形表示。它顯示主要表格以及將每個表格連接在一起的欄位。 +</p> + +<img src="{@docRoot}images/providers/datamodel.png" alt="Calendar Provider Data Model" /> +<p class="img-caption"><strong>圖 1.</strong>「日曆供應程式」資料模型。</p> + +<p>使用者可以有多個日曆,而且不同的日曆可以與不同類型的帳戶 (Google 日曆、Exchange 等等) 關聯。</p> + +<p>{@link android.provider.CalendarContract} 會定義日曆的資料模型和活動相關資訊。此資料儲存於一些表格,列示如下。</p> + +<table> + <tr> + <th>表格 (類別)</th> + <th>描述</th> + </tr> + <tr> + <td><p>{@link android.provider.CalendarContract.Calendars}</p></td> + + <td>此表格內含日曆特定的資訊。 +此表格中的每一列包含單一日曆的詳細資訊,例如名稱、色彩、同步資訊等等。 +</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Events}</td> + + <td>此表格內含活動特定的資訊。 +此表格中的每一列包含單一活動的資訊 — 例如,活動標題、位置、開始時間、結束時間等等。 + +活動可以只發生一次或發生多次。參與者、提醒以及延伸屬性儲存於個別的表格。 +它們每一個都有 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID},會參照「活動」表格中的 {@link android.provider.BaseColumns#_ID}。 + +</td> + + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances}</td> + + <td>此表格內含活動每次發生的開始和結束時間。 +此表格的每一列代表單一活動發生。 +單次活動執行個體和活動的對應為 1:1。 +對於週期性活動,則會自動產生多個列,對應到該活動的多次發生。 +</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Attendees}</td> + + <td>此表格內含活動參與者 (邀請對象) 的資訊。 +每一列代表一個活動的單一邀請對象。 +其中會指出邀請對象類型,以及邀請對象是否出席該活動的回應。 +</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Reminders}</td> + + <td>此表格內含警示/通知資訊。 +每一列代表一個活動的單一警示。一個活動可以設定多個提醒。 +每個活動的最大數量提醒指定於 +{@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS},此項是由擁有指定日曆的同步配接器所設定。 + + +提醒是以分鐘數指定活動發生前的時間,而且有一個方法用於決定通知使用者的方式。 +</td> + </tr> + +</table> + +<p>「日曆供應程式」API 的設計具備彈性且功能強大。提供良好的使用者體驗,以及保護日曆及其資料的完整性,兩者一樣重要。 + +關於這一點,使用此 API 時,請記得以下事項: +</p> + +<ul> + +<li><strong>插入、更新以及檢視日曆活動。</strong>如要從「日曆供應程式」直接插入、修改以及讀取活動,您需要具備適當的<a href="#manifest">權限</a>。然而,如果您不是要建置功能豐富的日曆應用程式,則不需要要求這些權限。您可以改用 Android「日曆」應用程式支援的意圖,將讀取和寫入操作交給您的應用程式。使用意圖時,您的應用程式可以將使用者傳送到「日曆」應用程式,在預先填好的表單中執行想要的操作。 +完成之後,使用者會回到您的應用程式。將應用程式設計成透過「日曆」來執行常見的操作,即可為使用者提供一致且完整的使用者介面。 + +我們建議您採用此方式。 +如需詳細資訊,請參閱<a href="#intents">日曆意圖</a>。 +</p> + + +<li><strong>同步配接器。</strong>同步配接器會將使用者裝置的日曆資料與另一台伺服器或資料來源進行同步。 +在 +{@link android.provider.CalendarContract.Calendars} 和 +{@link android.provider.CalendarContract.Events} 表格中,會保留某些欄讓同步配接器使用。供應程式和應用程式不應加以修改。 + +事實上,除非以同步配接器進行存取,否則看不到這些保留的欄。 +如需關於同步配接器的詳細資訊,請參閱<a href="#sync-adapter">同步配接器</a>。 +</li> + +</ul> + + +<h2 id="manifest">使用者權限</h2> + +<p>如要讀取日曆資料,應用程式必須在其宣示說明檔案中包含 {@link +android.Manifest.permission#READ_CALENDAR} 權限。宣示說明檔案必須包含 {@link android.Manifest.permission#WRITE_CALENDAR} 權限,才能刪除、插入或更新日曆資料: + +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"...> + <uses-sdk android:minSdkVersion="14" /> + <uses-permission android:name="android.permission.READ_CALENDAR" /> + <uses-permission android:name="android.permission.WRITE_CALENDAR" /> + ... +</manifest> +</pre> + + +<h2 id="calendar">日曆表格</h2> + +<p>{@link android.provider.CalendarContract.Calendars} 表格包含個別日曆的詳細資訊。 +下列「日曆」欄可以讓應用程式和<a href="#sync-adapter">同步配接器</a>寫入。 +如需關於支援欄位的完整清單,請參閱 +{@link android.provider.CalendarContract.Calendars} 參照。 +</p> +<table> + <tr> + <th>常數</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Calendars#NAME}</td> + <td>日曆的名稱。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}</td> + <td>使用者看到此日曆的名稱。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Calendars#VISIBLE}</td> + + <td>指出是否選擇要顯示日曆的布林值。值 0 表示與此日曆關聯的活動不應顯示。 + +值 1 表示與此日曆關聯的活動應顯示。 +此值會影響 {@link +android.provider.CalendarContract.Instances} 表格中列的產生。</td> + + + </tr> + <tr> + <td>{@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}</td> + + <td>指出日曆是否應同步,並且讓日曆的活動儲存在裝置上的布林值。 +值 0 表示不同步此日曆,並且不要在裝置上儲存其活動。 +值 1 表示同步此日曆的活動,並且在裝置上儲存其活動。 +</td> + </tr> +</table> + +<h3 id="query">查詢日曆</h3> + +<p>以下的範例顯示如何取得特定使用者擁有的日曆。 +為了簡化起見,本範例中的查詢操作顯示於使用者介面執行緒 (即「主要執行緒」) 中。 +實務上,應該以非同步執行緒來完成,而不是使用主要執行緒。 +如需更多討論,請參閱<a href="{@docRoot}guide/components/loaders.html">載入器</a>。 +如果您不只要讀取資料,還要加以修改,請參閱 {@link android.content.AsyncQueryHandler}。 + +</p> + + +<pre> +// Projection array. Creating indices for this array instead of doing +// dynamic lookups improves performance. +public static final String[] EVENT_PROJECTION = new String[] { + Calendars._ID, // 0 + Calendars.ACCOUNT_NAME, // 1 + Calendars.CALENDAR_DISPLAY_NAME, // 2 + Calendars.OWNER_ACCOUNT // 3 +}; + +// The indices for the projection array above. +private static final int PROJECTION_ID_INDEX = 0; +private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1; +private static final int PROJECTION_DISPLAY_NAME_INDEX = 2; +private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;</pre> + + +<div class="sidebox-wrapper"> <div class="sidebox"> <h3>為什麼一定要包含 ACCOUNT_TYPE? +</h3> <p>如果您要針對 {@link +android.provider.CalendarContract.Calendars#ACCOUNT_NAME +Calendars.ACCOUNT_NAME} 進行查詢,也必須在選項中包含 +{@link android.provider.CalendarContract.Calendars#ACCOUNT_TYPE Calendars.ACCOUNT_TYPE}。 +這是因為只有同時提供指定帳戶的 <code>ACCOUNT_NAME</code> 和 +<code>ACCOUNT_TYPE</code> 情況下,此帳戶才會視為唯一的。 +<code>ACCOUNT_TYPE</code> 是對應至帳戶驗證器的字串。帳戶使用 +{@link android.accounts.AccountManager} 註冊時,會使用此帳戶驗證器。 +沒有與裝置帳戶關聯的日曆也有一種特殊類型的帳戶,稱為 {@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL}。{@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} 帳戶不會進行同步。 + +</p> </div> </div> + + +<p> 在範例的下一個部分,您將建構查詢。選項會指定查詢的條件。 +在此範例中,查詢會尋找日曆中有 <code>ACCOUNT_NAME</code> "sampleuser@google.com"、<code>ACCOUNT_TYPE</code> "com.google" 以及 <code>OWNER_ACCOUNT</code> "sampleuser@google.com" 的內容。 + + + +如果您要查看使用者可以檢視的所有日曆,不是只查看使用者擁有的日曆,請略過 <code>OWNER_ACCOUNT</code>。此查詢會傳回 {@link android.database.Cursor} 物件。您可以使用此物件周遊資料庫查詢傳回的結果集。 + + + +如需關於在內容供應程式中使用查詢的詳細討論,請參閱<a href="{@docRoot}guide/topics/providers/content-providers.html">內容供應程式</a>。 +</p> + + +<pre>// Run query +Cursor cur = null; +ContentResolver cr = getContentResolver(); +Uri uri = Calendars.CONTENT_URI; +String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" + + Calendars.ACCOUNT_TYPE + " = ?) AND (" + + Calendars.OWNER_ACCOUNT + " = ?))"; +String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google", + "sampleuser@gmail.com"}; +// Submit the query and get a Cursor object back. +cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);</pre> + +<p>下一節會使用游標逐步檢視結果集。它會使用範例一開始設定好的常數,傳回每個欄位的值。 + +</p> + +<pre>// Use the cursor to step through the returned records +while (cur.moveToNext()) { + long calID = 0; + String displayName = null; + String accountName = null; + String ownerName = null; + + // Get the field values + calID = cur.getLong(PROJECTION_ID_INDEX); + displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX); + accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX); + ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX); + + // Do something with the values... + + ... +} +</pre> + +<h3 id="modify-calendar">修改日曆</h3> + +<p>如要執行日曆的更新,您可以提供日曆的 {@link +android.provider.BaseColumns#_ID},可以是 URI 的附加 ID +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 或以第一個選擇項目方式提供。 + + +選項的開頭應該是 <code>"_id=?"</code>,而且第一個 +<code>selectionArg</code> 應該是日曆的 {@link +android.provider.BaseColumns#_ID}。 +您也可以透過將 ID 編碼在 URI 中,以執行更新。此範例會使用 +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 方式變更日曆的顯示名稱: + + +</p> + +<pre>private static final String DEBUG_TAG = "MyActivity"; +... +long calID = 2; +ContentValues values = new ContentValues(); +// The new display name for the calendar +values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar"); +Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID); +int rows = getContentResolver().update(updateUri, values, null, null); +Log.i(DEBUG_TAG, "Rows updated: " + rows);</pre> + +<h3 id="insert-calendar">插入日曆</h2> + +<p>日曆的設計主要是由同步配接器進行管理,因此您只能以同步配接器的方式插入新日曆。 +在大多數情況下,應用程式只能對日曆進行與外觀相關的變更,例如變更顯示名稱 +。如果應用程式需要建立本機日曆,請以同步配接器的方式插入日曆,方法是使用 {@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} 的 {@link android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE}。 +{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} 是特殊的日曆帳戶類型,它沒有與裝置帳戶關聯。 + + + + + +此類型的日曆不會同步至伺服器。如需關於同步配接器的相關討論,請參閱<a href="#sync-adapter">同步配接器</a>。 +</p> + +<h2 id="events">活動表格</h2> + +<p>{@link android.provider.CalendarContract.Events} 表格包含個人活動的詳細資訊。 +如要新增、更新或刪除活動,應用程式必須在<a href="#manifest">宣示說明檔案</a>中包括 {@link android.Manifest.permission#WRITE_CALENDAR}權限。 + +</p> + +<p>下列「活動」欄可以讓應用程式和同步配接器寫入。 +如需關於支援欄位的完整清單,請參閱 {@link +android.provider.CalendarContract.Events} 參照。</p> + +<table> + <tr> + <th>常數</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}</td> + <td>活動所屬日曆的 {@link android.provider.BaseColumns#_ID}。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#ORGANIZER}</td> + <td>活動主辦人 (擁有者) 的電子郵件。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#TITLE}</td> + <td>活動的標題。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}</td> + <td>舉辦活動的地點。 </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}</td> + <td>活動的描述。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#DTSTART}</td> + <td>活動開始的時間,以紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。 </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#DTEND}</td> + <td>活動結束的時間,以紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。 </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}</td> + <td>活動的時區。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}</td> + <td>活動結束時間的時區。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#DURATION}</td> + + <td>活動的持續期間,以 <a href="http://tools.ietf.org/html/rfc5545#section-3.8.2.5">RFC5545</a> 格式表示。例如,值 <code>"PT1H"</code> 表示活動持續一小時,而值 <code>"P2W"</code> 指出持續 2 週。 + + + </td> + + + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#ALL_DAY}</td> + + <td>值 1 表示此活動需要整天,如同當地時區所定義。 +值 0 表示定期活動,會在一天中的任何時間開始和結束。 +</td> + + + </tr> + + + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#RRULE}</td> + + <td>活動的週期規則。例如,<code>"FREQ=WEEKLY;COUNT=10;WKST=SU"</code>。 +您可以在<a href="http://tools.ietf.org/html/rfc5545#section-3.8.5.3">這裡</a>查看更多範例。 +</td> + + </tr> + + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#RDATE}</td> + <td>活動重複發生的日期。您通常會將 {@link android.provider.CalendarContract.EventsColumns#RDATE} 和 {@link android.provider.CalendarContract.EventsColumns#RRULE} 一起使用,以定義週期性活動的彙總集合。 + + + +如需更多討論,請參閱 <a href="http://tools.ietf.org/html/rfc5545#section-3.8.5.2">RFC5545 規格</a>。</td> +</tr> + + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}</td> + + <td>活動要視為忙碌或有空 (可以安排其他活動) 的時間。 + </td> + + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}</td> + <td>邀請對象是否可以修改活動。 </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}</td> + <td>邀請對象是否可以邀請其他人。 </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}</td> + <td>邀請對象是否可以看到參與者清單。</td> + </tr> +</table> + +<h3 id="add-event">新增活動</h3> + +<p>您的應用程式插入新活動時,我們建議您使用 +{@link android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同<a href="#intent-insert">使用意圖插入活動</a>所述)。不過,如果需要,您也可以直接插入活動。 +本節將描述如何執行此動作。 +</p> + + +<p>以下是插入新活動的規則: </p> +<ul> + + <li>您必須包括 {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} 和 {@link +android.provider.CalendarContract.EventsColumns#DTSTART}。</li> + +<li>您必須包括 {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}。如要取得系統已安裝時區 ID 的清單,請使用 {@link +java.util.TimeZone#getAvailableIDs()}。 +請注意,如果您是透過 {@link +android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同<a href="#intent-insert">使用意圖插入活動</a> — 所描述的案例) 插入活動,則不適用此規則,將會提供預設時區。 + +</li> + + <li>對於非週期性活動,您必須包括 {@link +android.provider.CalendarContract.EventsColumns#DTEND}。 </li> + + + <li>對於週期性活動,您必須包括 {@link +android.provider.CalendarContract.EventsColumns#DURATION},以及 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 或 {@link +android.provider.CalendarContract.EventsColumns#RDATE}。請注意,如果您是透過 {@link +android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同<a href="#intent-insert">使用意圖插入活動</a> — 所描述的案例) 插入活動,則不適用此規則。您可以使用 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 搭配 {@link android.provider.CalendarContract.EventsColumns#DTSTART} 和 {@link android.provider.CalendarContract.EventsColumns#DTEND},然後「日曆」應用程式就會自動將它轉換為一段持續時間。 + + +</li> + +</ul> + +<p>以下是插入活動的範例。為了簡化,會在 UI 執行緒中執行此示範。 +實際運作時,插入和更新應該在非同步執行緒中完成,以便將動作移至背景執行緒。 +如需詳細資訊,請參閱 {@link android.content.AsyncQueryHandler}。 +</p> + + +<pre> +long calID = 3; +long startMillis = 0; +long endMillis = 0; +Calendar beginTime = Calendar.getInstance(); +beginTime.set(2012, 9, 14, 7, 30); +startMillis = beginTime.getTimeInMillis(); +Calendar endTime = Calendar.getInstance(); +endTime.set(2012, 9, 14, 8, 45); +endMillis = endTime.getTimeInMillis(); +... + +ContentResolver cr = getContentResolver(); +ContentValues values = new ContentValues(); +values.put(Events.DTSTART, startMillis); +values.put(Events.DTEND, endMillis); +values.put(Events.TITLE, "Jazzercise"); +values.put(Events.DESCRIPTION, "Group workout"); +values.put(Events.CALENDAR_ID, calID); +values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles"); +Uri uri = cr.insert(Events.CONTENT_URI, values); + +// get the event ID that is the last element in the Uri +long eventID = Long.parseLong(uri.getLastPathSegment()); +// +// ... do something with event ID +// +//</pre> + +<p class="note"><strong>注意:</strong>查看本範例如何在建立活動後擷取活動 ID。 +這是取得活動 ID 的最簡單方式。您經常需要活動 ID 來執行其他日曆操作 — 例如,在活動中新增參與者或提醒。 + +</p> + + +<h3 id="update-event">更新活動</h3> + +<p>您的應用程式允許使用者編輯活動時,我們建議您使用 {@link android.content.Intent#ACTION_EDIT EDIT} 意圖編輯活動 (如同<a href="#intent-edit">使用意圖插入活動</a>所述)。不過,如果需要,您也可以直接編輯活動。 + + +如要執行活動的更新,您要提供活動的 <code>_ID</code>,可以是 URI 的附加 ID ({@link +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 或以第一個選擇項目方式提供。 + + +選項的開頭應該是 <code>"_id=?"</code>,而且第一個 +<code>selectionArg</code> 應該是活動的 <code>_ID</code>。 +您也可以使用不含 ID 的選項進行更新。以下是更新活動的範例。 + +它使用 +{@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()} 方式變更活動的標題: +</p> + + +<pre>private static final String DEBUG_TAG = "MyActivity"; +... +long eventID = 188; +... +ContentResolver cr = getContentResolver(); +ContentValues values = new ContentValues(); +Uri updateUri = null; +// The new title for the event +values.put(Events.TITLE, "Kickboxing"); +updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); +int rows = getContentResolver().update(updateUri, values, null, null); +Log.i(DEBUG_TAG, "Rows updated: " + rows); </pre> + +<h3 id="delete-event">刪除活動</h3> + +<p>您可以透過活動 URI 的附加 ID {@link +android.provider.BaseColumns#_ID} 或使用標準選擇方式來刪除活動。 +如果您使用附加 ID,就不能進行選擇。刪除有兩種方式:以應用程式和以同步配接器。 +應用程式刪除會將 <em>deleted</em> 欄設定為 1。 +此旗標會告訴同步配接器該列已刪除,而且此刪除應傳播到伺服器。 + +同步配接器會從資料庫刪除活動及其相關的所有資料。 +以下是應用程式透過活動的 {@link android.provider.BaseColumns#_ID}刪除活動的範例: +</p> + + +<pre>private static final String DEBUG_TAG = "MyActivity"; +... +long eventID = 201; +... +ContentResolver cr = getContentResolver(); +ContentValues values = new ContentValues(); +Uri deleteUri = null; +deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); +int rows = getContentResolver().delete(deleteUri, null, null); +Log.i(DEBUG_TAG, "Rows deleted: " + rows); +</pre> + +<h2 id="attendees">參與者表格</h2> + +<p>{@link android.provider.CalendarContract.Attendees} 表格的每一列都代表活動的單一參與者或邀請對象。 +呼叫 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} 會傳回指定 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 活動的參與者清單。 + +此 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 必須符合特定活動的 {@link +android.provider.BaseColumns#_ID}。 + +</p> + +<p>下表列出可寫入的欄位。 +插入新的參與者時,您必須包括 <code>ATTENDEE_NAME</code> 以外的所有項目。 + +</p> + + +<table> + <tr> + <th>常數</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}</td> + <td>活動的 ID。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}</td> + <td>參與者的名稱。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}</td> + <td>參與者的電子郵件地址。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}</td> + <td><p>參與者與活動的關係。可以是以下其中一種:</p> + <ul> + <li>{@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}</li> + </ul> + </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}</td> + <td><p>參與者的類型。可以是以下其中一種: </p> + <ul> + <li>{@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}</li> + </ul></td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}</td> + <td><p>參與者的出席狀態。可以是以下其中一種:</p> + <ul> + <li>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}</li> + <li>{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}</li> + </ul></td> + </tr> +</table> + +<h3 id="add-attendees">新增參與者</h3> + +<p>以下是將單一參與者新增至活動的範例。請注意, +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 為必要的: +</p> + +<pre> +long eventID = 202; +... +ContentResolver cr = getContentResolver(); +ContentValues values = new ContentValues(); +values.put(Attendees.ATTENDEE_NAME, "Trevor"); +values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com"); +values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE); +values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL); +values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED); +values.put(Attendees.EVENT_ID, eventID); +Uri uri = cr.insert(Attendees.CONTENT_URI, values); +</pre> + +<h2 id="reminders">提醒表格</h2> + +<p>{@link android.provider.CalendarContract.Reminders} 表格的每一列都代表活動的單一提醒。 +呼叫 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} 會傳回指定 +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 活動的提醒清單。 +</p> + + +<p>下表列出提醒可寫入的欄位。插入新的提醒時,必須包括所有項目。 +請注意,同步配接器會在 {@link +android.provider.CalendarContract.Calendars} 表格中指出同步配接器支援的提醒類型。 +如需詳細資料,請參閱 +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS}。 +</p> + + +<table> + <tr> + <th>常數</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}</td> + <td>活動的 ID。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.RemindersColumns#MINUTES}</td> + <td>活動之前的幾分鐘要觸發提醒。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.RemindersColumns#METHOD}</td> + <td><p>鬧鐘方法 (如伺服器上所設定)。可以是以下其中一種:</p> + <ul> + <li>{@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}</li> + <li>{@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}</li> + <li>{@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}</li> + <li>{@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}</li> + </ul></td> + </tr> +</table> + +<h3 id="add-reminders">新增提醒</h3> + +<p>此範例會在活動新增提醒。此提醒會在活動 15 分鐘之前觸發。 +</p> +<pre> +long eventID = 221; +... +ContentResolver cr = getContentResolver(); +ContentValues values = new ContentValues(); +values.put(Reminders.MINUTES, 15); +values.put(Reminders.EVENT_ID, eventID); +values.put(Reminders.METHOD, Reminders.METHOD_ALERT); +Uri uri = cr.insert(Reminders.CONTENT_URI, values);</pre> + +<h2 id="instances">執行個體表格</h2> + +<p> +{@link android.provider.CalendarContract.Instances} 表格內含活動每次發生的開始和結束時間。 +此表格的每一列代表單一活動發生。 +執行個體表格無法寫入,僅供查詢活動的發生。 + </p> + +<p>下表列出您可以針對執行個體查詢的欄位。請注意,時區是由 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} 和 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES} 所定義。 + +</p> + + +<table> + <tr> + <th>常數</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#BEGIN}</td> + <td>執行個體的開始時間,以 UTC 毫秒數為單位。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#END}</td> + <td>執行個體的結束時間,以 UTC 毫秒數為單位。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#END_DAY}</td> + + <td>執行個體的凱撒曆結束日,與「日曆」的時區相關。 + + +</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#END_MINUTE}</td> + + <td>執行個體的結束分鐘,從「日曆」時區的午夜開始計算。 +</td> + + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#EVENT_ID}</td> + <td>此執行個體活動的 <code>_ID</code>。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#START_DAY}</td> + <td>執行個體的凱撒曆開始日,與「日曆」的時區相關。 + </td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.Instances#START_MINUTE}</td> + + <td>執行個體的開始分鐘,從午夜開始計算,與「日曆」的時區相關。 + +</td> + + </tr> + +</table> + +<h3 id="query-instances">查詢執行個體表格</h3> + +<p>如要查詢「執行個體」表格,您需要在 URI 中指定查詢的範圍時間。在本範例中,{@link android.provider.CalendarContract.Instances} 是透過 {@link android.provider.CalendarContract.EventsColumns} 介面的實作,得以存取 {@link +android.provider.CalendarContract.EventsColumns#TITLE} 欄位。 + + +換句話說,{@link +android.provider.CalendarContract.EventsColumns#TITLE} 是透過資料庫視觀表所傳回,而不是透過查詢原始 {@link +android.provider.CalendarContract.Instances} 表格。 + +</p> + +<pre> +private static final String DEBUG_TAG = "MyActivity"; +public static final String[] INSTANCE_PROJECTION = new String[] { + Instances.EVENT_ID, // 0 + Instances.BEGIN, // 1 + Instances.TITLE // 2 + }; + +// The indices for the projection array above. +private static final int PROJECTION_ID_INDEX = 0; +private static final int PROJECTION_BEGIN_INDEX = 1; +private static final int PROJECTION_TITLE_INDEX = 2; +... + +// Specify the date range you want to search for recurring +// event instances +Calendar beginTime = Calendar.getInstance(); +beginTime.set(2011, 9, 23, 8, 0); +long startMillis = beginTime.getTimeInMillis(); +Calendar endTime = Calendar.getInstance(); +endTime.set(2011, 10, 24, 8, 0); +long endMillis = endTime.getTimeInMillis(); + +Cursor cur = null; +ContentResolver cr = getContentResolver(); + +// The ID of the recurring event whose instances you are searching +// for in the Instances table +String selection = Instances.EVENT_ID + " = ?"; +String[] selectionArgs = new String[] {"207"}; + +// Construct the query with the desired date range. +Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); +ContentUris.appendId(builder, startMillis); +ContentUris.appendId(builder, endMillis); + +// Submit the query +cur = cr.query(builder.build(), + INSTANCE_PROJECTION, + selection, + selectionArgs, + null); + +while (cur.moveToNext()) { + String title = null; + long eventID = 0; + long beginVal = 0; + + // Get the field values + eventID = cur.getLong(PROJECTION_ID_INDEX); + beginVal = cur.getLong(PROJECTION_BEGIN_INDEX); + title = cur.getString(PROJECTION_TITLE_INDEX); + + // Do something with the values. + Log.i(DEBUG_TAG, "Event: " + title); + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(beginVal); + DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); + Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime())); + } + }</pre> + +<h2 id="intents">日曆意圖</h2> +<p>您的應用程式不需要<a href="#manifest">權限</a>,即可讀取和寫入日曆資料。您可以改用 Android「日曆」應用程式支援的意圖,將讀取和寫入操作交給您的應用程式。下表列出「日曆供應程式」支援的意圖:</p> +<table> + <tr> + <th>動作</th> + <th>URI</th> + + <th>描述</th> + <th>Extra</th> + </tr> + <tr> + <td><br> + {@link android.content.Intent#ACTION_VIEW VIEW} <br></td> + <td><p><code>content://com.android.calendar/time/<ms_since_epoch></code></p> + 您也可以使用 +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html#intent-view">使用意圖檢視日曆資料</a>。 + + + </td> + <td>開啟日曆到 <code><ms_since_epoch></code> 指定的時間。</td> + <td>無。</td> + </tr> + <tr> + <td><p>{@link android.content.Intent#ACTION_VIEW VIEW} </p> + + </td> + <td><p><code>content://com.android.calendar/events/<event_id></code></p> + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html#intent-view">使用意圖檢視日曆資料</a>。 + + + </td> + <td>檢視 <code><event_id></code> 指定的活動。</td> + + <td>{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}<br> + <br> + <br> + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}</td> + </tr> + + <tr> + <td>{@link android.content.Intent#ACTION_EDIT EDIT} </td> + <td><p><code>content://com.android.calendar/events/<event_id></code></p> + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html#intent-edit">使用意圖編輯活動</a>。 + + + + </td> + <td>編輯 <code><event_id></code> 指定的活動。</td> + + <td>{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}<br> + <br> + <br> + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}</td> + </tr> + + <tr> + <td>{@link android.content.Intent#ACTION_EDIT EDIT} <br> + <br> + {@link android.content.Intent#ACTION_INSERT INSERT} </td> + <td><p><code>content://com.android.calendar/events</code></p> + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html#intent-insert">使用意圖插入活動</a>。 + + + </td> + + <td>建立活動。</td> + <td>Extra 列於下表。</td> + </tr> +</table> + +<p>下表列出「日曆供應程式」支援的意圖 Extra: +</p> +<table> + <tr> + <th>意圖 Extra</th> + <th>描述</th> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}</td> + <td>活動的名稱。</td> + </tr> + <tr> + + <td>{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}</td> + <td>活動開始時間,以紀元元年 1 月 1 日零時起算經過的毫秒數為單位。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}</td> + + <td>活動結束時間,以紀元元年 1 月 1 日零時起算經過的毫秒數為單位。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}</td> + + <td>指出活動為整天的布林值。值可以是 +<code>true</code> 或 <code>false</code>。</td> </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}</td> + + <td>活動的地點。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}</td> + + <td>活動描述。</td> + </tr> + <tr> + <td> + {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}</td> + <td>邀請對象的電子郵件地址 (以逗號分隔的清單)。</td> + </tr> + <tr> + <td> + {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}</td> + <td>活動的週期規則。</td> + </tr> + <tr> + <td> + {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}</td> + + <td>活動為私人或公開性質。</td> + </tr> + <tr> + <td>{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}</td> + + <td>活動要視為忙碌或有空 (可以安排其他活動) 的時間。</td> + +</table> +<p>以下各節說明如何使用這些意圖。</p> + + +<h3 id="intent-insert">使用意圖插入活動</h3> + +<p>使用 {@link android.content.Intent#ACTION_INSERT INSERT} 意圖讓您的應用程式將活動插入工作交給「日曆」本身。使用此方式,您的應用程式就不需要將 {@link +android.Manifest.permission#WRITE_CALENDAR} 權限包括在其<a href="#manifest">宣示說明檔案</a>中。 + +</p> + + +<p>使用者執行採用此方式的應用程式時,此應用程式會將使用者 +傳送到「日曆」以完成新增活動的操作。{@link +android.content.Intent#ACTION_INSERT INSERT} 意圖會使用額外的欄位將「日曆」中活動的詳細資訊,預先填入表單。 +然後,使用者可以取消活動、視需要編輯表單或將活動儲存到其日曆。 + +</p> + + + +<p>以下的程式碼片段會在 2012 年 1 月 19 日安排活動,此活動的期間是從上午 7:30 到上午 8:30。 +請注意下列關於此程式碼片段的事項:</p> + +<ul> + <li>它指定 {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 作為 URI。 +</li> + + <li>它使用 {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} 和 {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} 額外欄位,將活動的時間預先填入表單。 +這些時間值必須以自紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。 +</li> + + <li>它使用 {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} 額外欄位提供逗號分隔的受邀者清單 (以電子郵件地址指定)。 +</li> + +</ul> +<pre> +Calendar beginTime = Calendar.getInstance(); +beginTime.set(2012, 0, 19, 7, 30); +Calendar endTime = Calendar.getInstance(); +endTime.set(2012, 0, 19, 8, 30); +Intent intent = new Intent(Intent.ACTION_INSERT) + .setData(Events.CONTENT_URI) + .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis()) + .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis()) + .putExtra(Events.TITLE, "Yoga") + .putExtra(Events.DESCRIPTION, "Group class") + .putExtra(Events.EVENT_LOCATION, "The gym") + .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY) + .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com"); +startActivity(intent); +</pre> + +<h3 id="intent-edit">使用意圖編輯活動</h3> + +<p>您可以直接更新活動,如同<a href="#update-event">更新活動</a>所述。不過,使用 {@link +android.content.Intent#ACTION_EDIT EDIT} 意圖可以讓不具備權限的應用程式將活動編輯操作交給「日曆」應用程式。使用者在「日曆」中完成活動的編輯操作時,使用者就會回到原來的應用程式。 + + +</p> <p>以下的意圖範例會為指定的活動設定名稱,然後讓使用者在「日曆」中編輯此活動。 +</p> + + +<pre>long eventID = 208; +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); +Intent intent = new Intent(Intent.ACTION_EDIT) + .setData(uri) + .putExtra(Events.TITLE, "My New Title"); +startActivity(intent);</pre> + +<h3 id="intent-view">使用意圖檢視日曆資料</h3> +<p>「日曆供應程式」提供兩種不同的方式來使用 {@link android.content.Intent#ACTION_VIEW VIEW} 意圖:</p> +<ul> + <li>開啟「日曆」到特定的日期。</li> + <li>檢視某個活動。</li> + +</ul> +<p>以下的範例顯示如何開啟「日曆」到特定的日期:</p> +<pre>// A date-time specified in milliseconds since the epoch. +long startMillis; +... +Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); +builder.appendPath("time"); +ContentUris.appendId(builder, startMillis); +Intent intent = new Intent(Intent.ACTION_VIEW) + .setData(builder.build()); +startActivity(intent);</pre> + +<p>以下的範例顯示如何開啟某個活動以便檢視。</p> +<pre>long eventID = 208; +... +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID); +Intent intent = new Intent(Intent.ACTION_VIEW) + .setData(uri); +startActivity(intent); +</pre> + + +<h2 id="sync-adapter">同步配接器</h2> + + +<p>應用程式和同步配接器存取「日曆供應程式」的方式只有些微差異: +</p> + +<ul> + <li>同步配接器需要透過將 {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} 設定為 <code>true</code>,以指出這是同步配接器。</li> + + + <li>同步配接器需要在 URI 中提供 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} 和 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} 作為查詢參數。 </li> + + <li>同步配接器與應用程式或小工具相比,有更多欄的寫入權限。 + 例如,應用程式只能修改日曆的一些特性,例如名稱、顯示名稱、能見度設定,以及日曆是否同步。 + +相較之下,同步配接器不只能存取這些欄,還可以存取很多其他欄,例如日曆色彩、時區、存取級別、位置等等。然而,同步配接器受限於所指定的 <code>ACCOUNT_NAME</code> 和 +<code>ACCOUNT_TYPE</code>。 + +</li> </ul> + +<p>以下是您可用於傳回 URI 的協助程式方法,以便與同步配接器搭配使用:</p> +<pre> static Uri asSyncAdapter(Uri uri, String account, String accountType) { + return uri.buildUpon() + .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true") + .appendQueryParameter(Calendars.ACCOUNT_NAME, account) + .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); + } +</pre> +<p>如需實作同步配接器的範例 (並非與「日曆」特別相關),請參閱 +<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">SampleSyncAdapter</a>。 diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd new file mode 100644 index 000000000000..b5f888012eed --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=聯絡人供應程式 +@jd:body +<div id="qv-wrapper"> +<div id="qv"> +<h2>快速檢視</h2> +<ul> + <li>Android 關於人員資訊的存放庫。</li> + <li> + 與網頁同步。 + </li> + <li> + 整合社交串流資料。 + </li> +</ul> +<h2>本文件內容</h2> +<ol> + <li> + <a href="#InformationTypes">聯絡人供應程式組織架構</a> + </li> + <li> + <a href="#RawContactBasics">原始聯絡人</a> + </li> + <li> + <a href="#DataBasics">資料</a> + </li> + <li> + <a href="#ContactBasics">聯絡人</a> + </li> + <li> + <a href="#Sources">來自同步配接器的資料</a> + </li> + <li> + <a href="#Permissions">必要權限</a> + </li> + <li> + <a href="#UserProfile">使用者設定檔</a> + </li> + <li> + <a href="#ContactsProviderMetadata">聯絡人供應程式中繼資料</a> + </li> + <li> + <a href="#Access">聯絡人供應程式存取</a> + <li> + </li> + <li> + <a href="#SyncAdapters">聯絡人供應程式同步配接器</a> + </li> + <li> + <a href="#SocialStream">社交串流資料</a> + </li> + <li> + <a href="#AdditionalFeatures">其他聯絡人供應程式功能</a> + </li> +</ol> +<h2>重要類別</h2> +<ol> + <li>{@link android.provider.ContactsContract.Contacts}</li> + <li>{@link android.provider.ContactsContract.RawContacts}</li> + <li>{@link android.provider.ContactsContract.Data}</li> + <li>{@code android.provider.ContactsContract.StreamItems}</li> +</ol> +<h2>相關範例</h2> +<ol> + <li> + <a href="{@docRoot}resources/samples/ContactManager/index.html">聯絡人管理員 +</a> + + </li> + <li> + <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html"> +範例同步配接器</a> + </li> +</ol> +<h2>另請參閱</h2> +<ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> + 內容供應程式基本概念 + </a> + </li> +</ol> +</div> +</div> +<p> + 聯絡人供應程式是強大且有彈性的 Android 元件,負責管理裝置上與人員相關的中央資料存放庫。 +聯絡人供應程式是裝置中聯絡人應用程式的資料來源,您也可以在自己的應用程式中存取其資料,並且在裝置和線上服務之間傳輸資料。 + +供應程式內含範圍寬廣的資料來源,而且嘗試管理每個人愈來愈多的資料,組織架構因而變得很複雜。 + +基於這個原因,供應程式的 API 包括豐富的合約類別和介面,可幫助資料的擷取和修改。 + + +</p> +<p> + 本指南描述以下各項: +</p> + <ul> + <li> + 基本的供應程式結構。 + </li> + <li> + 如何從供應程式擷取資料。 + </li> + <li> + 如何修改供應程式中的資料。 + </li> + <li> + 如何針對從伺服器到聯絡人供應程式的資料同步編寫同步配接器。 + + </li> + </ul> +<p> + 本指南假設您瞭解 Android 內容供應程式基本概念。如要更瞭解 Android 內容供應程式的詳細資訊,請閱讀<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">內容供應程式基本概念</a>指南。 + + +<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>範例應用程式是使用同步配接器在聯絡人供應程式和 Google 網路服務代管的範例應用程式之間傳輸資料的例子。 + + + +</p> +<h2 id="InformationTypes">聯絡人供應程式組織架構</h2> +<p> + 聯絡人供應程式是 Android 內容供應程式元件。負責維護三種與人有關的資料類型,每一種類型都會對應到供應程式所提供的表格,如圖 1 所示: + + +</p> +<img src="{@docRoot}images/providers/contacts_structure.png" alt="" height="364" id="figure1" /> +<p class="img-caption"> + <strong>圖 1.</strong>聯絡人供應程式表格結構。 +</p> +<p> + 這三個表格通常會以其合約類別的名稱來稱呼。類別會定義內容 URI 的常數、欄名稱以及表格所使用的欄值: + +</p> +<dl> + <dt> + {@link android.provider.ContactsContract.Contacts} 表格 + </dt> + <dd> + 根據原始聯絡人列的彙總,代表不同人員的列。 + </dd> + <dt> + {@link android.provider.ContactsContract.RawContacts} 表格 + </dt> + <dd> + 內含人員資料摘要的列,針對使用者帳戶和類型。 + </dd> + <dt> + {@link android.provider.ContactsContract.Data} 表格 + </dt> + <dd> + 內含原始聯絡人詳細資料的列,例如電子郵件地址或電話號碼。 + </dd> +</dl> +<p> + 合約類別在 {@link android.provider.ContactsContract} 中呈現的其他表格都是輔助表格。聯絡人供應程式使用輔助表格來管理其操作,或支援裝置中聯絡人或電話應用程式的特定功能。 + + +</p> +<h2 id="RawContactBasics">原始聯絡人</h2> +<p> + 原始聯絡人是來自單一帳戶類型和帳戶名稱的人員資料。 +因為聯絡人供應程式允許一個人有多種線上服務做為資料來源,所以聯絡人供應程式可以讓同一個人有多個原始聯絡人。 + + 多個原始聯絡人也可以讓使用者,將相同帳戶類型中多個帳戶的人員資料合併。 + +</p> +<p> + 原始聯絡人的大部分資料不是儲存在 +{@link android.provider.ContactsContract.RawContacts} 表格,而是儲存在 {@link android.provider.ContactsContract.Data} 表格中的一或多個列。 +每個資料列都有一欄 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID},當中包含其上層資料列 {@link android.provider.ContactsContract.RawContacts} 的 {@code android.provider.BaseColumns#_ID RawContacts._ID} 值。 + + +</p> +<h3 id="RawContactsColumns">重要的原始聯絡人欄</h3> +<p> + {@link android.provider.ContactsContract.RawContacts} 表格中的重要欄列於表 1。 +請詳閱表格下方的注意事項: +</p> +<p class="table-caption" id="table1"> + <strong>表 1.</strong>重要的原始聯絡人欄。 +</p> +<table> + <tr> + <th scope="col">欄名稱</th> + <th scope="col">用途</th> + <th scope="col">備註</th> + </tr> + <tr> + <td> + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + </td> + <td> + 帳戶類型 (此原始聯絡人的來源) 的帳戶名稱。 + 例如,Google 帳戶的帳戶名稱是裝置擁有者的其中一個 Gmail 地址。 +請參閱下一個項目,進一步瞭解 + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}。 + + </td> + <td> + 不同的帳戶類型的名稱格式各不相同。帳戶名稱不一定是電子郵件地址。 + + </td> + </tr> + <tr> + <td> + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + </td> + <td> + 帳戶類型是此原始聯絡人的來源。例如,Google 帳戶的帳戶類型為 <code>com.google</code>。 +一定要將帳戶類型加上您所擁有或控制網域的網域識別碼。 +這樣可以確認您的帳戶類型是唯一的。 + + </td> + <td> + 提供聯絡人資料的帳戶類型通常有關聯的同步配接器,可以與聯絡人供應程式同步。 + + </tr> + <tr> + <td> + {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + </td> + <td> + 原始聯絡人的「已刪除」旗標。 + </td> + <td> + 此旗標讓聯絡人供應程式可以在內部維護列,直到同步配接器從其伺服器刪除該列,最後從存放庫刪除該列。 + + + </td> + </tr> +</table> +<h4>備註</h4> +<p> + 以下是有關 + {@link android.provider.ContactsContract.RawContacts} 表格的注意事項: +</p> +<ul> + <li> + 原始聯絡人的名稱不是儲存於本身在 +{@link android.provider.ContactsContract.RawContacts} 的列,而是儲存在 {@link android.provider.ContactsContract.Data} 表格的 + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列中。 +原始聯絡人在 {@link android.provider.ContactsContract.Data} 表格中只有一列這種類型。 + + </li> + <li> + <strong>注意:</strong>如要使用您在原始聯絡人列中自己的帳戶資料,必須先向 {@link android.accounts.AccountManager} 註冊。 +如要註冊,請提示使用者將帳戶類型及其帳戶名稱新增至帳戶清單。 +如果沒有這麼做,聯絡人供應程式將自動刪除您的原始聯絡人列。 + + <p> + 例如,如果您希望應用程式維護網域為 {@code com.example.dataservice} 網頁式服務的聯絡人資料,此服務的使用者帳戶是 {@code becky.sharp@dataservice.example.com},這名使用者必須先新增帳戶「類型」({@code com.example.dataservice}) 和帳戶「名稱」 +({@code becky.smart@dataservice.example.com}),您的應用程式才能新增原始聯絡人列。 + + + + 您可以在文件中向使用者說明此需求,或提示使用者同時新增類型和名稱。 +帳戶類型和帳戶名稱在下一節有更詳細的說明。 + + </li> +</ul> +<h3 id="RawContactsExample">原始聯絡人資料的來源</h3> +<p> + 如要瞭解原始聯絡人的運作方式,假設一位使用者 Emily Dickinson 在其裝置上定義了下列三個使用者帳戶: + +</p> +<ul> + <li><code>emily.dickinson@gmail.com</code></li> + <li><code>emilyd@gmail.com</code></li> + <li>Twitter 帳戶「belle_of_amherst」</li> +</ul> +<p> + 此使用者在「帳戶」<em></em>設定中為這三個帳戶啟用了「同步聯絡人」<em></em>。 + +</p> +<p> + 假設 Emily Dickinson 開啟瀏覽器視窗,使用 + <code>emily.dickinson@gmail.com</code>登入 Gmail,開啟[聯絡人] 並新增「Thomas Higginson」。 +接著,她使用 + <code>emilyd@gmail.com</code>登入 Gmail,並寄送電子郵件給「Thomas Higginson」(系統已自動將他新增為聯絡人)。 +她也在 Twitter 上關注「colonel_tom」(Thomas Higginson 的 Twitter ID)。 + +</p> +<p> + 聯絡人供應程式建立三個原始聯絡人的方式如下: +</p> +<ol> + <li> + 「Thomas Higginson」的原始聯絡人與 <code>emily.dickinson@gmail.com</code> 相關聯。 + 該使用者帳戶類型為 Google。 + </li> + <li> + 「Thomas Higginson」的第二個原始聯絡人與 <code>emilyd@gmail.com</code> 相關聯。 + 該使用者帳戶類型也是 Google。儘管第二個原始聯絡人的名稱與前一個名稱完全相同,但此人是針對不同使用者帳戶所新增的。 + + + </li> + <li> + 「Thomas Higginson」的第三個原始聯絡人與「belle_of_amherst」相關聯。該使用者帳戶類型是 Twitter。 + + </li> +</ol> +<h2 id="DataBasics">資料</h2> +<p> + 如前所述,原始聯絡人的資料是儲存在 + {@link android.provider.ContactsContract.Data} 列,而此列會連結到原始聯絡人的 + <code>_ID</code> 值。這樣讓原始聯絡人的相同資料類型可以有多個執行個體,例如電子郵件地址或電話號碼。 +例如,如果{@code emilyd@gmail.com} 的 "Thomas Higginson" (Thomas Higginson 的原始聯絡人列,與 Google 帳戶 <code>emilyd@gmail.com</code> 關聯) 的住家電子郵件地址為 + <code>thigg@gmail.com</code>,而工作電子郵件地址為 + <code>thomas.higginson@gmail.com</code>,則聯絡人供應程式會儲存這兩個電子郵件地址列,並將兩者連結到原始聯絡人。 + + + +</p> +<p> + 請注意,此單一表格中儲存了不同類型的資料。{@link android.provider.ContactsContract.Data} 表格中可以看到顯示名稱、電話號碼、電子郵件、郵寄地址、相片以及網站詳細資料等列。 + +為了協助管理這些內容, +{@link android.provider.ContactsContract.Data} 表格有一些欄附有描述性名稱,而其他欄則有一般名稱。 +描述性名稱欄的內容有相同的意義 (無論列中的資料類型為何),而一般名稱欄的內容則視資料的類型會有不同的意義。 + + +</p> +<h3 id="DescriptiveColumns">描述性欄名稱</h3> +<p> + 以下提供幾個描述性欄名稱範例: +</p> +<dl> + <dt> + {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} + </dt> + <dd> + 原始聯絡人 <code>_ID</code> 欄的資料值。 + </dd> + <dt> + {@link android.provider.ContactsContract.Data#MIMETYPE} + </dt> + <dd> + 儲存在此列中的資料類型,以自訂 MIME 類型表示。聯絡人供應程式會使用 + {@link android.provider.ContactsContract.CommonDataKinds} 子類別中定義的MIME 類型。 +這些 MIME 類型屬開放原始碼,可以和聯絡人供應程式搭配的任何應用程式或同步配接器都可以加以使用。 + + </dd> + <dt> + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} + </dt> + <dd> + 如果原始聯絡人的此類型資料列出現多次,則 + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 欄會在包含主要資料類型的資料列加上旗標。 +例如,使用者長按聯絡人的電話號碼並選取 [設為預設值]<strong></strong>,則包含該號碼的 {@link android.provider.ContactsContract.Data} 列就會將它的 {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 欄設為零以外的值。 + + + + + </dd> +</dl> +<h3 id="GenericColumns">一般欄名稱</h3> +<p> + 一般欄有 15 個 (名稱為 <code>DATA1</code> 到 + <code>DATA15</code>),系統通常會提供這些欄。另外有 4 個一般欄(<code>SYNC1</code> 到 <code>SYNC4</code>) 只能透過同步配接器使用。 + +不管列中的資料類型為何,一定可以使用一般欄名稱常數。 + +</p> +<p> + <code>DATA1</code> 欄會建立索引。聯絡人供應程式一律會使用此欄的資料,而且供應程式預期此欄為最常查詢的目標。 +例如,在電子郵件列中,此欄內含實際的電子郵件地址。 + +</p> +<p> + 一般來說,<code>DATA15</code> 欄會保留用於儲存「二進位大型物件」(BLOB) 資料,例如相片縮圖。 + +</p> +<h3 id="TypeSpecificNames">類型特定的欄名稱</h3> +<p> + 為了要協助欄處理具有特定類型的列,聯絡人供應程式也提供類型特定的欄名稱常數。這些常數會在 + {@link android.provider.ContactsContract.CommonDataKinds}的子類別中定義。 +常數只是為相同欄名稱指定不同的常數名稱,以協助您存取列中特定類型的資料。 + + +</p> +<p> + 例如,{@link android.provider.ContactsContract.CommonDataKinds.Email} 類別定義了 {@link android.provider.ContactsContract.Data} 列的類型特定欄名稱常數。此列內含 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE}。 + + +類別含有電子郵件地址欄的常數 + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS}。 + + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS}的實際值是「data1」。此值與欄的一般名稱相同。 + +</p> +<p class="caution"> + <strong>注意:</strong>如果 + {@link android.provider.ContactsContract.Data} 表格使用供應程式預先定義 MIME 類型的其中一種,請不要將您自訂的資料新增至此表格。 +假如將您自訂的資料新增至此表格,可能會遺失資料或讓供應程式發生故障。 +例如,您不應該將含有 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE} (內含使用者名稱,而不是電子郵件地址) 的列新增至 <code>DATA1</code> 欄。 + +如果您的列使用自訂 MIME 類型,那麼您可以自行定義專屬的類型特定的欄名稱,並按照您的需求使用這些欄。 + +</p> +<p> + 圖 2 顯示描述性欄和資料欄顯示在 + {@link android.provider.ContactsContract.Data} 列的樣式,以及類型特定欄名稱與一般欄名稱的「重疊」方式。 + +</p> +<img src="{@docRoot}images/providers/data_columns.png" alt="How type-specific column names map to generic column names" height="311" id="figure2" /> +<p class="img-caption"> + <strong>圖 2.</strong>特定類型欄名稱與一般欄名稱。 +</p> +<h3 id="ColumnMaps">特定類型欄名稱類別</h3> +<p> + 表 2 列出最常用的特定類型欄名稱類別: +</p> +<p class="table-caption" id="table2"> + <strong>表 2.</strong>特定類型欄名稱類別</p> +<table> + <tr> + <th scope="col">對應類別</th> + <th scope="col">資料類型</th> + <th scope="col">備註</th> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.CommonDataKinds.StructuredName}</td> + <td>原始聯絡人 (與此資料列相關聯) 的名稱資料。</td> + <td>原始聯絡人只有一列此資料。</td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.CommonDataKinds.Photo}</td> + <td>原始聯絡人 (與此資料列相關聯) 的主要相片。</td> + <td>原始聯絡人只有一列此資料。</td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.CommonDataKinds.Email}</td> + <td>原始聯絡人 (與此資料列相關聯) 的電子郵件地址。</td> + <td>原始聯絡人可以有多個電子郵件地址。</td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}</td> + <td>原始聯絡人 (與此資料列相關聯) 的郵寄地址。</td> + <td>原始聯絡人可以有多個郵寄地址。</td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}</td> + <td>將原始聯絡人連結至聯絡人供應程式內其中一個群組的識別碼。</td> + <td> + 群組是帳戶類型和帳戶名稱的選用功能。如要進一步瞭解群組,請參閱<a href="#Groups">聯絡人群組</a>。 + + </td> + </tr> +</table> +<h3 id="ContactBasics">聯絡人</h3> +<p> + 聯絡人供應程式會合併所有帳戶類型和帳戶名稱的原始聯絡人,而成為「聯絡人」<strong></strong>。 +藉此協助使用者顯示及修改針對某個人所收集的所有資料。 +聯絡人供應程式負責建立新的聯絡人列,以及彙總原始聯絡人與現有的聯絡人列。 +應用程式或同步配接器都可以新增聯絡人,而聯絡人列中的某些欄屬於唯讀性質。 + +</p> +<p class="note"> + <strong>注意:</strong>如果您嘗試使用 + {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()} 將聯絡人新增至聯絡人供應程式,將會收到 {@link java.lang.UnsupportedOperationException} 例外狀況。 +如果您試著更新列為「唯讀」的欄,則會略過此更新作業。 + +</p> +<p> + 如果新增的原始聯絡人與現有的聯絡人都不相符,聯絡人供應程式就會建立新的聯絡人。 +如果現有原始聯絡人的資料在變更後,不再與之前附加的聯絡人相符,則供應程式也會建立新的聯絡人。 + +如果應用程式或同步配接器建立的新原始聯絡人「符合」<em></em>現有的聯絡人,則新的原始聯絡人會彙總為現有的聯絡人。 + + +</p> +<p> + 聯絡人供應程式使用 {@link android.provider.ContactsContract.Contacts Contacts} 表格中的聯絡人 + <code>_ID</code> 欄,將聯絡人列連結到其原始聯絡人列。 +原始聯絡人表格 + {@link android.provider.ContactsContract.RawContacts} 的 <code>CONTACT_ID</code> 欄,內含聯絡人列 (與每個原始聯絡人列相關聯) 的 <code>_ID</code> 值。 + +</p> +<p> + {@link android.provider.ContactsContract.Contacts} 表格也有 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 欄,此為聯絡人列的「永久」連結。 +因為聯絡人供應程式會自動維護聯絡人,它會變更聯絡人列的 {@code android.provider.BaseColumns#_ID} 值,以回應彙總或同步操作。 + +即使發生這種情況,與聯絡人的 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 合併的內容 URI {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} 仍會指向聯絡人列,因此,您可以使用 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} + 來維護「常用聯絡人」等聯絡人的連結。 + +此欄有自己的格式,與 {@code android.provider.BaseColumns#_ID} 欄的格式無關。 + +</p> +<p> + 圖 3 說明這三個主要表格彼此之間的關係。 +</p> +<img src="{@docRoot}images/providers/contacts_tables.png" alt="Contacts provider main tables" height="514" id="figure4" /> +<p class="img-caption"> + <strong>圖 3.</strong>聯絡人、原始聯絡人以及詳細資料表格的關係。 +</p> +<h2 id="Sources">來自同步配接器的資料</h2> +<p> + 使用者將聯絡人資料直接輸入裝置,但資料也會過「同步配接器」從網路服務流入聯絡人供應程式<strong></strong> (同步配接器會自動將資料在裝置和服務之間傳輸)。 + +同步配接器受到系統的控制、在背景執行,並且會呼叫 {@link android.content.ContentResolver} 方法來管理資料。 + + +</p> +<p> + 在 Android 中,與同步配接器搭配運作的網路服務,是透過帳戶類型加以識別。 + 每個同步配接器會與一種帳戶類型搭配,但可以支援該類型的多個帳戶名稱。 +帳戶類型和帳戶名稱在<a href="#RawContactsExample">原始聯絡人資料的來源</a>中會有更詳細的說明。 + 下列定義提供更詳細的資訊,說明帳戶類型和名稱與同步配接器和服務之間的關係。 + +</p> +<dl> + <dt> + 帳戶類型 + </dt> + <dd> + 識別使用者儲存資料的服務。在大部分情況下,使用者必須經過服務的驗證。 +例如,Google 聯絡人是一種帳戶類型,由程式碼 <code>google.com</code> 加以識別。 +此值會對應到 + {@link android.accounts.AccountManager} 所使用的帳戶類型。 + </dd> + <dt> + 帳戶名稱 + </dt> + <dd> + 識別特定帳戶或帳戶類型的登入。Google 聯絡人帳戶與 Google 帳戶相同,都是使用電子郵件地址做為帳戶名稱。 + + 其他服務可能是以單一文字使用者名稱或數值 ID 做為帳戶名稱。 + </dd> +</dl> +<p> + 帳戶類型不必是唯一的。使用者可以設定多個 Google 聯絡人帳戶,並將其資料下載至聯絡人供應程式;如果使用者有一組個人帳戶名稱的個人聯絡人,還有另一組工作用的聯絡人,就可能發生此情形。 + +帳戶名稱通常是唯一的。 +兩者加起來,就可以識別聯絡人供應程式和外部服務之間的特定資料流程。 + +</p> +<p> + 如果您要將服務的資料傳輸到聯絡人供應程式,則需要編寫您自己的同步配接器。 +如要進一步瞭解同步配接器,請參閱<a href="#SyncAdapters">聯絡人供應程式同步配接器</a>。 + +</p> +<p> + 圖 4 顯示聯絡人供應程式在人員相關的資料流程中所扮演的角色。 +在標記為「同步配接器」的方塊中,每個配接器都以其帳戶類型做為標籤。 +</p> +<img src="{@docRoot}images/providers/ContactsDataFlow.png" alt="Flow of data about people" height="252" id="figure5" /> +<p class="img-caption"> + <strong>圖 4.</strong>聯絡人供應程式資料流程。 +</p> +<h2 id="Permissions">必要權限</h2> +<p> + 要存取聯絡人供應程式的應用程式必須要求下列權限: + +</p> +<dl> + <dt>一或多份表格的讀取權限</dt> + <dd> + {@link android.Manifest.permission#READ_CONTACTS},在 + <code>AndroidManifest.xml</code> 的 + <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"> + <uses-permission></a></code> 元素中,以 + <code><uses-permission android:name="android.permission.READ_CONTACTS"></code> 指定。 + </dd> + <dt>一或多份表格的寫入權限</dt> + <dd> + {@link android.Manifest.permission#WRITE_CONTACTS},在 +<code>AndroidManifest.xml</code> 的 +<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"> + <uses-permission></a></code> 元素中,以 +<code><uses-permission android:name="android.permission.WRITE_CONTACTS"></code> 指定。 + </dd> +</dl> +<p> + 這些權限不會延伸到使用者設定檔資料。如要瞭解使用者設定檔及其必要權限,請參閱<a href="#UserProfile">使用者設定檔</a>。 + + +</p> +<p> + 請記住,使用者的聯絡人資料是個人的敏感資訊。使用者很關心隱私權相關的問題,因此不希望應用程式收集使用者本身或其聯絡人的相關資訊。 + + 如果沒有明確說明為何需要存取使用者的聯絡人資料,使用者可能會給您的應用程式低評分,或直接拒絕安裝。 + +</p> +<h2 id="UserProfile">使用者設定檔</h2> +<p> + {@link android.provider.ContactsContract.Contacts} 表格中有一列內含裝置使用者的設定檔資料, +此資料描述裝置的 <code>user</code>,而不是其中一位使用者的聯絡人。 +針對使用設定檔的每個系統,設定檔聯絡人列會連結到原始聯絡人列。 + + 每個設定檔原始聯絡人列可以有多個資料列。{@link android.provider.ContactsContract.Profile} 類別中提供存取使用者設定檔的常數。 + +</p> +<p> + 存取使用者設定檔需要特殊權限。除了 +{@link android.Manifest.permission#READ_CONTACTS} 和 +{@link android.Manifest.permission#WRITE_CONTACTS} 的讀取和寫入權限以外,存取使用者設定檔還分別需要 {@code android.Manifest.permission#READ_PROFILE} 讀取權限和 +{@code android.Manifest.permission#WRITE_PROFILE} 寫入權限。 + + +</p> +<p> + 請務必將使用者的設定檔視為敏感資訊。 +{@code android.Manifest.permission#READ_PROFILE}權限可以讓您存取裝置上使用者的身分識別資料。 +請務必在應用程式的簡介中告訴使用者,為何需要使用者設定檔存取權限。 + +</p> +<p> + 如要擷取內含使用者的設定檔的聯絡人列,請呼叫 {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) +ContentResolver.query()}。 +將內容 URI 設為 +{@link android.provider.ContactsContract.Profile#CONTENT_URI},並且不要提供任何選取條件。 +您也可以使用此內容 URI 做為擷取原始聯絡人或設定檔資料的基礎 URI。 +例如,以下程式碼片段會擷取設定檔資料: +</p> +<pre> +// Sets the columns to retrieve for the user profile +mProjection = new String[] + { + Profile._ID, + Profile.DISPLAY_NAME_PRIMARY, + Profile.LOOKUP_KEY, + Profile.PHOTO_THUMBNAIL_URI + }; + +// Retrieves the profile from the Contacts Provider +mProfileCursor = + getContentResolver().query( + Profile.CONTENT_URI, + mProjection , + null, + null, + null); +</pre> +<p class="note"> + <strong>注意:</strong>如果您擷取了多個聯絡人列,而想要判斷其中之一是否為使用者設定檔,請測試該列的 +{@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} 欄。 +如果聯絡人為使用者設定檔,此欄會設為「1」。 + +</p> +<h2 id="ContactsProviderMetadata">聯絡人供應程式中繼資料</h2> +<p> + 聯絡人供應程式管理的資料可以追蹤存放庫中聯絡人資料的 +狀態。存放庫相關的中繼資料儲存在不同的位置,包括 +「原始聯絡人」、「資料」以及「聯絡人」表格列, +{@link android.provider.ContactsContract.Settings} 表格以及 +{@link android.provider.ContactsContract.SyncState} 表格。以下表格說明 +這些中繼資料的作用: +</p> +<p class="table-caption" id="table3"> + <strong>表 3.</strong>聯絡人供應程式的中繼資料</p> +<table> + <tr> + <th scope="col">表格</th> + <th scope="col">欄</th> + <th scope="col">值</th> + <th scope="col">意義</th> + </tr> + <tr> + <td rowspan="2">{@link android.provider.ContactsContract.RawContacts}</td> + <td rowspan="2">{@link android.provider.ContactsContract.SyncColumns#DIRTY}</td> + <td>「0」:上次同步後沒有變更。</td> + <td rowspan="2"> + 標記裝置上經過變更,且必須同步回伺服器的原始聯絡人。 +Android 應用程式更新列時,聯絡人供應程式會自動設定此值。 + + <p> + 修改原始聯絡人或資料表格的同步配接器一律會將字串{@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER} 附加到其使用的內容 URI, + +藉此防止供應程式將列標記為已變更 (dirty)。 + 否則,同步配接器修改會顯示為本機修改,因而傳送到伺服器,儘管伺服器才是修改的來源。 + + </p> + </td> + </tr> + <tr> + <td>「1」:上次同步後已變更,需要同步回伺服器。</td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.RawContacts}</td> + <td>{@link android.provider.ContactsContract.SyncColumns#VERSION}</td> + <td>此列的版本號碼。</td> + <td> + 每當列或其相關資料變更時,聯絡人供應程式都會自動增加此值。 + + </td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.Data}</td> + <td>{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}</td> + <td>此列的版本號碼。</td> + <td> + 每當資料列變更時,聯絡人供應程式都會自動增加此值。 + + </td> + </tr> + <tr> + <td>{@link android.provider.ContactsContract.RawContacts}</td> + <td>{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}</td> + <td> + 可唯一識別帳戶原始聯絡人的字串值(此帳戶是以此字串值所建立)。 + + </td> + <td> + 同步配接器建立新的原始聯絡人時,此欄應設為原始聯絡人的伺服器唯一 ID。 +Android 應用程式建立新的原始聯絡人時,應用程式應將此欄保留空白。 +這樣會提供訊號給同步配接器,要在伺服器上建立新的原始聯絡人,然後取得 +{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} 值。 + + <p> + 尤其是來源 ID 對於每個帳戶類型必須具備<strong>唯一性</strong>,在同步時應該很穩定: + + </p> + <ul> + <li> + 唯一:帳戶的每個原始聯絡人都必須有自己的來源 ID。如果沒有強制執行此條件,則聯絡人應用程式會發生問題。 + + 請注意,同一個帳戶「類型」<em></em>的兩個原始聯絡人可能會有相同的來源 ID。 +例如,{@code emily.dickinson@gmail.com} 帳戶的原始聯絡人「Thomas Higginson」與 + {@code emilyd@gmail.com} 帳戶的原始聯絡人「Thomas Higginson」的來源 ID相同。 + + + </li> + <li> + 穩定:來源 ID 是線上服務中原始聯絡人資料的永久部分。 +例如,如果使用者從應用程式設定中清除「聯絡人儲存空間」,然後重新同步,則還原的原始聯絡人的來源 ID 應該與先前相同。 + +如果沒有強制執行此條件,捷徑將停止運作。 + + </li> + </ul> + </td> + </tr> + <tr> + <td rowspan="2">{@link android.provider.ContactsContract.Groups}</td> + <td rowspan="2">{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}</td> + <td>「0」:Android 應用程式 UI 中不應顯示此群組中的聯絡人。</td> + <td> + 有些伺服器可以讓使用者隱藏某些群組中的聯絡人,此欄的設計提供了與這類伺服器的相容性。 + + </td> + </tr> + <tr> + <td>「1」:應用程式 UI 中會顯示此群組中的聯絡人。</td> + </tr> + <tr> + <td rowspan="2">{@link android.provider.ContactsContract.Settings}</td> + <td rowspan="2"> + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE}</td> + <td> + 「0」:針對此帳戶和帳戶類型,Android 應用程式 UI 中不會顯示不屬於群組的聯絡人。 + + </td> + <td rowspan="2"> + 如果沒有任何原始聯絡人屬於某個群組,則聯絡人預設為不可見(原始聯絡人的群組成員資格是由{@link android.provider.ContactsContract.Data} 表格中的一或多個 +{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} 列所指出)。 + + + 在 {@link android.provider.ContactsContract.Settings} 表格列中為帳戶類型和帳戶設定此旗標,可以強制讓不屬於任何群組的聯絡人成為可見的。 + + 此旗標的其中一個用途是,顯示伺服器中不屬於任何群組的聯絡人。 + </td> + </tr> + <tr> + <td> + 「1」:針對此帳戶和帳戶類型,應用程式 UI 中會顯示不屬於群組的聯絡人。 + + </td> + + </tr> + <tr> + <td>{@link android.provider.ContactsContract.SyncState}</td> + <td>(全部)</td> + <td> + 使用此表格儲存同步配接器的中繼資料。 + </td> + <td> + 使用此表格,您可以儲存同步狀態,以及其他與同步相關、會永久放在裝置上的資料。 + + </td> + </tr> +</table> +<h2 id="Access">聯絡人供應程式存取</h2> +<p> + 本節說明從聯絡人供應程式存取資料的指導方針,著重於以下各項: + +</p> +<ul> + <li> + 實體查詢。 + </li> + <li> + 批次修改。 + </li> + <li> + 使用意圖進行擷取及修改。 + </li> + <li> + 資料完整性。 + </li> +</ul> +<p> + 從同步配接器進行修改,在<a href="#SyncAdapters">聯絡人供應程式同步配接器</a>中也提供更詳細的資訊。 + +</p> +<h3 id="Entities">實體查詢</h3> +<p> + 因為聯絡人供應程式為階層式表格,擷取某一列及連結至此列的所有「子」列時非常實用。 +例如,如要顯示人員的所有資訊,您可能要擷取單一 + {@link android.provider.ContactsContract.Contacts} 列的 +所有 {@link android.provider.ContactsContract.RawContacts} 列,或單一 +{@link android.provider.ContactsContract.RawContacts} 列的所有 +{@link android.provider.ContactsContract.CommonDataKinds.Email} 列。 +為了協助此操作,聯絡人供應程式提供<strong>實體</strong>建構,其運作方式就像是資料庫結合各個表格一樣。 + + +</p> +<p> + 實體就像是一份表格,由上層表格及其下層表格中的選取欄所組成。 + 查詢實體時,您會提供投影 (projection) 和搜尋條件根據該實體可用的欄。 +結果會是 {@link android.database.Cursor},擷取到的每個下層表格列在其中都會有一列。 +例如,如果您查詢 +{@link android.provider.ContactsContract.Contacts.Entity} 的聯絡人名稱,並且查詢所有 {@link android.provider.ContactsContract.CommonDataKinds.Email} 列中該名稱的所有原始聯絡人,則會取回 {@link android.database.Cursor},每個 {@link android.provider.ContactsContract.CommonDataKinds.Email} 列都會有一列。 + + + +</p> +<p> + 實體簡化查詢。使用實體,您可以一次擷取聯絡人或原始聯絡人的所有聯絡人資料,而不用先查詢父項表格以取得 ID,再以此 ID 查詢子項表格。另外,聯絡人供應程式會在單一交易中處理針對實體的查詢,以確保所擷取的資料在內部的一致性。 + + + + +</p> +<p class="note"> + <strong>注意:</strong>實體通常不會包含上層表格和下層表格的所有欄。 +如果您嘗試使用的欄名稱未列在實體的欄名稱常數中,將會收到 {@link java.lang.Exception}。 + +</p> +<p> + 以下程式碼片段展示如何擷取一位聯絡人的所有原始聯絡人列。此程式碼片段屬於大型應用程式的一部分,此應用程式有兩個 Activity:「主要」和「詳細」。 +主要 Activity 會顯示聯絡人列的清單,當使用者選取其中一項時,此 Activity 會將其 ID 傳送給詳細 Activity。 + +詳細 Activity 會使用 {@link android.provider.ContactsContract.Contacts.Entity},針對所選取的聯絡人,顯示與其關聯的所有原始聯絡人的所有資料列。 + + +</p> +<p> + 此程式碼片段是取自「詳細」Activity: +</p> +<pre> +... + /* + * Appends the entity path to the URI. In the case of the Contacts Provider, the + * expected URI is content://com.google.contacts/#/entity (# is the ID value). + */ + mContactUri = Uri.withAppendedPath( + mContactUri, + ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); + + // Initializes the loader identified by LOADER_ID. + getLoaderManager().initLoader( + LOADER_ID, // The identifier of the loader to initialize + null, // Arguments for the loader (in this case, none) + this); // The context of the activity + + // Creates a new cursor adapter to attach to the list view + mCursorAdapter = new SimpleCursorAdapter( + this, // the context of the activity + R.layout.detail_list_item, // the view item containing the detail widgets + mCursor, // the backing cursor + mFromColumns, // the columns in the cursor that provide the data + mToViews, // the views in the view item that display the data + 0); // flags + + // Sets the ListView's backing adapter. + mRawContactList.setAdapter(mCursorAdapter); +... +@Override +public Loader<Cursor> onCreateLoader(int id, Bundle args) { + + /* + * Sets the columns to retrieve. + * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. + * DATA1 contains the first column in the data row (usually the most important one). + * MIMETYPE indicates the type of data in the data row. + */ + String[] projection = + { + ContactsContract.Contacts.Entity.RAW_CONTACT_ID, + ContactsContract.Contacts.Entity.DATA1, + ContactsContract.Contacts.Entity.MIMETYPE + }; + + /* + * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw + * contact collated together. + */ + String sortOrder = + ContactsContract.Contacts.Entity.RAW_CONTACT_ID + + " ASC"; + + /* + * Returns a new CursorLoader. The arguments are similar to + * ContentResolver.query(), except for the Context argument, which supplies the location of + * the ContentResolver to use. + */ + return new CursorLoader( + getApplicationContext(), // The activity's context + mContactUri, // The entity content URI for a single contact + projection, // The columns to retrieve + null, // Retrieve all the raw contacts and their data rows. + null, // + sortOrder); // Sort by the raw contact ID. +} +</pre> +<p> + 載入完成時,{@link android.app.LoaderManager} 會呼叫 {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) + onLoadFinished()} 的回呼。 +此方法的其中一個傳入引數是內含查詢結果的 + {@link android.database.Cursor}。在您的應用程式中,您可以從這個 {@link android.database.Cursor} 取得資料,然後加以顯示或進一步處理。 + +</p> +<h3 id="Transactions">批次修改</h3> +<p> + 您應該儘可能透過建立 + {@link android.content.ContentProviderOperation} 物件的{@link java.util.ArrayList},然後呼叫 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()},以「批次模式」在聯絡人供應程式中進行資料的插入、更新以及刪除。 +因為聯絡人供應程式會在單一交易中執行 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的所有操作,所以您所做的修改不會讓聯絡人存放庫處於不一致的狀態。 + + +批次修改同時也有助於插入原始聯絡人及其詳細資料。 + +</p> +<p class="note"> + <strong>注意:</strong>如要修改「單一」<em></em>原始聯絡人,請考慮將意圖傳送到裝置的聯絡人應用程式,而不要在您的應用程式中處理修改操作。這些動作在<a href="#Intents">使用意圖擷取和修改</a>中有更詳細的資料。 + + + +</p> +<h4>降伏點</h4> +<p> + 包含大量操作的批次修改可能會封鎖其他處理程序,導致整體的使用者體驗不良。 +如要將您想要執行的所有修改,儘可能安排在較少的清單中執行,同時要避免這些修改讓系統無法進行其他操作,則應該要為一或多個操作設定「降伏點」<strong></strong>。 + + + 降伏點是一個 {@link android.content.ContentProviderOperation} 物件,而且其 +{@link android.content.ContentProviderOperation#isYieldAllowed()} 值是設為 +<code>true</code>。當聯絡人供應程式遇到降伏點時,會暫停它的工作,以便讓其他處理程序執行,並關閉目前的交易。 +供應程式再次啟動時,會繼續 {@link java.util.ArrayList} 中的下一項操作,並啟動新的交易。 + + +</p> +<p> + 降伏點會讓每次呼叫 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 產生一個以上的交易。基於這項原因,您必須將上次操作的降伏點設為一組相關的列。 + + 例如,您應該為以下兩種上次操作設定降伏點:新增原始聯絡人列及其相關資料列的一組動作,或與單一聯絡人相關的一組列。 + + +</p> +<p> + 降伏點也是微型操作的單位。兩個降伏點之間的所有存取會以單一單元來看待為成功或失敗。 +如果沒有設定任何降伏點,則最小的微型操作就是整批操作。 +如果使用降伏點,您可以防止操作降低系統效能,同時確保操作的子集是微型操作。 + + +</p> +<h4>修改反向參考</h4> +<p> + 以一組 + {@link android.content.ContentProviderOperation} 物件插入新的原始聯絡人及其關聯的資料列時,您必須透過插入原始聯絡人的 + {@code android.provider.BaseColumns#_ID} 值做為 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 值,將資料列連結到原始聯絡人列。 +不過,此 +值在您為資料列建立 {@link android.content.ContentProviderOperation} +時並不存在,這是因為您尚未替原始聯絡人列 +套用 {@link android.content.ContentProviderOperation}。為解決此狀況,{@link android.content.ContentProviderOperation.Builder} 類別提供了 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} 這個方法。 + + 此方法可以讓您使用之前操作的結果來插入或修改欄。 + +</p> +<p> + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +方法有兩個引數: +</p> + <dl> + <dt> + <code>key</code> + </dt> + <dd> + 鍵值對的鍵。此引數的值是您要修改表格中的欄名稱。 + + </dd> + <dt> + <code>previousResult</code> + </dt> + <dd> + +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 中的 +{@link android.content.ContentProviderResult} 物件以 0 開始的陣列索引值。套用批次操作時,每次操作結果都會儲存在結果的中繼陣列。 + +<code>previousResult</code> 值是這些結果的其中一個索引,這些結果是以 <code>key</code>值擷取並加以儲存。 + +這樣可以讓您插入新的原始聯絡人記錄,並取得其 +{@code android.provider.BaseColumns#_ID} 值,然後在您新增 {@link android.provider.ContactsContract.Data} 列時,做為此值的「反向參考」。 + + <p> + 您首次呼叫 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 時會建立整個結果陣列,此陣列的大小等同於您所提供 + {@link android.content.ContentProviderOperation} 物件的{@link java.util.ArrayList} 大小。 +不過,結果陣列中的所有元素會設為 <code>null</code>,如果您嘗試要針對尚未套用的操作結果製作反向參考, +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}則會擲回 {@link java.lang.Exception}。 + + + + + </p> + </dd> + </dl> +<p> + 以下程式碼片段展示如何插入大量新的原始聯絡人和資料。其中包括建立降伏點和使用反向參考的程式碼。 +此程式碼片段是 <code>createContacEntry()</code> 方法的擴充版本。而這個方法是 + <code><a href="{@docRoot}resources/samples/ContactManager/index.html"> + Contact Manager</a></code> 範例應用程式中 + <code>ContactAdder</code> 類別的一部分。 + +</p> +<p> + 第一個程式碼片段會從 UI 擷取聯絡人資料。此時,使用者已經選好要加入的新原始聯絡人帳戶。 + +</p> +<pre> +// Creates a contact entry from the current UI values, using the currently-selected account. +protected void createContactEntry() { + /* + * Gets values from the UI + */ + String name = mContactNameEditText.getText().toString(); + String phone = mContactPhoneEditText.getText().toString(); + String email = mContactEmailEditText.getText().toString(); + + int phoneType = mContactPhoneTypes.get( + mContactPhoneTypeSpinner.getSelectedItemPosition()); + + int emailType = mContactEmailTypes.get( + mContactEmailTypeSpinner.getSelectedItemPosition()); +</pre> +<p> + 下一個程式碼片段的操作會將原始聯絡人列插入 +{@link android.provider.ContactsContract.RawContacts} 表格: +</p> +<pre> + /* + * Prepares the batch operation for inserting a new raw contact and its data. Even if + * the Contacts Provider does not have any data for this person, you can't add a Contact, + * only a raw contact. The Contacts Provider will then add a Contact automatically. + */ + + // Creates a new array of ContentProviderOperation objects. + ArrayList<ContentProviderOperation> ops = + new ArrayList<ContentProviderOperation>(); + + /* + * Creates a new raw contact with its account type (server type) and account name + * (user's account). Remember that the display name is not stored in this row, but in a + * StructuredName data row. No other data is required. + */ + ContentProviderOperation.Builder op = + ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType()) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName()); + + // Builds the operation and adds it to the array of operations + ops.add(op.build()); +</pre> +<p> + 接著,程式碼會建立顯示名稱、電話以及電子郵件列的資料列。 +</p> +<p> + 每項操作建立器物件會使用 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}來取得 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}。 +參照會指向第一項操作中的 {@link android.content.ContentProviderResult} 物件 (第一項操作會新增原始聯絡人列,並傳回其新的 {@code android.provider.BaseColumns#_ID} 值)。 + + +因此,每個資料列會透過其 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 自動連結到它所屬的新 {@link android.provider.ContactsContract.RawContacts} 列。 + +</p> +<p> + 新增電子郵件列的 {@link android.content.ContentProviderOperation.Builder} 物件會帶有 {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) + withYieldAllowed()} 旗標,而這會設定降伏點: + +</p> +<pre> + // Creates the display name for the new raw contact, as a StructuredName data row. + op = + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + /* + * withValueBackReference sets the value of the first argument to the value of + * the ContentProviderResult indexed by the second argument. In this particular + * call, the raw contact ID column of the StructuredName data row is set to the + * value of the result returned by the first operation, which is the one that + * actually adds the raw contact row. + */ + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + + // Sets the data row's MIME type to StructuredName + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + + // Sets the data row's display name to the name in the UI. + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); + + // Builds the operation and adds it to the array of operations + ops.add(op.build()); + + // Inserts the specified phone number and type as a Phone data row + op = + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + /* + * Sets the value of the raw contact id column to the new raw contact ID returned + * by the first operation in the batch. + */ + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + + // Sets the data row's MIME type to Phone + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + + // Sets the phone number and type + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType); + + // Builds the operation and adds it to the array of operations + ops.add(op.build()); + + // Inserts the specified email and type as a Phone data row + op = + ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + /* + * Sets the value of the raw contact id column to the new raw contact ID returned + * by the first operation in the batch. + */ + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + + // Sets the data row's MIME type to Email + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + + // Sets the email address and type + .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) + .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType); + + /* + * Demonstrates a yield point. At the end of this insert, the batch operation's thread + * will yield priority to other threads. Use after every set of operations that affect a + * single contact, to avoid degrading performance. + */ + op.withYieldAllowed(true); + + // Builds the operation and adds it to the array of operations + ops.add(op.build()); +</pre> +<p> + 最後一個程式碼片段顯示 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的呼叫,以插入新的原始聯絡人和資料列。 + +</p> +<pre> + // Ask the Contacts Provider to create a new contact + Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" + + mSelectedAccount.getType() + ")"); + Log.d(TAG,"Creating contact: " + name); + + /* + * Applies the array of ContentProviderOperation objects in batch. The results are + * discarded. + */ + try { + + getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + } catch (Exception e) { + + // Display a warning + Context ctx = getApplicationContext(); + + CharSequence txt = getString(R.string.contactCreationFailure); + int duration = Toast.LENGTH_SHORT; + Toast toast = Toast.makeText(ctx, txt, duration); + toast.show(); + + // Log exception + Log.e(TAG, "Exception encountered while inserting contact: " + e); + } +} +</pre> +<p> + 批次操作也可以讓您實作<strong>開放式並行存取控制</strong>,此方法可以在套用修改交易時,不需要鎖定底層存放庫。 + + 如要使用此方法,您要套用交易,然後檢查同時發生的其他修改操作。 +如果您發現不一致的修改,請將交易復原並加以重試。 + +</p> +<p> + 開放式並行存取控制很適合用在行動裝置,這是因為行動裝置一次只會有一位使用者,而且很少會發生同時存取資料存放庫的情形。 +由於不會使用到鎖定,因此您不必花時間在設定鎖定,或等待其他交易釋放鎖定。 + +</p> +<p> + 如要在更新單一 + {@link android.provider.ContactsContract.RawContacts} 列時使用開放式並行存取控制,請遵循以下步驟: +</p> +<ol> + <li> + 隨著您要擷取的其他資料,一起擷取原始聯絡人的 {@link android.provider.ContactsContract.SyncColumns#VERSION} 欄。 + + </li> + <li> + 使用 + {@link android.content.ContentProviderOperation#newAssertQuery(Uri)} 方法建立適合用於強制執行限制的{@link android.content.ContentProviderOperation.Builder} 物件。 +如果是內容 URI,請使用 {@link android.provider.ContactsContract.RawContacts#CONTENT_URI +RawContacts.CONTENT_URI} 並附加原始聯絡人的 {@code android.provider.BaseColumns#_ID}。 + + + </li> + <li> + 如果是 {@link android.content.ContentProviderOperation.Builder} 物件,請呼叫 {@link android.content.ContentProviderOperation.Builder#withValue(String, Object) + withValue()},以比較 {@link android.provider.ContactsContract.SyncColumns#VERSION} 欄和您剛才擷取的版本號碼。 + + + </li> + <li> + 如果是相同的 {@link android.content.ContentProviderOperation.Builder},請呼叫 {@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) +withExpectedCount()} 以確保此判斷提示只測試一列。 + + </li> + <li> + 呼叫 {@link android.content.ContentProviderOperation.Builder#build()} 以建立 +{@link android.content.ContentProviderOperation} 物件,然後將此物件新增為您傳送到 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的第一個 {@link java.util.ArrayList} 物件。 + + </li> + <li> + 套用批次交易。 + </li> +</ol> +<p> + 如果在您讀取原始聯絡人列和嘗試加以修改之間,有另一項操作要加以更新,則「判斷提示」{@link android.content.ContentProviderOperation} 將會失敗,而且整個批次的操作將會退出。 + +您之後可以選擇重試此批次作業或採取其他動作。 + +</p> +<p> + 以下程式碼片段展示如何在使用 {@link android.content.CursorLoader} 查詢單一原始聯絡人後,建立「判斷提示」 +{@link android.content.ContentProviderOperation}: + +</p> +<pre> +/* + * The application uses CursorLoader to query the raw contacts table. The system calls this method + * when the load is finished. + */ +public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { + + // Gets the raw contact's _ID and VERSION values + mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); + mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)); +} + +... + +// Sets up a Uri for the assert operation +Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID); + +// Creates a builder for the assert operation +ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri); + +// Adds the assertions to the assert operation: checks the version and count of rows tested +assertOp.withValue(SyncColumns.VERSION, mVersion); +assertOp.withExpectedCount(1); + +// Creates an ArrayList to hold the ContentProviderOperation objects +ArrayList ops = new ArrayList<ContentProviderOperationg>; + +ops.add(assertOp.build()); + +// You would add the rest of your batch operations to "ops" here + +... + +// Applies the batch. If the assert fails, an Exception is thrown +try + { + ContentProviderResult[] results = + getContentResolver().applyBatch(AUTHORITY, ops); + + } catch (OperationApplicationException e) { + + // Actions you want to take if the assert operation fails go here + } +</pre> +<h3 id="Intents">使用意圖進行擷取及修改</h3> +<p> + 將意圖傳送給裝置的聯絡人應用程式,可讓您間接存取聯絡人供應程式。 +意圖會啟動裝置的聯絡人應用程式 UI,使用者可以在此執行與聯絡人相關的工作。 +透過此類型的存取方式,使用者可以: + <ul> + <li>從清單挑選聯絡人,並將它傳送給應用程式以進行其他操作。</li> + <li>編輯現有的聯絡人資料。</li> + <li>為使用者的任何帳戶插入新的原始聯絡人。</li> + <li>刪除聯絡人或聯絡人資料。</li> + </ul> +<p> + 如果使用者正在插入或更新資料,您可以先收集資料,然後讓它成為意圖的一部分加以傳送。 + +</p> +<p> + 當您透過裝置的聯絡人應用程式使用意圖來存取聯絡人供應程式時,不需要自已撰寫存取供應程式的 UI 或程式碼。 +您也不需要要求供應程式的讀取或寫入權限。 +裝置的聯絡人應用程式可以將某個聯絡人的讀取權限委派給您,而且因為是透過另一個應用程式對供應程式進行修改,所以不需要具備寫入權限。 + + +</p> +<p> + 傳送意圖以存取供應程式的一般流程在<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">內容供應程式基本概念</a>指南的「透過意圖存取資料」中有詳細的說明。 + +表 4 摘要說明您可以在工作中使用的動作、MIME 類型以及資料值,而您可以和 + {@link android.content.Intent#putExtra(String, String) putExtra()} 搭配使用的額外值則列於 {@link android.provider.ContactsContract.Intents.Insert} 的參考文件: + + + +</p> +<p class="table-caption" id="table4"> + <strong>表 4.</strong>聯絡人供應程式意圖。 +</p> +<table style="width:75%"> + <tr> + <th scope="col" style="width:10%">工作</th> + <th scope="col" style="width:5%">動作</th> + <th scope="col" style="width:10%">資料</th> + <th scope="col" style="width:10%">MIME 類型</th> + <th scope="col" style="width:25%">備註</th> + </tr> + <tr> + <td><strong>從清單挑選聯絡人</strong></td> + <td>{@link android.content.Intent#ACTION_PICK}</td> + <td> + 可以是以下其中一種: + <ul> + <li> +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI},可顯示聯絡人的清單。 + + </li> + <li> +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI},可顯示原始聯絡人的電話號碼清單。 + + </li> + <li> +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI},可顯示原始聯絡人的郵寄地址清單。 + + </li> + <li> +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI},可顯示原始聯絡人的電子郵件地址清單。 + + </li> + </ul> + </td> + <td> + 未使用 + </td> + <td> + 顯示原始聯絡人清單或原始聯絡人的資料清單,視您提供的內容 URI 類型而定。 + + <p> + 呼叫 +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}可傳回所選取列的內容 URI。 +URI 的格式是表格的內容 URI 附加列的 <code>LOOKUP_ID</code>。 + + 裝置的聯絡人應用程式會在 Activity 的生命週期內,將讀取和寫入權限委派給此內容 URI。 +詳情請參閱<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">內容供應程式基本概念</a>指南。 + + + </p> + </td> + </tr> + <tr> + <td><strong>插入新的原始聯絡人</strong></td> + <td>{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}</td> + <td>不適用</td> + <td> + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE +RawContacts.CONTENT_TYPE},一組原始聯絡人的 MIME 類型。 + </td> + <td> + 顯示裝置聯絡人應用程式中的「新增聯絡人」<strong></strong>畫面。會顯示您新增至意圖的額外值。 +如果隨著 +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} 一起傳送,則新增的原始聯絡人內容 URI 會在 {@link android.content.Intent} 引數的 [資料] 欄位中傳回給 Activity 的 +{@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()}回呼方法。 + + +如要取得此值,請呼叫 {@link android.content.Intent#getData()}。 + </td> + </tr> + <tr> + <td><strong>編輯聯絡人</strong></td> + <td>{@link android.content.Intent#ACTION_EDIT}</td> + <td> + 聯絡人的 {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}。 +編輯器 Activity 可讓使用者編輯與此聯絡人關聯的任何資料。 + + </td> + <td> + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE +Contacts.CONTENT_ITEM_TYPE},單一聯絡人。</td> + <td> + 顯示聯絡人應用程式中的「編輯聯絡人」畫面。顯示您新增至意圖的額外值。 +使用者按一下 [完成]<strong></strong> 來儲存編輯內容時,您的 Activity 會回到前景。 + + </td> + </tr> + <tr> + <td><strong>顯示也能夠新增資料的挑選器</strong></td> + <td>{@link android.content.Intent#ACTION_INSERT_OR_EDIT}</td> + <td> + 不適用 + </td> + <td> + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + </td> + <td> + 此意圖一律會顯示聯絡人應用程式的挑選器畫面。使用者可以挑選要編輯的聯絡人,或新增聯絡人。 +不論使用者選擇編輯或新增,所顯示的畫面中也會顯示您在意圖中傳送的額外資料。 + +如果您的應用程式顯示電子郵件或電話號碼之類的聯絡人資料,使用此意圖會讓使用者將資料新增至現有聯絡人。 + + + <p class="note"> + <strong>注意:</strong>不需要在意圖的額外值中傳送名稱值,這是因為使用者一定會挑選現有名稱或新增名稱。 +再者,如果您傳送名稱,而使用者選擇編輯,則聯絡人應用程式會覆寫之前的值,以顯示您傳送的名稱。 + +如果使用者沒有注意到此情形並儲存本次編輯,則會遺失舊的值。 + + </p> + </td> + </tr> +</table> +<p> + 裝置的聯絡人應用程式不會讓您刪除原始聯絡人或任何含有意圖的資料。 +如要刪除原始聯絡人,請改用 +{@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} 或 {@link android.content.ContentProviderOperation#newDelete(Uri) +ContentProviderOperation.newDelete()}。 + +</p> +<p> + 以下程式碼片段展示如何建構及傳送可插入新原始聯絡人和資料,的意圖: + +</p> +<pre> +// Gets values from the UI +String name = mContactNameEditText.getText().toString(); +String phone = mContactPhoneEditText.getText().toString(); +String email = mContactEmailEditText.getText().toString(); + +String company = mCompanyName.getText().toString(); +String jobtitle = mJobTitle.getText().toString(); + +// Creates a new intent for sending to the device's contacts application +Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION); + +// Sets the MIME type to the one expected by the insertion activity +insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE); + +// Sets the new contact name +insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name); + +// Sets the new company and job title +insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company); +insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle); + +/* + * Demonstrates adding data rows as an array list associated with the DATA key + */ + +// Defines an array list to contain the ContentValues objects for each row +ArrayList<ContentValues> contactData = new ArrayList<ContentValues>(); + + +/* + * Defines the raw contact row + */ + +// Sets up the row as a ContentValues object +ContentValues rawContactRow = new ContentValues(); + +// Adds the account type and name to the row +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType()); +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName()); + +// Adds the row to the array +contactData.add(rawContactRow); + +/* + * Sets up the phone number data row + */ + +// Sets up the row as a ContentValues object +ContentValues phoneRow = new ContentValues(); + +// Specifies the MIME type for this data row (all data rows must be marked by their type) +phoneRow.put( + ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE +); + +// Adds the phone number and its type to the row +phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); + +// Adds the row to the array +contactData.add(phoneRow); + +/* + * Sets up the email data row + */ + +// Sets up the row as a ContentValues object +ContentValues emailRow = new ContentValues(); + +// Specifies the MIME type for this data row (all data rows must be marked by their type) +emailRow.put( + ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE +); + +// Adds the email address and its type to the row +emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); + +// Adds the row to the array +contactData.add(emailRow); + +/* + * Adds the array to the intent's extras. It must be a parcelable object in order to + * travel between processes. The device's contacts app expects its key to be + * Intents.Insert.DATA + */ +insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); + +// Send out the intent to start the device's contacts app in its add contact activity. +startActivity(insertIntent); +</pre> +<h3 id="DataIntegrity">資料完整性</h3> +<p> + 因為聯絡人存放庫內含重要的敏感資料,使用者會期待這些資料為正確且為最新狀態,聯絡人供應程式對於資料完整性有定義良好的規則。 +因此,您在修改聯絡人資料時,必須符合這些規則。 +以下列出重要規則: + +</p> +<dl> + <dt> + 務必為您新增的每個 {@link android.provider.ContactsContract.RawContacts} 列新增 {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列。 + + </dt> + <dd> + {@link android.provider.ContactsContract.Data} 表格中不含 + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列的 +{@link android.provider.ContactsContract.RawContacts} 列,在彙總時會造成問題。 + + </dd> + <dt> + 務必將 {@link android.provider.ContactsContract.Data} 列連結到其上層的 +{@link android.provider.ContactsContract.RawContacts} 列。 + </dt> + <dd> + 裝置的聯絡人應用程式中將看不到未連結到 + {@link android.provider.ContactsContract.RawContacts} 的 {@link android.provider.ContactsContract.Data} 列,而且與同步配接器搭配使用時可能會造成問題。 + + </dd> + <dt> + 只針對您擁有的原始聯絡人變更資料。 + </dt> + <dd> + 請記住,聯絡人供應程式通常用來管理來自不同帳戶類型或線上服務的資料。 +您必須確認應用程式只會修改或刪除屬於您的資料列,並且確認應用程式插入的資料只含有您可控制的帳戶類型和名稱。 + + + </dd> + <dt> + 務必使用 {@link android.provider.ContactsContract} 及其子類別中定義的常數,做為授權、內容 URI、URI 路徑、欄名稱、MIME 類型以及 +{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} 值。 + + </dt> + <dd> + 使用這些常數可協助您避免發生錯誤。如果有任何常數已失效,則編譯器會發出通知。 + + </dd> +</dl> +<h3 id="CustomData">自訂資料列</h3> +<p> + 透過建立自訂的 MIME 類型,您可以插入、編輯、刪除以及擷取 {@link android.provider.ContactsContract.Data} 表格中您自己的資料列。 +儘管您可以將自己的類型特定欄名稱對應到預設的欄名稱,您的列仍受限於使用 + {@link android.provider.ContactsContract.DataColumns} 中所定義的欄。 + +在裝置的聯絡人應用程式中,可以顯示您的列中資料,但無法加以編輯或刪除,而且使用者無法新增其他資料。 + +如要讓使用者修改您自訂的資料列,您必須在自己的應用程式中提供編輯器 Activity。 + +</p> +<p> + 如要顯示自己的資料,請提供 <code>contacts.xml</code> 檔案,其中要包含 +<code><ContactsAccountType></code> 元素以及一或多個其 +<code><ContactsDataKind></code> 子元素。詳情請參閱 <a href="#SocialStreamDataKind"><code><ContactsDataKind> element</code></a>一節。 + +</p> +<p> + 如要更瞭解自訂 MIME 類型的詳細資訊,請閱讀<a href="{@docRoot}guide/topics/providers/content-provider-creating.html">建立內容供應程式</a>指南。 + + +</p> +<h2 id="SyncAdapters">聯絡人供應程式同步配接器</h2> +<p> + 聯絡人供應程式的設計是專門用來處理裝置和線上服務之間聯絡人資料的「同步作業」<strong></strong>。 +以便讓使用者將現有資料下載到新裝置,以及將現有資料上傳到新帳戶。 + + 同步作業也可以確保使用者手邊使用的是最新的資料,不論來源經過哪些新增和變更。 +同步作業的另一個好處是,即使裝置沒有連上網路,使用者仍然可以存取聯絡人資料。 + +</p> +<p> + 您可以用各種方式實作同步作業,不過 Android 系統提供的外掛程式同步架構可以將以下工作自動化: + + <ul> + + <li> + 檢查網路可用性。 + </li> + <li> + 根據使用者偏好設定,安排並執行同步作業。 + </li> + <li> + 重新啟動已停止的同步作業。 + </li> + </ul> +<p> + 如要使用此架構,您要提供同步配接器外掛程式。每個同步配接器對於服務和內容供應程式來說是唯一的,但可以處理相同服務的多個帳戶名稱。 +此架構也可以讓相同服務和供應程式使用多個同步配接器。 + +</p> +<h3 id="SyncClassesFiles">同步配接器類別和檔案</h3> +<p> + 您將同步配接器實做為 {@link android.content.AbstractThreadedSyncAdapter} 的子類別,並以 Android 應用程式的一部分加以安裝。 + +系統會從應用程式宣示說明中的元素,以及從宣示說明所指向的特殊 XML 檔案中瞭解同步配接器的相關資訊。 +此 XML 檔案定義線上服務的帳戶類型,以及內容供應程式的授權,這兩者可用來唯一識別此配接器。 + +同步配接器要在使用者新增同步配接器的帳戶類型, +並啟用要與同步配接器的內容供應程式同步後, +同步配接器才會變成使用中。此時,系統會開始管理配接器並視需要加以呼叫,以便在內容供應程式和伺服器之間進行同步。 + +</p> +<p class="note"> + <strong>注意:</strong>使用帳戶類型做為同步配接器識別的一部分,可以讓系統在偵測後,將存取不同服務、但來自相同組織的同步配接器群組在一起。 + +例如,Google 線上服務的同步配接器都有相同的帳戶類型 <code>com.google</code>。 +使用者將 Google 帳戶新增至其裝置後,所有已安裝的 Google 服務同步配接器會列在一起,每個列出的同步配接器會與裝置上不同的內容供應程式進行同步。 + + +</p> +<p> + 由於大多數服務都需要在存取資料之前先驗證其身分,因此 Android 系統提供類似的驗證架構,而且通常會與同步配接器架構一起搭配使用。 + +驗證架構使用外掛程式驗證器,這是 + {@link android.accounts.AbstractAccountAuthenticator} 的子類別。 +驗證器會以下列步驟驗證使用者的身分: + + <ol> + <li> + 收集使用者的名稱、密碼或類似資訊 (使用者的<strong>憑證</strong>)。 + + </li> + <li> + 將憑證傳送給服務 + </li> + <li> + 檢驗服務的回覆。 + </li> + </ol> +<p> + 如果服務接受此憑證,則驗證器可以儲存憑證供以後使用。 +由於外掛程式驗證器架構的緣故, +{@link android.accounts.AccountManager} 可以存取驗證器支援且選擇顯示的任何 authtoken,例如 OAuth2 authtoken。 + +</p> +<p> + 雖然驗證並非必要,大部分聯絡人服務仍會加以使用。 + 不過,您不一定要使用 Android 驗證架構來進行驗證動作。 +</p> +<h3 id="SyncAdapterImplementing">同步配接器實作</h3> +<p> + 如要實作聯絡人供應程式的同步配接器,要從建立內含以下各項的 Android 應用程式開始: + +</p> + <dl> + <dt> + 回應來自系統的要求,以繫結至同步配接器的 {@link android.app.Service} 元件。 + + </dt> + <dd> + 系統要執行同步時,會呼叫服務的 + {@link android.app.Service#onBind(Intent) onBind()} 方法,以取得同步配接器的 + {@link android.os.IBinder}。這樣可以讓系統以跨處理程序的方式呼叫配接器的方法。 + + <p> + 在<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>範例應用程式中,此服務的名稱為 + <code>com.example.android.samplesync.syncadapter.SyncService</code>。 + + </p> + </dd> + <dt> + 實際的同步配接器是以 + {@link android.content.AbstractThreadedSyncAdapter} 的實體子類別加以實作。 + </dt> + <dd> + 此類別會執行的工作包括:從伺服器下載資料、從裝置上傳資料以及解決衝突。 +配接器的主要工作會使用 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( +Account, Bundle, String, ContentProviderClient, SyncResult) +onPerformSync()} 方法完成。 +此類別必須以單一執行個體的方式加以具現化。 + <p> + 在<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>範例應用程式中,同步配接器定義在 +<code>com.example.android.samplesync.syncadapter.SyncAdapter</code> 類別中。 + + </p> + </dd> + <dt> + {@link android.app.Application} 的子類別。 + </dt> + <dd> + 此類別就像是同步配接器單一執行個體的工廠。使用 +{@link android.app.Application#onCreate()} 方法具現化同步配接器,並提供靜態的「getter」方法將單一執行個體傳回給同步配接器服務的 +{@link android.app.Service#onBind(Intent) onBind()} 方法。 + + + </dd> + <dt> + <strong>選用:</strong>回應系統針對使用者發出的驗證要求的 {@link android.app.Service} 元件。 + + </dt> + <dd> + {@link android.accounts.AccountManager} 會啟動此服以開始驗證程序。 +服務的 {@link android.app.Service#onCreate()} 方法會具現化為驗證器物件。 +系統需驗證應用程式同步配接器的使用者帳戶時,會呼叫服務的 +{@link android.app.Service#onBind(Intent) onBind()} 方法以取得驗證器的 + {@link android.os.IBinder}。 +這樣可以讓系統以跨處理程序的方式呼叫驗證器的方法。 + + <p> + 在<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>範例應用程式中,此服務的類別名稱為 +<code>com.example.android.samplesync.authenticator.AuthenticationService</code>。 + + </p> + </dd> + <dt> + <strong>選用:</strong>處理驗證要求的 +{@link android.accounts.AbstractAccountAuthenticator} 實體子類別。 + + </dt> + <dd> + {@link android.accounts.AccountManager} 會呼叫此類別提供的方法,透過伺服器驗證使用者的憑證。 +驗證程序的詳細方式會根據所使用的伺服器技術,而有很大的差異。 +建議您參閱伺服器軟體的說明文件,進一步瞭解驗證。 + + <p> + 在<a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>範例應用程式中,驗證器是在 +<code>com.example.android.samplesync.authenticator.Authenticator</code> 類別中完成定義。 + + </p> + </dd> + <dt> + 定義系統同步配接器和驗證器的 XML 檔案。 + </dt> + <dd> + 應用程式宣示說明中的 + <code><<a href="{@docRoot}guide/topics/manifest/service-element.html">service</a>></code> 元素會定義之前說明的同步配接器和驗證器服務元件。 + +這些元素包含的 +<code><<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>></code> 子元素可提供系統的特定資料: + + + + <ul> + <li> + 同步配接器服務的 + <code><<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>></code> 元素,此元素會指向 XML 檔案 <code>res/xml/syncadapter.xml</code>。 + +此檔案會按順序指出 +將與聯絡人供應程式同步的網路服務 URI, +以及網路服務的帳戶類型。 + </li> + <li> + <strong>選用:</strong>驗證器服務的 + <code><<a href="{@docRoot}guide/topics/manifest/meta-data-element.html">meta-data</a>></code> 元素,此元素會指向 XML 檔案 +<code>res/xml/authenticator.xml</code>。 +此檔案會按順序指出此驗證器支援的帳戶類型,以及驗證程序中會出現的 UI 資源。 + +此元素中指定的帳戶類型必須與同步配接器中指定的帳戶類型相同。 + + + </li> + </ul> + </dd> + </dl> +<h2 id="SocialStream">社交串流資料</h2> +<p> + {@code android.provider.ContactsContract.StreamItems} 和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格負責管理社交網路的傳入資料。 +您可以編寫一個同步配接器,將來自您自己網路的串流資料 +新增至這些表格,或是可以從這些表格讀取串流資料,然後 +顯示於您自己的應用程式中,或同時具備這兩種功能。有了這些功能,您的社交網路服務和應用程式就可以整合到 Android 的社交網路體驗。 + +</p> +<h3 id="StreamText">社交串流文字</h3> +<p> + 串流項目永遠會與原始聯絡人關聯。 +{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} 會連結到原始聯絡人的 + <code>_ID</code> 值。原始聯絡人的帳戶類型和帳戶名稱也會儲存在串流項目列。 + +</p> +<p> + 串流中的資料會儲存在下列各欄: +</p> +<dl> + <dt> + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} + </dt> + <dd> + <strong>必要。</strong>與此串流項目相關聯的原始聯絡人使用者帳戶類型。 +請記得在插入串流項目時設定此值。 + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} + </dt> + <dd> + <strong>必要。</strong>與此串流項目相關聯的原始聯絡人使用者帳戶名稱。 +請記得在插入串流項目時設定此值。 + </dd> + <dt> + 識別碼欄 + </dt> + <dd> + <strong>必要。</strong>您必須在插入串流項目時插入以下識別碼欄: + + <ul> + <li> + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}:與此串流項目相關聯的聯絡人 +{@code android.provider.BaseColumns#_ID} 值。 + + </li> + <li> + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}:與此串流項目相關聯的聯絡人 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 值。 + + </li> + <li> + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}:與此串流項目相關聯的原始聯絡人 +{@code android.provider.BaseColumns#_ID} 值。 + + </li> + </ul> + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} + </dt> + <dd> + 選用。儲存您可以在串流項目開頭顯示的摘要資訊。 + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} + </dt> + <dd> + 串流項目的文字,可能是項目來源張貼的內容,或者會產生串流項目動作的描述。 +此欄可以包含 +{@link android.text.Html#fromHtml(String) fromHtml()} 能夠轉譯的任何格式內嵌資源影像。 +供應程式會截斷較長的內容,但會試著避免破壞標籤。 + + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} + </dt> + <dd> + 內含插入或更新串流項目時間的文字字串, +,以「毫秒」<em></em>為單位。插入或更新串流項目的應用程式負責維護此欄,聯絡人供應程式不會自動加以維護。 + + + </dd> +</dl> +<p> + 如要顯示串流項目的識別資訊,請使用 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}、 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} 以及 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} 連結到應用程式中的資源。 + +</p> +<p> + {@code android.provider.ContactsContract.StreamItems} 表格也包含 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} 到 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4} 欄,專門供同步配接器使用。 + +</p> +<h3 id="StreamPhotos">社交串流相片</h3> +<p> + {@code android.provider.ContactsContract.StreamItemPhotos} 表格會儲存與串流項目相關聯的相片。 +表格的 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} 欄會連結到 + {@code android.provider.ContactsContract.StreamItems} 表格中 {@code android.provider.BaseColumns#_ID} 欄的值。 +相片參照會儲存在表格中的以下各欄: + +</p> +<dl> + <dt> + {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} 欄 (BLOB)。 + </dt> + <dd> + 相片的二進位檔,由供應程式調整大小以進行儲存和顯示。 + 此欄是要提供與使用舊版聯絡人供應程式儲存相片的向下相容性。 +不過,在目前版本中,您不應使用此欄來儲存相片。 +請改用 {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} 或 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (下文提供這兩者的相關說明) 在檔案中儲存相片。 + +此欄現在包含相片的縮圖可供讀取。 + + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} + </dt> + <dd> + 原始聯絡人相片的數字識別碼。將此值附加到常數 +{@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI} 可取得指向單一相片檔案的內容 URI,然後呼叫 {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 可取得此相片檔案的控制代碼。 + + + </dd> + <dt> + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} + </dt> + <dd> + 直接指向此列所呈現相片的相片檔案內容 URI。 + 使用此 URI 呼叫 {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 可取得相片檔案的控制代碼。 + </dd> +</dl> +<h3 id="SocialStreamTables">使用社交串流表格</h3> +<p> + 這些表格的運作方式與聯絡人供應程式中的其他主要表格大致相同,以下各項除外: +</p> + <ul> + <li> + 這些表格需要額外的存取權限。如要從中讀取,您的應用程式必須具備 {@code android.Manifest.permission#READ_SOCIAL_STREAM} 權限。 +如要加以修改,您的應用程式必須具備 +{@code android.Manifest.permission#WRITE_SOCIAL_STREAM} 權限。 + + </li> + <li> + 針對 {@code android.provider.ContactsContract.StreamItems} 表格,每個原始聯絡人的儲存列數是有限制的。 +達到此限制後,聯絡人供應程式會自動刪除 +{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} 最舊的列,為新的串流項目列騰出空間。 + +如要取得此限制,請發出查詢給內容 URI +{@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}。 +您只要將內容 URI 設為 <code>null</code> 即可,其餘引數不需要處理。 +此查詢會傳回內含單一列的 Cursor 與單一欄 +{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}。 + + </li> + </ul> + +<p> + 類別 {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} 定義了單一串流項目,且內含相片列的 {@code android.provider.ContactsContract.StreamItemPhotos} 子表格。 + + +</p> +<h3 id="SocialStreamInteraction">社交串流互動</h3> +<p> + 社交串流資料受到聯絡人供應程式與裝置聯絡人應用程式的管理,提供強大的方式將您的社交網路系統與現有聯絡人連接起來。 + +提供下列功能: +</p> + <ul> + <li> + 使用同步配接器將您的社交網路服務同步到聯絡人供應程式後,您可以擷取某位使用者的聯絡人最近 Activity,並將它儲存在 {@code android.provider.ContactsContract.StreamItems} 和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格中,供後續使用。 + + + </li> + <li> + 除了一般同步之外,您可以在使用者選取要檢視的聯絡人時,觸發您的同步配接器,以擷取其他資料。 +此舉可讓您的同步配接器擷取聯絡人高解析度的相片,以及聯絡人最近的串流項目。 + + </li> + <li> + 藉由向裝置的聯絡人應用程式和聯絡人供應程式註冊通知,您可以在檢視聯絡人時「收到」<em></em>意圖,並於此時更新您服務中的聯絡人狀態。 + +相較於與同步配接器執行完整同步, +此方式較快速且使用的頻寬較少。 + </li> + <li> + 使用者在裝置的聯絡人應用程式查看聯絡人時,可以將聯絡人新增至您的社交網路服務。 +您可以透過「邀請聯絡人」啟用上述功能。「邀請聯絡人」會啟用一連串的 Activity,將現有聯絡人新增至您的網路和 XML 檔案。此檔案會將您應用程式的詳細資訊提供給裝置的聯絡人應用程式和聯絡人供應程式。 + + + + </li> + </ul> +<p> + 串流項目與聯絡人供應程式的一般同步與其他同步相同。 +如要進一步瞭解同步,請參閱<a href="#SyncAdapters">聯絡人供應程式同步配接器</a>。 +以下兩節說明如何註冊通知和邀請聯絡人。 + +</p> +<h4>註冊以處理社交網路檢視</h4> +<p> + 註冊您的同步配接器,使其在使用者查看由您的同步配接器所管理的聯絡人時收到通知: + +</p> +<ol> + <li> + 在專案的 <code>res/xml/</code> 目錄中建立名稱為 <code>contacts.xml</code>的檔案。 +如果已經有這個檔案,可略過此步驟。 + </li> + <li> + 在此檔案中,新增 +<code><ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"></code> 元素。 + 如果這個元素已經存在,可略過此步驟。 + </li> + <li> + 為了註冊服務,讓使用者在裝置的聯絡人應用程式中開啟聯絡人的詳細資訊頁面時收到通知,請將 + <code>viewContactNotifyService="<em>serviceclass</em>"</code> 屬性新增至此元素,其中 + <code><em>serviceclass</em></code> 是該服務的完整類別名稱,而此服務會收到來自裝置聯絡人應用程式的意圖。 + +對於通知器服務而言,使用擴充 {@link android.app.IntentService} 的類別可以讓此服務接收意圖。 + +傳入意圖的資料中含有使用者所點擊該名原始聯絡人的內容 URI。 +您可以從通知器服務繫結,然後呼叫您的同步配接器,以更新原始聯絡人的資料。 + + </li> +</ol> +<p> + 如何註冊使用者點擊串流項目或相片 (或兩者) 時所呼叫的 Activity: +</p> +<ol> + <li> + 在專案的 <code>res/xml/</code> 目錄中建立名稱為 <code>contacts.xml</code>的檔案。 +如果已經有這個檔案,可略過此步驟。 + </li> + <li> + 在此檔案中,新增 +<code><ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"></code> 元素。 + 如果這個元素已經存在,可略過此步驟。 + </li> + <li> + 為了註冊其中一個 Activity,讓它處理使用者在裝置的聯絡人應用程式中點擊串流項目的 Activity,請將 + <code>viewStreamItemActivity="<em>activityclass</em>"</code> 屬性新增至此元素,其中 + <code><em>activityclass</em></code> 是該 Activity 的完整類別名稱,而此 Activity 會收到來自裝置聯絡人應用程式的意圖。 + + + </li> + <li> + 為了註冊其中一個 Activity,讓它處理使用者在裝置的聯絡人應用程式中點擊串流相片的活動,請將 + <code>viewStreamItemPhotoActivity="<em>activityclass</em>"</code> 屬性新增至此元素,其中 + <code><em>activityclass</em></code> 是該 Activity 的完整類別名稱,而此 Activity 會收到來自裝置聯絡人應用程式的意圖。 + + + </li> +</ol> +<p> + 如要進一步瞭解 <code><ContactsAccountType></code> 元素,請參閱 <a href="#SocialStreamAcctType"><ContactsAccountType> 元素</a>。 + +</p> +<p> + 傳入意圖的資料中含有使用者所按下項目或相片的內容 URI。 + 如要針對文字項目和相片採取不同的 Activity,請在相同的檔案中同時使用兩個屬性。 +</p> +<h4>與社交網路服務互動</h4> +<p> + 使用者不需要離開裝置的聯絡人應用程式,就可以邀請聯絡人到您的社交網路網站。 +您可以改為讓裝置的聯絡人應用程式傳送意圖,以邀請聯絡人前往您的 Activity。 +如要進行此設定: +</p> +<ol> + <li> + 在專案的 <code>res/xml/</code> 目錄中建立名稱為 <code>contacts.xml</code>的檔案。 +如果已經有這個檔案,可略過此步驟。 + </li> + <li> + 在此檔案中,新增 <code><ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"></code> 元素。 + + 如果這個元素已經存在,可略過此步驟。 + </li> + <li> + 新增下列屬性: + <ul> + <li><code>inviteContactActivity="<em>activityclass</em>"</code></li> + <li> + <code>inviteContactActionLabel="@string/<em>invite_action_label</em>"</code> + </li> + </ul> + <code><em>activityclass</em></code> 值是 Activity 的完整類別名稱,以此 Activity 接收意圖。 +<code><em>invite_action_label</em></code> 值是顯示在裝置聯絡人應用程式內 [新增連線]<strong></strong> 選單中的文字字串。 + + + </li> +</ol> +<p class="note"> + <strong>注意:</strong><code>ContactsSource</code> 是 + <code>ContactsAccountType</code> 已淘汰的標籤名稱。 +</p> +<h3 id="ContactsFile">contacts.xml 參照</h3> +<p> + <code>contacts.xml</code> 檔案包含的 XML 元素,可控制您的同步配接器與應用程式 (聯絡人應用程式和聯絡人供應程式) 之間的互動。 +這些元素在以下各節有詳細的說明。 + +</p> +<h4 id="SocialStreamAcctType"><ContactsAccountType> 元素</h4> +<p> + <code><ContactsAccountType></code> 元素控制您的應用程式與聯絡人應用程式之間的互動。 +此元素的語法如下: +</p> +<pre> +<ContactsAccountType + xmlns:android="http://schemas.android.com/apk/res/android" + inviteContactActivity="<em>activity_name</em>" + inviteContactActionLabel="<em>invite_command_text</em>" + viewContactNotifyService="<em>view_notify_service</em>" + viewGroupActivity="<em>group_view_activity</em>" + viewGroupActionLabel="<em>group_action_text</em>" + viewStreamItemActivity="<em>viewstream_activity_name</em>" + viewStreamItemPhotoActivity="<em>viewphotostream_activity_name</em>"> +</pre> +<p> + <strong>包含於:</strong> +</p> +<p> + <code>res/xml/contacts.xml</code> +</p> +<p> + <strong>可以包含:</strong> +</p> +<p> + <strong><code><ContactsDataKind></code></strong> +</p> +<p> + <strong>描述:</strong> +</p> +<p> + 宣告 Android 元件和 UI 標籤,讓使用者可以邀請聯絡人加入社交網路、使用者的社交網路串流更新內容時通知使用者等等。 + + +</p> +<p> + 請注意,<code><ContactsAccountType></code> 的屬性不需要使用屬性前置詞 <code>android:</code>。 + +</p> +<p> + <strong>屬性:</strong> +</p> +<dl> + <dt>{@code inviteContactActivity}</dt> + <dd> + 使用者從裝置的聯絡人應用程式選取[新增連線]<strong></strong>時,您希望在應用程式中啟動的 Activity 完整類別名稱。 + + + </dd> + <dt>{@code inviteContactActionLabel}</dt> + <dd> + 在 [新增連線]<strong></strong> 選單的 + {@code inviteContactActivity} 中所指定 Activity 的顯示文字字串。 + 例如,您可以使用「關注我的網路活動」字串。此標籤可以使用字串資源識別碼。 + + </dd> + <dt>{@code viewContactNotifyService}</dt> + <dd> + 使用者檢視聯絡人時,要接收通知的應用程式中的服務完整類別名稱。 +此通知是由裝置的聯絡人應用程式所傳送,這樣可以讓您的應用程式延後要處理大量資料的操作,需要時再加以處理。 + +例如,您的應用程式可以藉由讀取並顯示聯絡人的高解析度相片,以及最近的社交串流項目,以回應此通知。 + +如要進一步瞭解此功能,請參閱<a href="#SocialStreamInteraction">社交串流互動</a>。 +您可以在 + <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">SampleSyncAdapter</a> 範例應用程式的 <code>NotifierService.java</code>中查看通知服務的範例。 + + + </dd> + <dt>{@code viewGroupActivity}</dt> + <dd> + 應用程式中可以顯示群組資訊的 Activity 完整類別名稱。 +使用者在裝置的聯絡人應用程式中按一下群組標籤時,會顯示此 Activity 的 UI。 + + </dd> + <dt>{@code viewGroupActionLabel}</dt> + <dd> + 聯絡人應用程式顯示 UI 控制項的標籤,可以讓使用者在您的應用程式中查看群組。 + + <p> + 例如,如果您在裝置上安裝 Google+ 應用程式,而您將Google+ 與聯絡人應用程式進行同步,您會看到 Google+ 社交圈已列為聯絡人應用程式 [群組]<strong></strong> 標籤中的群組。 + +如果按一下 Google+ 社交圈,您會看到該社交圈中的人員已列為「群組」。 +系統會在畫面頂端顯示 Google+ 圖示,如果您按一下此圖示,則控制權會切換到 Google+ 應用程式。聯絡人應用程式使用 +{@code viewGroupActivity} 執行此動作,並使用 Google+ 圖示做為 +{@code viewGroupActionLabel} 的值。 + + + </p> + <p> + 此屬性可以使用字串資源識別碼。 + </p> + </dd> + <dt>{@code viewStreamItemActivity}</dt> + <dd> + 使用者按一下原始聯絡人的串流項目時,裝置的聯絡人應用程式所啟動應用程式中的 Activity 完整類別名稱。 + + </dd> + <dt>{@code viewStreamItemPhotoActivity}</dt> + <dd> + 使用者按一下原始聯絡人串流項目中的相片時,裝置的聯絡人應用程式所啟動應用程式中的 Activity 完整類別名稱。 + + + </dd> +</dl> +<h4 id="SocialStreamDataKind"><ContactsDataKind> 元素</h4> +<p> + <code><ContactsDataKind></code> 元素控制聯絡人應用程式的 UI 中,您的應用程式自訂資料列所顯示的控制項。此元素的語法如下: + +</p> +<pre> +<ContactsDataKind + android:mimeType="<em>MIMEtype</em>" + android:icon="<em>icon_resources</em>" + android:summaryColumn="<em>column_name</em>" + android:detailColumn="<em>column_name</em>"> +</pre> +<p> + <strong>包含於:</strong> +</p> +<code><ContactsAccountType></code> +<p> + <strong>描述:</strong> +</p> +<p> + 使用此元素讓聯絡人應用程式,將自訂資料列的內容顯示為原始聯絡人詳細資訊的一部分。 +<code><ContactsAccountType></code> 的每個 <code><ContactsDataKind></code> 子元素都代表同步配接器新增至 {@link android.provider.ContactsContract.Data} 表格的自訂資料列類型。 + +針對您使用的每個自訂 MIME 類型,新增一個 + <code><ContactsDataKind></code> 元素。如果您不要顯示某個自訂資料列的資料,就不用為該列新增元素。 + +</p> +<p> + <strong>屬性:</strong> +</p> +<dl> + <dt>{@code android:mimeType}</dt> + <dd> + 您在 + {@link android.provider.ContactsContract.Data} 表格中已經為自訂資料列類型所定義的自訂 MIME 類型。例如, +<code>vnd.android.cursor.item/vnd.example.locationstatus</code> 值可能是 +記錄聯絡人最後已知位置資料列的自訂 MIME 類型。 + </dd> + <dt>{@code android:icon}</dt> + <dd> + 聯絡人應用程式顯示在資料旁邊的 Android +<a href="{@docRoot}guide/topics/resources/drawable-resource.html">可繪資源</a>。 +使用此項向使用者指出資料是來自您的服務。 + + </dd> + <dt>{@code android:summaryColumn}</dt> + <dd> + 從資料列擷取兩個值,其中第一個值的欄名稱。此資料列的值會顯示為該項目的第一行。 +第一行的用意是做為資料的摘要使用,但為選用。 +另請參閱 + <a href="#detailColumn">android:detailColumn</a>。 + </dd> + <dt>{@code android:detailColumn}</dt> + <dd> + 從資料列擷取兩個值,其中第二個值的欄名稱。此資料列的值會顯示為該項目的第二行。 +另請參閱 +{@code android:summaryColumn}。 + </dd> +</dl> +<h2 id="AdditionalFeatures">其他聯絡人供應程式功能</h2> +<p> + 除了上一節所描述的主要功能之外,聯絡人供應程式也提供以下實用功能來處理聯絡人資料: + +</p> + <ul> + <li>聯絡人群組</li> + <li>相片功能</li> + </ul> +<h3 id="Groups">聯絡人群組</h3> +<p> + 聯絡人供應程式可以選擇為群組<strong></strong>資料相關的聯絡人集合貼上標籤。 +如果與使用者帳戶關聯的伺服器要維護群組,該帳戶的帳戶類型所屬的同步配接器,應該要在聯絡人供應程式和伺服器之間傳輸群組資料。 + +使用者將新的聯絡人新增至伺服器,然後將此聯絡人放置於新群組時,同步配接器必須將新群組新增至 {@link android.provider.ContactsContract.Groups} 表格。 + +原始聯絡人所屬的一或多個群組會使用 {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} MIME 類型儲存在 {@link android.provider.ContactsContract.Data} 表格。 + + +</p> +<p> + 如果您設計的同步配接器,會將原始聯絡人資料從伺服器新增至聯絡人供應程式,表示您並未使用群組,那麼您需要告訴供應程式讓您的資料變成可見的。 + +在使用者將帳戶新增至裝置時,要執行的程式碼中,更新聯絡人供應程式為帳戶新增的 {@link android.provider.ContactsContract.Settings} 列。 + +在此列中,將 {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE +Settings.UNGROUPED_VISIBLE} 欄的值設為 1。 +這麼做之後,即使您沒有使用群組,聯絡人供應程式 +一律會讓您的聯絡人資料成為可見的。 +</p> +<h3 id="Photos">聯絡人相片</h3> +<p> + {@link android.provider.ContactsContract.Data} 表格會使用 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 在列中儲存相片。 +列的 +{@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} 欄是連結到其所屬原始聯絡人的 + {@code android.provider.BaseColumns#_ID} 欄。 + 類別 {@link android.provider.ContactsContract.Contacts.Photo} 定義了 + {@link android.provider.ContactsContract.Contacts} 的子表格,其中包含聯絡人主要相片的相片資訊,也就是聯絡人之主要原始聯絡人的主要相片。 +同樣地, +類別 {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 定義了 {@link android.provider.ContactsContract.RawContacts} 的子表格,其中包含原始聯絡人之主要相片的相片資訊。 + + +</p> +<p> + {@link android.provider.ContactsContract.Contacts.Photo} 和 +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 的參考文件含有擷取相片資訊的範例。 +擷取原始聯絡人的主要縮圖沒有方便使用的類別,不過您可以傳送查詢到 +{@link android.provider.ContactsContract.Data} 表格,然後選取原始聯絡人的 +{@code android.provider.BaseColumns#_ID}、{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 以及 {@link android.provider.ContactsContract.Data#IS_PRIMARY} 欄,以尋找原始聯絡人的主要相片列。 + + + +</p> +<p> + 人員的社交串流資料可能也包括相片。這些資訊都儲存在 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格。<a href="#StreamPhotos">社交串流相片</a>中針對此表格會有更詳細的說明。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 000000000000..78314784ba9e --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=內容供應程式基本概念 +@jd:body +<div id="qv-wrapper"> +<div id="qv"> +<!-- In this document --> +<h2>本文件內容</h2> +<ol> + <li> + <a href="#Basics">總覽</a> + <ol> + <li> + <a href="#ClientProvider">存取供應程式</a> + </li> + <li> + <a href="#ContentURIs">內容 URI</a> + </li> + </ol> + </li> + <li> + <a href="#SimpleQuery">從供應程式擷取資料</a> + <ol> + <li> + <a href="#RequestPermissions">要求讀取權限</a> + </li> + <li> + <a href="#Query">建構查詢</a> + </li> + <li> + <a href="#DisplayResults">顯示查詢結果</a> + </li> + <li> + <a href="#GettingResults">從查詢結果取得資料</a> + </li> + </ol> + </li> + <li> + <a href="#Permissions">內容供應程式權限</a> + </li> + <li> + <a href="#Modifications">插入、更新及刪除資料</a> + <ol> + <li> + <a href="#Inserting">插入資料</a> + </li> + <li> + <a href="#Updating">更新資料</a> + </li> + <li> + <a href="#Deleting">刪除資料</a> + </li> + </ol> + </li> + <li> + <a href="#DataTypes">供應程式資料類型</a> + </li> + <li> + <a href="#AltForms">供應程式存取權的替代形式</a> + <ol> + <li> + <a href="#Batch">批次存取</a> + </li> + <li> + <a href="#Intents">透過意圖存取資料</a> + </li> + </ol> + </li> + <li> + <a href="#ContractClasses">合約類別</a> + </li> + <li> + <a href="#MIMETypeReference">MIME 類型參考資料</a> + </li> +</ol> + + <!-- Key Classes --> +<h2>重要類別</h2> + <ol> + <li> + {@link android.content.ContentProvider} + </li> + <li> + {@link android.content.ContentResolver} + </li> + <li> + {@link android.database.Cursor} + </li> + <li> + {@link android.net.Uri} + </li> + </ol> + + <!-- Related Samples --> +<h2>相關範例</h2> + <ol> + <li> + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List2.html"> + 游標 (使用者)</a> + </li> + <li> + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html"> + 游標 (電話)</a> + </li> + </ol> + + <!-- See also --> +<h2>另請參閱</h2> + <ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-creating.html"> + 建立內容供應程式</a> + </li> + <li> + <a href="{@docRoot}guide/topics/providers/calendar-provider.html"> + 日曆供應程式</a> + </li> + </ol> +</div> +</div> + + <!-- Intro paragraphs --> +<p> + 內容供應程式可管理中央資料存放庫的存取權。供應程式是 Android 應用程式的一部分,通常可提供本身的 UI 方便使用者處理資料。 + +不過,內容供應程式主要是供其他應用程式使用 (透過供應程式用戶端物件進行存取)。 +供應程式與供應程式用戶端可提供一致的標準介面,除了可用於存取資料,還能用來處理程序間通訊以及保護資料存取的安全。 + + +</p> +<p> + 本主題涵蓋以下基本概念: +</p> + <ul> + <li>內容供應程式的運作方式。</li> + <li>可用於從內容供應程式擷取資料的 API。</li> + <li>可用於插入、更新或刪除內容供應程式資料的 API。</li> + <li>有助於使用供應程式的其他 API 功能。</li> + </ul> + + <!-- Basics --> +<h2 id="Basics">總覽</h2> +<p> + 內容供應程式會向外部應用程式以一或多份表格顯示資料,這些表格的樣式類似於關聯式資料庫中的表格。 +每個資料列代表供應程式所收集部分資料類型的單一執行個體,而列中的每個資料欄則代表針對該執行個體收集的個別資料。 + + +</p> +<p> + 例如,Android 平台內建的其中一個供應程式是使用者字典,其中儲存使用者想保留的非標準字詞的拼寫方式。 +表 1 說明此供應程式表格顯示這種資料的方式: + +</p> +<p class="table-caption"> + <strong>表 1:</strong>使用者字典表格範例。 +</p> +<table id="table1" style="width: 50%;"> + <tr> + <th style="width:20%" align="center" scope="col">字詞</th> + <th style="width:20%" align="center" scope="col">應用程式 ID</th> + <th style="width:20%" align="center" scope="col">頻率</th> + <th style="width:20%" align="center" scope="col">地區</th> + <th style="width:20%" align="center" scope="col">_ID</th> + </tr> + <tr> + <td align="center" scope="row">mapreduce</td> + <td align="center">user1</td> + <td align="center">100</td> + <td align="center">en_US</td> + <td align="center">1</td> + </tr> + <tr> + <td align="center" scope="row">precompiler</td> + <td align="center">user14</td> + <td align="center">200</td> + <td align="center">fr_FR</td> + <td align="center">2</td> + </tr> + <tr> + <td align="center" scope="row">applet</td> + <td align="center">user2</td> + <td align="center">225</td> + <td align="center">fr_CA</td> + <td align="center">3</td> + </tr> + <tr> + <td align="center" scope="row">const</td> + <td align="center">user1</td> + <td align="center">255</td> + <td align="center">pt_BR</td> + <td align="center">4</td> + </tr> + <tr> + <td align="center" scope="row">int</td> + <td align="center">user5</td> + <td align="center">100</td> + <td align="center">en_UK</td> + <td align="center">5</td> + </tr> +</table> +<p> + 在表 1 中,每個資料列代表標準字典未收錄的某個字詞執行個體。 +每個資料欄則代表該字詞的部分資料,例如初次出現該字詞的地區。 +欄標題為儲存在供應程式中的欄名稱。 +如果想得知某一列的地區,請查看相對應的 <code>locale</code> 欄。以這個供應程式為例,<code>_ID</code> 欄的用途與供應程式自動維護的「主索引鍵」欄相同。 + + +</p> +<p class="note"> + <strong>注意:</strong>供應程式不需具備主索引鍵,也不必採用 <code>_ID</code> 做為主索引鍵欄的名稱 (如果有此欄的話)。 +不過,如果您想將供應程式的資料繫結至 {@link android.widget.ListView},就必須將其中一個資料欄命名為 <code>_ID</code>。 + +如要進一步瞭解這項規定,請參閱<a href="#DisplayResults">顯示查詢結果</a>。 + +</p> +<h3 id="ClientProvider">存取供應程式</h3> +<p> + 應用程式會透過 {@link android.content.ContentResolver} 用戶端物件存取內容供應程式的資料。 +此物件內含的方法可呼叫供應程式物件 ({@link android.content.ContentProvider} 子類別的其中一項執行個體) 中的同名方法。 + + +{@link android.content.ContentResolver} 方法可提供永久儲存空間的「CRUD」(建立、擷取、更新、刪除) 基本功能。 + +</p> +<p> + 用戶端應用程式處理程序中的 {@link android.content.ContentResolver} 物件以及擁有供應程式的應用程式中的 {@link android.content.ContentProvider} 物件,會自動處理程序間通訊。 +{@link android.content.ContentProvider} 還可當作其資料存放庫與外部資料表格之間的抽象層。 + + + +</p> +<p class="note"> + <strong>注意:</strong>如要存取供應程式,您的應用程式通常必須透過本身的宣示說明檔案要求特定權限。 +如需詳細資料,請參閱<a href="#Permissions">內容供應程式權限</a>。 + +</p> +<p> + 例如,如要從使用者字典供應程式取得一份列出字詞及其地區的清單,請呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 + + {@link android.content.ContentResolver#query query()} 方法會呼叫使用者字典供應程式所定義的 +{@link android.content.ContentProvider#query ContentProvider.query()} 方法。 +以下是 +{@link android.content.ContentResolver#query ContentResolver.query()} 呼叫的程式碼: +<p> +<pre> +// Queries the user dictionary and returns results +mCursor = getContentResolver().query( + UserDictionary.Words.CONTENT_URI, // The content URI of the words table + mProjection, // The columns to return for each row + mSelectionClause // Selection criteria + mSelectionArgs, // Selection criteria + mSortOrder); // The sort order for the returned rows +</pre> +<p> + 表 2 列出 +{@link android.content.ContentResolver#query +query(Uri,projection,selection,selectionArgs,sortOrder)} 的引數及相對應的 SQL SELECT 陳述式: +</p> +<p class="table-caption"> + <strong>表 2:</strong>Query() 與 SQL 查詢的對照表。 +</p> +<table id="table2" style="width: 75%;"> + <tr> + <th style="width:25%" align="center" scope="col">query() 引數</th> + <th style="width:25%" align="center" scope="col">SELECT 關鍵字/參數</th> + <th style="width:50%" align="center" scope="col">備註</th> + </tr> + <tr> + <td align="center"><code>Uri</code></td> + <td align="center"><code>FROM <em>table_name</em></code></td> + <td><code>Uri</code> 會對應至供應程式中名為的「table_name」<em></em>表格。</td> + </tr> + <tr> + <td align="center"><code>projection</code></td> + <td align="center"><code><em>col,col,col,...</em></code></td> + <td> + <code>projection</code> 代表需針對每個擷取的資料列 +納入的一系列資料欄。 + </td> + </tr> + <tr> + <td align="center"><code>selection</code></td> + <td align="center"><code>WHERE <em>col</em> = <em>value</em></code></td> + <td><code>selection</code> 會指定資料列選取條件。</td> + </tr> + <tr> + <td align="center"><code>selectionArgs</code></td> + <td align="center"> + (沒有任何相對應的關鍵字/參數。選取引數會取代選取子句中的 +<code>?</code> 預留位置。) + </td> + </tr> + <tr> + <td align="center"><code>sortOrder</code></td> + <td align="center"><code>ORDER BY <em>col,col,...</em></code></td> + <td> + <code>sortOrder</code> 會指定資料列在傳回的 {@link android.database.Cursor} 中的顯示順序。 + + </td> + </tr> +</table> +<h3 id="ContentURIs">內容 URI</h3> +<p> + <strong>內容 URI</strong> 是指用於識別供應程式資料的 URI,內容 URI 包括整個供應程式的符號名稱 (亦即供應程式的<strong>授權</strong>),以及指向表格的名稱 (亦即<strong>路徑</strong>)。 + +當您呼叫用戶端方法來存取供應程式中的表格時,該表格的內容 URI 即為其中一個引數。 + + +</p> +<p> + 在上方程式碼中, +{@link android.provider.UserDictionary.Words#CONTENT_URI} 常數包含使用者字典表格「字詞」的內容 URI。 +{@link android.content.ContentResolver} 物件會剖析該 URI 的授權,然後比較授權和已知供應程式的系統表格,藉此「解析」供應程式。 + +接著 {@link android.content.ContentResolver} 可以查詢引數分派給正確的供應程式。 + + +</p> +<p> + {@link android.content.ContentProvider} 會使用內容 URI 的路徑部分選擇要存取的表格。 +供應程式通常包含用於公開每個表格的「路徑」<strong></strong>。 +</p> +<p> + 以上方程式碼為例,「字詞」的完整 URI 會如下所示: +</p> +<pre> +content://user_dictionary/words +</pre> +<p> + 其中的 <code>user_dictionary</code> 字串代表供應程式的授權,而 <code>words</code> 字串則是表格的路徑。 +字串 <code>content://</code> (<strong>配置</strong>) 一律會顯示,而起會將此項目識別為內容 URI。 + + +</p> +<p> + 許多供應程式都可讓您存取表格中的單一資料列,方法是在 URI 後方附加 ID 值。例如,如要從使用者字典擷取 <code>_ID</code> 為 <code>4</code> 的資料列,請使用以下內容 URI: + + +</p> +<pre> +Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4); +</pre> +<p> + 在更新或刪除您擷取的一組資料列時,通常需要使用 ID 值。 + +</p> +<p class="note"> + <strong>注意:</strong>{@link android.net.Uri} 和 {@link android.net.Uri.Builder} 類別包含可用於從字串建構格式正確之 URI 物件的簡便方法。 +而 {@link android.content.ContentUris} 則包含可用於將 ID 值附加至 URI 的簡便方法。上方程式碼片段是使用 {@link android.content.ContentUris#withAppendedId +withAppendedId()} 將 ID 附加至 UserDictionary 內容 URI。 + + +</p> + + + <!-- Retrieving Data from the Provider --> +<h2 id="SimpleQuery">從供應程式擷取資料</h2> +<p> + 本節以使用者字典供應程式為例,說明如何從供應程式擷取資料。 + +</p> +<p class="note"> + 為了避免造成混淆,本節中的程式碼片段是透過「UI 執行緒」呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 +不過在實際程式碼中,您需要透過個別執行緒以非同步方式進行查詢。 +您可以使用 {@link android.content.CursorLoader} 類別 (詳情請參閱<a href="{@docRoot}guide/components/loaders.html">載入器</a>指南) 來完成這項作業。 + + +此外,本節中的程式碼都只是程式碼片段,無法呈現完整的應用程式。 + +</p> +<p> + 如要從供應程式擷取資料,請按照下列基本步驟操作: +</p> +<ol> + <li> + 要求供應程式的讀取權限。 + </li> + <li> + 定義可將查詢傳送至供應程式的程式碼。 + </li> +</ol> +<h3 id="RequestPermissions">要求讀取權限</h3> +<p> + 您的應用程式需取得供應程式的「讀取權限」,才能從供應程式擷取資料。 +您無法在執行階段要求此權限;您必須使用 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素和供應程式所定義的確切權限名稱,在宣示說明中指明您需要此權限。 + + + +在宣示說明中指定此元素後,即可為您的應用程式「要求」這項權限。 +當使用者安裝您應用程式時,他們會間接核准此要求。 + +</p> +<p> + 如要找出所使用供應程式的確切讀取權限名稱,以及該供應程式使用的其他存取權限名稱,請查閱供應程式的說明文件。 + + +</p> +<p> + 如要進一步瞭解用於存取供應程式的權限角色,請參閱<a href="#Permissions">內容供應程式權限</a>。 + +</p> +<p> + 使用者字典供應程式會在本身的宣示說明檔案中定義 <code>android.permission.READ_USER_DICTIONARY</code> 權限,因此想讀取該供應程式的應用程式都必須要求此權限。 + + +</p> +<!-- Constructing the query --> +<h3 id="Query">建構查詢</h3> +<p> + 從供應程式擷取資料的下一個步驟是建構查詢。本節中的第一個程式碼片段可定義數個用於存取使用者字典供應程式的變數: + +</p> +<pre class="prettyprint"> + +// A "projection" defines the columns that will be returned for each row +String[] mProjection = +{ + UserDictionary.Words._ID, // Contract class constant for the _ID column name + UserDictionary.Words.WORD, // Contract class constant for the word column name + UserDictionary.Words.LOCALE // Contract class constant for the locale column name +}; + +// Defines a string to contain the selection clause +String mSelectionClause = null; + +// Initializes an array to contain selection arguments +String[] mSelectionArgs = {""}; + +</pre> +<p> + 下一個程式碼片段以使用者字典供應程式為例,示範 {@link android.content.ContentResolver#query ContentResolver.query()} 的使用方式。 + +供應程式用戶端查詢類似於 SQL 查詢,包含一組要傳回的資料欄、一組選取條件以及一個排序順序。 + +</p> +<p> + 查詢需傳回的一組資料欄稱為「投影」<strong></strong>(即 <code>mProjection</code> 變數)。 + +</p> +<p> + 指定要擷取的資料列的運算式會分為選取子句和選取引數。 +選取子句包含邏輯運算式和布林運算式、欄名稱和值 (即 <code>mSelectionClause</code> 變數)。 +如果您指定可替換的參數 <code>?</code>,而不是某個值,則查詢方法會從選取引數陣列 (即 <code>mSelectionArgs</code> 變數) 擷取相關值。 + + +</p> +<p> + 在下方程式碼片段中,如果使用者未輸入任何字詞,選取子句會設定為 <code>null</code>,此時查詢會傳回供應程式中的所有字詞。 +如果使用者輸入了某個字詞,則選取子句會設定為 <code>UserDictionary.Words.WORD + " = ?"</code>,而選取引數陣列的第一個元素會設為使用者所輸入的字詞。 + + +</p> +<pre class="prettyprint"> +/* + * This defines a one-element String array to contain the selection argument. + */ +String[] mSelectionArgs = {""}; + +// Gets a word from the UI +mSearchString = mSearchWord.getText().toString(); + +// Remember to insert code here to check for invalid or malicious input. + +// If the word is the empty string, gets everything +if (TextUtils.isEmpty(mSearchString)) { + // Setting the selection clause to null will return all words + mSelectionClause = null; + mSelectionArgs[0] = ""; + +} else { + // Constructs a selection clause that matches the word that the user entered. + mSelectionClause = UserDictionary.Words.WORD + " = ?"; + + // Moves the user's input string to the selection arguments. + mSelectionArgs[0] = mSearchString; + +} + +// Does a query against the table and returns a Cursor object +mCursor = getContentResolver().query( + UserDictionary.Words.CONTENT_URI, // The content URI of the words table + mProjection, // The columns to return for each row + mSelectionClause // Either null, or the word the user entered + mSelectionArgs, // Either empty, or the string the user entered + mSortOrder); // The sort order for the returned rows + +// Some providers return null if an error occurs, others throw an exception +if (null == mCursor) { + /* + * Insert code here to handle the error. Be sure not to use the cursor! You may want to + * call android.util.Log.e() to log this error. + * + */ +// If the Cursor is empty, the provider found no matches +} else if (mCursor.getCount() < 1) { + + /* + * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily + * an error. You may want to offer the user the option to insert a new row, or re-type the + * search term. + */ + +} else { + // Insert code here to do something with the results + +} +</pre> +<p> + 這個查詢類似下方的 SQL 陳述式: +</p> +<pre> +SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC; +</pre> +<p> + 這個 SQL 陳述式會使用實際的欄名稱,而不是合約類別常數。 +</p> +<h4 id="Injection">防範惡意輸入</h4> +<p> + 如果內容供應程式所管理的資料是儲存在 SQL 資料庫中,在原始 SQL 陳述式中納入不受信任的外部資料可能會導致 SQL 遭植入惡意程式碼。 + +</p> +<p> + 以下提供選取子句範例: +</p> +<pre> +// Constructs a selection clause by concatenating the user's input to the column name +String mSelectionClause = "var = " + mUserInput; +</pre> +<p> + 使用這個選取子句可讓使用者能夠將惡意 SQL 串連至您的 SQL 陳述式。 + 例如,使用者可針對 <code>mUserInput</code> 輸入「nothing; DROP TABLE *;」,以產生 <code>var = nothing; DROP TABLE *;</code> 選取子句。 +由於系統會將選取子句視為 SQL 陳述式,因此這種選取子句可能會導致供應程式清除底層 SQLite 資料庫中的所有表格 (除非您設定供應程式捕捉 <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL 植入</a>嘗試)。 + + + +</p> +<p> + 為了避免發生這個問題,請使用包含 <code>?</code> 可替換參數和一系列選取引數的選取子句。 +當您執行此動作時,使用者輸入會直接繫結到查詢,而不是被解譯為 SQL 陳述式的一部分。 + + 這樣一來,系統就不會將選取子句視為 SQL,進而防止使用者輸入植入惡意 SQL。您也可以使用以下選取子句,而不是透過串連方式納入使用者輸入: + +</p> +<pre> +// Constructs a selection clause with a replaceable parameter +String mSelectionClause = "var = ?"; +</pre> +<p> + 設定如下所示的一系列選取引數: +</p> +<pre> +// Defines an array to contain the selection arguments +String[] selectionArgs = {""}; +</pre> +<p> + 如下所示在選取引數中加入一個值: +</p> +<pre> +// Sets the selection argument to the user's input +selectionArgs[0] = mUserInput; +</pre> +<p> + 建議您使用包含 <code>?</code> 可替換參數和一系列選取引數的選取子句,即使供應程式並非以 SQL 資料庫為基礎亦然。 + + +</p> +<!-- Displaying the results --> +<h3 id="DisplayResults">顯示查詢結果</h3> +<p> + {@link android.content.ContentResolver#query ContentResolver.query()} 用戶端方法一律會針對符合查詢選取條件的資料列,傳回內含查詢的投影所指定資料欄的 {@link android.database.Cursor}。 + +{@link android.database.Cursor} 物件會針對本身包含的資料列和資料欄提供隨機讀取存取權。 + +您可以使用 {@link android.database.Cursor} 方法逐一查看結果中的資料列、決定每個資料欄的資料類型、匯出資料欄的資料,以及檢查結果的其他屬性。 + +實作某些 {@link android.database.Cursor} 方法可在供應程式的資料變更時自動更新相關物件,或在 {@link android.database.Cursor} 變更時觸發觀察器物件中的方法,或是同時進行以上兩者。 + + +</p> +<p class="note"> + <strong>注意:</strong>供應程式可能會根據建立查詢物件的屬性限制資料欄的存取權。 +例如,內容供應程式會限制同步配接器存取部分資料欄,避免將這些資料欄傳回 Activity 或服務。 + +</p> +<p> + 如果沒有任何資料欄符合選取條件,則供應程式會傳回 {@link android.database.Cursor#getCount Cursor.getCount()} 為 0 的 +{@link android.database.Cursor} 物件 (即沒有任何內容的游標)。 + +</p> +<p> + 如果發生內部錯誤,查詢結果將取決於供應程式的決定。供應程式可能會選擇傳回 <code>null</code>,或是擲回 {@link java.lang.Exception}。 + +</p> +<p> + 由於 {@link android.database.Cursor} 是一份資料欄「清單」,因此要顯示 {@link android.database.Cursor} 的內容,最佳做法是透過 {@link android.widget.SimpleCursorAdapter} 將其連結至 {@link android.widget.ListView}。 + + +</p> +<p> + 以下程式碼片段是上一個程式碼片段的延伸。它會建立內含查詢所擷取 {@link android.database.Cursor} 的 +{@link android.widget.SimpleCursorAdapter} 物件,並將該物件設定為 {@link android.widget.ListView} 的配接器: + + +</p> +<pre class="prettyprint"> +// Defines a list of columns to retrieve from the Cursor and load into an output row +String[] mWordListColumns = +{ + UserDictionary.Words.WORD, // Contract class constant containing the word column name + UserDictionary.Words.LOCALE // Contract class constant containing the locale column name +}; + +// Defines a list of View IDs that will receive the Cursor columns for each row +int[] mWordListItems = { R.id.dictWord, R.id.locale}; + +// Creates a new SimpleCursorAdapter +mCursorAdapter = new SimpleCursorAdapter( + getApplicationContext(), // The application's Context object + R.layout.wordlistrow, // A layout in XML for one row in the ListView + mCursor, // The result from the query + mWordListColumns, // A string array of column names in the cursor + mWordListItems, // An integer array of view IDs in the row layout + 0); // Flags (usually none are needed) + +// Sets the adapter for the ListView +mWordList.setAdapter(mCursorAdapter); +</pre> +<p class="note"> + <strong>注意:</strong>如要返回內含 {@link android.database.Cursor} 的{@link android.widget.ListView},您必須為游標加入名為 <code>_ID</code> 的資料欄。 + + 因為這樣,上述查詢會擷取「字詞」表格的 <code>_ID</code> 欄,即使 {@link android.widget.ListView} 未顯示該資料欄亦然。 + + 這項限制也是為何大多數供應程式會針對本身的所有表格設定 <code>_ID</code> 欄的原因。 + +</p> + + <!-- Getting data from query results --> +<h3 id="GettingResults">從查詢結果取得資料</h3> +<p> + 除了單純顯示查詢結果以外,您還可以將它們用於其他工作。例如,您可以從使用者字典擷取拼字,然後在其他供應程式中查閱這些拼字。 + +如要這麼做,請逐一查看 {@link android.database.Cursor} 中的資料列: +</p> +<pre class="prettyprint"> + +// Determine the column index of the column named "word" +int index = mCursor.getColumnIndex(UserDictionary.Words.WORD); + +/* + * Only executes if the cursor is valid. The User Dictionary Provider returns null if + * an internal error occurs. Other providers may throw an Exception instead of returning null. + */ + +if (mCursor != null) { + /* + * Moves to the next row in the cursor. Before the first movement in the cursor, the + * "row pointer" is -1, and if you try to retrieve data at that position you will get an + * exception. + */ + while (mCursor.moveToNext()) { + + // Gets the value from the column. + newWord = mCursor.getString(index); + + // Insert code here to process the retrieved word. + + ... + + // end of while loop + } +} else { + + // Insert code here to report an error if the cursor is null or the provider threw an exception. +} +</pre> +<p> + {@link android.database.Cursor} 實作方法包含數個用於從物件擷取不同資料類型的「get」方法。 +例如,上方程式碼片段使用了 {@link android.database.Cursor#getString getString()}。 +此外,這種實作方法還包括 +{@link android.database.Cursor#getType getType()} 方法,可傳回指定資料欄資料類型的值。 + +</p> + + + <!-- Requesting permissions --> +<h2 id="Permissions">內容供應程式權限</h2> +<p> + 供應程式的應用程式可指定其他應用程式在存取供應程式的資料時所需的權限。 +這些權限可確保使用者瞭解應用程式嘗試存取的資料為何。 +根據供應程式需求,其他應用程式必須取得相關必要權限才能存取供應程式。 +使用者可在安裝應用程式時得知這些必要的權限。 + +</p> +<p> + 如果供應程式的應用程式未指定任何權限,則其他應用程式就無法存取供應程式的資料。 +不過,供應程式的應用程式元件一律會具備完整的讀取及寫入存取權,無論供應程式指定的權限為何。 + +</p> +<p> + 如上所述,使用者字典供應程式要求其他應用程式需取得 <code>android.permission.READ_USER_DICTIONARY</code> 權限,才能擷取其中的資料。 + + 而該供應程式還個別針對插入、更新或刪除資料用途,指定了不同的 <code>android.permission.WRITE_USER_DICTIONARY</code> 權限。 + +</p> +<p> + 如要取得存取供應程式時所需的權限,應用程式可利用 <code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 元素在本身的宣示說明檔案中要求相關權限。 + +當 Android 套件管理員安裝應用程式時,使用者必須授予該應用程式要求的所有權限。 +使用者授予所有權限後,套件管理員才能繼續進行安裝作業;如果使用者不授予權限,則套件管理員就會取消安裝。 + + +</p> +<p> + 以下的 +<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> + 元素會要求使用者字典供應程式的讀取存取權: +</p> +<pre> + <uses-permission android:name="android.permission.READ_USER_DICTIONARY"> +</pre> +<p> + 如要進一步瞭解權限對供應程式存取的影響,請參閱<a href="{@docRoot}guide/topics/security/security.html">安全性和權限</a>指南。 + +</p> + + +<!-- Inserting, Updating, and Deleting Data --> +<h2 id="Modifications">插入、更新及刪除資料</h2> +<p> + 透過從供應程式擷取資料的相同方式,您也可以利用供應程式用戶端與供應程式的 {@link android.content.ContentProvider} 之間的互動來修改資料。 + + 如要這麼做,請呼叫其中引數已傳送到相對應 {@link android.content.ContentProvider} 方法的 {@link android.content.ContentResolver} 方法。 +供應程式和供應程式用戶端會自動處理安全性和處理程序間通訊。 + +</p> +<h3 id="Inserting">插入資料</h3> +<p> + 如要在供應程式中插入資料,請呼叫 +{@link android.content.ContentResolver#insert ContentResolver.insert()} 方法。 +這個方法會在供應程式中插入新的資料列,並傳回該列的內容 URI。 + 以下程式碼片段示範如何在使用者字典供應程式中插入新的字詞: +</p> +<pre class="prettyprint"> +// Defines a new Uri object that receives the result of the insertion +Uri mNewUri; + +... + +// Defines an object to contain the new values to insert +ContentValues mNewValues = new ContentValues(); + +/* + * Sets the values of each column and inserts the word. The arguments to the "put" + * method are "column name" and "value" + */ +mNewValues.put(UserDictionary.Words.APP_ID, "example.user"); +mNewValues.put(UserDictionary.Words.LOCALE, "en_US"); +mNewValues.put(UserDictionary.Words.WORD, "insert"); +mNewValues.put(UserDictionary.Words.FREQUENCY, "100"); + +mNewUri = getContentResolver().insert( + UserDictionary.Word.CONTENT_URI, // the user dictionary content URI + mNewValues // the values to insert +); +</pre> +<p> + 新資料列的資料會傳入單一 {@link android.content.ContentValues} 物件,該物件的格式與單列游標類似。 +您不必為這個物件中的資料欄指定相同的資料類型,而且如果您不想指定任何值,可以將資料欄設定為 <code>null</code> 以使用 {@link android.content.ContentValues#putNull ContentValues.putNull()}。 + + +</p> +<p> + 該程式碼片段並不會新增 <code>_ID</code> 欄,這是因為系統自動會維護該欄。 +供應程式會將不重複值 <code>_ID</code> 指派給新增的所有資料列。 +供應程式通常會採用該值做為表格的主索引鍵。 +</p> +<p> + <code>newUri</code> 以下方格式傳回的內容 URI 可用於識別新增的資料列: + +</p> +<pre> +content://user_dictionary/words/<id_value> +</pre> +<p> + <code><id_value></code> 是 ID 為 <code>_ID</code> 的資料列內容。 + 大多數供應程式可自動偵測這種格式的內容 URI,並據以針對指定的資料列執行特定作業。 + +</p> +<p> + 如要從傳回的 {@link android.net.Uri} 取得 <code>_ID</code> 的值,請呼叫 +{@link android.content.ContentUris#parseId ContentUris.parseId()}。 +</p> +<h3 id="Updating">更新資料</h3> +<p> + 如要更新資料列,請使用內含經過更新的值 (與您在插入資料時所使用的值相同) 以及選取條件 (與您在建立查詢時所使用的選取條件相同) 的 {@link android.content.ContentValues} 物件。 + + 您所使用的用戶端方法為 +{@link android.content.ContentResolver#update ContentResolver.update()}。您只需針對要更新的資料欄,將相關值加到 {@link android.content.ContentValues} 物件即可。 +如果您想清除資料欄的內容,請將值設定為 <code>null</code>。 + +</p> +<p> + 以下程式碼片段會針對地區代碼含有「en」字詞的所有資料列,將其地區代碼變更為 <code>null</code>。 +系統會傳回已更新的資料列數量: +</p> +<pre> +// Defines an object to contain the updated values +ContentValues mUpdateValues = new ContentValues(); + +// Defines selection criteria for the rows you want to update +String mSelectionClause = UserDictionary.Words.LOCALE + "LIKE ?"; +String[] mSelectionArgs = {"en_%"}; + +// Defines a variable to contain the number of updated rows +int mRowsUpdated = 0; + +... + +/* + * Sets the updated value and updates the selected words. + */ +mUpdateValues.putNull(UserDictionary.Words.LOCALE); + +mRowsUpdated = getContentResolver().update( + UserDictionary.Words.CONTENT_URI, // the user dictionary content URI + mUpdateValues // the columns to update + mSelectionClause // the column to select on + mSelectionArgs // the value to compare to +); +</pre> +<p> + 建議您在呼叫 {@link android.content.ContentResolver#update ContentResolver.update()} 時對使用者輸入進行檢測。 +如需詳細資訊,請參閱<a href="#Injection">防範惡意輸入</a>。 + +</p> +<h3 id="Deleting">刪除資料</h3> +<p> + 刪除資料列的方法與擷取資料列資料類似:您必須為想刪除的資料列指定選取條件,用戶端方法最後會傳回已刪除的資料列數量。 + + 以下程式碼片段可刪除應用程式 ID 為「user」的資料列。此外,這個方法還會傳回已刪除的資料列數量。 + +</p> +<pre> + +// Defines selection criteria for the rows you want to delete +String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?"; +String[] mSelectionArgs = {"user"}; + +// Defines a variable to contain the number of rows deleted +int mRowsDeleted = 0; + +... + +// Deletes the words that match the selection criteria +mRowsDeleted = getContentResolver().delete( + UserDictionary.Words.CONTENT_URI, // the user dictionary content URI + mSelectionClause // the column to select on + mSelectionArgs // the value to compare to +); +</pre> +<p> + 建議您在呼叫 {@link android.content.ContentResolver#delete ContentResolver.delete()} 時對使用者輸入進行檢測。 +如需詳細資訊,請參閱<a href="#Injection">防範惡意輸入</a>。 + +</p> +<!-- Provider Data Types --> +<h2 id="DataTypes">供應程式資料類型</h2> +<p> + 內容供應程式可提供多種資料類型。使用者字典供應程式只能提供文字,但供應程式還可提供下列格式: + +</p> + <ul> + <li> + 整數 + </li> + <li> + 長整數 (long) + </li> + <li> + 浮點數 + </li> + <li> + 長浮點數 (double) + </li> + </ul> +<p> + 供應程式慣用的其他資料類型為二進位大型物件 (BLOB),這種資料會實作成 64 KB 位元組陣列。 +如果想瞭解可用的資料類型,請查閱 {@link android.database.Cursor} 類別的「get」方法。 + +</p> +<p> + 供應程式的說明文件通常會列出其中每個資料欄的資料類型。 + 使用者字典供應程式的資料類型列在其合約類別 {@link android.provider.UserDictionary.Words} 的參考文件 (如需合約類別的相關資訊,請參閱<a href="#ContractClasses">合約類別</a>一節) 中。 + + + 您也可以呼叫 {@link android.database.Cursor#getType +Cursor.getType()} 來確認可用的資料類型。 +</p> +<p> + 此外,供應程式也會保留任何所定義內容 URI 的 MIME 資料類型資訊。您可以利用 MIME 類型資訊確認您的應用程式是否可處理供應程式所提供的資料,或是根據 MIME 類型選擇處理方式類型。 + +當您使用內含複雜的資料結構或檔案的供應程式時,通常需要使用 MIME 類型。 + +例如,聯絡人供應程式中的 {@link android.provider.ContactsContract.Data} 表格會使用 MIME 類型為每個資料列中儲存的聯絡人資料加上標籤。 + +如要取得與內容 URI 相對應的 MIME 類型,請呼叫 +{@link android.content.ContentResolver#getType ContentResolver.getType()}。 +</p> +<p> + 如要瞭解標準與自訂 MIME 類型的語法,請參閱 <a href="#MIMETypeReference">MIME 類型參考資料</a>。 + +</p> + + +<!-- Alternative Forms of Provider Access --> +<h2 id="AltForms">供應程式存取權的替代形式</h2> +<p> + 開發應用程式時會使用到 3 種供應程式存取權的替代形式: +</p> +<ul> + <li> + <a href="#Batch">批次存取</a>:您可以使用 {@link android.content.ContentProviderOperation} 類別中的方法建立批次存取權呼叫,然後利用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 套用這些呼叫。 + + + </li> + <li> + 非同步查詢:建議您在個別執行緒中進行查詢。您可以使用 {@link android.content.CursorLoader} 類別來完成這項作業。 +如需相關示範說明,請參閱<a href="{@docRoot}guide/components/loaders.html">載入器</a>指南中的範例。 + + + </li> + <li> + <a href="#Intents">透過意圖存取資料</a>:您無法直接將意圖傳送到供應程式,但可以將意圖傳送到供應程式的應用程式;供應程式的應用程式通常是修改供應程式資料的最佳環境。 + + + </li> +</ul> +<p> + 如需透過意圖進行批次存取和修改作業的相關資訊,請參閱下文。 +</p> +<h3 id="Batch">批次存取</h3> +<p> + 您可利用供應程式的批次存取功能插入大量資料列、透過相同方法呼叫在多個表格中插入資料列,或是透過單次交易 (微型作業) 在處理程序之間執行一組作業。 + + +</p> +<p> + 如要以「批次模式」存取供應程式,請建立一系列 {@link android.content.ContentProviderOperation} 物件,然後使用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 將這些物件分派給內容供應程式。 + + +請將內容供應程式的「授權」<em></em>(而不是特定內容 URI) 傳入這個方法。這樣可讓陣列中的所有 {@link android.content.ContentProviderOperation} 物件能夠在不同表格中運作。 + + +如果您呼叫 {@link android.content.ContentResolver#applyBatch +ContentResolver.applyBatch()},則系統會傳回一系列結果。 +</p> +<p> + {@link android.provider.ContactsContract.RawContacts} 合約類別的說明包含可展示批次插入作業的程式碼片段。 +<a href="{@docRoot}resources/samples/ContactManager/index.html">聯絡人管理員</a>範例應用程式的 <code>ContactAdder.java</code> 來源檔案包含批次存取範例供您參考。 + + + +</p> +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>使用協助程式顯示資料</h2> +<p> + 如果您的應用程式「沒有」<em></em>存取權限,而您仍想使用意圖在其他應用程式中顯示資料。 +例如,日曆應用程式接受用於顯示特定日期或活動的 {@link android.content.Intent#ACTION_VIEW} 意圖。 + + 這樣可讓您顯示日曆資訊,而不必自行建立使用者介面。如要進一步瞭解此功能,請參閱<a href="{@docRoot}guide/topics/providers/calendar-provider.html">日曆供應程式</a>指南。 + + +</p> +<p> + 您傳送意圖的目標應用程式未必要與供應程式建立關聯。 +例如,您可以從聯絡人供應程式擷取聯絡人資料,然後將內含聯絡人圖片內容 URI 的 {@link android.content.Intent#ACTION_VIEW} 意圖傳送到圖片檢視器。 + + +</p> +</div> +</div> +<h3 id="Intents">透過意圖存取資料</h3> +<p> + 意圖可提供內容供應程式的間接存取權。即使您的應用程式沒有存取權限,您仍可透過以下方式允許使用者存取供應程式的資料:從具備權限的應用程式取回結果意圖,或是啟用具備權限的應用程式並允許使用者存取該應用程式。 + + + +</p> +<h4>透過臨時權限取得存取權</h4> +<p> + 即使沒有適當的存取權限,您仍可存取內容供應程式的資料,方法是傳送意圖到沒有權限的應用程式,然後接收內含「URI」權限的結果意圖。 + + + URI 權限是特定內容 URI 專用的權限;在接收權限的 Activity 結束之前,這類權限會維持有效狀態。 +具備永久權限的應用程式可授予臨時權限,只要在結果意圖中設定旗標即可: + +</p> +<ul> + <li> + <strong>讀取權限:</strong> + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} + </li> + <li> + <strong>寫入權限:</strong> + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} + </li> +</ul> +<p class="note"> + <strong>注意:</strong>這些旗標並不會將讀取或寫入存取權授予系統在內容 URI 中提供授權的供應程式。存取權僅供 URI 使用。 + +</p> +<p> + 供應程式會使用 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> + 元素的 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn">android:grantUriPermission</a></code> + 屬性以及 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> + 元素的 +<code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"><grant-uri-permission></a></code> + 子元素在本身的宣示說明中為內容 URI 定義 URI 權限。如要進一步瞭解 URI 權限的運作機制,請參閱<a href="{@docRoot}guide/topics/security/security.html">安全性和權限</a>指南的「URI 權限」。 + + +</p> +<p> + 例如,即使您沒有 {@link android.Manifest.permission#READ_CONTACTS} 權限,您仍可透過聯絡人供應程式擷取聯絡人資料。 +您可能會想透過可用來在聯絡人的生日當天傳送電子賀卡給對方的應用程式進行這項動作。 +您偏好讓使用者控管要讓您的應用程式使用的聯絡人資料,而不是要求 {@link android.Manifest.permission#READ_CONTACTS} 授權您存取使用者的所有聯絡人以及個人資訊。 + + +為了達到這個目的,您進行了下列程序: +</p> +<ol> + <li> + 您的應用程式使用 {@link android.app.Activity#startActivityForResult +startActivityForResult()} 方法,傳送了內含 +{@link android.content.Intent#ACTION_PICK} 動作的意圖以及「聯絡人」MIME 類型 +{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}。 + + </li> + <li> + 由於該意圖符合聯絡人應用程式的「選取」Activity 的意圖篩選器,因此該 Activity 會在移動到前景。 + + </li> + <li> + 在選取 Activity 中,使用者選取了要更新的聯絡人。 +一旦使用者進行這項動作,選取 Activity 便會呼叫 +{@link android.app.Activity#setResult setResult(resultcode, intent)} 來設定要傳回您應用程式的意圖。 +該意圖包含使用者所選聯絡人的內容 URI,以及「額外」的旗標 +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}。 +這些旗標可將 URI 權限授予您的應用程式,以便其讀取內容 URI 指向的聯絡人資料。選取 Activity 隨後會呼叫 {@link android.app.Activity#finish()} 來傳回您應用程式的控制權。 + + + + </li> + <li> + 您的 Activity 返回前景,而系統呼叫您 Activity 的 +{@link android.app.Activity#onActivityResult onActivityResult()} 方法。 +這個方法可接收聯絡人應用程式中的選取 Activity 所建立的結果意圖。 + + </li> + <li> + 即使您未在宣示說明中要求供應程式的永久讀取權限,只要利用結果意圖的內容 URI 即可從聯絡人供應程式讀取聯絡人資料。 + +您之後可以取得聯絡人的出生日期資訊或聯絡人的電子郵件地址,以便傳送電子賀卡給對方。 + + </li> +</ol> +<h4>使用其他應用程式</h4> +<p> + 允許使用者修改您無法存取的資料的簡單方式,是啟用具備相關權限的應用程式,並且讓使用者透過該應用程式進行修改作業。 + +</p> +<p> + 例如,日曆應用程式接受可讓您啟用應用程式插入 UI 的 +{@link android.content.Intent#ACTION_INSERT} 意圖。您可以在該意圖中傳入「額外」的資料,供應用程式用於預先填入使用者介面。由於週期性活動的語法較為複雜,因此建議您利用 {@link android.content.Intent#ACTION_INSERT} 啟用日曆應用程式,然後讓使用者透過該應用程式將活動插入日曆供應程式。 + + + + +</p> +<!-- Contract Classes --> +<h2 id="ContractClasses">合約類別</h2> +<p> + 合約類別可定義協助應用程式使用內容 URI、欄名稱、意圖動作,以及內容供應程式的其他功能的常數。 +合約類別並不會自動納入供應程式;供應程式的開發人員必須自行定義合約類別,然後將其提供給其他開發人員。 + +Android 平台內建的大多數供應程式都可在 {@link android.provider} 套件中取得對應的合約類別。 + +</p> +<p> + 例如,使用者字典供應程式有一個內含內容 URI 和欄名稱常數的 {@link android.provider.UserDictionary} 合約類別。 +「字詞」表格的內容 URI 是在 +{@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI} 常數中定義。 + + 此外,{@link android.provider.UserDictionary.Words} 類別也包含欄名稱常數,可用於本指南中的程式碼片段範例。 +例如,您可以將查詢投影定義成如下所示: + +</p> +<pre> +String[] mProjection = +{ + UserDictionary.Words._ID, + UserDictionary.Words.WORD, + UserDictionary.Words.LOCALE +}; +</pre> +<p> + 聯絡人供應程式的另一個合約類別為 {@link android.provider.ContactsContract}。 + 此類別的參考文件附有程式碼片段範例。其中一個 +{@link android.provider.ContactsContract.Intents.Insert} 子類別為內含意圖常數和意圖資料的合約類別。 + +</p> + + +<!-- MIME Type Reference --> +<h2 id="MIMETypeReference">MIME 類型參考資料</h2> +<p> + 內容供應程式可傳回標準 MIME 媒體類型或自訂媒體類型字串,或是以上兩者。 +</p> +<p> + 以下是 MIME 類型的格式 +</p> +<pre> +<em>type</em>/<em>subtype</em> +</pre> +<p> + 例如,常見的 MIME 類型 <code>text/html</code> 包含 <code>text</code> 類型以及 <code>html</code> 子類型。 +如果供應程式傳回這種 URI 類型,代表採用該 URI 的查詢會傳回內含 HTML 標記的文字。 + +</p> +<p> + 自訂 MIME 類型字串 (亦稱為「廠商專用」MIME 類型) 包含較為複雜的類型<em></em>和子類型<em></em>值。 +針對多個資料欄,類型值<em></em>一律會如下所示 +</p> +<pre> +vnd.android.cursor.<strong>dir</strong> +</pre> +<p> + 或者,針對單一資料欄,類型值一律會如下所示 +</p> +<pre> +vnd.android.cursor.<strong>item</strong> +</pre> +<p> + 。 +</p> +<p> + 子類型<em></em>為供應程式專用。Android 內建的供應程式通常包含簡易的子類型。 +例如,當聯絡人應用程式建立電話號碼資料列時,會為該列設定下列 MIME 類型: + +</p> +<pre> +vnd.android.cursor.item/phone_v2 +</pre> +<p> + 請注意,子類型值為 <code>phone_v2</code>。 +</p> +<p> + 其他的供應程式開發人員可能會根據供應程式的授權和表格名稱,自行建立專屬的子類型模式。 +例如,考慮一個包含火車時刻表的供應程式。 + 供應程式的授權為 <code>com.example.trains</code> 且包括 Line1、Line2 和 Line3 表格。 +根據 Line1 表格的內容 URI +</p> +<p> +<pre> +content://com.example.trains/Line1 +</pre> +<p> + 供應程式會傳回 MIME 類型 +</p> +<pre> +vnd.android.cursor.<strong>dir</strong>/vnd.example.line1 +</pre> +<p> + 根據 Line1 表格的內容 URI +</p> +<pre> +content://com.example.trains/Line2/5 +</pre> +<p> + 供應程式會傳回 MIME 類型 +</p> +<pre> +vnd.android.cursor.<strong>item</strong>/vnd.example.line2 +</pre> +<p> + 大多數內容供應程式會針對其使用的 MIME 類型定義合約類別常數。例如,聯絡人供應程式的合約類別 {@link android.provider.ContactsContract.RawContacts} 會為某個 MIME 類型的原始聯絡人列定義 {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} 常數。 + + + + +</p> +<p> + 如要進一步瞭解個別資料列的內容 URI,請參閱<a href="#ContentURIs">內容 URI</a>。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 000000000000..3d46ee4b4c80 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=建立內容供應程式 +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + + +<h2>本文件內容</h2> +<ol> + <li> + <a href="#DataStorage">設計資料儲存空間</a> + </li> + <li> + <a href="#ContentURI">設計內容 URI</a> + </li> + <li> + <a href="#ContentProvider">實作 ContentProvider 類別</a> + <ol> + <li> + <a href="#RequiredAccess">必要方法</a> + </li> + <li> + <a href="#Query">實作 query() 方法</a> + </li> + <li> + <a href="#Insert">實作 insert() 方法</a> + </li> + <li> + <a href="#Delete">實作 delete() 方法</a> + </li> + <li> + <a href="#Update">實作 update() 方法</a> + </li> + <li> + <a href="#OnCreate">實作 onCreate() 方法</a> + </li> + </ol> + </li> + <li> + <a href="#MIMETypes">實作內容供應程式 MIME 類型</a> + <ol> + <li> + <a href="#TableMIMETypes">表格 MIME 類型</a> + </li> + <li> + <a href="#FileMIMETypes">檔案 MIME 類型</a> + </li> + </ol> + </li> + <li> + <a href="#ContractClass">實作合約類別</a> + </li> + <li> + <a href="#Permissions">實作內容供應程式權限</a> + </li> + <li> + <a href="#ProviderElement"><provider> 元素</a> + </li> + <li> + <a href="#Intents">意圖和資料存取權</a> + </li> +</ol> +<h2>重要類別</h2> + <ol> + <li> + {@link android.content.ContentProvider} + </li> + <li> + {@link android.database.Cursor} + </li> + <li> + {@link android.net.Uri} + </li> + </ol> +<h2>相關範例</h2> + <ol> + <li> + <a href="{@docRoot}resources/samples/NotePad/index.html"> Note Pad 範例應用程式 + </a> + + </li> + </ol> +<h2>另請參閱</h2> + <ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 內容供應程式基本存放庫概念</a> + + </li> + <li> + <a href="{@docRoot}guide/topics/providers/calendar-provider.html"> 日曆供應程式</a> + + </li> + </ol> +</div> +</div> + + +<p> + 內容供應程式可管理中央資料存放庫的存取權。您可以將供應程式實作成 Android 應用程式的一或多個類別,以及宣示說明檔案的元素。 + +您的其中一個類別會實作子類別 +{@link android.content.ContentProvider} (供應程式與其他應用程式之間的介面)。 +雖然內容供應程式的用途是將資料提供給其他應用程式,不過您也可以在應用程式中加入 Activity,讓使用者查詢及修改供應程式所管理的資料。 + + +</p> +<p> + 本主題的其餘部分為一份說明建置內容供應程式的步驟清單,以及一份可供您使用的 API 清單。 + +</p> + + +<!-- Before You Start Building --> +<h2 id="BeforeYouStart">建置供應程式的前置作業</h2> +<p> + 請先完成下列事項,再開始建置供應程式: +</p> +<ol> + <li> + <strong>評估建置內容供應程式的必要性</strong>。如果您想提供下列功能,您就必須建置內容供應程式: + + <ul> + <li>您想將複雜的資料或檔案提供給其他應用程式。</li> + <li>您想讓使用者從您的應用程式將複雜的資料複製到其他應用程式。</li> + <li>您想使用搜尋架構提供自訂搜尋建議。</li> + </ul> + <p> + 如果您的應用程式內建所有您想提供的功能,您就「不需要」<em></em>建置供應程式來使用 SQLite 資料庫。 + + </p> + </li> + <li> + 詳閱<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">內容供應程式基本概念</a>進一步瞭解供應程式 (如果您尚未閱讀該文章的話)。 + + + </li> +</ol> +<p> + 完成上述事項後,請按照下列步驟建置供應程式: +</p> +<ol> + <li> + 為您的資料設計原始儲存空間。內容供應程式會以兩種方式提供資料: + <dl> + <dt> + 檔案資料 + </dt> + <dd> + 通常儲存在檔案中的資料,例如相片、音訊或影片。 +這種檔案會儲存在您應用程式的私人空間中。 +您的供應程式可針對此檔案提供處理支援,藉此回應其他應用程式發出的檔案要求。 + + </dd> + <dt> + 「結構化」資料 + </dt> + <dd> + 通常儲存在資料庫、陣列或類似結構中的資料。 + 這種資料會採用與內含資料列和資料欄的表格相容的格式儲存。資料列代表一個實體,例如某個人或庫存中的商品。 +而資料欄則代表實體的部分資料,例如某個人的名稱或商品的售價。 +這種資料類型一般會儲存在 SQLite 資料庫,但您也可以將它儲存在其他類型的永久儲存空間。 + +如要進一步瞭解 Android 系統提供的儲存空間類型,請參閱<a href="#DataStorage">設計資料儲存空間</a>。 + + + </dd> + </dl> + </li> + <li> + 定義 {@link android.content.ContentProvider} 類別及其所需方法的實作方式。 +這個類別是您的資料與 Android 系統其餘部分之間的介面。 +如要進一步瞭解這個類別,請參閱<a href="#ContentProvider">實作 ContentProvider 類別</a>。 + + </li> + <li> + 定義供應程式的授權字串、內容 URI 和資料列名稱。如果您希望供應程式的應用程式能控制意圖,請同時定義意圖動作、額外資料和旗標。 + +此外,您還需要針對想存取您資料的應用程式,定義其所需的權限。 +建議您將這些值定義為個別合約類別中的常數;您之後可將這個類別提供給其他開發人員。 +如要進一步瞭解內容 URI,請參閱<a href="#ContentURI">設計內容 URI</a>。 + + + 如要進一步瞭解意圖,請參閱<a href="#Intents">意圖和資料存取權</a>。 + + </li> + <li> + 視需要進行其他動作,例如新增範例資料,或實作 {@link android.content.AbstractThreadedSyncAdapter} 讓應程式與雲端資料之間的資料保持同步。 + + + </li> +</ol> + + +<!-- Designing Data Storage --> +<h2 id="DataStorage">設計資料儲存空間</h2> +<p> + 內容供應程式是模組化格式資料的介面。建議這個介面之前,請先決定您儲存資料的方式。 +您可用任何偏好格式儲存資料,然後設計對應介面來讀取及寫入所需資料。 + +</p> +<p> + 以下是 Android 提供的部分資料儲存技術: +</p> +<ul> + <li> + Android 系統內含 SQLite 資料庫 API,可讓 Android 本身的供應程式用於儲存以表格為基礎的資料。 + +{@link android.database.sqlite.SQLiteOpenHelper} 類別可協助您建立資料庫,而 {@link android.database.sqlite.SQLiteDatabase} 類別則是用於存取資料庫的基礎類別。 + + + <p> + 請記住,您不需要使用資料庫實作存放庫。供應程式會以一組表格的形式對外顯示 (類似於相關的資料庫),不過這並非在內部實作供應程式的必要條件。 + + + </p> + </li> + <li> + 如果您想儲存檔案資料,請使用 Android 提供的多種適用於檔案的 API。 + 如要進一步瞭解檔案儲存空間,請參閱<a href="{@docRoot}guide/topics/data/data-storage.html">資料儲存空間</a>。 +如果您想設計可提供媒體相關資料 (例如音樂或影片) 的供應程式,建議您建立結合表格資料與檔案的供應程式。 + + + </li> + <li> + 如果您想存取以網路為基準的資料,請使用 {@link java.net} 和 +{@link android.net} 中的類別。您也可以將以網路為基準的資料同步到本機資料儲存空間 (例如資料庫),然後以表格或檔案的形式提供資料。 + + <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html">範例同步配接器</a>應用程式範例示範了如何進行這種同步處理作業。 + + </li> +</ul> +<h3 id="DataDesign"> + 資料設計注意事項 +</h3> +<p> + 以下提供一些有關設計供應程式資料結構的祕訣: +</p> +<ul> + <li> + 表格資料一律需包含「主索引鍵」欄,方便供應程式保存每個資料列的數值。 +您可以使用這些值將資料列連結至其他表格中的相關資料列 (也就是將這些值當作「外部索引鍵」使用)。 +事實上,您也可以使用此資料欄的任何名稱進行連結,但使用 {@link android.provider.BaseColumns#_ID BaseColumns._ID} 是最佳做法,這是因為將供應程式的查詢結果連結至 +{@link android.widget.ListView} 時需要將某個擷取出的資料列命名為 +<code>_ID</code>。 + + + </li> + <li> + 如果您想提供點陣圖或檔案資料的一大部分,請將資料儲存在檔案中,然後以間接方式提供該檔案,而不要直接將該檔案儲存在表格中。 + +如果您採用這種做法,請務必通知供應程式的使用者採用 +{@link android.content.ContentResolver} 檔案方法來存取資料。 + </li> + <li> + 使用二進位大型物件 (BLOB) 資料類型儲存大小或結構不同的資料。 +例如,您可以使用 BLOB 欄儲存<a href="http://code.google.com/p/protobuf">通訊協定緩衝區</a> 或 <a href="http://www.json.org">JSON 結構</a>。 + + + <p> + 此外,您也可以使用 BLOB 實作「按結構定義分門別類」<em></em>的表格。在這種表格中,您需要定義主索引鍵欄、MIME 類型欄和一或多個 BLOB 一般資料欄。 + +MIME 類型欄中的值會決定 BLOB 欄的資料定義, +這可讓您在同一表格中儲存多種資料列類型。 +聯絡人供應程式的「資料」表格 +{@link android.provider.ContactsContract.Data} 即為按結構定義分門別類的表格範例。 + + </p> + </li> +</ul> +<!-- Designing Content URIs --> +<h2 id="ContentURI">設計內容 URI</h2> +<p> + <strong>內容 URI</strong> 是指用於識別供應程式資料的 URI,其中包括整個供應程式的符號名稱 (亦即供應程式的<strong>授權</strong>),以及指向表格或檔案的名稱 (亦即<strong>路徑</strong>)。 + +選用的 ID 部分則會指向表格中的個別資料欄。 + +{@link android.content.ContentProvider} 的每個資料存取方法均包括一個內容 URI (引數);該內容 URI 可讓您決定要存取哪個表格、資料列或檔案。 + +</p> +<p> + 如需內容 URI 的基本概念,請參閱<a href="{@docRoot}guide/topics/providers/content-provider-basics.html">內容供應程式基本概念</a>。 + + +</p> +<h3>設計授權</h3> +<p> + 供應程式通常會包含一個授權,可當做供應程式 Android 內部名稱。為了避免與其他供應程式發生衝突,請務必反向使用網際網路網域擁有權作為供應程式授權的基礎。 + +此外,也請針對 Android 套件名稱採取此建議做法;您可以將供應程式授權定義為內含供應程式的套件名稱的副檔名。 + +例如,假設您 Android 套件的名稱為 + <code>com.example.<appname></code>,則請將供應程式的授權定義為 +<code>com.example.<appname>.provider</code>。 +</p> +<h3>設計路徑結構</h3> +<p> + 開發人員通常只要附加指向個別表格的路徑,即可從授權建立內容 URI。 +例如,假設您有「table1」<em></em>和「table2」<em></em>這兩個表格,則您可以結合上述範例中的授權來產生內容 URI +<code>com.example.<appname>.provider/table1</code> 和 +<code>com.example.<appname>.provider/table2</code>。 + +路徑並不侷限於單一區隔,而您也不必為每個路徑層級產生表格。 + +</p> +<h3>處理內容 URI ID</h3> +<p> + 一般來說,供應程式可利用 URI 尾端資料列的 ID 值接受內容 URI,藉此提供某個資料列的存取權。此外,供應程式通常也可比對 ID 值與表格的 <code>_ID</code> 欄,然後對相符的資料列執行要求的存取動作。 + + + +</p> +<p> + 這種機制可協助採用一般設計模式應用程式存取供應程式,讓應用程式對供應程式執行查詢,並且利用 {@link android.widget.CursorAdapter} 在 {@link android.widget.ListView} 中顯示最終的 {@link android.database.Cursor}。 + + + 定義 {@link android.widget.CursorAdapter} 時需要將 +{@link android.database.Cursor} 的其中一個資料列設為 <code>_ID</code>。 +</p> +<p> + 使用者之後挑選了顯示在 UI 中的某一資料列,以查看或修改資料。 +應用程式會從支援 + {@link android.widget.ListView} 的 {@link android.database.Cursor} 取得對應的資料列、取得該資料列的 <code>_ID</code> 值,然後將該值附加到內容 URI 並向供應程式發出存取要求。 +供應程式之後可對使用者挑選的資料列進行查詢或修改。 + +</p> +<h3>內容 URI 模式</h3> +<p> + 為協助您決定要對傳入內容 URI 採取什麼動作,供應程式 API 提供了簡便類別 {@link android.content.UriMatcher},可將內容 URI「模式」對應至整數值。 + +您可以在 <code>switch</code> 陳述式中使用整數值,決定要對符合特定模式的內容 URI 採取的動作。 + +</p> +<p> + 內容 URI 模式會比對採用萬用字元的內容 URI: +</p> + <ul> + <li> + <strong><code>*</code>:</strong>比對由任何有效字元組成的字串;長度不限。 + </li> + <li> + <strong><code>#</code>:</strong>比對由數字組成的字串;長度不限。 + </li> + </ul> +<p> + 以設計及編寫內容 URI 處理方式為例,假設供應程式內含 <code>com.example.app.provider</code> 授權,可識別下列指向表格的內容 URI: + + +</p> +<ul> + <li> + <code>content://com.example.app.provider/table1</code>:名稱為 <code>table1</code> 的表格。 + </li> + <li> + <code>content://com.example.app.provider/table2/dataset1</code>:名稱為 +<code>dataset1</code> 的表格。 + </li> + <li> + <code>content://com.example.app.provider/table2/dataset2</code>:名稱為 +<code>dataset2</code> 的表格。 + </li> + <li> + <code>content://com.example.app.provider/table3</code>:名稱為 <code>table3</code> 的表格。 + </li> +</ul> +<p> + 此外,供應程式也會識別附有資料列 ID 的內容 URI,例如 <code>content://com.example.app.provider/table3/1</code> 會指定 <code>table3</code> 中 ID 為 + <code>1</code> 的資料列。 + +</p> +<p> + 以下是可能的內容 URI 模式: +</p> +<dl> + <dt> + <code>content://com.example.app.provider/*</code> + </dt> + <dd> + 與供應程式的任何內容 URI 相符。 + </dd> + <dt> + <code>content://com.example.app.provider/table2/*</code>: + </dt> + <dd> + 與 <code>dataset1</code> 和 <code>dataset2</code> 表格的內容 URI 相符,但與 <code>table1</code> 或 +<code>table3</code> 的內容 URI 不符。 + + </dd> + <dt> + <code>content://com.example.app.provider/table3/#</code>:比對 <code>table3</code> 中單一資料列的內容 URI,例如 + <code>content://com.example.app.provider/table3/6</code> 會比對其中的 + <code>6</code> 所指定的資料列。 + + </dt> +</dl> +<p> + 以下程式碼片段說明各種方法在 {@link android.content.UriMatcher} 中的運作方式。 + 這個程式碼會以不同方式處理整個表格的 URI 以及單一資料列的 URI;針對表格使用內容 URI 模式 +<code>content://<authority>/<path></code>,針對單一資料列則使用 +<code>content://<authority>/<path>/<id></code>。 + +</p> +<p> + {@link android.content.UriMatcher#addURI(String, String, int) addURI()} 方法會將授權和 +路徑對應至某個整數值,而 {@link android.content.UriMatcher#match(Uri) + match()} 方法會傳回 URI 的整數值。<code>switch</code> 陳述式則會要查詢整個表格,還是查詢單一記錄: + +</p> +<pre class="prettyprint"> +public class ExampleProvider extends ContentProvider { +... + // Creates a UriMatcher object. + private static final UriMatcher sUriMatcher; +... + /* + * The calls to addURI() go here, for all of the content URI patterns that the provider + * should recognize. For this snippet, only the calls for table 3 are shown. + */ +... + /* + * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used + * in the path + */ + sUriMatcher.addURI("com.example.app.provider", "table3", 1); + + /* + * Sets the code for a single row to 2. In this case, the "#" wildcard is + * used. "content://com.example.app.provider/table3/3" matches, but + * "content://com.example.app.provider/table3 doesn't. + */ + sUriMatcher.addURI("com.example.app.provider", "table3/#", 2); +... + // Implements ContentProvider.query() + public Cursor query( + Uri uri, + String[] projection, + String selection, + String[] selectionArgs, + String sortOrder) { +... + /* + * Choose the table to query and a sort order based on the code returned for the incoming + * URI. Here, too, only the statements for table 3 are shown. + */ + switch (sUriMatcher.match(uri)) { + + + // If the incoming URI was for all of table3 + case 1: + + if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; + break; + + // If the incoming URI was for a single row + case 2: + + /* + * Because this URI was for a single row, the _ID value part is + * present. Get the last path segment from the URI; this is the _ID value. + * Then, append the value to the WHERE clause for the query + */ + selection = selection + "_ID = " uri.getLastPathSegment(); + break; + + default: + ... + // If the URI is not recognized, you should do some error handling here. + } + // call the code to actually do the query + } +</pre> +<p> + 另一個 {@link android.content.ContentUris} 類別可提供使用內容 URI 的 <code>id</code> 部分的簡便方法。 +{@link android.net.Uri} 和 +{@link android.net.Uri.Builder} 類別則可提供剖析現有 +{@link android.net.Uri} 物件及建置新物件的簡便方法。 +</p> + +<!-- Implementing the ContentProvider class --> +<h2 id="ContentProvider">實作 ContentProvider 類別</h2> +<p> + {@link android.content.ContentProvider} 執行個體可透過處理其他應用程式所發出要求的方式,管理一組結構化資料的存取權。 +所有形式的存取權最終都會呼叫 {@link android.content.ContentResolver},而這個類別隨後會呼叫 {@link android.content.ContentProvider} 方法來取得存取權。 + + +</p> +<h3 id="RequiredAccess">必要方法</h3> +<p> + 抽象類別 {@link android.content.ContentProvider} 會定義 6 種方法,而您必須將這些方法實作成您所擁有子類別的一部分。 +嘗試存取您內容供應程式的用戶端應用程式會呼叫以下所有方法 +({@link android.content.ContentProvider#onCreate() onCreate()} 除外): + +</p> +<dl> + <dt> + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + query()} + </dt> + <dd> + 從您的供應程式擷取檔案。您可以使用引數來選取要查詢的表格、要傳回的資料列和資料欄,以及最終結果的排序順序。 + + 以 {@link android.database.Cursor} 物件的形式傳回資料。 + </dd> + <dt> + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} + </dt> + <dd> + 在供應程式中插入新的資料列。您可以使用引數來選取目標表格,以及取得要使用的資料欄值。 +此外,這個方法還可傳回新插入資料欄的內容 URI。 + + </dd> + <dt> + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} + </dt> + <dd> + 更新供應程式中的現有資料欄。您可以使用引數來選取要更新的表格和資料列,以及取得更新過後的資料欄值。 +此外,這個方法還可傳回經過更新的資料列數量。 + </dd> + <dt> + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} + </dt> + <dd> + 從您的供應程式中刪除資料列。您可以使用引數來選取要刪除的表格和資料列。 +此外,這個方法還可傳回遭刪除的資料列數量。 + </dd> + <dt> + {@link android.content.ContentProvider#getType(Uri) getType()} + </dt> + <dd> + 傳回與內容 URI 相對應的 MIME 類型。如要進一步瞭解這個方法,請參閱<a href="#MIMETypes">實作內容供應程式 MIME 類型</a>。 + + </dd> + <dt> + {@link android.content.ContentProvider#onCreate() onCreate()} + </dt> + <dd> + 初始化您的供應程式。Android 系統會在建立供應程式後立即呼叫這個方法。 +請注意,一旦 + {@link android.content.ContentResolver} 嘗試存取您的供應程式,Android 系統便會建立供應程式。 + </dd> +</dl> +<p> + 請注意,上述方法採用的簽名與同名的 +{@link android.content.ContentResolver} 方法相同。 +</p> +<p> + 實作這些方法時請注意下列事項: +</p> +<ul> + <li> + 多重執行緒可能會同時呼叫 {@link android.content.ContentProvider#onCreate() onCreate()} 以外的所有方法,因此請務必確保這些方法不會對執行緒造成負面影響。 +如要進一步瞭解多重執行緒,請參閱<a href="{@docRoot}guide/components/processes-and-threads.html">處理程序和執行緒</a>。 + + + + </li> + <li> + 避免透過 {@link android.content.ContentProvider#onCreate() + onCreate()} 進行需大量時間才可完成的作業。除非有實質上的需求,否則請延遲初始化工作。 + 如需相關資訊,請參閱<a href="#OnCreate">實作 onCreate() 方法</a>。 + + </li> + <li> + 雖然您必須實作這些方法,但您的程式碼只需傳回預期的資料類型即可,不必傳回任何其他資料。 +例如,您可能想避免其他應用程式將資料插入部分表格。 +在這種情況下,您可以略過呼叫 +{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 並傳回 0。 + + </li> +</ul> +<h3 id="Query">實作 query() 方法</h3> +<p> + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 方法如果未傳回 {@link android.database.Cursor} 物件,便會發生錯誤而擲回 {@link java.lang.Exception}。 + +如果您採用 SQLite 資料庫做為資料儲存空間,您可以直接傳回 +{@link android.database.sqlite.SQLiteDatabase} 類別的任一 <code>query()</code> 方法所傳回的 {@link android.database.Cursor}。 + + 如果查詢不符任何資料列,您就必須傳回其中的 {@link android.database.Cursor#getCount()} 方法傳回 0 的 + {@link android.database.Cursor} 執行個體。 + 請注意,只有在查詢程序發生內部錯誤時,您才需要傳回 <code>null</code>。 +</p> +<p> + 如果您並非採用 SQLite 資料庫做為資料儲存空間,請使用 + {@link android.database.Cursor} 的子類別。例如,{@link android.database.MatrixCursor} 類別 +會一系列 {@link java.lang.Object} 的所有資料列中實作游標。您可以搭配這個類別使用 {@link android.database.MatrixCursor#addRow(Object[]) addRow()} 來新增資料列。 + +</p> +<p> + 請記住,您必須確保 Android 系統能夠與 {@link java.lang.Exception} 通訊,而不會受到處理程序的限制。 +Android 可以針對下列例外狀況執行這項動作,藉此協助解決查詢錯誤: + +</p> +<ul> + <li> + {@link java.lang.IllegalArgumentException} (如果供應程式接收的內容 URI 無效,您可以選擇擲回這個類別) + + </li> + <li> + {@link java.lang.NullPointerException} + </li> +</ul> +<h3 id="Insert">實作 insert() 方法</h3> +<p> + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 方法會利用 {@link android.content.ContentValues} 引數中的值,在適當的表格中新增資料列。 + +如果 {@link android.content.ContentValues} 引數中沒有資料欄名稱,建議您在供應程式的程式碼或資料庫的結構定義中,提供預設的資料欄名稱。 + + +</p> +<p> + 這個方法會傳回新資料列的內容 URI。如要建構這個方法,請使用 {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()},將新資料列的 <code>_ID</code> (或其他主索引鍵) 值附加到表格的內容 URI。 + + +</p> +<h3 id="Delete">實作 delete() 方法</h3> +<p> + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 方法並不會從您的資料儲存空間中刪除資料列。 +如果您搭配供應程式使用同步配接器,建議您為已刪除的資料列加上「已刪除」標示,而不是徹底移除資料列。 + +同步配接器可檢查已刪除的資料列,並且將這些資料列從伺服器中移除,然後再從供應程式中將它們刪除。 + +</p> +<h3 id="Update">實作 update() 方法</h3> +<p> + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} 方法會採用 {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 所使用的相同 {@link android.content.ContentValues} 引數,以及 + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 和 {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 所使用的相同 <code>selection</code> 與 <code>selectionArgs</code> 引數, + + +以便讓您針對這些方法重複使用相同的程式碼。 +</p> +<h3 id="OnCreate">實作 onCreate() 方法</h3> +<p> + Android 系統會在啟動供應程式時呼叫 {@link android.content.ContentProvider#onCreate() + onCreate()}。建議您只使用這個方法執行可快速完成的初始化工作,並且等到供應程式實際收到資料要求後,再建立資料庫以及載入資料。 + +如果您使用 {@link android.content.ContentProvider#onCreate() onCreate()} 進行需大量時間才能完成工作,啟動供應程式所需的時間就會延長。 + +此外,這樣也會延遲供應程式回應其他應用程式的時間。 + +</p> +<p> + 例如,如果您採用 SQLite 資料庫,您可以透過 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 建立新的 {@link android.database.sqlite.SQLiteOpenHelper} 物件,然後在初次開啟資料庫時建立 SQL 表格。 + +為了加快這個程序,當您初次呼叫 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase +getWritableDatabase()} 時,該方法會自動呼叫 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()} 方法。 + + +</p> +<p> + 以下兩個程式碼片段展示了 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 與 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} 之間的互動過程。 +而第一個程式碼片段是用於實作 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}: +</p> +<pre class="prettyprint"> +public class ExampleProvider extends ContentProvider + + /* + * Defines a handle to the database helper object. The MainDatabaseHelper class is defined + * in a following snippet. + */ + private MainDatabaseHelper mOpenHelper; + + // Defines the database name + private static final String DBNAME = "mydb"; + + // Holds the database object + private SQLiteDatabase db; + + public boolean onCreate() { + + /* + * Creates a new helper object. This method always returns quickly. + * Notice that the database itself isn't created or opened + * until SQLiteOpenHelper.getWritableDatabase is called + */ + mOpenHelper = new MainDatabaseHelper( + getContext(), // the application context + DBNAME, // the name of the database) + null, // uses the default SQLite cursor + 1 // the version number + ); + + return true; + } + + ... + + // Implements the provider's insert method + public Cursor insert(Uri uri, ContentValues values) { + // Insert code here to determine which table to open, handle error-checking, and so forth + + ... + + /* + * Gets a writeable database. This will trigger its creation if it doesn't already exist. + * + */ + db = mOpenHelper.getWritableDatabase(); + } +} +</pre> +<p> + 第二個程式碼片段則是用於實作 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()},包括協助程式類別: + +</p> +<pre class="prettyprint"> +... +// A string that defines the SQL statement for creating a table +private static final String SQL_CREATE_MAIN = "CREATE TABLE " + + "main " + // Table's name + "(" + // The columns in the table + " _ID INTEGER PRIMARY KEY, " + + " WORD TEXT" + " FREQUENCY INTEGER " + + " LOCALE TEXT )"; +... +/** + * Helper class that actually creates and manages the provider's underlying data repository. + */ +protected static final class MainDatabaseHelper extends SQLiteOpenHelper { + + /* + * Instantiates an open helper for the provider's SQLite data repository + * Do not do database creation and upgrade here. + */ + MainDatabaseHelper(Context context) { + super(context, DBNAME, null, 1); + } + + /* + * Creates the data repository. This is called when the provider attempts to open the + * repository and SQLite reports that it doesn't exist. + */ + public void onCreate(SQLiteDatabase db) { + + // Creates the main table + db.execSQL(SQL_CREATE_MAIN); + } +} +</pre> + + +<!-- Implementing ContentProvider MIME Types --> +<h2 id="MIMETypes">實作 ContentProvider MIME 類型</h2> +<p> + {@link android.content.ContentProvider} 類別包含兩種用於傳回 MIME 類型的方法: +</p> +<dl> + <dt> + {@link android.content.ContentProvider#getType(Uri) getType()} + </dt> + <dd> + 您必須針對任何供應程式實作的其中一個必要方法。 + </dd> + <dt> + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} + </dt> + <dd> + 如果您的供應程式會提供檔案,您就需要實作這個方法。 + </dd> +</dl> +<h3 id="TableMIMETypes">表格 MIME 類型</h3> +<p> + {@link android.content.ContentProvider#getType(Uri) getType()} 方法會採用 MIME 格式傳回 + {@link java.lang.String},以說明 URI 引數所傳回的資料類型。 +{@link android.net.Uri} 可以是一個模式 (而非特定 URI);在這種情況下,建議您傳回與符合模式的內容 URI 相關聯的資料類型。 + + +</p> +<p> + 針對文字、HTML、JPEG 等常見資料類型, +{@link android.content.ContentProvider#getType(Uri) getType()} 會傳回該資料的標準 MIME 類型。 +如需這些標準模式的完整清單,請造訪 + <a href="http://www.iana.org/assignments/media-types/index.htm">IANA MIME 媒體類型</a>網站。 + +</p> +<p> + 針對指向表格資料的資料列的內容 URI, +{@link android.content.ContentProvider#getType(Uri) getType()} 會採用 Android 廠商專用的 MINE 格式傳回 MIME 類型: + +</p> +<ul> + <li> + 類型部分:<code>vnd</code> + </li> + <li> + 子類型部分: + <ul> + <li> + 如果 URI 模式適用於單一資料列:<code>android.cursor.<strong>item</strong>/</code> + </li> + <li> + 如果 URI 模式適用於一個以上的資料列:<code>android.cursor.<strong>dir</strong>/</code> + </li> + </ul> + </li> + <li> + 供應程式專用部分:<code>vnd.<name></code>.<code><type></code> + <p> + 您需要提供 <code><name></code> 和 <code><type></code>。 + <code><name></code> 必須是全域唯一值,而 <code><type></code> 必須為相對應 URI 模式的專屬值。 + +建議您使用貴公司的名稱或您應用程式的部分 Android 套件名稱做為 <code><name></code>。 +針對 +<code><type></code>,則建議您使用可識別與 URI 相關的表格的字串。 + + </p> + + </li> +</ul> +<p> + 例如,假設供應程式的授權為 +<code>com.example.app.provider</code>,而該授權可提供 +<code>table1</code> 這個表格,則 <code>table1</code> 中多個資料列的 MIME 類型會如下所示: +</p> +<pre> +vnd.android.cursor.<strong>dir</strong>/vnd.com.example.provider.table1 +</pre> +<p> + 而 <code>table1</code> 中單一資料列的 MIME 類型則會如下所示: +</p> +<pre> +vnd.android.cursor.<strong>item</strong>/vnd.com.example.provider.table1 +</pre> +<h3 id="FileMIMETypes">檔案 MIME 類型</h3> +<p> + 如果您的供應程式會提供檔案,您就需要實作 +{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}。 + 該方法會針對您的供應程式可為特定 URI 傳回的檔案,傳回一系列 MIME 類型的 {@link java.lang.String}。建議您按 MIME 類型篩選器引數篩選您提供的 MIME 類型,方便您只傳回用戶端想處理的 MIME 類型。 + + +</p> +<p> + 例如,假設您的供應程式會採用 <code>.jpg</code>、 +<code>.png</code> 和 <code>.gif</code> 檔案格式提供相片。 + 當有應用程式使用篩選器字串 <code>image/*</code> (類型為「圖片」的任何檔案) 呼叫 {@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()} 時,{@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} 方法就會傳回如下所示的陣列: + + +</p> +<pre> +{ "image/jpeg", "image/png", "image/gif"} +</pre> +<p> + 如果應用程式只想取得 <code>.jpg</code> 檔案,則可以使用篩選器字串 <code>*\/jpeg</code> 呼叫 {@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()},此時 {@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} 會傳回如下所示的陣列: + + +<pre> +{"image/jpeg"} +</pre> +<p> + 如果您的供應程式並未提供任何篩選器字串所要求的 MIME 類型,則 + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} 會傳回 <code>null</code>。 + +</p> + + +<!-- Implementing a Contract Class --> +<h2 id="ContractClass">實作合約類別</h2> +<p> + 合約類別是 <code>public final</code> 類別,內含以下項目的固定不變定義:URI、欄名稱、MIME 類型以及供應程式擁有的其他中繼資料。 +這個類別會在供應程式與其他應用程式之間建立合約,藉此確保使用者在 URI、欄名等值有變更的情況下,仍可正常存取供應程式。 + + + +</p> +<p> + 此外,由於合約類別針對其常數採用了好記的名稱,因此可協助開發人員避免使用錯誤的欄名或 URI 值。 +與其他類別相同,合約類別也包含 Javadoc 說明文件。 +如 Eclipse 這類整合式開發環境可利用合約類別自動填入常數名稱,並針對常數顯示 Javadoc。 + + +</p> +<p> + 開發人員無法存取您應用程式中合約類別的類別檔案,但可以靜態方式從您提供的 <code>.jar</code> 檔案中將類別檔案編入其應用程式。 + +</p> +<p> + {@link android.provider.ContactsContract} 類別及其巢狀類別均為合約類別範例。 + +</p> +<h2 id="Permissions">實作內容供應程式權限</h2> +<p> + 如需 Android 系統提供的所有權限和存取權的詳細資訊,請參閱<a href="{@docRoot}guide/topics/security/security.html">安全性和權限</a>。 + + 您也可以參閱<a href="{@docRoot}guide/topics/data/data-storage.html">資料儲存空間</a>,瞭解各種儲存空間實際提供的安全性和權限。 + + 簡言之,請注意下列重點: +</p> +<ul> + <li> + 在預設情況下,儲存在裝置內部儲存空間的資料檔案只有您的應用程式和供應程式可存取。 + + </li> + <li> + 您所建立的 {@link android.database.sqlite.SQLiteDatabase} 資料庫只有您的應用程式和供應程式可存取。 + + </li> + <li> + 在預設情況下,您儲存到外部儲存空間的資料檔案為「公開分享」<em></em>,任何人均可讀取<em></em>。 +您無法使用內容供應程式針對外部儲存空間中的檔案限制存取權,這是因為其他應用程式可使用其他 API 呼叫讀取及寫入這類檔案。 + + </li> + <li> + 呼叫用於開啟/建立檔案,或是用於在裝置內部儲存空間中開啟/建立 SQLite 儲存庫的方法,可能會同時將讀取及寫入存取權授予其他所有應用程式。 +如果您採用內部檔案或資料庫做為供應程式的存放庫,並且將「開放讀取」或「開放寫入」存取權授予該存放庫,則您在供應程式的宣示說明中設定的權限無助於保護您的資料安全。 + + +內部儲存空間中的檔案和儲存庫的預設存取權為「不公開」;請勿針對供應程式的存放庫變更此存取權。 + + </li> +</ul> +<p> + 如果您想使用內容供應程式權限控制資料的存取權,建議您將資料儲存在內部檔案、SQLite 資料庫或「雲端」(例如遠端伺服器),並確保只有您的應用程式可以存取這些檔案和資料庫。 + + +</p> +<h3>實作權限</h3> +<p> + 在預設情況下,您的供應程式不會設定任何權限,因此即使底層資料的存取權限為「不公開」,任何應用程式都可透過您的供應程式讀取及寫入資料。 +如果想改變這種情況,請使用屬性或 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素的子元素,在供應程式的宣示說明檔案中設定權限。 + +您可以選擇讓設定好的權限套用至整個供應程式、特定表格或記錄,或是套用至以上三者。 + +</p> +<p> + 您可以使用一或多項 <code><a href="{@docRoot}guide/topics/manifest/permission-element.html"> + <permission></a></code> 元素,在供應程式的宣示說明檔案中定義權限。 +為了將權限設為僅適用於您的供應程式,請針對 + <code><a href="{@docRoot}guide/topics/manifest/permission-element.html#nm"> + android:name</a></code> 屬性使用 Java 式範圍。 +例如,請將讀取權限命名為 +<code>com.example.app.provider.permission.READ_PROVIDER</code>。 + +</p> +<p> + 以下列出供應程式權限範圍的詳細說明;這份清單將從套用至整個供應程式的權限開始說明,接著逐一說明套用範圍較小的權限。 + + 套用範圍較小權限的優先等級會比套用範圍較大的權限來得高: +</p> +<dl> + <dt> + 供應程式層級的單一讀取寫入權限 + </dt> + <dd> + 這項權限是由 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素的 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> + android:permission</a></code> 屬性所指定,可控制整個供應程式的讀取及寫入存取權。 + + </dd> + <dt> + 供應程式層級的個別讀取及寫入權限 + </dt> + <dd> + 整個供應程式的讀取權限及寫入權限。您可以使用 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素的 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> + android:readPermission</a></code> 和 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> + android:writePermission</a></code> 屬性指定這兩項權限。 +這些權限的優先等級比 +<code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> + android:permission</a></code> 所需的權限來得高。 + </dd> + <dt> + 路徑層級權限 + </dt> + <dd> + 供應程式內容 URI 的讀取、寫入或讀取/寫入權限。您可以使用 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素的 + <code><a href="{@docRoot}guide/topics/manifest/path-permission-element.html"> + <path-permission></a></code> 子元素指定您想控制的所有 URI。 +針對您所指定的每個內容 URI,您可以指定讀取/寫入權限、讀取權限或寫入權限,或是以上三種權限。 +讀取及寫入權限的優先等級比讀取/寫入權限來得高。 +此外,路徑層級權限的優先等級比供應程式層級權限來得高。 + + </dd> + <dt> + 臨時權限 + </dt> + <dd> + 這種權限層級可將臨時存取權授予某個應用程式,即使該應用程式不具備一般的必要權限。 +臨時存取功能可減少應用程式的宣示說明所需的權限數量。 + +在啟用臨時權限的情況下,只有會繼續存取您資料的應用程式,需要供應程式的「永久」權限。 + + + <p> + 如果想允許外部的圖片檢視器應用程式顯示您供應程式中的相片附加檔案,請將實作電子郵件供應程式和應用程式時所需的權限納入考量。 + +如要授予圖片檢視器必要的存取權,而不發出權限要求,請設定相片內容 URI 的臨時權限。 +並且將您的電子郵件應用程式設計成在使用者想顯示相片時,將內含相片內容 URI 和權限旗標的意圖傳送給圖片檢視器。 + +讓圖片檢視器在檢視器沒有供應程式的一般讀取權限的情況下,仍可查詢電子郵件供應程式來擷取相片。 + + + </p> + <p> + 如要啟用臨時權限,請設定 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素的 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> + android:grantUriPermissions</a></code> 元素,或是在 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素中新增一或多項 + <code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> + <grant-uri-permission></a></code> 子元素。如果您有使用臨時權限,您就必須在從供應程式中移除內容 URI 支援,以及將內容 URI 與臨時權限建立關聯時呼叫 {@link android.content.Context#revokeUriPermission(Uri, int) +Context.revokeUriPermission()}。 + + + </p> + <p> + 屬性的值會決定供應程式的可存取部分。 + 如果將屬性設為 <code>true</code>,則系統會將臨時權限授予整個供應程式,從而覆寫供應程式層級或路徑層級權限所需的任何其他權限。 + + + </p> + <p> + 如果將這個旗標設為 <code>false</code>,您就必須在 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素中加入 + <code><a href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> + <grant-uri-permission></a></code> 子元素。每項子元素都會指定要授予臨時存取權的內容 URI。 + + </p> + <p> + 如要將臨時存取權委派給某款應用程式,您就必須在意圖中加入 +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 或 +{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 旗標,或是同時加入以上兩者。請使用 +{@link android.content.Intent#setFlags(int) setFlags()} 方法設定這些旗標。 + </p> + <p> + 如果系統未顯示 <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> + android:grantUriPermissions</a></code> 屬性,代表該屬性是設為 + <code>false</code>。 + </p> + </dd> +</dl> + + + +<!-- The Provider Element --> +<h2 id="ProviderElement"><provider> 元素</h2> +<p> + 與 {@link android.app.Activity} 和 {@link android.app.Service} 元件相同,您必須使用 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素在應用程式的宣示說明檔案中定義 {@link android.content.ContentProvider} 子類別:Android 系統會從元素取得下列資訊: + + + +<dl> + <dt> + 授權 + (<a href="{@docRoot}guide/topics/manifest/provider-element.html#auth">{@code + android:authorities}</a>) + </dt> + <dd> + 用於識別系統內整個供應程式的符號名稱。如要進一步瞭解這項屬性,請參閱<a href="#ContentURI">設定內容 URI</a>。 + + + </dd> + <dt> + 供應程式類別名稱 + (<code> +<a href="{@docRoot}guide/topics/manifest/provider-element.html#nm">android:name</a> + </code>) + </dt> + <dd> + 實作 {@link android.content.ContentProvider} 的類別。如要進一步瞭解這個類別,請參閱<a href="#ContentProvider">實作 ContentProvider </a>。 + + + </dd> + <dt> + 權限 + </dt> + <dd> + 這些屬性可指定其他應用程式在存取供應程式的資料時所需的權限: + + <ul> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> + android:grantUriPermssions</a></code>:臨時的權限旗標。 + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn"> + android:permission</a></code>:供應程式範圍的單一讀取/寫入權限。 + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#rprmsn"> + android:readPermission</a></code>:供應程式範圍的讀取權限。 + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#wprmsn"> + android:writePermission</a></code>:供應程式範圍的寫入權限。 + </li> + </ul> + <p> + 如要進一步瞭解權限及相對應的屬性,請參閱<a href="#Permissions">實作內容供應程式權限</a>。 + + + </p> + </dd> + <dt> + 啟動及控制屬性 + </dt> + <dd> + 這些屬性可決定 Android 系統啟動供應程式的方式和時間、供應程式的處理程序特性,以及其他執行階段設定: + + <ul> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#enabled"> + android:enabled</a></code>:可讓系統啟動供應程式的旗標。 + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#exported"> + android:exported</a></code>:可讓其他應用程式使用這個供應程式的旗標。 + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#init"> + android:initOrder</a></code>:這個供應程式的啟動順序 (相對於相同處理程序中的其他供應程式)。 + + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#multi"> + android:multiProcess</a></code>:可讓系統將相同處理程序中的供應程式啟動成呼叫用戶端的旗標。 + + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#proc"> + android:process</a></code>:執行供應程式的處理程序名稱。 + + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#sync"> + android:syncable</a></code>:指示系統將供應程式的資料與伺服器的資料保持同步的旗標。 + + </li> + </ul> + <p> + 如需上述屬性的完整說明,請參閱開發人員指南的 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素。 + + </p> + </dd> + <dt> + 資訊屬性 + </dt> + <dd> + 選用的供應程式圖示和標籤: + <ul> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#icon"> + android:icon</a></code>:內含供應程式圖示的可繪資源。 + 這個圖示會顯示在應用程式清單 ([設定]<em></em> > [應用程式]<em></em> > [全部]<em></em>) 中,供應程式標籤的旁邊。 + + </li> + <li> + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html#label"> + android:label</a></code>:附有供應程式或其資料或以上兩者的說明的資訊標籤。 +這個標籤會顯示在應用程式清單 ([設定]<em></em> > [應用程式]<em></em> > [全部]<em></em>) 中。 + + </li> + </ul> + <p> + 如需上述屬性的完整說明,請參閱開發人員指南的 + <code><a href="{@docRoot}guide/topics/manifest/provider-element.html"> + <provider></a></code> 元素。 + </p> + </dd> +</dl> + +<!-- Intent Access --> +<h2 id="Intents">意圖和資料存取權</h2> +<p> + 應用程式可透過 {@link android.content.Intent} 以間接方式存取內容供應程式。 + 利用這種存取方式的應用程式不會呼叫 {@link android.content.ContentResolver} 或 +{@link android.content.ContentProvider} 的任何方法,而是會傳送可啟動 Activity (此 Activity 通常屬於供應程式本身的應用程式) 的意圖。 +目標 Activity 會負責擷取資料並在本身的 UI 中顯示該資料。視意圖中的動作而定,目標 Activity 也可能會提示使用者修改供應程式的資料。 + + + 此外,意圖還可能會包含目標 Activity 顯示在 UI 中的「額外」資料;使用者之後可選擇是否要先變更這些資料,然後再將其用於修改供應程式的資料。 + + +</p> +<p> + +</p> +<p> + 您可以使用意圖存取權來確保資料的完整性。您的供應程式可能會將根據詳細定義的業務邏輯插入、更新及刪除的資料做為運作依據。 +如果是這樣,允許其他應用程式直接修改您的資料可能會導致資料失效。 + +如果想讓開發人員使用意圖存取權,請務必保留相關的完整記錄。 + 並且向他們說明為何使用您應用程式 UI 的意圖存取權,比嘗試使用自己的程式碼修改資料來得好。 + +</p> +<p> + 處理想修改供應程式資料的傳入意圖的方式與處理其他意圖完全相同。 +如要進一步瞭解如何使用意圖,請參閱<a href="{@docRoot}guide/components/intents-filters.html">意圖和意圖篩選器</a>。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd new file mode 100644 index 000000000000..7f7fa34b9c0c --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=內容供應程式 +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + + +<!-- In this document --> +<h2>本文主題</h2> +<ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> 內容供應程式基本概念</a> + + </li> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-creating.html"> 建立內容供應程式</a> + + </li> + <li> + <a href="{@docRoot}guide/topics/providers/calendar-provider.html">日曆供應程式</a> + </li> + <li> + <a href="{@docRoot}guide/topics/providers/contacts-provider.html">聯絡人供應程式</a> + </li> +</ol> + + <!-- Related Samples --> +<h2>相關範例</h2> + <ol> + <li> + <a href="{@docRoot}resources/samples/ContactManager/index.html"> 聯絡人管理員</a>應用程式 + + </li> + <li> + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List2.html"> 「游標 (使用者)」 </a> + + + </li> + <li> + <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/List7.html"> 「游標 (電話)」</a> + + </li> + <li> + <a href="{@docRoot}resources/samples/SampleSyncAdapter/index.html"> 範例同步配接器</a> + + </li> + </ol> +</div> +</div> +<p> + 內容供應程式可管理一組結構化資料的存取權、壓縮資料以及提供用於定義資料安全性的機制。 +內容供應程式是一種標準介面,可將某個處理程序中的資料與另一個處理程序中執行的程式碼建立連結。 + +</p> +<p> + 如果想透過內容供應程式存取資料,請使用您應用程式的 {@link android.content.Context} 中的{@link android.content.ContentResolver} 物件,以用戶端的身分與供應程式通訊。 + + + {@link android.content.ContentResolver} 物件會與供應程式物件 (實作 {@link android.content.ContentProvider} 的類別執行個體) 通訊。 +而供應程式物件則會接收用戶端發出的資料要求、執行要求的動作,以及傳回最終結果。 + + +</p> +<p> + 如果您打算與其他應用程式分享您的資料,您不必自行開發供應程式。 +不過,您必須為自己的應用程式準備專屬供應程式,以提供自訂搜尋建議。 +此外,如果您想從自己的應用程式複製複雜的資料或檔案並貼到其他應用程式,也需要準備專屬的供應程式。 + +</p> +<p> + Android 隨附內容供應程式,可用於管理音訊、影片、圖片、個人聯絡資訊等資料。 +如果想查看 Android 隨附的部分內容供應程式,請參閱 <code><a href="{@docRoot}reference/android/provider/package-summary.html">android.provider</a> + </code> 套件的參考文件。 + +這些供應程式可透過任何 Android 應用程式存取,只不過使用上有些許限制。 + +</p><p> + 如果想進一步瞭解內容供應程式,請參閱下列主題: +</p> +<dl> + <dt> + <strong><a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> + 內容供應程式基本概念</a></strong> + </dt> + <dd> + 如何在資料已整理成表格的情況下,透過內容供應程式存取資料。 + </dd> + <dt> + <strong><a href="{@docRoot}guide/topics/providers/content-provider-creating.html"> + 建立內容供應程式</a></strong> + </dt> + <dd> + 如何自行建立內容供應程式。 + </dd> + <dt> + <strong><a href="{@docRoot}guide/topics/providers/calendar-provider.html"> + 日曆供應程式</a></strong> + </dt> + <dd> + 如何存取屬於 Android 平台一部分的日曆供應程式。 + </dd> + <dt> + <strong><a href="{@docRoot}guide/topics/providers/contacts-provider.html"> + 聯絡人供應程式</a></strong> + </dt> + <dd> + 如何存取屬於 Android 平台一部分的聯絡人供應程式。 + </dd> +</dl> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd new file mode 100644 index 000000000000..1dc7c46f4387 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=儲存空間存取架構 +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容 +<a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle"> + <span class="more">顯示更多</span> + <span class="less" style="display:none">顯示較少</span></a></h2> +<ol id="toc44" class="hide-nested"> + <li> + <a href="#overview">總覽</a> + </li> + <li> + <a href="#flow">控管流程</a> + </li> + <li> + <a href="#client">編寫用戶端應用程式</a> + <ol> + <li><a href="#search">搜尋文件</a></li> + <li><a href="#process">處理結果</a></li> + <li><a href="#metadata">檢查文件中繼資料</a></li> + <li><a href="#open">開啟文件</a></li> + <li><a href="#create">建立新文件</a></li> + <li><a href="#delete">刪除文件</a></li> + <li><a href="#edit">編輯文件</a></li> + <li><a href="#permissions">保留權限</a></li> + </ol> + </li> + <li><a href="#custom">編寫自訂文件供應程式</a> + <ol> + <li><a href="#manifest">宣示說明</a></li> + <li><a href="#contract">合約</a></li> + <li><a href="#subclass">將 DocumentsProvider 設為子類別</a></li> + <li><a href="#security">安全性</a></li> + </ol> + </li> + +</ol> +<h2>重要類別</h2> +<ol> + <li>{@link android.provider.DocumentsProvider}</li> + <li>{@link android.provider.DocumentsContract}</li> +</ol> + +<h2>相關影片</h2> + +<ol> + <li><a href="http://www.youtube.com/watch?v=zxHVeXbK1P4">DevBytes: +Android 4.4 儲存空間存取架構:供應程式</a></li> + <li><a href="http://www.youtube.com/watch?v=UFj9AEz0DHQ">DevBytes: +Android 4.4 儲存空間存取架構:用戶端</a></li> +</ol> + + +<h2>程式碼範例</h2> + +<ol> + <li><a href="{@docRoot}samples/StorageProvider/index.html">儲存空間供應程式</a> +</li> + <li><a href="{@docRoot}samples/StorageClient/index.html">StorageClient</a> +</li> +</ol> + +<h2>另請參閱</h2> +<ol> + <li> + <a href="{@docRoot}guide/topics/providers/content-provider-basics.html"> + 內容供應程式基本概念 + </a> + </li> +</ol> + +</div> +</div> + + +<p>Android 4.4 (API 級別 19) 導入了「儲存空間存取架構」(Storage Access Framework (SAF)),SAF 可方便使用者透過偏好的文件儲存空間供應程式開啟文件、圖片等其他檔案。 + +提供簡單易用的標準 UI 可讓使用者在各種應用程式和供應程式中,以相同的方式瀏覽檔案及存取近期開啟的檔案。 +</p> + +<p>雲端或本機儲存服務可實作會封裝服務本身的 +{@link android.provider.DocumentsProvider},藉此加入這個生態系統。您只需編寫幾行程式碼,即可將需要存取供應程式文件的用戶端應用程式與 SAF 整合。 + +</p> + +<p>SAF 內含下列項目:</p> + +<ul> +<li><strong>文件供應程式</strong> — 可讓儲存服務 (例如 Google 雲端硬碟) 顯示所管理檔案的內容供應程式。 +文件供應程式是當作 +{@link android.provider.DocumentsProvider} 類別的子類別使用。文件供應程式結構定義是以傳統檔案階層為依據,不論您為文件供應程式設定的資料儲存方式為何。Android 平台內建數種文件供應程式,例如「下載」、「圖片」和「影片」。 + + + +</li> + +<li><strong>用戶端應用程式</strong> — 可呼叫 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和 (或) +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖及接收文件供應程式所傳回檔案的自訂應用程式。 +</li> + +<li><strong>挑選器</strong> — 可讓使用者透過所有符合用戶端應用程式搜尋條件的文件供應程式存取文件的系統 UI。 +</li> +</ul> + +<p>以下是 SAF 提供的部分功能:</p> +<ul> +<li>可讓使用者透過所有文件供應程式 (而非單一應用程式) 瀏覽內容。</li> +<li>可將文件供應程式所擁有文件的存取權永久授予您的應用程式, +方便使用者透過相關供應程式新增、編輯、儲存及刪除檔案。 +</li> +<li>支援多個使用者帳戶和暫時性的根目錄,例如只在插入電腦時才會顯示的 USB 儲存空間供應程式。 + </li> +</ul> + +<h2 id ="overview">總覽</h2> + +<p>SAF 是以內容供應程式 ({@link android.provider.DocumentsProvider} 類別的子類別) 為基礎。 +「文件供應程式」<em></em>中的資料結構採用如下所示的傳統檔案階層: +</p> +<p><img src="{@docRoot}images/providers/storage_datamodel.png" alt="data model" /></p> +<p class="img-caption"><strong>圖 1.</strong>文件供應程式資料模型。「根目錄」會指向單一「文件」,接著該文件會展開成樹狀結構的分支。請注意下列事項: +</p> + +<p></p> +<ul> + +<li>每個文件供應程式都會回報一或多個「根目錄」(也就是文件樹狀結構的起始點)。每個根目錄都有專屬的 {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID},可導向至代表該根目錄所含內容的某份文件 (某個目錄)。根目錄的設計架構是動態的,能夠支援多重帳戶、暫時性 USB 儲存裝置或使用者登入/登出等使用狀況。 + + + + + +</li> + +<li>每個根目錄都內含一份文件,而該文件會指向 N<em></em> 份文件的 1,每份文件又可指向另外 N<em></em> 份文件的 1。 + </li> + +<li>每個儲存空間後端都會透過唯一的 +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 來參照個別檔案,藉此顯示這些檔案及目錄。文件 ID 不得重複而且一旦核發便不得更改,原因在於裝置重新啟動時會將這些 ID 用於永久 URI 授權。 + + +</li> + + +<li>文件可以是可開啟的檔案 (類型為 MIME) 或內含其他文件的目錄 (類型為 +{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR} MIME )。 +</li> + +<li>每份文件的功能會視 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} 而有所不同,例如 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}、 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE} 和 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}。相同的 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 可以納入多個目錄中。 + + +</li> +</ul> + +<h2 id="flow">控管流程</h2> +<p>如上所述,文件供應程式資料模型是以傳統檔案階層為基礎。 +不過,您可以自己偏好的方式儲存您的資料,只要所儲存資料可透過 {@link android.provider.DocumentsProvider} API 存取即可。例如,您可以將資料存放在標籤式的雲端儲存空間。 + +</p> + +<p>圖 2 是相片應用程式如何使用 SAF 存取已儲存資料的說明範例: +</p> +<p><img src="{@docRoot}images/providers/storage_dataflow.png" alt="app" /></p> + +<p class="img-caption"><strong>圖 2.</strong>儲存空間存取架構</p> + +<p>請注意下列事項:</p> +<ul> + +<li>在 SAF 中,供應程式與用戶端無法直接進行互動。 +用戶端必須取得相關權限才能與檔案進行互動 (也就是讀取、編輯、建立或刪除檔案)。 +</li> + +<li>應用程式 (在此範例中為相片應用程式) 觸發 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 或 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖後,互動程序便會開始。意圖可能包括用於縮小條件範圍的篩選器 — 例如「將所有內含 MIME 類型『圖片』的可開啟檔案提供給我」。 + +</li> + +<li>一旦觸發意圖,系統挑選器就會前往所有已註冊的供應程式,並且向使用者顯示相符的內容根目錄。 +</li> + +<li>即便底層文件供應程式可能不盡相同,挑選器仍會提供使用者可用於存取文件的標準介面。 +例如圖 2 中的 Google 雲端硬碟供應程式、USB 供應程式和雲端供應程式。 +</li> +</ul> + +<p>圖 3 顯示的是使用者搜尋指定 Google 雲端硬碟帳戶中的圖片時所用的挑選器: +</p> + +<p><img src="{@docRoot}images/providers/storage_picker.png" width="340" alt="picker" style="border:2px solid #ddd" /></p> + +<p class="img-caption"><strong>圖 3.</strong>挑選器</p> + +<p>使用者選取 Google 雲端硬碟後,系統就會顯示相關圖片 (如圖 4 所示)。 +此時,使用者即可與這些圖片進行供應程式和用戶端應用程式支援的互動。 + + +<p><img src="{@docRoot}images/providers/storage_photos.png" width="340" alt="picker" style="border:2px solid #ddd" /></p> + +<p class="img-caption"><strong>圖 4.</strong>相關圖片</p> + +<h2 id="client">編寫用戶端應用程式</h2> + +<p>如果您想讓應用程式在搭載 Android 4.3 以下版本的裝置上從其他應用程式擷取檔案,您的應用程式就必須呼叫 {@link android.content.Intent#ACTION_PICK}或 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖。 + +接著,使用者必須選取某款應用程式來選取檔案,而且選定的應用程式必須提供使用者介面,讓使用者瀏覽及挑選可用的檔案。 + + </p> + +<p>針對搭載 Android 4.4 以上版本的裝置,您的應用程式還可以呼叫 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖,以顯示系統所控管的挑選器 UI,方便使用者瀏覽其他應用程式提供的所有檔案。 + +透過這個單一 UI,使用者可以從任何受支援的應用程式挑選檔案。 +</p> + +<p>{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 並不是 {@link android.content.Intent#ACTION_GET_CONTENT} 的替代意圖,實際上應呼叫的意圖取決於您應用程式的需求。 + +</p> + +<ul> +<li>如果您只想讓應用程式讀取/匯入資料,請呼叫 {@link android.content.Intent#ACTION_GET_CONTENT}, +以便應用程式匯入資料 (例如圖片檔) 的複本。 +</li> + +<li>如果您想將文件供應程式所擁有文件的存取權永久授予您的應用程式,請呼叫 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}。 + +例如可讓使用者編輯文件供應程式中所儲存圖片的相片編輯應用程式。 + </li> + +</ul> + + +<p>本節說明如何根據 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖編寫用戶端應用程式。</p> + + +<h3 id="search">搜尋文件</h3> + +<p> +以下程式碼片段採用 {@link android.content.Intent#ACTION_OPEN_DOCUMENT},可搜尋內含圖片檔的文件供應程式: + +</p> + +<pre>private static final int READ_REQUEST_CODE = 42; +... +/** + * Fires an intent to spin up the "file chooser" UI and select an image. + */ +public void performFileSearch() { + + // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file + // browser. + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + // Filter to only show results that can be "opened", such as a + // file (as opposed to a list of contacts or timezones) + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Filter to show only images, using the image MIME data type. + // If one wanted to search for ogg vorbis files, the type would be "audio/ogg". + // To search for all documents available via installed storage providers, + // it would be "*/*". + intent.setType("image/*"); + + startActivityForResult(intent, READ_REQUEST_CODE); +}</pre> + +<p>請注意下列事項:</p> +<ul> +<li>當應用程式觸發 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖時,挑選器便會啟動並顯示所有相符的文件供應程式。 +</li> + +<li>將 {@link android.content.Intent#CATEGORY_OPENABLE} 這個類別加入意圖中可篩選搜尋結果,限定系統只顯示可開啟的文件 (例如圖片檔)。 +</li> + +<li>使用 {@code intent.setType("image/*")} 陳述式可進一步篩選搜尋結果,顯示系統只顯示內含 MIME 類型圖片的文件。 +</li> +</ul> + +<h3 id="results">處理結果</h3> + +<p>使用者在挑選器中選取某份文件後,便會呼叫 +{@link android.app.Activity#onActivityResult onActivityResult()}。指向所選文件的 URI 包含在 {@code resultData} 參數中。 + +請使用 {@link android.content.Intent#getData getData()} 擷取 URI,然後使用該 URI 擷取使用者所需的文件。 +例如: +</p> + +<pre>@Override +public void onActivityResult(int requestCode, int resultCode, + Intent resultData) { + + // The ACTION_OPEN_DOCUMENT intent was sent with the request code + // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the + // response to some other intent, and the code below shouldn't run at all. + + if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + // The document selected by the user won't be returned in the intent. + // Instead, a URI to that document will be contained in the return intent + // provided to this method as a parameter. + // Pull that URI using resultData.getData(). + Uri uri = null; + if (resultData != null) { + uri = resultData.getData(); + Log.i(TAG, "Uri: " + uri.toString()); + showImage(uri); + } + } +} +</pre> + +<h3 id="metadata">檢查文件中繼資料</h3> + +<p>取得文件的 URI 後,您就可以存取該文件的中繼資料。以下程式碼片段會擷取 URI 所指定文件的中繼資料並且加以記錄: +</p> + +<pre>public void dumpImageMetaData(Uri uri) { + + // The query, since it only applies to a single document, will only return + // one row. There's no need to filter, sort, or select fields, since we want + // all fields for one document. + Cursor cursor = getActivity().getContentResolver() + .query(uri, null, null, null, null, null); + + try { + // moveToFirst() returns false if the cursor has 0 rows. Very handy for + // "if there's anything to look at, look at it" conditionals. + if (cursor != null && cursor.moveToFirst()) { + + // Note it's called "Display Name". This is + // provider-specific, and might not necessarily be the file name. + String displayName = cursor.getString( + cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + Log.i(TAG, "Display Name: " + displayName); + + int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE); + // If the size is unknown, the value stored is null. But since an + // int can't be null in Java, the behavior is implementation-specific, + // which is just a fancy term for "unpredictable". So as + // a rule, check if it's null before assigning to an int. This will + // happen often: The storage API allows for remote files, whose + // size might not be locally known. + String size = null; + if (!cursor.isNull(sizeIndex)) { + // Technically the column stores an int, but cursor.getString() + // will do the conversion automatically. + size = cursor.getString(sizeIndex); + } else { + size = "Unknown"; + } + Log.i(TAG, "Size: " + size); + } + } finally { + cursor.close(); + } +} +</pre> + +<h3 id="open-client">開啟文件</h3> + +<p>取得文件的 URI 後,您就可以開啟該文件或是對該文件執行任何所需操作。 +</p> + +<h4>點陣圖</h4> + +<p>以下範例可開啟 {@link android.graphics.Bitmap}:</p> + +<pre>private Bitmap getBitmapFromUri(Uri uri) throws IOException { + ParcelFileDescriptor parcelFileDescriptor = + getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; +} +</pre> + +<p>請注意,請不要針對 UI 執行緒進行這項作業,請在背景中使用 {@link android.os.AsyncTask} 進行。 +開啟點陣圖後,您就可以在 {@link android.widget.ImageView} 中顯示該點陣圖。 + +</p> + +<h4>取得 InputStream</h4> + +<p>以下範例可從 URI 中取得 {@link java.io.InputStream}。在這個程式碼片段中,系統會將每行檔案解讀為單一字串: +</p> + +<pre>private String readTextFromUri(Uri uri) throws IOException { + InputStream inputStream = getContentResolver().openInputStream(uri); + BufferedReader reader = new BufferedReader(new InputStreamReader( + inputStream)); + StringBuilder stringBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } + fileInputStream.close(); + parcelFileDescriptor.close(); + return stringBuilder.toString(); +} +</pre> + +<h3 id="create">建立新文件</h3> + +<p>應用程式可在文件供應程式中使用 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖建立新文件。 +如要建立新檔案,請將 MIME 類型和檔案名稱提供給意圖,然後使用專屬的要求程式碼執行該意圖。 +系統會為您完成其餘的作業:</p> + + +<pre> +// Here are some examples of how you might call this method. +// The first parameter is the MIME type, and the second parameter is the name +// of the file you are creating: +// +// createFile("text/plain", "foobar.txt"); +// createFile("image/png", "mypicture.png"); + +// Unique request code. +private static final int WRITE_REQUEST_CODE = 43; +... +private void createFile(String mimeType, String fileName) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + + // Filter to only show results that can be "opened", such as + // a file (as opposed to a list of contacts or timezones). + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Create a file with the requested MIME type. + intent.setType(mimeType); + intent.putExtra(Intent.EXTRA_TITLE, fileName); + startActivityForResult(intent, WRITE_REQUEST_CODE); +} +</pre> + +<p>建立新文件後,您可在 +{@link android.app.Activity#onActivityResult onActivityResult()} 中取得該文件的 URI, +以便繼續在其中編寫程式碼。</p> + +<h3 id="delete">刪除文件</h3> + +<p>如果您已取得文件的 URI,而且文件的 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} 含有 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},您就可以刪除該文件。 + +例如:</p> + +<pre> +DocumentsContract.deleteDocument(getContentResolver(), uri); +</pre> + +<h3 id="edit">編輯文件</h3> + +<p>您可以使用 SAF 即時編輯文字文件。以下程式碼片段會觸發 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖並使用 {@link android.content.Intent#CATEGORY_OPENABLE} 類別限制系統只顯示可開啟的文件。 + + + +此外,這個程式碼還會進一步篩選搜尋結果,讓系統只顯示文字檔:</p> + +<pre> +private static final int EDIT_REQUEST_CODE = 44; +/** + * Open a file for writing and append some text to it. + */ + private void editDocument() { + // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's + // file browser. + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + // Filter to only show results that can be "opened", such as a + // file (as opposed to a list of contacts or timezones). + intent.addCategory(Intent.CATEGORY_OPENABLE); + + // Filter to show only text files. + intent.setType("text/plain"); + + startActivityForResult(intent, EDIT_REQUEST_CODE); +} +</pre> + +<p>接著,您可以利用 {@link android.app.Activity#onActivityResult onActivityResult()} (詳情請參閱<a href="#results">處理結果</a>) 呼叫程式碼執行編輯動作。以下程式碼片段會利用 {@link android.content.ContentResolver} 取得 {@link java.io.FileOutputStream}。 + + +在預設情況下,這個程式碼片段會使用「寫入」模式。這種方法可索取最少量的所需存取權,因此如果您只需要寫入存取權,請勿要求讀取/寫入: + +</p> + +<pre>private void alterDocument(Uri uri) { + try { + ParcelFileDescriptor pfd = getActivity().getContentResolver(). + openFileDescriptor(uri, "w"); + FileOutputStream fileOutputStream = + new FileOutputStream(pfd.getFileDescriptor()); + fileOutputStream.write(("Overwritten by MyCloud at " + + System.currentTimeMillis() + "\n").getBytes()); + // Let the document provider know you're done by closing the stream. + fileOutputStream.close(); + pfd.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } +}</pre> + +<h3 id="permissions">保留權限</h3> + +<p>應用程式開啟要讀取或寫入的檔案後,系統會將該檔案的 URI 權限授予您的應用程式。 +除非使用者重新啟動裝置,否則這項權限會持續保持有效狀態。不過,假如您的應用程式為圖片編輯應用程式,而您希望使用者可直接透過您的應用程式存取他們最近編輯的 5 張圖片。如果使用者重新啟動的裝置,就您必須將使用者傳回系統挑選器來搜尋所需檔案,而這並非最佳做法。 + + + +</p> + +<p>為了避免這種情況發生,您可以保留系統授予您應用程式的權限。實際上,您的應用程式會「取得」系統授予的永久性 URI 權限。 + +這種權限可讓使用者持續透過您的應用程式存取檔案,即使其裝置重新啟動也無妨: +</p> + + +<pre>final int takeFlags = intent.getFlags() + & (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); +// Check for the freshest data. +getContentResolver().takePersistableUriPermission(uri, takeFlags);</pre> + +<p>除了上述指示外,您還需要完成最後一個步驟。您儲存了您的應用程式最近存取的 URI,但這些 URI 有可能已失效 — 原因在於其他應用程式刪除或修改了文件。 + +因此,建議您一律呼叫 +{@code getContentResolver().takePersistableUriPermission()} 檢查最新資料。 +</p> + +<h2 id="custom">編寫自訂文件供應程式</h2> + +<p> +如果您想開發可提供檔案儲存服務 (例如雲端儲存服務) 的應用程式,可以編寫自訂文件供應程式透過 SAF 提供您的檔案。 + +本節說明如何編寫這類程式。 +</p> + + +<h3 id="manifest">宣示說明</h3> + +<p>如要實作自訂文件供應程式,請將以下項目加入應用程式的宣示說明: +</p> +<ul> + +<li>19 以上的 API 級別目標。</li> + +<li>宣告自訂儲存空間供應程式的 +<code><provider></code> 元素。 </li> + +<li>供應程式的名稱 (也就是供應程式的類別名稱),包括套件名稱。範例:<code>com.example.android.storageprovider.MyCloudProvider</code>。 +</li> + +<li>授權的名稱 (也就是套件的名稱;在此範例中為 +<code>com.example.android.storageprovider</code>) 以及內容供應程式的類型 +(<code>documents</code>)。範例:{@code com.example.android.storageprovider.documents}。</li> + +<li>設為 <code>"true"</code> 的 <code>android:exported</code> 屬性。您必須將供應程式匯出,方便其他應用程式加以偵測。 +</li> + +<li>設為 <code>"true"</code> 的 +<code>android:grantUriPermissions</code> 屬性。這項設定可讓系統將供應程式內容的存取權授予其他應用程式。 +如果想瞭解如何保留特定文件的權限,請參閱<a href="#permissions">保留權限</a>。 +</li> + +<li>{@code MANAGE_DOCUMENTS} 權限。在預設情況下,所有人都可使用供應程式。 +加入這項權限可針對系統設定供應程式限制,藉此提高其安全性。 +</li> + +<li>設定資源檔案所定義布林值的 {@code android:enabled} 屬性。 +這項屬性可用於針對搭載 Android 4.3 以下版本的裝置停用供應程式。範例:{@code android:enabled="@bool/atLeastKitKat"}。 +除了在宣示說明中加入這項屬性以外,您還必須執行下列操作: + +<ul> +<li>在位於 {@code res/values/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: + <pre><bool name="atLeastKitKat">false</bool></pre></li> + +<li>在位於 {@code res/values-v19/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: + <pre><bool name="atLeastKitKat">true</bool></pre></li> +</ul></li> + +<li>內含 +{@code android.content.action.DOCUMENTS_PROVIDER} 動作的意圖篩選器,讓您的供應程式能夠在系統搜尋供應程式時顯示在挑選器中。 +</li> + +</ul> +<p>以下是內含供應程式的範例宣示說明例外狀況:</p> + +<pre><manifest... > + ... + <uses-sdk + android:minSdkVersion="19" + android:targetSdkVersion="19" /> + .... + <provider + android:name="com.example.android.storageprovider.MyCloudProvider" + android:authorities="com.example.android.storageprovider.documents" + android:grantUriPermissions="true" + android:exported="true" + android:permission="android.permission.MANAGE_DOCUMENTS" + android:enabled="@bool/atLeastKitKat"> + <intent-filter> + <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> + </intent-filter> + </provider> + </application> + +</manifest></pre> + +<h4 id="43">支援搭載 Android 4.3 以下版本的裝置</h4> + +<p>只有搭載 Android 4.4 以上版本的裝置可使用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖。如果您想讓應用程式支援 {@link android.content.Intent#ACTION_GET_CONTENT} 以便與搭載 Android 4.3 以下版本的裝置相容,請針對搭載 Android 4.4 以上版本的裝置停用宣示說明中的 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器。 + + + + +文件供應器和 {@link android.content.Intent#ACTION_GET_CONTENT} 是完全不同的項目。 + +如果您同時支援這兩個項目,您的應用程式就會重複出現在系統挑選器 UI 中,讓使用者可透過兩種不同方式存取您儲存的資料, + +而這樣會造成混淆。</p> + +<p>以下提供針對搭載 Android 4.4 以上版本的裝置停用 +{@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器的建議做法: +</p> + +<ol> +<li>在位於 {@code res/values/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: + <pre><bool name="atMostJellyBeanMR2">true</bool></pre></li> + +<li>在位於 {@code res/values-v19/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: + <pre><bool name="atMostJellyBeanMR2">false</bool></pre></li> + +<li>新增 +<a href="{@docRoot}guide/topics/manifest/activity-alias-element.html">Activity 別名</a>來針對搭載 Android 4.4 (API 級別 19) 以上版本的裝置停用 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器。 + +例如: + +<pre> +<!-- This activity alias is added so that GET_CONTENT intent-filter + can be disabled for builds on API level 19 and higher. --> +<activity-alias android:name="com.android.example.app.MyPicker" + android:targetActivity="com.android.example.app.MyActivity" + ... + android:enabled="@bool/atMostJellyBeanMR2"> + <intent-filter> + <action android:name="android.intent.action.GET_CONTENT" /> + <category android:name="android.intent.category.OPENABLE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="image/*" /> + <data android:mimeType="video/*" /> + </intent-filter> +</activity-alias> +</pre> +</li> +</ol> +<h3 id="contract">合約</h3> + +<p>一般來說,當您編寫自訂內容供應程式時,需要完成的其中一項工作為實作合約類別 (詳情請參閱<a href="{@docRoot}guide/topics/providers/content-provider-creating.html#ContractClass">內容供應程式</a>開發人員指南)。 + + +合約類別是 {@code public final} 類別,內含以下項目的固定不變定義:URI、欄名稱、MIME 類型以及供應程式擁有的其他中繼資料。 + +SAF 可為您提供以下合約類別,因此您不必自行編 寫合約: + +</p> + +<ul> + <li>{@link android.provider.DocumentsContract.Document}</li> + <li>{@link android.provider.DocumentsContract.Root}</li> +</ul> + +<p>例如,以下是在文件供應程式查詢文件或根目錄時可能會傳回的資料欄: +</p> + +<pre>private static final String[] DEFAULT_ROOT_PROJECTION = + new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, + Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, + Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, + Root.COLUMN_AVAILABLE_BYTES,}; +private static final String[] DEFAULT_DOCUMENT_PROJECTION = new + String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, + Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, + Document.COLUMN_FLAGS, Document.COLUMN_SIZE,}; +</pre> + +<h3 id="subclass">將 DocumentsProvider 設為子類別</h3> + +<p>編寫自動文件供應程式的下一個步驟是,將抽象類別 {@link android.provider.DocumentsProvider} 設為子類別。 +您至少必須實作下列方法: +</p> + +<ul> +<li>{@link android.provider.DocumentsProvider#queryRoots queryRoots()}</li> + +<li>{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}</li> + +<li>{@link android.provider.DocumentsProvider#queryDocument queryDocument()}</li> + +<li>{@link android.provider.DocumentsProvider#openDocument openDocument()}</li> +</ul> + +<p>以上是您必須實作的方法,不過您可能會視需要實作其他方法。 +詳情請參閱 {@link android.provider.DocumentsProvider}。 +</p> + +<h4 id="queryRoots">實作 queryRoots</h4> + +<p>實作 {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 後系統會使用 {@link android.provider.DocumentsContract.Root} 中定義的資料欄,傳回指向文件供應程式所有根目錄的 {@link android.database.Cursor}。 + +</p> + +<p>在以下程式碼片段中,{@code projection} 參數代表呼叫者想返回的特定欄位。 +這個程式碼片隊會建立新游標並在其中加入一列 — 也就是根目錄或頂層目錄 (例如「下載」或「圖片」)。 + +大多數供應程式只有一個根目錄。而您可以有多個根目錄,例如擁有多個使用者帳戶的情況下。 +在這種情況下,只要在游標中加入第二列即可。 +</p> + +<pre> +@Override +public Cursor queryRoots(String[] projection) throws FileNotFoundException { + + // Create a cursor with either the requested fields, or the default + // projection if "projection" is null. + final MatrixCursor result = + new MatrixCursor(resolveRootProjection(projection)); + + // If user is not logged in, return an empty root cursor. This removes our + // provider from the list entirely. + if (!isUserLoggedIn()) { + return result; + } + + // It's possible to have multiple roots (e.g. for multiple accounts in the + // same app) -- just add multiple cursor rows. + // Construct one row for a root called "MyCloud". + final MatrixCursor.RowBuilder row = result.newRow(); + row.add(Root.COLUMN_ROOT_ID, ROOT); + row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary)); + + // FLAG_SUPPORTS_CREATE means at least one directory under the root supports + // creating documents. FLAG_SUPPORTS_RECENTS means your application's most + // recently used documents will show up in the "Recents" category. + // FLAG_SUPPORTS_SEARCH allows users to search all documents the application + // shares. + row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | + Root.FLAG_SUPPORTS_RECENTS | + Root.FLAG_SUPPORTS_SEARCH); + + // COLUMN_TITLE is the root title (e.g. Gallery, Drive). + row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title)); + + // This document id cannot change once it's shared. + row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir)); + + // The child MIME types are used to filter the roots and only present to the + // user roots that contain the desired type somewhere in their file hierarchy. + row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir)); + row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace()); + row.add(Root.COLUMN_ICON, R.drawable.ic_launcher); + + return result; +}</pre> + +<h4 id="queryChildDocuments">實作 queryChildDocuments</h4> + +<p>實作 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 後系統會使用 +{@link android.provider.DocumentsContract.Document} 中定義的資料欄,傳回指向特定目錄中所有檔案的 {@link android.database.Cursor}。 + +</p> + +<p>當您在挑選器 UI 中選擇應用程式的根目錄後,就會呼叫這個方法,藉此取得根目錄內某個目錄中的下層文件。 +您可以在檔案階層的任何層級中呼叫這個方法,而不單單只能在根目錄中呼叫。 +以下程式碼片段會使用要求的資料欄建立新游標,然後加入該游標中上層目錄的任何下層物件相關資訊。下層物件可以是圖片、其他目錄等任何檔案: + + +</p> + +<pre>@Override +public Cursor queryChildDocuments(String parentDocumentId, String[] projection, + String sortOrder) throws FileNotFoundException { + + final MatrixCursor result = new + MatrixCursor(resolveDocumentProjection(projection)); + final File parent = getFileForDocId(parentDocumentId); + for (File file : parent.listFiles()) { + // Adds the file's display name, MIME type, size, and so on. + includeFile(result, null, file); + } + return result; +} +</pre> + +<h4 id="queryDocument">實作 queryDocument</h4> + +<p>實作 +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} 後系統會使用 {@link android.provider.DocumentsContract.Document} 中定義的資料欄,傳回指向特定檔案的 {@link android.database.Cursor}。 + + +</p> + +<p>{@link android.provider.DocumentsProvider#queryDocument queryDocument()}方法會針對特定檔案傳回 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 所傳送的相同資訊: + +</p> + + +<pre>@Override +public Cursor queryDocument(String documentId, String[] projection) throws + FileNotFoundException { + + // Create a cursor with the requested projection, or the default projection. + final MatrixCursor result = new + MatrixCursor(resolveDocumentProjection(projection)); + includeFile(result, documentId, null); + return result; +} +</pre> + +<h4 id="openDocument">實作 openDocument</h4> + +<p>您必須實作 {@link android.provider.DocumentsProvider#openDocument +openDocument()} 來傳回代表特定檔案的 +{@link android.os.ParcelFileDescriptor}。其他應用程式可利用傳回的 {@link android.os.ParcelFileDescriptor} 傳輸資料。 +使用者選取檔案而且用戶端應用程式呼叫 +{@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()} 要求存取該檔案後,系統就會呼叫這個方法。範例: + +</p> + +<pre>@Override +public ParcelFileDescriptor openDocument(final String documentId, + final String mode, + CancellationSignal signal) throws + FileNotFoundException { + Log.v(TAG, "openDocument, mode: " + mode); + // It's OK to do network operations in this method to download the document, + // as long as you periodically check the CancellationSignal. If you have an + // extremely large file to transfer from the network, a better solution may + // be pipes or sockets (see ParcelFileDescriptor for helper methods). + + final File file = getFileForDocId(documentId); + + final boolean isWrite = (mode.indexOf('w') != -1); + if(isWrite) { + // Attach a close listener if the document is opened in write mode. + try { + Handler handler = new Handler(getContext().getMainLooper()); + return ParcelFileDescriptor.open(file, accessMode, handler, + new ParcelFileDescriptor.OnCloseListener() { + @Override + public void onClose(IOException e) { + + // Update the file with the cloud server. The client is done + // writing. + Log.i(TAG, "A file with id " + + documentId + " has been closed! + Time to " + + "update the server."); + } + + }); + } catch (IOException e) { + throw new FileNotFoundException("Failed to open document with id " + + documentId + " and mode " + mode); + } + } else { + return ParcelFileDescriptor.open(file, accessMode); + } +} +</pre> + +<h3 id="security">安全性</h3> + +<p>假如您的文件供應程式為受密碼保護的雲端儲存服務,而您先想確認使用者都已登入,然後再開始分享其檔案。那麼在使用者未登入的情況下,您的應用程式應採取什麼行動? + +解決方案是不要讓文件供應程式在您實作 {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 後傳回任何根目錄。 +換句話說,就是讓供應程式傳回空的根目錄游標:</p> + +<pre> +public Cursor queryRoots(String[] projection) throws FileNotFoundException { +... + // If user is not logged in, return an empty root cursor. This removes our + // provider from the list entirely. + if (!isUserLoggedIn()) { + return result; +} +</pre> + +<p>另一個需採取的步驟是呼叫 {@code getContentResolver().notifyChange()}。還記得 {@link android.provider.DocumentsContract} 嗎? +我們會使用該類別建立 URI。以下程式碼片段會指示系統在使用者的登入狀態變更時,查詢文件供應程式的根目錄。 + +如果使用者未登入,呼叫 {@link android.provider.DocumentsProvider#queryRoots queryRoots()} 就會如上所述傳回空的游標。 + +這樣可確保只有登入供應程式的使用者可存取其中的文件。 +</p> + +<pre>private void onLoginButtonClick() { + loginOrLogout(); + getContentResolver().notifyChange(DocumentsContract + .buildRootsUri(AUTHORITY), null); +} +</pre>
\ No newline at end of file diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd new file mode 100644 index 000000000000..3a5a96121d94 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=存取資源 +parent.title=應用程式資源 +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>快速檢視</h2> + <ul> + <li>使用 {@code R.java} 中的整數可以參照程式碼中的資源,例如 +{@code R.drawable.myimage}</li> + <li>使用特殊 XML 語法可以參照資源中的資源,例如 {@code +@drawable/myimage}</li> + <li>您也可以使用 +{@link android.content.res.Resources} 中的方法存取應用程式資源</li> + </ul> + + <h2>重要類別</h2> + <ol> + <li>{@link android.content.res.Resources}</li> + </ol> + + <h2>本文件內容</h2> + <ol> + <li><a href="#ResourcesFromCode">存取程式碼中的資源</a></li> + <li><a href="#ResourcesFromXml">存取 XML 中的資源</a> + <ol> + <li><a href="#ReferencesToThemeAttributes">參照樣式屬性</a></li> + </ol> + </li> + <li><a href="#PlatformResources">存取平台資源</a></li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="providing-resources.html">提供資源</a></li> + <li><a href="available-resources.html">資源類型</a></li> + </ol> +</div> +</div> + + + + +<p>提供應用程式中的資源 (於<a href="providing-resources.html">提供資源</a>中討論) 後,您可以參照其資源 ID 加以應用。所有資源 ID 會在專案的 {@code R} 類別中定義。此類別是由 {@code aapt} 工具自動產生的。 + +</p> + +<p>編譯應用程式後,{@code aapt} 會產生 {@code R} 類別。此類別內含 {@code +res/} 目錄中所有資源的資源 ID。 +每一種資源類型都有 {@code R} 子類別 (例如,所有可繪項目資源的 +{@code R.drawable}),而這種類型的每一種資源都有靜態整數 (例如,{@code R.drawable.icon})。 +此整數就是資源 ID,您可以用於擷取資源。 +</p> + +<p>儘管資源 ID 都指定於 {@code R} 類別,您不需要為了取得資源 ID 而加以查看。資源 ID 的組成如下: +</p> +<ul> + <li>資源類型<em></em>:每一種資源會以「類型」分類,例如 {@code +string}、{@code drawable} 以及 {@code layout}。如需有關不同類型的詳細資訊,請參閱<a href="available-resources.html">資源類型</a>。 + </li> + <li>資源名稱<em></em>可能是:檔案名稱,不含副檔名;或 XML {@code android:name} 屬性中的值,如果資源是簡單值 (例如字串)。 + +</li> +</ul> + +<p>您有兩種方式可存取資源:</p> +<ul> + <li><strong>在程式碼中:</strong>使用 {@code R} 類別中子類別的靜態整數,例如: + + <pre class="classic no-pretty-print">R.string.hello</pre> + <p>{@code string} 是資源類型,而 {@code hello} 是資源名稱。若您以此格式提供資源 ID 時,很多 Android API 都可以存取您的資源。 +請參閱<a href="#ResourcesFromCode">在程式碼中存取資源</a>。 +</p> + </li> + <li><strong>在 XML 中:</strong>使用特殊 XML 語法,也可以對應到您在 {@code R} 類別中定義的資源 ID,例如: + + <pre class="classic no-pretty-print">@string/hello</pre> + <p>{@code string} 是資源類型,而 {@code hello} 是資源名稱。您可以在 XML 資源中的任何位置使用此語法,只要符合您在資源中所提供的預期值即可。 +請參閱<a href="#ResourcesFromXml">存取 XML 中的資源</a>。</p> + </li> +</ul> + + + +<h2 id="ResourcesFromCode">在程式碼中存取資源 </h2> + +<p>您可以將資源 ID 作為方法參數傳遞,在程式碼中使用資源。例如,您可以利用 {@link android.widget.ImageView#setImageResource(int) setImageResource()} 將 {@link android.widget.ImageView} 設定為使用 {@code res/drawable/myimage.png} 資源: + +</p> +<pre> +ImageView imageView = (ImageView) findViewById(R.id.myimageview); +imageView.setImageResource(<strong>R.drawable.myimage</strong>); +</pre> + +<p>您也可以使用 {@link +android.content.res.Resources} (以 {@link android.content.Context#getResources()} 可取得執行個體) 中的方法擷取個別的資源。 +</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>存取原始檔案</h2> + +<p>雖然不常見,但您有時需要存取原始檔案和目錄。如果有此需求,則不能將檔案儲存在 {@code res/},因為要從 +{@code res/} 讀取資源的唯一方式是透過資源 ID。不過,您可以將資源儲存在 +{@code assets/} 目錄。 +</p> +<p>儲存在 {@code assets/} 目錄中的檔案「不會」<em></em>指定資源 ID,因此您無法透過 {@code R} 類別或從 XML 資源參照這些檔案。 +您可以改為查詢 {@code assets/} 目錄中的檔案,就像一般檔案系統一樣,並使用 +{@link android.content.res.AssetManager} 讀取原始資料。 +</p> +<p>不過,如果您只是要讀取原始資料 (例如影片或音訊檔案),可以將檔案儲存在 {@code res/raw/} 目錄,然後使用 {@link +android.content.res.Resources#openRawResource(int) openRawResource()} 讀取位元組串流。 +</p> + +</div> +</div> + + +<h3>語法</h3> + +<p>在程式碼中參照資源的語法如下:</p> + +<pre class="classic no-pretty-print"> +[<em><package_name></em>.]R.<em><resource_type></em>.<em><resource_name></em> +</pre> + +<ul> + <li>{@code <package_name>}<em></em> 資源所在的封裝名稱 (從您自己的封裝中參照資源時,不需要此名稱)。 +</li> + <li>{@code <resource_type>}<em></em> 是資源類型的 {@code R} 子類別。</li> + <li>{@code <resource_name>}<em></em> 可以是不含副檔名的資源檔案名稱,或是 XML 元素中的 {@code android:name} 屬性值 (簡單值)。 + +</li> +</ul> +<p>請參閱<a href="available-resources.html">資源類型</a>,以取得關於每個資源類型及其參照方式的詳細資訊。 +</p> + + +<h3>使用案例</h3> + +<p>很多方法都接受資源 ID 參數,您可以使用 +{@link android.content.res.Resources} 中的方法擷取資源。您可以透過以下方式取得 {@link +android.content.res.Resources} 的執行個體:{@link android.content.Context#getResources +Context.getResources()}。</p> + + +<p>以下是在程式碼中存取資源的範例:</p> + +<pre> +// Load a background for the current screen from a drawable resource +{@link android.app.Activity#getWindow()}.{@link +android.view.Window#setBackgroundDrawableResource(int) +setBackgroundDrawableResource}(<strong>R.drawable.my_background_image</strong>) ; + +// Set the Activity title by getting a string from the Resources object, because +// this method requires a CharSequence rather than a resource ID +{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence) +setTitle}(getResources().{@link android.content.res.Resources#getText(int) +getText}(<strong>R.string.main_title</strong>)); + +// Load a custom layout for the current screen +{@link android.app.Activity#setContentView(int) +setContentView}(<strong>R.layout.main_screen</strong>); + +// Set a slide in animation by getting an Animation from the Resources object +mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation) +setInAnimation}(AnimationUtils.loadAnimation(this, + <strong>R.anim.hyperspace_in</strong>)); + +// Set the text on a TextView object using a resource ID +TextView msgTextView = (TextView) findViewById(<strong>R.id.msg</strong>); +msgTextView.{@link android.widget.TextView#setText(int) +setText}(<strong>R.string.hello_message</strong>); +</pre> + + +<p class="caution"><strong>注意:</strong>您不應手動修改 {@code +R.java} 檔案 — 此檔案是在編譯專案時由 {@code aapt} 工具所產生。 +下次編譯時會覆寫所有變更內容。</p> + + + +<h2 id="ResourcesFromXml">存取 XML 中的資源</h2> + +<p>您可以使用現有資源的參照,為某些 XML 屬性和元素定義值。 +您在建立版面配置檔案時,會經常以此方式提供字串和影像讓小工具使用。 +</p> + +<p>例如,如果您要將 {@link android.widget.Button} 加入版面配置,應該使用按鈕文字的<a href="string-resource.html">字串資源</a>: +</p> + +<pre> +<Button + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="<strong>@string/submit</strong>" /> +</pre> + + +<h3>語法</h3> + +<p>在 XML 資源中參照資源的語法如下:</p> + +<pre class="classic no-pretty-print"> +@[<em><package_name></em>:]<em><resource_type></em>/<em><resource_name></em> +</pre> + +<ul> + <li>{@code <package_name>} 資源所在的封裝名稱 (從同一個封裝中參照資源時,不需要此名稱) +</li> + <li>{@code <resource_type>} 是資源類型的 +{@code R} 子類別。</li> + <li>{@code <resource_name>} 可以是不含副檔名的資源檔案名稱,或是 XML 元素中的 {@code android:name} 屬性值 (簡單值)。 + +</li> +</ul> + +<p>請參閱<a href="available-resources.html">資源類型</a>,以取得關於每個資源類型及其參照方式的詳細資訊。 +</p> + + +<h3>使用案例</h3> + +<p>有時候您必須在 XML 中使用資源,而不能使用值 (例如,將可繪項目影像套用至小工具)。不過,您也可以在 XML 中接受簡單值的任何位置使用資源。 +例如,如果您有下列資源檔案,其中內含<a href="more-resources.html#Color">色彩資源</a>和<a href="string-resource.html">字串資源</a>: +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="opaque_red">#f00</color> + <string name="hello">Hello!</string> +</resources> +</pre> + +<p>您可以在下列版面配置檔案中使用這些資源,以設定文字色彩和文字字串: +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<EditText xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:textColor="<strong>@color/opaque_red</strong>" + android:text="<strong>@string/hello</strong>" /> +</pre> + +<p>在此情況下,您不用在資源參照中指定封裝名稱,因為資源是來自您自己的封裝。 +如要參照系統資源,則需要使用封裝名稱。 +範例:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<EditText xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:textColor="<strong>@android:color/secondary_text_dark</strong>" + android:text="@string/hello" /> +</pre> + +<p class="note"><strong>注意:</strong>您一律都要使用字串資源,便於應用程式當地語系化。 +如需關於建立替代資源 (例如當地語系化的字串) 的詳細資訊,請參閱<a href="providing-resources.html#AlternativeResources">提供替代資源</a>。 + + +如需將應用程式當地語系化的詳細指南,請參閱<a href="localization.html">當地語系化</a>。 +</p> + +<p>您甚至可以在 XML 中使用資源以建立別名。例如:您可以用其他可繪項目資源的別名,建立可繪項目資源: +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/other_drawable" /> +</pre> + +<p>聽起來多此一舉,但是,當您使用替代資源時,就非常實用。深入瞭解<a href="providing-resources.html#AliasResources">建立別名資源</a>。 +</p> + + + +<h3 id="ReferencesToThemeAttributes">參照樣式屬性</h3> + +<p>樣式屬性資源可以讓您在目前套用的設計風格中參照屬性的值。 +參照樣式屬性可以讓您透過目前設計風格提供的標準變化自訂 UI 元素外觀,而不需要使用硬式編碼值。 + +參照樣式屬性基本上就是「使用目前設計風格中此屬性所定義的樣式」。 +</p> + +<p>如要參照樣式屬性,名稱語法幾乎和一般資源格式相同,只要把小老鼠符號 ({@code @}) 換成問號 ({@code ?}) 即可,而且您可以選擇是否要使用資源類型。 + +範例:</p> + +<pre class="classic"> +?[<em><package_name></em>:][<em><resource_type></em>/]<em><resource_name></em> +</pre> + +<p>例如,您可以參照屬性,讓文字色彩符合系統設計風格的「主要」文字色彩,方式如下: +</p> + +<pre> +<EditText id="text" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textColor="<strong>?android:textColorSecondary</strong>" + android:text="@string/hello_world" /> +</pre> + +<p>這裡的 {@code android:textColor} 屬性指出目前設計風格中樣式屬性的名稱。 +Android 現在會將套用至 {@code android:textColorSecondary} 樣式屬性的值,當成此小工具中 {@code android:textColor} 的值。 +因為系統資源工具知道在此環境中會有一個屬性資源,您不需要明確陳述其類型 ( +<code>?android:attr/textColorSecondary</code>)— 可以不需要寫上出 {@code attr} 類型。 + +</p> + + + + +<h2 id="PlatformResources">存取平台資源</h2> + +<p>Android 內含多種標準資源,例如樣式、設計風格以及版面配置。如要存取這些資源,則必須在參照資源時使用 +<code>android</code> 封裝名稱。 +例如,Android 在 {@link android.widget.ListAdapter} 中提供版面配置資源,您可以用於列出項目: +</p> + +<pre> +{@link android.app.ListActivity#setListAdapter(ListAdapter) +setListAdapter}(new {@link +android.widget.ArrayAdapter}<String>(this, <strong>android.R.layout.simple_list_item_1</strong>, myarray)); +</pre> + +<p>在此範例中,{@link android.R.layout#simple_list_item_1} 是一個版面配置資源,由平台為 {@link android.widget.ListView} 中的項目所定義。 +您可以加以使用,而不用自行建立清單項目的版面配置。 +如需詳細資訊,請參閱<a href="{@docRoot}guide/topics/ui/layout/listview.html">清單檢視</a>開發人員指南。 +</p> + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd new file mode 100644 index 000000000000..68197d17233a --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=資源總覽 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文主題</h2> + <ol> + <li><a href="providing-resources.html">提供資源</a></li> + <li><a href="accessing-resources.html">存取資源</a></li> + <li><a href="runtime-changes.html">處理執行階段變更</a></li> + <li><a href="localization.html">本地化</a></li> + </ol> + + <h2>參考資料</h2> + <ol> + <li><a href="available-resources.html">資源類型</a></li> + </ol> +</div> +</div> + + +<p>建議您一律將應用程式程式碼當中像是圖片和字串的資源具體化,以便個別加以維護。 +將資源具體化也可讓您提供支援特定裝置設定的替代資源,例如不同語言或螢幕大小,隨著提供不同設定的 Android 裝置日漸增加,這點也變得日益重要。 + + +為了提供不同設定的相容性,您必須整理專案的 {@code res/} 目錄中的資源,使用各種子目錄按類型與設定將資源分門別類。 + + +</p> + +<div class="figure" style="width:429px"> +<img src="{@docRoot}images/resources/resource_devices_diagram1.png" height="167" alt="" /> +<p class="img-caption"> +<strong>圖 1.</strong>兩個不同的裝置,個別使用預設的版面配置 (應用程式未提供替代的版面配置)。 +</p> +</div> + +<div class="figure" style="width:429px"> +<img src="{@docRoot}images/resources/resource_devices_diagram2.png" height="167" alt="" /> +<p class="img-caption"> +<strong>圖 2.</strong>兩個不同的裝置,個別使用不同螢幕大小所提供的不同版面配置。 +</p> +</div> + +<p>針對任何資源類型,您都能為應用程式指定「預設」<em></em>與多項「替代」<em></em>資源。 +</p> +<ul> + <li>不論裝置設定為何,或在沒有符合目前設定的替代資源時,您都應該使用預設資源。 + +</li> + <li>您設計用來與特定設定搭配使用的就是替代資源。 +如要為某個設定指定一組資源,請將適當的設定限定詞附加到目錄名稱。 +</li> +</ul> + +<p>例如,假設您的預設 UI 版面配置儲存在 {@code res/layout/} 目錄,您可能要指定一個不同的版面配置並儲存在 +{@code res/layout-land/} 目錄中,以在螢幕處於橫向時使用。 + +Android 會在比對裝置目前的設定與您的資源目錄名稱後,自動套用適當的資源。 +</p> + +<p>圖 1 說明系統如何在沒有替代資源可用時,對兩個不同裝置套用相同的版面配置。 +圖 2 說明相同的應用程式為較大的螢幕新增替代版面配置資源的情況。 +</p> + +<p>下文提供完整的說明,指引您如何整理應用程式資源、指定替代資源、在您的應用程式中存取這些資源等等: +</p> + +<dl> + <dt><strong><a href="providing-resources.html">提供資源</a></strong></dt> + <dd>您可在應用程式中加入哪些資源類型,儲存在哪裡以及如何針對不同的裝置設定建立替代資源。 +</dd> + <dt><strong><a href="accessing-resources.html">存取資源</a></strong></dt> + <dd>如何使用您提供的資源 (在應用程式的程式碼中或在其他 XML 資源中參考)。 +</dd> + <dt><strong><a href="runtime-changes.html">處理執行階段變更</a></strong></dt> + <dd>如何管理在 Activity 執行期間發生的設定變更。</dd> + <dt><strong><a href="localization.html">本地化</a></strong></dt> + <dd>從細節到整體的說明,指引您使用替代資源將應用程式本地化。雖然這只是替代資源的一種特殊用途,但為了觸及更多使用者,這樣做非常重要。 + +</dd> + <dt><strong><a href="available-resources.html">資源類型</a></strong></dt> + <dd>您能提供的各種資源類型參考資料,描述其 XML 元件、屬性及語法。 +例如,此參考資料說明如何建立應用程式選單、可繪項目、動畫等資源。 +</dd> +</dl> + +<!-- +<h2>Raw Assets</h2> + +<p>An alternative to saving files in {@code res/} is to save files in the {@code +assets/} directory. This should only be necessary if you need direct access to original files and +directories by name. Files saved in the {@code assets/} directory will not be given a resource +ID, so you can't reference them through the {@code R} class or from XML resources. Instead, you can +query data in the {@code assets/} directory like an ordinary file system, search through the +directory and +read raw data using {@link android.content.res.AssetManager}. For example, this can be more useful +when dealing with textures for a game. However, if you only need to read raw data from a file +(such as a video or audio file), then you should save files into the {@code res/raw/} directory and +then read a stream of bytes using {@link android.content.res.Resources#openRawResource(int)}. This +is uncommon, but if you need direct access to original files in {@code assets/}, refer to the {@link +android.content.res.AssetManager} documentation.</p> +--> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd new file mode 100644 index 000000000000..0938dc00e12d --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=提供資源 +parent.title=應用程式資源 +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>快速檢視</h2> + <ul> + <li>不同類型的資源屬於 {@code res/} 中不同的子目錄</li> + <li>替代資源提供特定設定資源檔案</li> + <li>始終要包含預設資源,如此應用程式才不會依賴特定裝置設定 +</li> + </ul> + <h2>本文件內容</h2> + <ol> + <li><a href="#ResourceTypes">分組資源類型</a></li> + <li><a href="#AlternativeResources">提供替代資源</a> + <ol> + <li><a href="#QualifierRules">限定詞名稱規則</a></li> + <li><a href="#AliasResources">建立別名資源</a></li> + </ol> + </li> + <li><a href="#Compatibility">使用資源提供最佳的裝置相容性</a></li> + <li><a href="#BestMatch">Android 如何尋找最相符的資源</a></li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="accessing-resources.html">存取資源</a></li> + <li><a href="available-resources.html">資源類型</a></li> + <li><a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a> +</li> + </ol> +</div> +</div> + +<p>您應該一律將應用程式資源具體化,例如影像和程式碼字串,以便單獨維護它們。 +同時,您還應該將替代資源分組到特殊命名的資源目錄中,為特定裝置設定提供替代資源。 +Android 會在執行階段根據目前的設定使用適當的資源。 +例如,您可能會想根據螢幕大小提供不同的 UI 版面配置,或者根據語言設定提供不同的字串。 + +</p> + +<p>具體化您的應用程式資源後,可以使用在專案 {@code R} 類別中產生的資源 ID 來存取它們。 +在應用程式使用資源的方法在<a href="accessing-resources.html">存取資源</a>中有相關討論。 + +本文件說明如何在 Android 專案分組您的資源,並為特定裝置設定提供替代資源。 +</p> + + +<h2 id="ResourceTypes">分組資源類型</h2> + +<p>您應該將每個資源類型放在專案 +{@code res/} 目錄的特定子目錄中。例如,下列為簡單專案的檔案階層:</p> + +<pre class="classic no-pretty-print"> +MyProject/ + src/ <span style="color:black"> + MyActivity.java </span> + res/ + drawable/ <span style="color:black"> + graphic.png </span> + layout/ <span style="color:black"> + main.xml + info.xml</span> + mipmap/ <span style="color:black"> + icon.png </span> + values/ <span style="color:black"> + strings.xml </span> +</pre> + +<p>如您在此範例所見,{@code res/} 目錄包含所有資源 (在子目錄中):一個影像資源、兩個版面配置資源、啟動器圖示的 {@code mipmap/} 目錄,以及字串資源檔案。 + +資源目錄名稱非常重要,在表 1 有相關說明。 +</p> + +<p class="note"><strong>注意:</strong>如需有關使用 mipmap 資料夾的詳細資訊,請參閱<a href="{@docRoot}tools/projects/index.html#mipmap">管理專案總覽</a>。 +</p> + +<p class="table-caption" id="table1"><strong>表 1.</strong>專案 {@code res/} 目錄內部支援資源目錄。 +</p> + +<table> + <tr> + <th scope="col">目錄</th> + <th scope="col">資源類型</th> + </tr> + + <tr> + <td><code>animator/</code></td> + <td>定義<a href="{@docRoot}guide/topics/graphics/prop-animation.html">屬性動畫</a>的 XML 檔案。 +</td> + </tr> + + <tr> + <td><code>anim/</code></td> + <td>定義 <a href="{@docRoot}guide/topics/graphics/view-animation.html#tween-animation">tween 動畫</a>的 XML 檔案。 +(屬性動畫也能儲存在這個目錄中,但建議將屬性動畫放在{@code animator/} 目錄中,以區分這兩個類型。) + +</td> + </tr> + + <tr> + <td><code>color/</code></td> + <td>定義色彩狀態清單的 XML 檔案。請參閱<a href="color-list-resource.html">色彩狀態清單資源</a> +</td> + </tr> + + <tr> + <td><code>drawable/</code></td> + + <td><p>編譯成下列可繪項目資源子類型的點陣圖檔案 ({@code .png}、{@code .9.png}、{@code .jpg}、{@code .gif}) 或 XML 檔案: +</p> + <ul> + <li>點陣圖檔案</li> + <li>九宮格影像 (可重新調整大小的點陣圖)</li> + <li>狀態清單</li> + <li>形狀</li> + <li>動畫可繪項目</li> + <li>其他可繪項目</li> + </ul> + <p>請參閱<a href="drawable-resource.html">可繪項目資源</a>。</p> + </td> + </tr> + + <tr> + <td><code>mipmap/</code></td> + <td>適用於不同啟動器圖示密度的可繪項目檔案。如需有關使用 {@code mipmap/} 資料夾管理啟動器圖示的詳細資訊,請參閱<a href="{@docRoot}tools/project/index.html#mipmap">管理專案總覽</a>。 + +</td> + </tr> + + <tr> + <td><code>layout/</code></td> + <td>定義使用者介面版面配置的 XML 檔案。 + 請參閱<a href="layout-resource.html">版面配置資源</a>。</td> + </tr> + + <tr> + <td><code>menu/</code></td> + <td>定義應用程式選單 (例如,選項選單、操作選單或子選單) 的 XML 檔案。 +請參閱<a href="menu-resource.html">選單資源</a>。</td> + </tr> + + <tr> + <td><code>raw/</code></td> + <td><p>以原始格式儲存的任意檔案。如要使用原始 +{@link java.io.InputStream} 開啟這些資源,使用資源 ID 呼叫 {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()},資源 ID 為 {@code R.raw.<em>filename</em>}。</p> + <p>然而,如果您需要存取原始檔案名稱和檔案階層,可以考慮將一些資源儲存在 {@code +assets/} 目錄中 (而不是 {@code res/raw/})。 +位於 {@code assets/} 的檔案不會有資源 ID,因此您只能使用 {@link android.content.res.AssetManager} 讀取它們。 +</p></td> + </tr> + + <tr> + <td><code>values/</code></td> + <td><p>包含簡單值 (例如,字串、整數和色彩) 的 XML 檔案。</p> + <p>位於其他 {@code res/} 子目錄的 XML 資源檔案會根據 XML 檔案名稱定義單一資源,而位於 {@code values/} 目錄的檔案則會描述多個資源。 +針對這個目錄中的檔案,{@code <resources>} 元素的每個子項都會定義單一資源。 + +例如,{@code <string>} 元素建立一個 +{@code R.string} 資源,{@code <color>} 元素建立一個 {@code R.color}資源。 +</p> + <p>由於每個資源都是以自己的 XML 元素定義,您可以依照自己的喜好命名檔案,並將不同的資源類型放在一個檔案中。 +然而,為了更加清楚,您可以將唯一資源類型放在不同的檔案中。 +例如,針對您可以在此目錄建立的資源,這裡提供一些檔案名稱慣例: +</p> + <ul> + <li>arrays.xml 適用於資源陣列 (<a href="more-resources.html#TypedArray">具類型的陣列</a>)。</li> + <li>colors.xml 適用於<a href="more-resources.html#Color">色彩值</a></li> + <li>dimens.xml 適用於<a href="more-resources.html#Dimension">維度值</a>。</li> + <li>strings.xml 適用於<a href="string-resource.html">字串值</a>。 +</li> + <li>styles.xml 適用於<a href="style-resource.html">樣式</a>。</li> + </ul> + <p>請參閱<a href="string-resource.html">字串資源</a>、 +<a href="style-resource.html">樣式資源</a>和 +<a href="more-resources.html">更多資源類型</a>。</p> + </td> + </tr> + + <tr> + <td><code>xml/</code></td> + <td>可以在執行階段讀取的任意 XML 檔案,方法是呼叫 {@link +android.content.res.Resources#getXml(int) Resources.getXML()}。各種 XML 設定檔案都必須儲存在這裡,例如<a href="{@docRoot}guide/topics/search/searchable-config.html">可搜尋項目設定</a>。 + +<!-- or preferences configuration. --></td> + </tr> +</table> + +<p class="caution"><strong>注意:</strong>千萬不要將資源檔案直接儲存在 +{@code res/} 目錄內 — 會發生編譯錯誤。</p> + +<p>如需特定資源類型的詳細資訊,請參閱<a href="available-resources.html">資源類型</a>文件。</p> + +<p>您儲存在表 1 定義之子目錄的資源是您的「預設」資源。 +也就是說,這些資源會定義您應用程式的預設設計和內容。 +然而,不同類型的 Android 平台裝置可能需要不同類型的資源。 +例如,如果裝置的螢幕大於一般螢幕,則您需要提供不同的版面配置資源,以充分利用額外的螢幕空間。 +或者,如果裝置有不同的語言設定,則您需要提供不同的字串資源,以翻譯您使用者介面中的文字。 + +如要為不同裝置設定提供這些不同的資源,除了預設資源之外,您還需要提供替代資源。 + +</p> + + +<h2 id="AlternativeResources">提供替代資源</h2> + + +<div class="figure" style="width:429px"> +<img src="{@docRoot}images/resources/resource_devices_diagram2.png" height="167" alt="" /> +<p class="img-caption"> +<strong>圖 1.</strong>兩個不同的裝置,個別使用不同的版面配置資源。</p> +</div> + +<p>幾乎每個應用程式都應提供替代資源以支援特定裝置設定。 +例如,您應該為不同的螢幕密度包含替代可繪項目資源,並為不同語言提供替代字串資源。 +Android 會在執行階段偵測目前的裝置設定,並為您的應用程式載入適當的資源。 + +</p> + +<p>如要為一組資源指定特定設定的替代項目:</p> +<ol> + <li>在 {@code res/} 建立一個新的目錄,命名的格式為 {@code +<em><resources_name></em>-<em><config_qualifier></em>}。 + <ul> + <li><em>{@code <resources_name>}</em> 是對應預設資源 (定義在表 1) 的目錄名稱。 +</li> + <li><em>{@code <qualifier>}</em> 是用來指定要使用這些資源之個別設定的名稱 (定義在表 2)。 +</li> + </ul> + <p>您可以附加一個以上的 <em>{@code <qualifier>}</em>。使用破折號分隔每個項目。 +</p> + <p class="caution"><strong>注意:</strong>附加多個限定詞時,您必須以表 2 列出的相同順序放置它們。 +如果限定詞的順序錯誤,將會忽略該資源。 +</p> + </li> + <li>將各個替代資源儲存在這個新的目錄。資源檔案的名稱必須和預設資源檔案的名稱完全相同。 +</li> +</ol> + +<p>例如,這裡有些預設和替代資源:</p> + +<pre class="classic no-pretty-print"> +res/ + drawable/ <span style="color:black"> + icon.png + background.png </span> + drawable-hdpi/ <span style="color:black"> + icon.png + background.png </span> +</pre> + +<p>{@code hdpi} 限定詞代表該目錄的資源適用於高密度螢幕裝置。 +這些可繪項目目錄中的影像大小已調整為符合特定螢幕密度,但是檔案名稱完全相同。 + +如此一來,您用來參照 {@code icon.png} 或 {@code +background.png} 影像的資源 ID 一律都會相同,但 Android 會透過比對裝置設定資訊和資源目錄名稱中的限定詞,選擇最符合目前裝置的每個資源版本。 + +</p> + +<p>Android 支援多種設定限定詞,而且您可以將多個限定詞加到一個目錄名稱,並用破折號分隔每個限定詞。 +表 2 依優先等級列出有效的設定限定詞 — 如果您針對一個資源目錄使用多個限定詞,您必須依表格中列出的順序將它們新增到目錄名稱。 + + +</p> + + +<p class="table-caption" id="table2"><strong>表 2.</strong>設定限定詞名稱。 +</p> +<table> + <tr> + <th>設定</th> + <th>限定詞值</th> + <th>描述</th> + </tr> + <tr id="MccQualifier"> + <td>MCC 和 MNC</td> + <td>範例:<br/> + <code>mcc310</code><br/> + <code><nobr>mcc310-mnc004</nobr></code><br/> + <code>mcc208-mnc00</code><br/> + 等等。 + </td> + <td> + <p>行動裝置國家/地區代碼 (MCC) 後面會選擇性加上裝置 SIM 卡上的行動裝置網路代碼 (MNC) +。例如,<code>mcc310</code> 代表美國地區的所有行動通訊業者、 + <code>mcc310-mnc004</code> 代表美國地區的 Verizon,以及 <code>mcc208-mnc00</code> 代表法國地區的 Orange。 +</p> + <p>如果裝置使用無線電連線 (GSM 手機),MCC 和 MNC 值都會來自SIM 卡。 +</p> + <p>您也可以單獨使用 MCC (例如,在應用程式包含國家特定法律資源)。 +如果您只需根據語言進行指定,則改為使用「語言和區域」<em></em>限定詞 (稍後將會討論)。 +如果您決定使用 MCC 和 MNC 限定詞,應謹慎使用,並測試是否可按預期運作。 +</p> + <p>另請查看設定欄位 {@link +android.content.res.Configuration#mcc} 和 {@link +android.content.res.Configuration#mnc},分別提供目前的行動裝置國家代碼和行動裝置網路代碼。 +</p> + </td> + </tr> + <tr id="LocaleQualifier"> + <td>語言和區域</td> + <td>範例:<br/> + <code>en</code><br/> + <code>fr</code><br/> + <code>en-rUS</code><br/> + <code>fr-rFR</code><br/> + <code>fr-rCA</code><br/> + 等等。 + </td> + <td><p>語言是以兩個字母的 <a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">ISO 639-1</a> 語言代碼定義,後面可以視需要加上兩個字母的 <a href="http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">ISO 3166-1-alpha-2</a> 區域代碼 (前面加上小寫 "{@code r}")。 + + + + </p><p> + 代碼「沒有」<em></em>大小寫之分;{@code r} 首碼是用來區分區域部分。 + + 您不能只指定區域。</p> + <p>如果使用者在系統設定變更其語言,此設定就會在應用程式生命週期內發生變更。 +請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + <p>請參閱<a href="localization.html">當地語系化</a>,以取得將您的應用程式當地語系化成其他語言的詳細指南。 +</p> + <p>另請查看 {@link android.content.res.Configuration#locale} 設定欄位,這會提供目前的地區設定。 +</p> + </td> + </tr> + <tr id="LayoutDirectionQualifier"> + <td>版面配置方向</td> + <td><code>ldrtl</code><br/> + <code>ldltr</code><br/> + </td> + <td><p>應用程式的版面配置方向。{@code ldrtl} 代表「版面配置方向右至左」。 + {@code ldltr} 代表「版面配置方向左至右」,而且是預設隱含值。 + </p> + <p>這適用於任何資源,例如版面配置、可繪項目或值。 + </p> + <p>例如,如果您想針對阿拉伯語言提供一些特定版面配置,以及為任何其他「右至左」語言 (像是波斯文或希伯來文) 提供幾個一般版面配置,則程式碼如下: + + </p> +<pre class="classic no-pretty-print"> +res/ + layout/ <span style="color:black"> + main.xml </span>(Default layout) + layout-ar/ <span style="color:black"> + main.xml </span>(Specific layout for Arabic) + layout-ldrtl/ <span style="color:black"> + main.xml </span>(Any "right-to-left" language, except + for Arabic, because the "ar" language qualifier + has a higher precedence.) +</pre> + <p class="note"><strong>注意:</strong>如要為您的應用程式啟用右至左版面配置功能,您必須將 <a href="{@docRoot}guide/topics/manifest/application-element.html#supportsrtl">{@code + supportsRtl}</a> 設成 {@code "true"},以及將 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> 設成 17 或更高。 +</p> + <p>已新增至 API 級別 17。<em></em></p> + </td> + </tr> + <tr id="SmallestScreenWidthQualifier"> + <td>smallestWidth</td> + <td><code>sw<N>dp</code><br/><br/> + 範例:<br/> + <code>sw320dp</code><br/> + <code>sw600dp</code><br/> + <code>sw720dp</code><br/> + 等等。 + </td> + <td> + <p>螢幕的基本大小,也就是可用 +螢幕區域的最短維度。具體而言,裝置的 smallestWidth 是螢幕最短的可用高度和寬度 (您也可以把它當成螢幕的「最小寬度」)。 +您可以使用此限定詞確保無論螢幕目前的方向為何,您的應用程式至少有 {@code <N>} dps 的寬度可供 UI 使用。 + +</p> + <p>例如,如果您的版面配置需要隨時保持至少 600 dp 的最小螢幕區域維度,則您可以使用此限定詞建立版面配置資源 {@code +res/layout-sw600dp/}。 +系統只會在可用螢幕的最小維度至少是 600dp 時使用這些資源,無論 600dp 的長度對使用者而言是高度或寬度都一樣。 + +smallestWidth 是螢幕的固定螢幕大小特性;<strong>裝置的 smallestWidth 不會隨著螢幕方向的改變而變更。</strong> +</p> + <p>裝置的 smallestWidth 會將螢幕裝飾和系統 UI 列入計算。例如,如果裝置的螢幕上有一些永久的 UI 元素,且這些元素佔用了 smallestWidth 座標軸上的空間,系統會宣告 smallestWidth 小於實際螢幕大小,因為這些是 UI 無法使用的螢幕像素。 + + +因此,您使用的值應該是版面配置所需的實際最小維度<em></em> (這個值通常是您版面配置支援的「最小寬度」,無論螢幕目前的方向為何都一樣)。 + +</p> + <p>您可以針對一般螢幕大小在這裡使用的一些值:</p> + <ul> + <li>320 適用於採用以下螢幕設定的裝置: + <ul> + <li>240x320 ldpi (QVGA 手機)</li> + <li>320x480 mdpi (手機)</li> + <li>480x800 hdpi (高密度手機)</li> + </ul> + </li> + <li>480 適用於 480x800 mdpi 這類螢幕 (平板電腦/手機)。</li> + <li>600 適用於 600x1024 mdpi 這類螢幕 (7" 平板電腦)。</li> + <li>720 適用於 720x1280 mdpi 這類螢幕 (10" 平板電腦)。</li> + </ul> + <p>當您的應用程式為 smallestWidth 限定詞提供多個值不相同的資源目錄時,系統會使用最接近 (不超過)裝置 smallestWidth 的值。 + + </p> + <p>已新增至 API 級別 13。<em></em></p> + <p>另請查看 <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html#requiresSmallest">{@code +android:requiresSmallestWidthDp}</a> 屬性,該屬性會宣告與您應用程式相容的最小 smallestWidth,以及 {@link +android.content.res.Configuration#smallestScreenWidthDp} 設定欄位,此欄位保留裝置的 smallestWidth 值。 + +</p> + <p>如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>開發人員指南。 + +</p> + </td> + </tr> + <tr id="ScreenWidthQualifier"> + <td>可用寬度</td> + <td><code>w<N>dp</code><br/><br/> + 範例:<br/> + <code>w720dp</code><br/> + <code>w1024dp</code><br/> + 等等。 + </td> + <td> + <p>指定資源應使用的最小可用螢幕寬度,以 {@code dp} 單位計算 — 由 <code><N></code> 值定義。 +這個設定值會隨著螢幕方向變更為橫向或直向而改變,以符合目前實際的寬度。 + +</p> + <p>當您的應用程式為這個設定提供多個值不相同的資源目錄時,系統會使用最接近 (不超過) 裝置目前螢幕寬度的值。 + +這個值會將螢幕裝飾列入計算,因此如果裝置顯示的左邊緣或右邊緣有一些永久的 UI 元素,則會使用比實際螢幕大小更小的寬度值,將這些 UI 元素列入計算並減少應用程式的可用空間。 + + + +</p> + <p>已新增至 API 級別 13。<em></em></p> + <p>另請查看 {@link android.content.res.Configuration#screenWidthDp} 設定欄位,該欄位保留目前的螢幕寬度。 +</p> + <p>如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>開發人員指南。 + +</p> + </td> + </tr> + <tr id="ScreenHeightQualifier"> + <td>可用高度</td> + <td><code>h<N>dp</code><br/><br/> + 範例:<br/> + <code>h720dp</code><br/> + <code>h1024dp</code><br/> + 等等。 + </td> + <td> + <p>指定資源應使用的最小可用螢幕高度,以「dp」單位計算 — 由 <code><N></code> 值定義。 +這個設定值會隨著螢幕方向變更為橫向或直向而改變,以符合目前實際的高度。 + +</p> + <p>當您的應用程式為這個設定提供多個值不相同的資源目錄時,系統會使用最接近 (不超過) 裝置目前螢幕高度的值。 + +這個值會將螢幕裝飾列入計算,因此如果裝置顯示的上邊緣或下邊緣有一些永久的 UI 元素,則會使用比實際螢幕大小更小的高度值,將這些 UI 元素列入計算並減少應用程式的可用空間。 + + + +不固定的螢幕裝飾 (例如,可在全螢幕時隱藏的手機狀態列) 在這裡不<em></em>列入計算,標題列或動作列這類視窗裝飾也不會列入計算,因此應用程式必須做好準備因應比其指定還小的空間。 + + + + + <p>已新增至 API 級別 13。<em></em></p> + <p>另請查看 {@link android.content.res.Configuration#screenHeightDp} 設定欄位,該欄位保留目前的螢幕寬度。 +</p> + <p>如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>開發人員指南。 + +</p> + </td> + </tr> + <tr id="ScreenSizeQualifier"> + <td>螢幕大小</td> + <td> + <code>small</code><br/> + <code>normal</code><br/> + <code>large</code><br/> + <code>xlarge</code> + </td> + <td> + <ul class="nolist"> + <li>{@code small}:與低密度 QVGA 螢幕大小相似的螢幕 +。小螢幕的最低版面配置大小約 320x426 dp 單位。 +範例包含 QVGA 低密度和 VGA 高密度。 +</li> + <li>{@code normal}:與中密度 HVGA 螢幕大小相似的螢幕。 +一般螢幕的最低版面配置大小約 320x470 dp 單位。 +這類螢幕的範例有 WQVGA 低密度、HVGA 中密度、WVGA高密度。 + +</li> + <li>{@code large}:與中密度 VGA 螢幕大小相似的螢幕。 + + 大螢幕的最低版面配置大小約 480x640 dp 單位。 + 範例包含 VGA 和 WVGA 中密度螢幕。</li> + <li>{@code xlarge}:比傳統中密度 HVGA 螢幕大很多的螢幕。 +超大螢幕的最低版面配置大小約 720x960 dp 單位。 +在大多數情況下,使用超大螢幕的裝置由於尺寸過大無法放入口袋,因此最有可能是平板電腦樣式的裝置。 + +已新增至 API 級別 9。<em></em></li> + </ul> + <p class="note"><strong>注意:</strong>使用大小限定詞不代表資源僅適用於該大小的螢幕。<em></em> +如果您提供的替代資源沒有更符合目前裝置設定的限定詞,系統將使用<a href="#BestMatch">最符合</a>的資源。 + +</p> + <p class="caution"><strong>注意:</strong>如果所有資源都使用比目前螢幕更大<em></em>的大小限定詞,系統將不會<strong></strong>使用這些資源,您的應用程式將會在執行階段當機 (例如,如果所有版面配置資源都標記為 {@code +xlarge} 限定詞,但裝置為一般大小螢幕)。 + +</p> + <p>已新增至 API 級別 4。<em></em></p> + + <p>請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>以取得詳細資訊。 +</p> + <p>另請查看 {@link android.content.res.Configuration#screenLayout} 設定欄位,該欄位會指出螢幕為小螢幕、一般螢幕或大螢幕。 + +</p> + </td> + </tr> + <tr id="ScreenAspectQualifier"> + <td>螢幕外觀</td> + <td> + <code>long</code><br/> + <code>notlong</code> + </td> + <td> + <ul class="nolist"> + <li>{@code long}:長螢幕,例如 WQVGA、WVGA、FWVGA</li> + <li>{@code notlong}:非長螢幕,例如 QVGA、HVGA、VGA</li> + </ul> + <p>已新增至 API 級別 4。<em></em></p> + <p>這純粹是依照螢幕的外觀比例來判定 (「長」螢幕是較寬的螢幕)。這與螢幕方向無關。 +</p> + <p>另請查看 {@link android.content.res.Configuration#screenLayout} 設定欄位,該欄位會指出螢幕是否為長螢幕。 +</p> + </td> + </tr> + <tr id="OrientationQualifier"> + <td>螢幕方向</td> + <td> + <code>port</code><br/> + <code>land</code> <!-- <br/> + <code>square</code> --> + </td> + <td> + <ul class="nolist"> + <li>{@code port}:裝置的方向為直向 (垂直)</li> + <li>{@code land}:裝置的方向為橫向 (水平)</li> + <!-- Square mode is currently not used. --> + </ul> + <p>如果使用者旋轉螢幕,此設定就會在應用程式生命週期內發生變更。 +請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + <p>另請查看 {@link android.content.res.Configuration#orientation} 設定欄位,該欄位會指出目前的裝置方向。 +</p> + </td> + </tr> + <tr id="UiModeQualifier"> + <td>UI 模式</td> + <td> + <code>car</code><br/> + <code>desk</code><br/> + <code>television</code><br/> + <code>appliance</code> + <code>watch</code> + </td> + <td> + <ul class="nolist"> + <li>{@code car}:在車用座架顯示的裝置</li> + <li>{@code desk}:在桌面座架顯示的裝置</li> + <li>{@code television}:在電視上顯示的裝置,提供在離使用者很遠的大螢幕上顯示 UI 的「十英呎」體驗,主要透過 DPAD 或其他非指標互動指定方向。 + + +</li> + <li>{@code appliance}:做為設備使用的裝置,沒有顯示器 +</li> + <li>{@code watch}:裝置有佩戴在手腕上的顯示器</li> + </ul> + <p>已新增至 API 級別 8、電視已新增至 API 13、手錶已新增至 API 20。<em></em></p> + <p>如要瞭解從座架插入或移除裝置時,您的應用程式可以如何回應的相關資訊,請參閱<a href="{@docRoot}training/monitoring-device-state/docking-monitoring.html">判斷和監控座架狀態和類型</a>。 + +</p> + <p>如果使用者將裝置放入座架,此設定就會在應用程式生命週期內發生變更。 +您可以使用 {@link +android.app.UiModeManager} 啟用或停用這些模式。請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + </td> + </tr> + <tr id="NightQualifier"> + <td>夜間模式</td> + <td> + <code>night</code><br/> + <code>notnight</code> + </td> + <td> + <ul class="nolist"> + <li>{@code night}:夜間時間</li> + <li>{@code notnight}:日間模式</li> + </ul> + <p>已新增至 API 級別 8。<em></em></p> + <p>如果從自動模式 (預設) 的夜間模式離開,在這種情況下模式會隨著日間時間變更,此設定就會在應用程式生命週期內發生變更。 +您可以使用 {@link android.app.UiModeManager} 啟用或停用這個模式。 +請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + </td> + </tr> + <tr id="DensityQualifier"> + <td>螢幕像素密度 (dpi)</td> + <td> + <code>ldpi</code><br/> + <code>mdpi</code><br/> + <code>hdpi</code><br/> + <code>xhdpi</code><br/> + <code>xxhdpi</code><br/> + <code>xxxhdpi</code><br/> + <code>nodpi</code><br/> + <code>tvdpi</code> + </td> + <td> + <ul class="nolist"> + <li>{@code ldpi}:低密度螢幕;約 120dpi。</li> + <li>{@code mdpi}:中密度 (在傳統 HVGA) 螢幕;約 160dpi。 +</li> + <li>{@code hdpi}:高密度螢幕;約 240dpi。</li> + <li>{@code xhdpi}:特高密度螢幕;約 320dpi。已新增至 API 級別 8。 +<em></em></li> + <li>{@code xxhdpi}:特特高密度螢幕;約 480dpi。已新增至 API 級別 16。 +<em></em></li> + <li>{@code xxxhdpi}:特特特高密度使用 (僅限啟動器圖示,請參閱支援多個螢幕中的<a href="{@docRoot}guide/practices/screens_support.html#xxxhdpi-note">注意</a><em></em>);約 640dpi。 + +已新增至 API 級別 18。 +<em></em></li> + <li>{@code nodpi}:如果您不想縮放點陣圖資源以符合裝置密度,可使用此設定。 +</li> + <li>{@code tvdpi}:介於 mdpi 和 hdpi 的螢幕;約 213dpi。這不屬於「主要」密度群組。 +這大部分用於電視,大多數應用程式應該不需要用到 —mdpi 和 hdpi 資源對大多數應用程式已經足夠,系統將視需要縮放它們。 + +這個限定詞由 API 級別 13 導入。</li> + </ul> + <p>六個主要密度之間有一個 3:4:6:8:12:16 縮放比例 (忽略tvdpi 密度)。 +因此,ldpi 的 9x9 點陣圖等同於 mdpi 的 12x12 點陣圖、hdpi 的 18x18 點陣圖、xhdpi 的 24x24 點陣圖,以此類推。 +</p> + <p>如果您覺得影像資源在電視或其他特定裝置上看起來不夠美觀,而想嘗試 tvdpi 資源,比例因數為 1.33*mdpi。 +例如,mdpi 螢幕上的 100px x 100px 影像在 tvdpi 螢幕應該是 133px x 133px。 +</p> + <p class="note"><strong>注意:</strong>使用密度限定詞不代表資源僅適用於該密度的螢幕。 +<em></em>如果您提供的替代資源沒有更符合目前裝置設定的限定詞,系統將使用<a href="#BestMatch">最符合</a>的資源。 + +</p> + <p>請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>,深入瞭解如何處理不同的螢幕密度,以及 Android會如何縮放點陣圖以符合目前的密度。 + +</p> + </td> + </tr> + <tr id="TouchscreenQualifier"> + <td>觸控螢幕類型</td> + <td> + <code>notouch</code><br/> + <code>finger</code> + </td> + <td> + <ul class="nolist"> + <li>{@code notouch}:沒有觸控螢幕的裝置。</li> + <li>{@code finger}:裝置含有觸控螢幕,其目的是透過使用者的手指進行方向互動。 +</li> + </ul> + <p>另請查看 {@link android.content.res.Configuration#touchscreen} 設定欄位,該欄位會指出裝置的觸控螢幕類型。 +</p> + </td> + </tr> + <tr id="KeyboardAvailQualifier"> + <td>鍵盤可用性</td> + <td> + <code>keysexposed</code><br/> + <code>keyshidden</code><br/> + <code>keyssoft</code> + </td> + <td> + <ul class="nolist"> + <li>{@code keysexposed}:裝置包含鍵盤。如果裝置啟用軟體鍵盤 (可能性很高),即使沒有將硬體鍵盤展示給使用者或甚至裝置沒有硬體鍵盤時,都可使用此設定。 + +<em></em>如果沒有提供或已停用軟體鍵盤,則只能在展示硬體鍵盤時使用此設定。 + +</li> + <li>{@code keyshidden}:裝置有可用的硬體鍵盤但已隱藏起來,且裝置沒有啟用軟體鍵盤。 +<em></em><em></em></li> + <li>{@code keyssoft}:裝置已啟用軟體鍵盤,無論是可見或不可見。 +</li> + </ul> + <p>如果您提供 <code>keysexposed</code> 資源但沒有 <code>keyssoft</code>資源,只要系統已啟用軟體鍵盤,無論鍵盤是否可見,系統都會使用 <code>keysexposed</code> 資源。 + +</p> + <p>如果使用者開啟硬體鍵盤,此設定就會在應用程式生命週期內發生變更。 +請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + <p>另請查看設定欄位 {@link +android.content.res.Configuration#hardKeyboardHidden} 和 {@link +android.content.res.Configuration#keyboardHidden},這兩個欄位會分別指出硬體鍵盤的可見度,以及任何其他類型鍵盤 (包含軟體) 的可見度。 +</p> + </td> + </tr> + <tr id="ImeQualifier"> + <td>主要文字輸入方式</td> + <td> + <code>nokeys</code><br/> + <code>qwerty</code><br/> + <code>12key</code> + </td> + <td> + <ul class="nolist"> + <li>{@code nokeys}:裝置沒有硬體按鍵可輸入文字。</li> + <li>{@code qwerty}:裝置有硬體 qwerty 軟體鍵盤,無論使用者是否可見。 + +</li> + <li>{@code 12key}:裝置有硬體 12 鍵鍵盤,無論使用者是否可見。 +</li> + </ul> + <p>另請查看 {@link android.content.res.Configuration#keyboard} 設定欄位,該欄位會指出可用的主要文字輸入方式。 +</p> + </td> + </tr> + <tr id="NavAvailQualifier"> + <td>導覽鍵可用性</td> + <td> + <code>navexposed</code><br/> + <code>navhidden</code> + </td> + <td> + <ul class="nolist"> + <li>{@code navexposed}:使用者可使用導覽鍵。</li> + <li>{@code navhidden}:不提供導覽鍵 (例如,位於密合蓋子的後方)。 +</li> + </ul> + <p>如果使用者顯示導覽鍵,此設定就會在應用程式生命週期內發生變更。 +請參閱<a href="runtime-changes.html">處理執行階段變更</a>,以瞭解這會如何在執行階段期間影響您的應用程式。 +</p> + <p>另請查看 {@link android.content.res.Configuration#navigationHidden} 設定欄位,該欄位會指出導覽鍵是否隱藏起來。 +</p> + </td> + </tr> + <tr id="NavigationQualifier"> + <td>主要的非觸控導覽方式</td> + <td> + <code>nonav</code><br/> + <code>dpad</code><br/> + <code>trackball</code><br/> + <code>wheel</code> + </td> + <td> + <ul class="nolist"> + <li>{@code nonav}:裝置除了使用觸控螢幕之外沒有其他導覽設施。 +</li> + <li>{@code dpad}:裝置有導覽用的方向鍵 (d-pad)。</li> + <li>{@code trackball}:裝置有導覽用的軌跡球。</li> + <li>{@code wheel}:裝置有導覽用的方向輪 (不常見)。</li> + </ul> + <p>另請查看 {@link android.content.res.Configuration#navigation} 設定欄位,該欄位會指出可用的導覽方式類型。 +</p> + </td> + </tr> +<!-- DEPRECATED + <tr> + <td>Screen dimensions</td> + <td>Examples:<br/> + <code>320x240</code><br/> + <code>640x480</code><br/> + etc. + </td> + <td> + <p>The larger dimension must be specified first. <strong>This configuration is deprecated +and should not be used</strong>. Instead use "screen size," "wider/taller screens," and "screen +orientation" described above.</p> + </td> + </tr> +--> + <tr id="VersionQualifier"> + <td>平台版本 (API 級別)</td> + <td>範例:<br/> + <code>v3</code><br/> + <code>v4</code><br/> + <code>v7</code><br/> + 等等。</td> + <td> + <p>裝置支援的 API 級別。例如,<code>v1</code> 代表 API 級別1 (裝置安裝 Android 1.0 以上版本),以及 <code>v4</code> 代表 API 級別 4 (裝置安裝 Android1.6 以上版本)。 + +請參閱 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Android API 級別</a>文件以取得有關這些值的詳細資訊。 +</p> + </td> + </tr> +</table> + + +<p class="note"><strong>注意:</strong>有些設定限定詞從 Android1.0 就已新增,因此並非所有 Android 版本都支援所有限定詞。 +使用新的限定詞會以隱含的方式新增平台版本的限定詞,因此較舊的裝置一定會忽略它。 +例如,使用 + <code>w600dp</code> 限定詞最自動包含 <code>v13</code> 限定詞,因為可用寬度限定詞是 API 級別 13 的新項目。 +為了避免發生任何問題,永遠要包含一組預設資源 (一組沒有限定詞的資源<em></em>)。 +如需詳細資訊,請參閱<a href="#Compatibility">使用資源提供最佳的裝置相容性</a>一節。 + +</p> + + + +<h3 id="QualifierRules">限定詞名稱規則</h3> + +<p>這裡有一些使用設定限定詞名稱的規則:</p> + +<ul> + <li>您可以為一組資源指定多個限定詞,以破折號分隔。例如, +<code>drawable-en-rUS-land</code> 適用於橫向的美國英文裝置。 +</li> + <li>限定詞的順序必須與<a href="#table2">表 2</a> 所列的一樣。例如: + + <ul> + <li>錯誤:<code>drawable-hdpi-port/</code></li> + <li>正確:<code>drawable-port-hdpi/</code></li> + </ul> + </li> + <li>替代資源目錄不可為巢狀。例如,您不能有 +<code>res/drawable/drawable-en/</code>。</li> + <li>值有大小寫之分。資源編譯器會將目錄名稱轉換為小寫再處理,以避免有大小寫之分的檔案系統發生問題。 + +名稱中的任何大寫只是為了方便閱讀。</li> + <li>每個限定詞類型只支援一個值。例如,如果您想在西班牙文和法文使用相同的可繪項目檔案,則不能將目錄命名為 <em></em> +<code>drawable-rES-rFR/</code>。 +您必須有兩個資源目錄,例如 +<code>drawable-rES/</code> 和 <code>drawable-rFR/</code>,這兩個目錄要包含適當的檔案。 +但是,您不需要實際將相同的檔案複製到這兩位置。您可以為資源建立別名。 +請參閱下方的 +<a href="#AliasResources">建立別名資源</a>。</li> +</ul> + +<p>當您將替代資源儲存到以這些限定詞命名的目錄後,Android 會根據目前的裝置設定自動將資源套用到您的應用程式。 + +每次要求資源時,Android 會檢查包含要求之資源檔案的替代資源目錄,然後<a href="#BestMatch">尋找最符合的資源 +</a> (下方有相關討論)。 +如果沒有符合特定裝置設定的替代資源,則 Android 會使用對應的預設資源 (特定資源類型的資源組,不含設定限定詞)。 + + +</p> + + + +<h3 id="AliasResources">建立別名資源</h3> + +<p>如果您要將資源用於一個以上的裝置設定 (但不想以預設資源的形式提供),您不需要將相同的資源放入一個以上的替代資源目錄。 + +您可以 (在某些情況下) 建立一個替代資源,將它做為儲存在您預設資源目錄的資源別名。 + +</p> + +<p class="note"><strong>注意:</strong>並非所有資源都提供為其他資源建立別名的機制。 +特別是動畫、選單、原始項目和其他在 {@code xml/} 目錄中未指定的資源都不提供這項功能。 +</p> + +<p>例如,想像您有一個應用程式圖示 {@code icon.png},且必須為不同的地區設定提供唯一的版本。 +但是,加拿大英文和加拿大法文這兩個地區設定必須使用相同的版本。 +您可能會假設必須在加拿大英文和加拿大法文這兩個資源目錄中複製相同的影像,但並不需要這樣做。 + +您可以將這兩個地區設定的影像儲存成 {@code icon_ca.png} ({@code icon.png} 以外的任何名稱),然後將該影像放入預設的 {@code res/drawable/} 目錄。 + +之後,在 {@code +res/drawable-en-rCA/} 建立一個 {@code icon.xml} 檔案,以及建立 {@code res/drawable-fr-rCA/},用於參照使用 {@code <bitmap>} 元素的 {@code icon_ca.png} 資源。 +這樣可以讓您只儲存一個版本的 PNG 檔案,以及指向該檔案的兩個小 XML 檔案。 +(範例 XML 檔案如下所示。)</p> + + +<h4>可繪項目</h4> + +<p>如要為現有的可繪項目建立別名,請使用 {@code <bitmap>} 元素。 +例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<bitmap xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/icon_ca" /> +</pre> + +<p>如果您將此檔案儲存成 {@code icon.xml} (在替代資源目錄中,像是 +{@code res/drawable-en-rCA/}),系統會將它編譯成可當作 {@code R.drawable.icon} 參照的資源,但它實際上是 {@code +R.drawable.icon_ca} 資源 (儲存在 {@code res/drawable/}) 的別名。 +</p> + + +<h4>版面配置</h4> + +<p>如要為現有的可繪項目建立別名,請使用以 {@code <merge>} 包裝的{@code <include>} 元素。 +例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<merge> + <include layout="@layout/main_ltr"/> +</merge> +</pre> + +<p>如果您將此檔案儲存成 {@code main.xml},系統會將它編譯成可當作 {@code R.layout.main} 參照的資源,但它實際上是 {@code R.layout.main_ltr} 資源的別名。 + +</p> + + +<h4>字串和其他簡單值</h4> + +<p>如要為現有的字串建立別名,只要將所需字串的資源 ID 當作新字串的值即可。 +例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="hello">Hello</string> + <string name="hi">@string/hello</string> +</resources> +</pre> + +<p>{@code R.string.hi} 資源現在是 {@code R.string.hello} 的別名。</p> + +<p> <a href="{@docRoot}guide/topics/resources/more-resources.html">其他簡易值</a>的運作方法也一樣。 +例如,一個顏色:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="yellow">#f00</color> + <color name="highlight">@color/red</color> +</resources> +</pre> + + + + +<h2 id="Compatibility">使用資源提供最佳的裝置相容性</h2> + +<p>如要讓您的應用程式支援多個裝置設定,務必要為您應用程式使用的每個資源類型提供預設資源。 +</p> + +<p>例如,如果您的應用程式支援多個語言,一律要包含一個 {@code +values/} 目錄 (儲存字串的位置),其中「不包含」<em></em><a href="#LocaleQualifier">語言和區域限定詞</a>。如果您將所有字串檔案放在含有語言和區域限定詞的目錄中,當執行應用程式的裝置,其語言設定不受字串支援時,應用程式就會當機。 + +不過,只要您提供預設 {@code values/} 資源, +應用程式就能正常運作 (即便使用者不懂該語言 — 仍然比當機好)。 +</p> + +<p>同樣地,如果您根據螢幕方向提供不同的版面配置資源,您應該選擇一個方向做為您的預設值。 +例如,不要在 {@code +layout-land/} 提供橫向版面配置資源及在 {@code layout-port/} 提供直向版面配置資源,而是留下一個做為預設值,例如 {@code layout/} 做為橫向和 {@code layout-port/} 做為直向。 +</p> + +<p>提供預設資源很重要,這不只是因為您的應用程式可能執行您未預期到的設定,還因為新的 Android 版本有時候會新增舊版不支援的設定限定詞。 + +如果您使用新的資源限定詞,但與舊版 Android 保持程式碼相容,則舊版 Android 執行您的應用程式時,如果您沒有提供預設資源,應用程式就會當機,這是因為它無法使用以新限定詞命名的資源。 + + +例如,如果您的 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code +minSdkVersion}</a> 設為 4,而且您讓所有可繪項目資源具備使用<a href="#NightQualifier">夜間模式</a>的資格 (已新增到 API 級別 8 的 {@code night} 或 {@code notnight}),則 API 級別 4 裝置將無法存取您的可繪項目資源且會當機。 +在這種情況下,您可能會想要將 {@code notnight} 當作預設資源,因此您應該要排除該限定詞,讓您的可繪項目資源成為 {@code drawable/} 或 {@code drawable-night/}。 + +</p> + +<p>因此,如要提供最佳的裝置相容性,請務必針對應用程式正常執行所需的資源提供預設資源。 +然後,使用設定限定詞為特定裝置設定建立替代資源。 +</p> + +<p>這個規則有一個例外狀況:如果您應用程式的 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> 為 4 或高,當您提供含有<em></em><a href="#DensityQualifier">螢幕密度</a>限定詞的替代可繪項目資源時,則不需要預設可繪項目資源。 + +即使沒有預設可繪項目資源,Android 仍然可從替代螢幕密度中找到最符合的項目,並視需要縮放點陣圖。 + +不過,為了在所有裝置類型獲得最佳體驗,您應該為這三種密度類型提供替代可繪項目。 +</p> + + + +<h2 id="BestMatch">Android 如何尋找最相符的資源</h2> + +<p>當您要求已提供替代項目的資源時,Android 會根據目前的裝置設定,選擇執行階段要使用的替代資源。 +為了示範 Android 如何選取替代資源,假設下列可繪項目目錄分別包含相同影像的不同版本: + +</p> + +<pre class="classic no-pretty-print"> +drawable/ +drawable-en/ +drawable-fr-rCA/ +drawable-en-port/ +drawable-en-notouch-12key/ +drawable-port-ldpi/ +drawable-port-notouch-12key/ +</pre> + +<p>同時假設裝置設定如下:</p> + +<p style="margin-left:1em;"> +地區設定 = <code>en-GB</code> <br/> +螢幕方向 = <code>port</code> <br/> +螢幕像素密度 = <code>hdpi</code> <br/> +觸控螢幕類型 = <code>notouch</code> <br/> +主要文字輸入方式 = <code>12key</code> +</p> + +<p>Android 透過比對裝置設定和可用的替代資源,從 {@code drawable-en-port} 選取可繪項目。 +</p> + +<p>系統使用下列邏輯決定要使用的資源: +</p> + + +<div class="figure" style="width:371px"> +<img src="{@docRoot}images/resources/res-selection-flowchart.png" alt="" height="471" /> +<p class="img-caption"><strong>圖 2.</strong>Android 如何尋找最相符資源的流程圖。 +</p> +</div> + + +<ol> + <li>排除與裝置設定衝突的資源檔案。 + <p>已排除 <code>drawable-fr-rCA/</code> 目錄,因為它與 <code>en-GB</code> 地區設定衝突。 +</p> +<pre class="classic no-pretty-print"> +drawable/ +drawable-en/ +<strike>drawable-fr-rCA/</strike> +drawable-en-port/ +drawable-en-notouch-12key/ +drawable-port-ldpi/ +drawable-port-notouch-12key/ +</pre> +<p class="note"><strong>例外狀況:</strong>螢幕像素密度是唯一沒有因為衝突而被排除的限定詞。 +即使裝置的螢幕密度是 hdpi 但仍然沒有排除 +<code>drawable-port-ldpi/</code>,因為此刻每個螢幕密度都視為相符。 +如需詳細資訊,請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>文件。 +</p></li> + + <li>在清單 (<a href="#table2">表 2</a>) 中選擇 (下一個) 最高優先等級的限定詞(從 MCC 往下移動)。 + </li> + <li>有任何資源目錄包含此限定詞嗎? </li> + <ul> + <li>如果沒有,回到步驟 2 查看下一個限定詞。(在此範例中,回答一直是「否」,直到語言限定詞為止。) +</li> + <li>如果有,繼續到步驟 4。</li> + </ul> + </li> + + <li>排除不包含此限定詞的來源目錄。在此範例中,系統排除所有不包含語言限定詞的目錄: +</li> +<pre class="classic no-pretty-print"> +<strike>drawable/</strike> +drawable-en/ +drawable-en-port/ +drawable-en-notouch-12key/ +<strike>drawable-port-ldpi/</strike> +<strike>drawable-port-notouch-12key/</strike> +</pre> +<p class="note"><strong>例外狀況:</strong>如果提到的限定詞是螢幕像素密度,Android 會選擇最符合裝置螢幕密度的選項。一般而言,Android 偏好縮小較大的原始影像以放大較小的原始影像。 + + +請參閱<a href="{@docRoot}guide/practices/screens_support.html">支援多個螢幕</a>。 +</p> + </li> + + <li>重複步驟 2、3 和 4,直到剩下一個目錄為止。在此範例中,螢幕方向是下個有相符項目的限定詞。 +因此,將排除沒有指定螢幕方向的資源: + +<pre class="classic no-pretty-print"> +<strike>drawable-en/</strike> +drawable-en-port/ +<strike>drawable-en-notouch-12key/</strike> +</pre> +<p>剩下的目錄是 {@code drawable-en-port}。</p> + </li> +</ol> + +<p>雖然會針對每個要求的資源執行此程序,但系統還是會進一步最佳化某些方面。 +其中一個最佳化是一旦知道裝置設定,它將排除永遠無法符合的替代資源。 +例如,如果設定語言是英文 ("en"),則語言限定詞設定為不是英文的任何資源目錄永遠都不會包含在檢查的資源集區內 (但仍會包含<em>不含</em>語言限定詞的資源目錄)。 + + +</p> + +<p>根據螢幕大小限定詞選取資源時,如果沒有最符合的資源,系統會使用適用於比目前螢幕更小之螢幕設計的資源 (例如,如有需要,大型螢幕會使用一般大小螢幕資源)。 + +然而,如果唯一可用的資源比目前螢幕<em>更大</em>,系統將<strong>不會</strong>使用這些資源,如果沒有其他資源符合裝置設定,則應用程式將會當機 (例如,如果所有版面配置資源都標記為 {@code xlarge} 限定詞,但裝置為一般大小螢幕)。 + + + +</p> + +<p class="note"><strong>注意:</strong>限定詞的優先等級<em></em> (在<a href="#table2">表 2</a>) 比完全符合裝置的限定詞數量更重要。 +例如,在上述步驟 4,清單的最後一個選擇包含三個完全符合裝置的限定詞 (方向、觸控螢幕類型和輸入方法),而 <code>drawable-en</code> 只有一個相符的參數(語言)。 + + +然而,語言的優先等級高於這些其他限定詞,因此將排除 +<code>drawable-port-notouch-12key</code>。</p> + +<p>如需深入瞭解在應用程式使用資源的方法,請繼續閱讀<a href="accessing-resources.html">存取資源</a>。</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd new file mode 100644 index 000000000000..7a8b3ae7fc3a --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=處理執行階段變更 +page.tags=Activity、生命週期 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>本文件內容</h2> + <ol> + <li><a href="#RetainingAnObject">變更設定期間保留物件</a></li> + <li><a href="#HandlingTheChange">自行處理設定變更</a> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="providing-resources.html">提供資源</a></li> + <li><a href="accessing-resources.html">存取資源</a></li> + <li><a href="http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html">快速的螢幕方向變更 +</a></li> + </ol> +</div> +</div> + +<p>有些裝置設定可以在執行階段期間進行變更 (例如,螢幕方向、鍵盤可用性和語言)。 +進行這類變更時,Android 會重新啟動執行中的 +{@link android.app.Activity} (呼叫 {@link android.app.Activity#onDestroy()},後面加上 {@link +android.app.Activity#onCreate(Bundle) onCreate()})。 +重新啟動行為的設計是以符合新裝置設定的替代資源自動重新載入您的應用程式,以協助您的應用程式適應新的設定。 + +</p> + +<p>如要正確處理重新啟動,務必透過一般 <a href="{@docRoot}guide/components/activities.html#Lifecycle">Activity 生命週期</a>將您的 Activity 還原為之前的狀態,其中 Android 會先呼叫 +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} 再終結您的 Activity,讓您能儲存應用程式狀態的相關資料。 + + +之後,您便能在 {@link android.app.Activity#onCreate(Bundle) onCreate()} 或 {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()} 期間還原狀態。 +</p> + +<p>如要測試您的應用程式是否使用原本的應用程式狀態重新啟動,您應該在應用程式執行各種工作時呼叫設定變更 (例如變更螢幕方向)。 + +您的應用程式應該能隨時重新啟動而不會遺失使用者資料或狀態,以便處理設定變更這類事件,或是使用者接聽來電,然後很久後才在應用程式程序可能終結後才返回應用程式的這類情況。 + + +如要瞭解如何還原您的 Activity 狀態,請參閱 <a href="{@docRoot}guide/components/activities.html#Lifecycle">Activity 生命週期</a>。</p> + +<p>不過,您可能遇到重新啟動應用程式以及還原大量資料的成本很昂貴,而且會產生使用者體驗不佳的情況。 +在這種情況下,您有兩種其他選擇: +</p> + +<ol type="a"> + <li><a href="#RetainingAnObject">變更設定期間保留物件</a> + <p>允許您的 Activity 在設定變更時重新啟動,但將可設定狀態的物件帶到 Activity 的新執行個體中。 +</p> + + </li> + <li><a href="#HandlingTheChange">自行處理設定變更</a> + <p>避免系統在某些設定變更期間重新啟動您的 Activity,但在設定變更時接收回呼,您便能視需要手動更新您的 Activity。 + +</p> + </li> +</ol> + + +<h2 id="RetainingAnObject">變更設定期間保留物件</h2> + +<p>如果重新啟動您的 Activity 需要復原大量資料、重新建立網路連線或執行其他密集型操作,則由於設定變更造成的完整重新啟動可能會拖慢使用者體驗。 + +此外,您可能無法透過 {@link android.os.Bundle} 完全還原您的 Activity 狀態,這是系統透過 {@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} 回呼所為您所儲存的—它的設計並不是用來傳送大型物件 (例如點陣圖),而且其中的資料必須先序列化再還原序列化,這會耗用大量記憶體並讓設定變更變慢。 + + + +在這種情況下,您可以在 Activity 因設定變更而重新啟動時,透過保留 {@link +android.app.Fragment} 的方式來減少重新初始化 Activity 的負擔。 +這個片段可包含您要保留可設定狀態物件的參考資料。 +</p> + +<p>當 Android 系統因設定變更而關閉您的 Activity 時,您標示要保留的 Activity 片段不會被終結。 +您可以將這類片段新增至您的 Activity 以保留可設定狀態的物件。 +</p> + +<p>如要在執行階段設定變更期間,在片段中保留可設定狀態的物件:</p> + +<ol> + <li>延伸 {@link android.app.Fragment} 類別並宣告可設定狀態物件的參考資料。 +</li> + <li>片段建立之後,呼叫 {@link android.app.Fragment#setRetainInstance(boolean)}。 + </li> + <li>將片段新增至您的 Activity。</li> + <li>當 Activity 重新啟動時,使用 {@link android.app.FragmentManager} 擷取片段。 +</li> +</ol> + +<p>例如,將您的片段定義如下:</p> + +<pre> +public class RetainedFragment extends Fragment { + + // data object we want to retain + private MyDataObject data; + + // this method is only called once for this fragment + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // retain this fragment + setRetainInstance(true); + } + + public void setData(MyDataObject data) { + this.data = data; + } + + public MyDataObject getData() { + return data; + } +} +</pre> + +<p class="caution"><strong>注意:</strong>雖然您可以儲存任何物件,但您不應該傳送與 {@link android.app.Activity} 相關的物件,例如 {@link +android.graphics.drawable.Drawable}、{@link android.widget.Adapter}、{@link android.view.View} 或任何與 {@link android.content.Context} 關聯的其他物件。 + +如果您這樣做,會流失原始 Activity 執行個體的所有檢視和資源。 +(資源流失表示您的應用程式會繼續保留資源但無法回收記憶體,因此會流失大量記憶體。) + +</p> + +<p>然後使用 {@link android.app.FragmentManager} 將片段新增至您的 Activity。您可以在執行階段設定變更期間,於 Activity 再次啟動時,從片段取得資料物件。 + +例如,將您的 Activity 定義如下:</p> + +<pre> +public class MyActivity extends Activity { + + private RetainedFragment dataFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + // find the retained fragment on activity restarts + FragmentManager fm = getFragmentManager(); + dataFragment = (DataFragment) fm.findFragmentByTag(“data”); + + // create the fragment and data the first time + if (dataFragment == null) { + // add the fragment + dataFragment = new DataFragment(); + fm.beginTransaction().add(dataFragment, “data”).commit(); + // load the data from the web + dataFragment.setData(loadMyData()); + } + + // the data is available in dataFragment.getData() + ... + } + + @Override + public void onDestroy() { + super.onDestroy(); + // store the data in the fragment + dataFragment.setData(collectMyLoadedData()); + } +} +</pre> + +<p>在此範例中,{@link android.app.Activity#onCreate(Bundle) onCreate()} 會在 Activity 新增片段或還原參考資料。 +{@link android.app.Activity#onCreate(Bundle) onCreate()} 也會在片段執行個體內儲存可設定狀態的物件。 + +{@link android.app.Activity#onDestroy() onDestroy()} 會在保留的片段執行個體內更新可設定狀態的物件。 +</p> + + + + + +<h2 id="HandlingTheChange">自行處理設定變更</h2> + +<p>如果您的應用程式在特定設定變更期間不需要更新資源,「且」<em></em>您具有效能限制,要求您避免 Activity 重新啟動,則您可以宣告您的 Activity 自行處理設定變更,這樣可避免系統重新啟動您的 Activity。 + + +</p> + +<p class="note"><strong>注意:</strong>自行處理設定變更讓替代資源的使用變得更加困難,因為系統無法幫您自動套用。 + +當您必須避免因為設定變更而造成重新啟動時,應將這個方式視為最後手段,而且不建議對大部分的應用程式使用。 +</p> + +<p>如要宣告您的 Activity 處理設定變更,可在宣示說明檔案中編輯適當的 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 元素以包含 <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code +android:configChanges}</a> 屬性和值,代表您要處理的設定。 + +<a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code +android:configChanges}</a> 屬性的可能值列於文件中 (最常用的值是 {@code "orientation"},可避免在螢幕方向變更時重新啟動,而 {@code "keyboardHidden"} 可避免鍵盤可用性變更時重新啟動)。 + +您可以使用直立線符號 {@code |} 字元來分隔,在屬性中宣告多個設定值。 +</p> + +<p>例如,下列宣示說明程式碼宣告同時處理螢幕方向變更和鍵盤可用性變更的 Activity: +</p> + +<pre> +<activity android:name=".MyActivity" + android:configChanges="orientation|keyboardHidden" + android:label="@string/app_name"> +</pre> + +<p>現在,當其中一個設定變更時, {@code MyActivity} 便不會重新啟動。 +而是由 {@code MyActivity} 接收對 {@link android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 的呼叫。 +這個方法傳送一個 {@link android.content.res.Configuration} 物件,指定新裝置設定。 + +讀取 {@link android.content.res.Configuration} 中的欄位時,您可判斷新的設定,並且更新您介面中使用的資源來進行適當的變更。 + +此時,會呼叫這個方法,Activity 的 {@link android.content.res.Resources} 物件會根據新設定進行更新以傳回資源,因此您便能輕鬆地重新設定 UI 的元素,系統無需重新啟動您的 Activity。 + + +</p> + +<p class="caution"><strong>注意:</strong>從 Android 3.2 (API 級別 13) 開始,裝置在橫向與直向之間進行切換時,<strong>「螢幕大小」也會跟著變更</strong>。 + +因此,在開發 API 級別 13 或更高級別時 (如 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> 和 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> 屬性所宣告),如果您要避免因為方向變更而造成執行階段重新啟動,除了{@code +"orientation"} 值之外,您還必須包含 {@code "screenSize"} 值。 + +也就是說,您必須宣告 {@code +android:configChanges="orientation|screenSize"}。不過,如果您的應用程式是針對 API 級別 12 或更低級別,則您的 Activity 一律要自行處理這個設定變更 (即使在 Android 3.2 或更高版本的裝置上執行時,這個設定變更也不會重新啟動您的 Activity)。 + +</p> + +<p>例如,下列 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 實作會檢查目前的裝置方向: +</p> + +<pre> +@Override +public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Checks the orientation of the screen + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show(); + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ + Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>{@link android.content.res.Configuration} 物件代表目前所有的設定,而不只是已變更的設定。 +大多數情況下,您不用特別留意設定如何變更,只要重新指派所有的資源,提供替代項目給您正在處理的設定。 + +例如,因為 {@link +android.content.res.Resources} 物件現在已更新,您可以使用 {@link android.widget.ImageView#setImageResource(int) +setImageResource()} 重新設定任何 {@link android.widget.ImageView}並且針對新設定,使用適當的資源 (如<a href="providing-resources.html#AlternateResources">提供資源</a>中所述)。 + +</p> + +<p>請注意,{@link +android.content.res.Configuration} 欄位的值,是與 {@link android.content.res.Configuration} 類別的特定常數相符的整數。 +如需與每個欄位搭配使用之常數的相關文件,請參閱 {@link +android.content.res.Configuration} 參考資料中的適當欄位。 +</p> + +<p class="note"><strong>請記住:</strong>當您宣告您的 Activity 以處理設定變更時,您要負責為提供的替代項目重新設定所有元素。 +如果您宣告您的 Activity 以處理方向變更,而且具有應該在橫向與直向之間進行方向變更的影像,則您必須在 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 期間對每個元素重新指派每個資源。 + +</p> + +<p>如果您不需要根據這些設定變更來更新您的應用程式,可改為不必<em></em>實作 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}。 +在此情況下,仍然可使用設定變更前使用的所有資源,而您只要避免重新啟動您的 Activity 即可。 + +不過,您的應用程式應該能夠關閉以及重新啟動成之前原本的狀態,因此在一般 Activity 生命週期期間無法保留您的狀態時,就不應考慮使用這個方法。 + +這不只是因為有其他設定變更讓您無法防止重新啟動應用程式,還因為有您應該處理事件,例如當使用者離開應用程式後,使用者返回應用程式之前應用程式已終結。 + + +</p> + +<p>如需有關您在 Activity 中可以處理哪些設定變更的詳細資訊,請參閱 <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code +android:configChanges}</a> 文件和 {@link android.content.res.Configuration} 類別。 +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd new file mode 100644 index 000000000000..0f27ae4e8803 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=輸入控制項 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +<div class="figure" style="margin:0"> + <img src="{@docRoot}images/ui/ui-controls.png" alt="" style="margin:0" /> +</div> + +<p>輸入控制項是應用程式使用者介面中的互動元件。Android 提供了多種控制項讓您在 UI 中使用,例如按鈕、文字欄位、搜尋列、核取方塊、縮放按鈕、切換按鈕等。 + +</p> + +<p>在使用者介面中加入輸入控制項,就如同將 XML 元素加到 <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML 版面配置</a>一樣簡單。例如,以下是包含文字欄位和按鈕的版面配置: +</p> + +<pre style="clear:right"> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + <EditText android:id="@+id/edit_message" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:hint="@string/edit_message" /> + <Button android:id="@+id/button_send" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/button_send" + android:onClick="sendMessage" /> +</LinearLayout> +</pre> + +<p>每個輸入控制項均支援一組特定輸入事件,方便您處理使用者輸入文字或輕觸按鈕等事件。 +</p> + + +<h2 id="CommonControls">一般控制項</h2> +<p>下方列出您可在應用程式中使用的部分一般控制項。只要點進清單中的相關連結,即可進一步瞭解如何使用這些控制項。 +</p> + +<p class="note"><strong>注意:</strong>本文並未列出 Android 提供的部分控制項。 +如果想查看未列出的控制項,請瀏覽 {@link android.widget} 套件。如果您的應用程式需要特定類型的輸入控制項,您可以自行建置<a href="{@docRoot}guide/topics/ui/custom-components.html">自訂元件</a>。 +</p> + +<table> + <tr> + <th scope="col">控制項類型</th> + <th scope="col">說明</th> + <th scope="col">相關類別</th> + </tr> + <tr> + <td><a href="controls/button.html">按鈕</a></td> + <td>可供使用者按下或點擊來執行某項動作的按鈕。</td> + <td>{@link android.widget.Button Button} </td> + </tr> + <tr> + <td><a href="controls/text.html">文字欄位</a></td> + <td>可編輯的文字欄位。您可以使用 <code>AutoCompleteTextView</code> 小工具建立可提供自動完成建議的文字輸入小工具。</td> + <td>{@link android.widget.EditText EditText}、{@link android.widget.AutoCompleteTextView}</td> + </tr> + <tr> + <td><a href="controls/checkbox.html">核取方塊</a></td> + <td>可供使用者切換的開啟/關閉開關。如果想為使用者提供一組互不相斥的可選取選項時,請使用核取方塊。</td> + <td>{@link android.widget.CheckBox CheckBox} </td> + </tr> + <tr> + <td><a href="controls/radiobutton.html">圓形按鈕</a></td> + <td>功用與核取方塊類似,但會限制使用者只能從一組選項中選取一個選項。</td> + <td>{@link android.widget.RadioGroup RadioGroup} + <br>{@link android.widget.RadioButton RadioButton} </td> + </tr> + <tr> + <td><a href="controls/togglebutton.html" style="white-space:nowrap">切換按鈕</a></td> + <td>附有亮光指標的開啟/關閉按鈕。</td> + <td>{@link android.widget.ToggleButton ToggleButton} </td> + </tr> + <tr> + <td><a href="controls/spinner.html">微調按鈕</a></td> + <td>可供使用者從一組選項中選取單一值的下拉式清單。</td> + <td>{@link android.widget.Spinner Spinner} </td> + </tr> + <tr> + <td><a href="controls/pickers.html">挑選器</a></td> + <td>可供使用者透過向上/向下按鈕或滑動手勢選取單一值的對話方塊。此外,挑選器還會提供 <code>DatePicker</code> 程式碼小工具和 <code>TimePicker</code> 小工具,分別讓使用者輸入日期值 (年、月、日) 以及時間值 (小時、分鐘、AM/PM);系統會自動根據使用者所在的地區為這些值設定對應的格式。</td> + <td>{@link android.widget.DatePicker}、{@link android.widget.TimePicker}</td> + </tr> +</table> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd new file mode 100644 index 000000000000..72755715e3a2 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=版面配置 +page.tags=view,viewgroup +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> +<ol> + <li><a href="#write">編寫 XML</a></li> + <li><a href="#load">載入 XML 資源</a></li> + <li><a href="#attributes">屬性</a> + <ol> + <li><a href="#id">ID</a></li> + <li><a href="#layout-params">版面配置參數</a></li> + </ol> + </li> + <li><a href="#Position">版面配置位置</a></li> + <li><a href="#SizePaddingMargins">大小、邊距和邊界</a></li> + <li><a href="#CommonLayouts">常見版面配置</a></li> + <li><a href="#AdapterViews">使用配接器建置版面配置</a> + <ol> + <li><a href="#FillingTheLayout">將資料填入配接器檢視</a></li> + <li><a href="#HandlingUserSelections">處理點擊事件</a></li> + </ol> + </li> +</ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.view.View}</li> + <li>{@link android.view.ViewGroup}</li> + <li>{@link android.view.ViewGroup.LayoutParams}</li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="{@docRoot}training/basics/firstapp/building-ui.html">建置簡易使用者 +介面</a></li> </div> +</div> + +<p>版面配置會定義使用者介面 (例如 <a href="{@docRoot}guide/components/activities.html">Activity</a> 或<a href="{@docRoot}guide/topics/appwidgets/index.html">應用程式小工具</a>的使用者介面) 的視覺結構。您可以兩種方式宣告版面配置: +</p> +<ul> +<li><strong>在 XML 中宣告 UI 元素</strong>。Android 提供的 XML 字彙不僅簡單明瞭,還可對應至 View 類別和子類別 (例如小工具和版面配置)。 +</li> +<li><strong>在執行階段啟動版面配置元素</strong>。您的應用程式可透過程式建立 View 和 ViewGroup 物件 (以及操控其屬性)。 + </li> +</ul> + +<p>Android 架構可讓您彈性使用上述任一或兩種方法來宣告及管理應用程式的 UI。例如,您可以在 XML 中宣告應用程式的預設版面配置,包括會顯示在應用程式中的元素及其屬性。接著,您可在執行階段為應用程式加入程式碼來修改螢幕物件的狀態,包括您在 XML 中宣告的物件。 </p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <ul> + <li><a href="{@docRoot}tools/sdk/eclipse-adt.html">Eclipse 專用的 ADT 外掛程式</a>可提供 XML 的版面配置預覽 — 只要開啟您想預覽的 XML 檔案,然後選取 [版面配置]<strong></strong> 標籤即可。 + +</li> + <li>同時也建議您使用 <a href="{@docRoot}tools/debugging/debugging-ui.html#hierarchyViewer">階層檢視器</a>工具針對版面配置進行除錯 — 這項工具會顯示版面配置屬性值、繪製線框並在其中加上編距/邊界標記,以及提供完整檢視 (如果您是透過模擬器或裝置進行除錯的話)。 + + + +</li> + <li><a href="{@docRoot}tools/debugging/debugging-ui.html#layoutopt">layoutopt</a> 工具可讓您快速分析版面配置和階層,以找出效能不佳等其他問題。 +</li> +</div> +</div> + +<p>在 XML 中宣告 UI 可讓您進一步將應用程式的顯示畫面與控制應用程式行為的程式碼區隔開來。這樣可將 UI 說明置於應用程式的程式碼外部,方便您修改或調整使用者介面說明,而不必修改並重新編譯原始碼。例如,您可以針對不同的螢幕方向、裝置螢幕大小和語言建立各種 XML 檔案。此外,在 XML 中宣告版面配置可協助以視覺效果呈現 UI 的結構,方便您進行除錯。針對上述優點,本文件著重於說明如何在 XML 中宣告版面配置。如果您想在執行階段啟動 View 物件,請參閱 {@link android.view.ViewGroup} 和 {@link android.view.View} 類別參考文件。 + +</p> + +<p>一般來說,用於宣告 UI 元素的 XML 字彙會遵從類別與方法的結構和名稱,其中元素名稱對應至類別名稱,而屬性名稱則對應至方法。事實上,這些對應關係十分直接,您可以輕易猜出某個 XML 屬性對應的哪個類別方法,或某個類別對應的特定 XML 元素為何。不過請注意,並非所有字彙均為相同。在部分情況下,您會發現某些不同的名稱。例如,EditText 元素包含對應至 <code>EditText.setText()</code> 的 <code>text</code> 屬性。 + + </p> + +<p class="note"><strong>提示:</strong>如要進一步瞭解各種版面配置類型,請參閱<a href="{@docRoot}guide/topics/ui/layout-objects.html">常見版面配置物件</a>。 +您也可以參閱 <a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a> 教學指南,取得一系列說明如何建置各種版面配置的教學課程。 +</p> + +<h2 id="write">編寫 XML</h2> + +<p>您可以使用 Android 的 XML 字彙,快速設計 UI 版面配置和其中包含的螢幕元素,方法與採用 HTML 建立網頁相同 — 都需要建置一系列巢狀元素。 </p> + +<p>每個版面配置檔案均需包含 1 個根元素 (必須為 View 或 ViewGroup 物件)。定義根元素後,您就可以將額外的物件或小工具新增為子元素,逐步建置檢檢視階層視階層來定義您的版面配置。例如,以下是使用直向 {@link android.widget.LinearLayout} 以納入 +{@link android.widget.TextView} 和 {@link android.widget.Button} 的 XML 版面配置:</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + <TextView android:id="@+id/text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hello, I am a TextView" /> + <Button android:id="@+id/button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Hello, I am a Button" /> +</LinearLayout> +</pre> + +<p>在 XML 中宣告版面配置後,請使用 <code>.xml</code> 副檔名將檔案儲存到 Android 專案的 <code>res/layout/</code> 目錄中,藉此讓系統妥善加以編譯。 + </p> + +<p>如要進一步瞭解 XML 版面配置檔案的語法,請參閱<a href="{@docRoot}guide/topics/resources/layout-resource.html">版面配置資源</a>。</p> + +<h2 id="load">載入 XML 資源</h2> + +<p>當您編譯應用程式時,系統會將所有 XML 版面配置檔案編入 +{@link android.view.View} 資源。在這種情況下,您必須透過 {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} 回呼,載入應用程式的程式碼中的版面配置資源,方法是呼叫 <code>{@link android.app.Activity#setContentView(int) setContentView()}</code>,然後採用以下格式將其參考資源傳送到版面配置資源: +<code>R.layout.<em>layout_file_name</em></code>。例如,假設您將 XML 版面配置儲存成 <code>main_layout.xml</code>,您就必須針對如下所示的 Activity 載入該 XML: + + + + +</p> +<pre> +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main_layout); +} +</pre> + +<p>Android 架構會在 Activity 啟動時呼叫 Activity 中的 <code>onCreate()</code> 回呼方法 (如需生命週期相關資訊,請參閱 <a href="{@docRoot}guide/components/activities.html#Lifecycle">Activity</a>)。 + + +</p> + + +<h2 id="attributes">屬性</h2> + +<p>所有 View 和 ViewGroup 物件均支援本身專用的各種 XML 屬性。部分屬性僅適用於 View 物件 (例如 TextView 可支援 <code>textSize</code> 屬性),不過可能延伸這個類別的 View 物件也會沿用這些屬性。由於某些屬性是沿用自 View 根類別,因此會成為所有 View 物件的常用屬性 (例如 <code>id</code> 屬性)。 + + + +而系統會將其他屬性視為「版面配置參數」,這些屬性可說明 View 物件的特定版面配置方向 (View 由物件的 ViewGroup 上層物件所定義)。 + +</p> + +<h3 id="id">ID</h3> + +<p>任何 View 物件都可能包含一個相關聯的整數 ID,可用於識別樹狀結構中的 View。在應用程式編寫期間,雖然這個 ID 通常是在 XML 版面配置檔案的 <code>id</code> 屬性中指派為字串,系統仍會將其解讀為整數。此為所有 View 物件常用的 XML 屬性 (由 {@link android.view.View} 類別所定義);您會經常使用的這項屬性。以下是 XML 標記中的 ID 語法: + + + + +</p> +<pre>android:id="@+id/my_button"</pre> + +<p>字串開頭的 @ 符號可指示 XML 剖析器應剖析或展開 ID 字串的其餘部分,並且將其視為 ID 資源。 +加號符號 (+) 表示此為系統必須建立並加到資源 (<code>R.java</code> 檔案) 中的新資源名稱。 +Android 架構提供了多種其他 ID 資源。 +參照 Android 資源 ID 時,您不必加入 + 符號,但必須加入 <code>android</code> 套件命名空間,如下所示: +</p> +<pre>android:id="@android:id/empty"</pre> +<p>只要加入 <code>android</code> 套件命名空間,系統就會從 <code>android.R</code> 資源類別參照 ID,而不是從本機資源類別。 +</p> + +<p>以下是透過應用程式建立檢視和參照的一般方法:</p> +<ol> + <li>在版面配置檔案中定義檢視/小工具,並為該檔案指派不重複 ID: +<pre> +<Button android:id="@+id/my_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/my_button_text"/> +</pre> + </li> + <li>接著,建立檢視物件執行個體,並從版面配置中擷取該執行個體 (通常使用 <code>{@link android.app.Activity#onCreate(Bundle) onCreate()}</code> 方法進行擷取): + +<pre> +Button myButton = (Button) findViewById(R.id.my_button); +</pre> + </li> +</ol> +<p>請務必在建立 {@link android.widget.RelativeLayout} 時為檢視物件定義 ID。在相關的版面配置中,同層級的檢視可將本身的版面配置與其他同層級的檢視建立關聯,而不重複 ID 正是這些版面配置的參照依據。 + +</p> +<p>整個樹狀結構的 ID 不必是不重複 ID,但您所搜尋樹狀結構部分的 ID 就必須是不重複 ID (由於您搜尋的部分通常是整個樹狀結構,因此建議您為其定義不重複 ID)。 + +</p> + + +<h3 id="layout-params">版面配置參數</h3> + +<p>名為 <code>layout_<em>something</em></code> 的 XML 版面配置屬性會為適用於所屬 ViewGroup 的 View 定義版面配置參數。 +</p> + +<p>所有 ViewGroup 類別都會實作可延伸 {@link +android.view.ViewGroup.LayoutParams} 的巢狀類別。這個子類別包含定義下層檢視大小和位置的屬性類型,而這些屬性類型也適用於檢視群組。 + +如圖 1 所示,上層檢視群組為每個下層檢視 (包括下層檢視群組) 定義了版面配置參數。 +</p> + +<img src="{@docRoot}images/layoutparams.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>檢視層級以及與每個檢視相關聯的版面配置參數。 +</p> + +<p>請注意,所有 LayoutParams 子類別都包含用於設定值的專屬語法。 +而每個子元素都必須定義適用於其上層元素的 LayoutParams,即便子元素也會為其下層物件定義不同的 LayoutParams。 + </p> + +<p>所有檢視群組均包含寬度和高度 (<code>layout_width</code> 和 +<code>layout_height</code>),而每個檢視都必須定義這些值。大多數 LayoutParams 還會包含選用的標界和邊框。 + <p> + +<p>您可以依據測量結果指定寬度和高度 (您通常不必經常指定這些值)。 +在大多數情況下,您需使用以下任一常數設定寬度或高度: + </p> + +<ul> + <li><var>wrap_content</var> 可指示檢視自行將大小調整成其內容所需的尺寸。 +</li> + <li><var>match_parent</var> (在 API 級別 8 之前稱為 <var>fill_parent</var> ) 可指示檢視自行將大小擴展成上層檢視群組允許的上限。 +</li> +</ul> + +<p>一般來說,我們不建議您使用絕對單位 (例如像素) 指定版面配置的寬度和高度。 +建議做法是使用相對測量單位 (例如依據密度的像素單位 ( +<var>dp</var>)、 <var>wrap_content</var>或 +<var>match_parent</var>指定這些值,這是因為這樣可協助確保應用程式在各種裝置螢幕大小中能保有最佳顯示效果。如需支援的測量類型的定義,請參閱<a href="{@docRoot}guide/topics/resources/available-resources.html#dimension">可用資源</a>。 + + + +</p> + + +<h2 id="Position">版面配置位置</h2> + <p> + 檢視的形狀為矩形。檢視包含一個位置值 (以一組「水平」<em></em>和「垂直」<em></em>座標表示),以及兩個尺寸值 (以寬度和高度表示)。 + +位置和尺寸的單位均為像素。 + + </p> + + <p> + 您可以呼叫 {@link android.view.View#getLeft()} 和 {@link android.view.View#getTop()} 方法來擷取檢視的位置值。 +第一個方法會傳回顯示檢視的矩形區塊的水平座標 (X 座標); +第二個方法則會傳回顯示檢視的矩形區塊的垂直座標 (Y 座標)。 +這兩個方法都會傳回檢視相對於其上層項目的位置。 +例如,假設 <code>getLeft()</code> 傳回 20,表示該檢視位於其直屬上層項目左側邊緣向右平移 20 像素的位置。 + + + </p> + + <p> + 我們也提供了 {@link android.view.View#getRight()} 和 {@link android.view.View#getBottom()} 這兩個簡易方法,可用於避免不必要的運算程序。 + + 這些方法會傳回險是檢視的矩形區塊的右下角座標。 +例如,呼叫 {@link android.view.View#getRight()} 的用途與以下運算式類似:<code>getLeft() + getWidth()</code>。 + + </p> + + +<h2 id="SizePaddingMargins">大小、邊距和邊界</h2> + <p> + 檢視的大小是以寬度和高度表示。單一檢視會包含兩組寬度和高度值。 + + </p> + + <p> + 第一組值稱為「寬度測定值」<em></em>和「高度測定值」<em></em>, +可定義檢視在上層項目中的尺寸。 +您可以呼叫 {@link android.view.View#getMeasuredWidth()} 和 {@link android.view.View#getMeasuredHeight()} 來取得尺寸測定值。 + + + </p> + + <p> + 第二組值簡稱「寬度」<em></em>和「高度」<em></em>,或是「寬度描繪值」<em></em>和「高度描繪值」<em></em>, +可定義檢視在螢幕中的實際大小 (描繪期間以及版面配置之後)。 + +這些值可能 (但未必) 會與寬度和高度測量值不同。 +您可以呼叫 +{@link android.view.View#getWidth()} 和 {@link android.view.View#getHeight()} 來取得尺寸描繪值。 + </p> + + <p> + 測量檢視的尺寸時,系統會將檢視的邊距納入考量。邊距是指檢視的上、下、左、右部分 (單位為像素), + + 可用於將檢視內容偏移特定像素。 +例如,2 像素的左側邊距可將檢視內容由左側邊緣向右平移 2 像素。 +您可以使用 +{@link android.view.View#setPadding(int, int, int, int)} 方法來設定邊距,以及呼叫 +{@link android.view.View#getPaddingLeft()}、{@link android.view.View#getPaddingTop()}、 +{@link android.view.View#getPaddingRight()} 和 {@link android.view.View#getPaddingBottom()} 來查詢邊距。 + </p> + + <p> + 雖然檢視可定義邊距,但無法針對邊界提供任何支援。 +不過,檢視群組可提供這類支援。詳情請參閱 +{@link android.view.ViewGroup} 和 +{@link android.view.ViewGroup.MarginLayoutParams}。 + </p> + + <p>如要進一步瞭解尺寸,請參閱<a href="{@docRoot}guide/topics/resources/more-resources.html#Dimension">尺寸值</a>。 + + </p> + + + + + + +<style type="text/css"> +div.layout { + float:left; + width:200px; + margin:0 0 20px 20px; +} +div.layout.first { + margin-left:0; + clear:left; +} +</style> + + + + +<h2 id="CommonLayouts">常見版面配置</h2> + +<p>{@link android.view.ViewGroup} 類別的所有子類別均可以獨有方式顯示您在當中套疊的檢視。 +以下提供幾個 Android 平台內建的常見版面配置類型。 +</p> + +<p class="note"><strong>注意:</strong>雖然您可以將版面配置套疊在一起來符合您的 UI 設計,但建議您盡可能讓版面配置階層維持淺薄。 + +套疊的版面配置越少,版面配置的描繪速度就越快 (建議您使用寬的檢視階層,而不是深的檢視階層)。 +</p> + +<!-- +<h2 id="framelayout">FrameLayout</h2> +<p>{@link android.widget.FrameLayout FrameLayout} is the simplest type of layout +object. It's basically a blank space on your screen that you can +later fill with a single object — for example, a picture that you'll swap in and out. +All child elements of the FrameLayout are pinned to the top left corner of the screen; you cannot +specify a different location for a child view. Subsequent child views will simply be drawn over +previous ones, +partially or totally obscuring them (unless the newer object is transparent). +</p> +--> + + +<div class="layout first"> + <h4><a href="layout/linear.html">線性版面配置</a></h4> + <a href="layout/linear.html"><img src="{@docRoot}images/ui/linearlayout-small.png" alt="" /></a> + <p>這種版面配置可將其下層物件整合至單一水平或垂直列。如果視窗長度超過螢幕長度,線性版面配置就會建立捲軸方便使用者捲動畫面。 +</p> +</div> + +<div class="layout"> + <h4><a href="layout/relative.html">相對版面配置</a></h4> + <a href="layout/relative.html"><img src="{@docRoot}images/ui/relativelayout-small.png" alt="" /></a> + <p>這種版面配置可讓您指定下層物件之間的相對位置 (指定下層物件 A 位於下層物件 B 的左側),或指定下層物件與上層物件的相對位置 (指定下層物件緊貼上層物件的頂端)。 +</p> +</div> + +<div class="layout"> + <h4><a href="{@docRoot}guide/webapps/webview.html">網頁檢視</a></h4> + <a href="{@docRoot}guide/webapps/webview.html"><img src="{@docRoot}images/ui/webview-small.png" alt="" /></a> + <p>這種版面配置可顯示網頁。</p> +</div> + + + + +<h2 id="AdapterViews" style="clear:left">使用配接器建置版面配置</h2> + +<p>如果您版面配置的內容為動態內容,而不是預先定義的內容,您可以在執行階段將子類別為 {@link android.widget.AdapterView} 的版面配置填入檢視中。 + +{@link android.widget.AdapterView} 類別的子類別會使用 {@link android.widget.Adapter} 將資料繫結至本身的版面配置。 +{@link android.widget.Adapter} 就如同是資料來源與 {@link android.widget.AdapterView} 版面配置的中間人 — {@link android.widget.Adapter} 會從陣列或資料庫查詢等來源擷取資料,然後將這些資料轉換成可加入 {@link android.widget.AdapterView} 版面配置的檢視。 + + +</p> + +<p>以下是配接器支援的常見版面配置:</p> + +<div class="layout first"> + <h4><a href="layout/listview.html">清單檢視</a></h4> + <a href="layout/listview.html"><img src="{@docRoot}images/ui/listview-small.png" alt="" /></a> + <p>這種版面配置可顯示能夠捲動的單欄清單。</p> +</div> + +<div class="layout"> + <h4><a href="layout/gridview.html">格狀檢視</a></h4> + <a href="layout/gridview.html"><img src="{@docRoot}images/ui/gridview-small.png" alt="" /></a> + <p>這種版面配置可顯示能夠捲動的資料欄和資料列網格。</p> +</div> + + + +<h3 id="FillingTheLayout" style="clear:left">將資料填入配接器檢視</h3> + +<p>您可將 {@link android.widget.AdapterView} 執行個體繫結至 +{@link android.widget.Adapter} 以便從外部來源擷取資料,並建立可顯示所有資料的 {@link +android.view.View},藉此填入 {@link android.widget.AdapterView} (例如 {@link android.widget.ListView} 或 {@link android.widget.GridView}。 +</p> + +<p>Android 提供數個 {@link android.widget.Adapter} 子類別可用於擷取不同資料類型以及為 {@link android.widget.AdapterView} 建置檢視。 +以下是兩種常見的配接器: +</p> + +<dl> + <dt>{@link android.widget.ArrayAdapter}</dt> + <dd>如果您的資料來源是陣列,請使用這個配接器。在預設情況下,{@link +android.widget.ArrayAdapter} 會針對每個陣列項目呼叫 {@link +java.lang.Object#toString()} 並取代 {@link +android.widget.TextView} 中的內容,藉此為每個陣列項目建立檢視。 + <p>例如,如果您想在 {@link +android.widget.ListView} 中顯示一系列字串,請使用建構函式為每個字串和字串陣列指定版面配置,藉此初始化新的 {@link android.widget.ArrayAdapter}: +</p> +<pre> +ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, + android.R.layout.simple_list_item_1, myStringArray); +</pre> +<p>這個建構函式的引數包括:</p> +<ul> + <li>您的應用程式 {@link android.content.Context}</li> + <li>含有陣列中所有字串的 {@link android.widget.TextView} 的版面配置</li> + <li>字串陣列</li> +</ul> +<p>接著,針對您的 {@link android.widget.ListView} 呼叫 +{@link android.widget.ListView#setAdapter setAdapter()}:</p> +<pre> +ListView listView = (ListView) findViewById(R.id.listview); +listView.setAdapter(adapter); +</pre> + + <p>如要自訂每個項目的外觀,您可以針對陣列中的物件覆寫 {@link +java.lang.Object#toString()} 方法。或者,如果想為 {@link android.widget.TextView} 以外的所有項目建立檢視 (例如,如果您想為所有陣列項目建立 {@link android.widget.ImageView}),請延伸 {@link +android.widget.ArrayAdapter} 類別並覆寫 {@link android.widget.ArrayAdapter#getView +getView()},以傳回您想為所有項目建立的檢視類型。 + +</p> + +</dd> + + <dt>{@link android.widget.SimpleCursorAdapter}</dt> + <dd>如果您的資料來自 {@link android.database.Cursor},請使用這個配接器。使用 +{@link android.widget.SimpleCursorAdapter} 時,您必須指定要用於 {@link android.database.Cursor} 中所有資料列的版面配置,以及指定要將 {@link android.database.Cursor} 中的哪些資料欄插入哪些版面配置檢視。 + +例如,如果您想建立一份列出使用者名稱和電話號碼的清單,請執行查詢來傳回 {@link +android.database.Cursor},其中包含一個列出每位使用者的資料列,以及多個列出名稱和電話號碼的資料欄。 + +接著,請建立一個字串陣列以便從 {@link +android.database.Cursor} 指定您想在版面配置中列出每項結果的資料欄,以及建立一個整數陣列為每個資料欄指定對應的值: +</p> +<pre> +String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, + ContactsContract.CommonDataKinds.Phone.NUMBER}; +int[] toViews = {R.id.display_name, R.id.phone_number}; +</pre> +<p>當您啟動 {@link android.widget.SimpleCursorAdapter} 時,請傳送用於顯示所有結果的版面配置、包含結果的 {@link android.database.Cursor},以及下列兩個陣列: +</p> +<pre> +SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, + R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); +ListView listView = getListView(); +listView.setAdapter(adapter); +</pre> +<p>{@link android.widget.SimpleCursorAdapter} 隨即會將每個 {@code +fromColumns} 項目插入相對應的 {@code toViews} 檢視,藉此利用提供的版面配置為 +{@link android.database.Cursor} 中的所有資料列建立專屬檢視。</p>.</dd> +</dl> + + +<p>如果您在應用程式的效期內更改配接器讀取的底層資料,您就必須呼叫 {@link android.widget.ArrayAdapter#notifyDataSetChanged()}。 +這樣會通知附加的檢視由於資料已變更,因此需進行重新整理。 +</p> + + + +<h3 id="HandlingUserSelections">處理點擊事件</h3> + +<p>只要實作 {@link android.widget.AdapterView.OnItemClickListener} 介面,即可回應 {@link android.widget.AdapterView} 中所有項目的點擊事件。 +例如:</p> + +<pre> +// Create a message handling object as an anonymous class. +private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + // Do something in response to the click + } +}; + +listView.setOnItemClickListener(mMessageClickedHandler); +</pre> + + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd new file mode 100644 index 000000000000..b0ae12ea3a19 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=對話方塊 +page.tags=alertdialog,dialogfragment + +@jd:body + + + +<div id="qv-wrapper"> + <div id="qv"> + <h2>本文件內容</h2> +<ol> + <li><a href="#DialogFragment">建立對話方塊片段</a></li> + <li><a href="#AlertDialog">建置快訊對話方塊</a> + <ol> + <li><a href="#AddingButtons">加入按鈕</a></li> + <li><a href="#AddingAList">加入清單</a></li> + <li><a href="#CustomLayout">建立自訂版面配置</a></li> + </ol> + </li> + <li><a href="#PassingEvents">將事件傳回對話方塊的主控件</a></li> + <li><a href="#ShowingADialog">顯示對話方塊</a></li> + <li><a href="#FullscreenDialog">顯示全螢幕對話方塊或內嵌片段</a> + <ol> + <li><a href="#ActivityAsDialog">針對大型螢幕將 Activity 顯示為對話方塊</a></li> + </ol> + </li> + <li><a href="#DismissingADialog">關閉對話方塊</a></li> +</ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.app.DialogFragment}</li> + <li>{@link android.app.AlertDialog}</li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="{@docRoot}design/building-blocks/dialogs.html">對話方塊設計指南</a></li> + <li><a href="{@docRoot}guide/topics/ui/controls/pickers.html">挑選器</a> (日期/時間對話方塊)</li> + </ol> + </div> +</div> + +<p>對話方塊是一種小型視窗,可提示使用者做出決定或輸入額外資訊。 +對話視窗並不會佔滿整個螢幕,而且通常是供強制回應事件使用,這種事件會要求使用者必須先完成特定動作,才能繼續下一步。 +</p> + +<div class="note design"> +<p><strong>設計對話方塊</strong></p> + <p>如要瞭解如何設計對話方塊,包括建議使用的設計語言,請參閱<a href="{@docRoot}design/building-blocks/dialogs.html">對話方塊</a>設計指南。 +</p> +</div> + +<img src="{@docRoot}images/ui/dialogs.png" /> + +<p>{@link android.app.Dialog} 類別是對話方塊的基礎類別,但請避免直接啟動 {@link android.app.Dialog}。建議您改用下列其中一個子類別: + +</p> +<dl> + <dt>{@link android.app.AlertDialog}</dt> + <dd>這種對話方塊可以顯示一個標題、最多三個按鈕、一系列可選取項目或一個自訂版面配置。 +</dd> + <dt>{@link android.app.DatePickerDialog} 或 {@link android.app.TimePickerDialog}</dt> + <dd>這種對話方塊會提供預先定義的 UI,可讓使用者選取日期或時間。</dd> +</dl> + +<div class="sidebox"> +<h2>避免使用 ProgressDialog</h2> +<p>Android 包含另一個稱為 +{@link android.app.ProgressDialog} 的對話方塊類別,可顯示內含進度列的對話方塊。不過,如果您需要向使用者指明載入進度或不明朗的進度,建議您按照<a href="{@docRoot}design/building-blocks/progress.html">進度和 Activity</a> 設計指導方針中的指示操作,並且在版面配置中使用 {@link android.widget.ProgressBar}。 + + +</p> +</div> + +<p>上述類別可定義對話方塊的樣式和結構,但建議您使用 {@link android.support.v4.app.DialogFragment} 做為對話方塊的容器。 +{@link android.support.v4.app.DialogFragment} 類別可提供您所需的所有控制項,方便您建立對話方塊及管理對話方塊的外觀,如此您不必對 {@link android.app.Dialog} 物件呼叫方法。 + + +</p> + +<p>使用 {@link android.support.v4.app.DialogFragment} 管理對話方塊可確保對話方塊能正確控制生命週期事件,例如使用者點擊 [返回]<em></em> 按鈕或旋轉螢幕。 + +此外,{@link +android.support.v4.app.DialogFragment} 類別還能像傳統的 {@link +android.support.v4.app.Fragment} 一樣讓您重複使用對話方塊的 UI,做為大型 UI 中的可嵌入元件 (例如當您想讓對話方塊使用者介面在大型和小型螢幕上呈現不同外觀時)。 + +</p> + +<p>本指南的以下各節說明如何搭配 {@link android.app.AlertDialog} 物件使用 {@link +android.support.v4.app.DialogFragment}。 +如果您是想建立日期或時間挑選器,請改為參閱<a href="{@docRoot}guide/topics/ui/controls/pickers.html">挑選器</a>指南。 +</p> + +<p class="note"><strong>注意:</strong>{@link android.app.DialogFragment} 類別最初是在 Android 3.0 (API 級別 11) 中導入,因此本文說明如何使用<a href="{@docRoot}tools/support-library/index.html">支援程式庫</a>提供的 {@link +android.support.v4.app.DialogFragment} 類別。 + +只要將這個程式庫加到您的應用程式,即可在搭載 Android 1.6 以上版本的裝置上使用 {@link android.support.v4.app.DialogFragment} 以及多種其他 API。 + +如果您應用程式支援的最低版本為 API 級別 11 以上版本,您就可以使用架構版本的 {@link +android.app.DialogFragment},但請注意,本文中提供的連結是用於取得支援程式庫 API。 + +使用支援程式庫時,請務必匯入 <code>android.support.v4.app.DialogFragment</code> 類別,而「不是」<em></em><code>android.app.DialogFragment</code>。 + +</p> + + +<h2 id="DialogFragment">建立對話方塊片段</h2> + +<p>如果想建立多種對話方塊設計 — 包括自訂版面配置和<a href="{@docRoot}design/building-blocks/dialogs.html">對話方塊</a>設計指南中所述的對話方塊設計 — 只要延伸 +{@link android.support.v4.app.DialogFragment} 然後利用 +{@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()} 回呼方法建立 {@link android.app.AlertDialog} 即可。 + +</p> + +<p>例如,假設您利用 {@link android.support.v4.app.DialogFragment} 管理以下的基本 +{@link android.app.AlertDialog}:</p> + +<pre> +public class FireMissilesDialogFragment extends DialogFragment { + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Use the Builder class for convenient dialog construction + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.dialog_fire_missiles) + .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // FIRE ZE MISSILES! + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + } + }); + // Create the AlertDialog object and return it + return builder.create(); + } +} +</pre> + +<div class="figure" style="width:290px;margin:0 0 0 20px"> +<img src="{@docRoot}images/ui/dialog_buttons.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong> +包含一段訊息和兩個動作按鈕的對話方塊。</p> +</div> + +<p>在這種情況下,如果您建立這個類別執行個體,然後對該物件呼叫 {@link +android.support.v4.app.DialogFragment#show show()},就會顯示如圖 1 所示的對話方塊。 +</p> + +<p>如要進一步瞭解如何使用 {@link android.app.AlertDialog.Builder} API 建立對話方塊,請參閱下一節。 +</p> + +<p>視對話方塊的複雜程度而定,您可以在 {@link android.support.v4.app.DialogFragment} 中實作多種其他回呼方法,包括所有的基本<a href="{@docRoot}guide/components/fragments.html#Lifecycle">片段生命週期方法</a>。 + + + + + + + +<h2 id="AlertDialog">建置快訊對話方塊</h2> + + +<p>{@link android.app.AlertDialog} 通常是您需要使用的唯一對話方塊類別,可讓您建置多種對話方塊設計。如圖 2 所示,快訊對話方塊是由 3 個區塊組合而成: + +</p> + +<div class="figure" style="width:311px;margin-top:0"> +<img src="{@docRoot}images/ui/dialogs_regions.png" alt="" style="margin-bottom:0" /> +<p class="img-caption"><strong>圖 2.</strong>對話方塊的版面配置。</p> +</div> + +<ol> +<li><b>標題</b> + <p>此為選用區塊;只有在內容區域提供詳細訊息、一份清單或自訂版面配置時,您才需要使用標題。 +如果您想提供一段簡短訊息或一個簡易問題 (如圖 1 所示的對話方塊),您就不必使用標題。 +</li> +<li><b>內容區塊</b> + <p>這個區塊可以顯示一段訊息、一份清單或其他自訂版面配置。</p></li> +<li><b>動作按鈕</b> + <p>單一對話方塊最多只能包含 3 個動作按鈕。</p></li> +</ol> + +<p>{@link android.app.AlertDialog.Builder} 類別提供的 API 可讓您建立包含這些內容類型 (包括自訂版面配置) 的 {@link android.app.AlertDialog}。 + +</p> + +<p>如何建置 {@link android.app.AlertDialog}:</p> + +<pre> +<b>// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor</b> +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + +<b>// 2. Chain together various setter methods to set the dialog characteristics</b> +builder.setMessage(R.string.dialog_message) + .setTitle(R.string.dialog_title); + +<b>// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}</b> +AlertDialog dialog = builder.create(); +</pre> + +<p>以下主題說明如何定義多種採用 {@link android.app.AlertDialog.Builder} 類別的對話方塊屬性。 +</p> + + + + +<h3 id="AddingButtons">加入按鈕</h3> + +<p>想要加入如圖 2 所示的動作按鈕,請呼叫 {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} 和 +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()} 方法: +</p> + +<pre style="clear:right"> +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); +// Add the buttons +builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User clicked OK button + } + }); +builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // User cancelled the dialog + } + }); +// Set other dialog properties +... + +// Create the AlertDialog +AlertDialog dialog = builder.create(); +</pre> + +<p><code>set...Button()</code> 方法會要求<a href="{@docRoot}guide/topics/resources/string-resource.html">字串資源</a>提供的按鈕標題,以及可定義使用者點擊按鈕後需要完成的動作的{@link android.content.DialogInterface.OnClickListener}。 + + +</p> + +<p>您可以加入的動作按鈕分為 3 種:</p> +<dl> + <dt>正面</dt> + <dd>這種按鈕的用途是接受及繼續進行特定動作 (「確定」按鈕)。</dd> + <dt>負面</dt> + <dd>這種按鈕的用途是取消動作。</dd> + <dt>中立</dt> + <dd>如果使用者不想繼續進行特定動作,但並非要取消動作,請使用這種按鈕。 +這種按鈕會顯示在正面和負面按鈕之間。 +範例:[稍後提醒我] 按鈕。</dd> +</dl> + +<p>您可以將以上其中一種按鈕加入 {@link +android.app.AlertDialog};換句話說,您最多只能加入一個「正面」按鈕。</p> + + + +<div class="figure" style="width:290px;margin:0 0 0 40px"> +<img src="{@docRoot}images/ui/dialog_list.png" alt="" /> +<p class="img-caption"><strong>圖 3.</strong> +包含一個標題和一份清單的對話方塊。</p> +</div> + +<h3 id="AddingAList">加入清單</h3> + +<p>{@link android.app.AlertDialog} API 可提供以下 3 種清單類型:</p> +<ul> +<li>傳統的單一選項清單</li> +<li>永續性的單一選項清單 (圓形按鈕)</li> +<li>永續性的多重選項清單 (核取方塊)</li> +</ul> + +<p>想要建立如圖 3 所示的單一選項清單,請使用 {@link android.app.AlertDialog.Builder#setItems setItems()} 方法: +</p> + +<pre style="clear:right"> +@Override +public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.pick_color) + .setItems(R.array.colors_array, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // The 'which' argument contains the index position + // of the selected item + } + }); + return builder.create(); +} +</pre> + +<p>由於清單會出現在對話方塊的內容區塊中,因此對話方塊無法同時顯示訊息和清單,而且您必須使用 {@link android.app.AlertDialog.Builder#setTitle setTitle()} 為對話方塊設定標題。 + +如要指定清單列出的項目,請呼叫 {@link +android.app.AlertDialog.Builder#setItems setItems()} 來傳送陣列。或者,您也可以使用 {@link +android.app.AlertDialog.Builder#setAdapter setAdapter()} 指定清單。 + +如此一來,您就可以使用 {@link android.widget.ListAdapter} 為清單加入動態資料 (例如資料庫中的動態資料)。 +</p> + +<p>如果您選擇讓清單採用 {@link android.widget.ListAdapter},請一律使用 {@link android.support.v4.content.Loader} 以非同步方式載入內容。 + +詳情請參閱<a href="{@docRoot}guide/topics/ui/declaring-layout.html#AdapterViews">使用配接器建置版面配置</a>和<a href="{@docRoot}guide/components/loaders.html">載入器</a>指南。 + + +</p> + +<p class="note"><strong>注意:</strong>在預設情況下,輕觸某個清單項目會關閉對話方塊,除非您使用以下的永續性選項清單。 +</p> + +<div class="figure" style="width:290px;margin:-30px 0 0 40px"> +<img src="{@docRoot}images/ui/dialog_checkboxes.png" /> +<p class="img-caption"><strong>圖 4.</strong> +多重選項清單。</p> +</div> + + +<h4 id="Checkboxes">加入永續性的多重選項或單一選項清單</h4> + +<p>如要加入多重選項 (核取方塊) 或單一選項 (圓形按鈕),請使用 +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} 或 +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} 方法。 +</p> + +<p>例如,以下說明如何建立如圖 4 所示能夠在 {@link java.util.ArrayList} 中儲存所選項目的多重選項清單: + +</p> + +<pre style="clear:right"> +@Override +public Dialog onCreateDialog(Bundle savedInstanceState) { + mSelectedItems = new ArrayList(); // Where we track the selected items + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // Set the dialog title + builder.setTitle(R.string.pick_toppings) + // Specify the list array, the items to be selected by default (null for none), + // and the listener through which to receive callbacks when items are selected + .setMultiChoiceItems(R.array.toppings, null, + new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, + boolean isChecked) { + if (isChecked) { + // If the user checked the item, add it to the selected items + mSelectedItems.add(which); + } else if (mSelectedItems.contains(which)) { + // Else, if the item is already in the array, remove it + mSelectedItems.remove(Integer.valueOf(which)); + } + } + }) + // Set the action buttons + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + // User clicked OK, so save the mSelectedItems results somewhere + // or return them to the component that opened the dialog + ... + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + ... + } + }); + + return builder.create(); +} +</pre> + +<p>雖然傳統清單和包含圓形按鈕的清單都可提供「單選」動作,但如果您想保留使用者的選擇,請使用 {@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()}。也就是說,如果您想讓對話方塊再次開啟時顯示使用者目前所選的選項,請建立包含圓形按鈕的清單。 + + +</p> + + + + + +<h3 id="CustomLayout">建立自訂版面配置</h3> + +<div class="figure" style="width:290px;margin:-30px 0 0 40px"> +<img src="{@docRoot}images/ui/dialog_custom.png" alt="" /> +<p class="img-caption"><strong>圖 5.</strong>自訂的對話方塊版面配置。</p> +</div> + +<p>如果您想自訂對話方塊的版面配置,請建立所需的版面配置,然後對 {@link +android.app.AlertDialog.Builder} 物件呼叫 {@link +android.app.AlertDialog.Builder#setView setView()},將新建的版面配置加到 {@link android.app.AlertDialog}。 +</p> + +<p>自訂版面配置預設會佔滿整個對話方塊視窗,但您仍可使用 {@link android.app.AlertDialog.Builder} 方法在其中加入按鈕和標題。 +</p> + +<p>例如,以下是圖 5 所示的對話方塊版面配置檔案:</p> + +<p style="clear:right" class="code-caption">res/layout/dialog_signin.xml</p> +<pre> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <ImageView + android:src="@drawable/header_logo" + android:layout_width="match_parent" + android:layout_height="64dp" + android:scaleType="center" + android:background="#FFFFBB33" + android:contentDescription="@string/app_name" /> + <EditText + android:id="@+id/username" + android:inputType="textEmailAddress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:layout_marginLeft="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="4dp" + android:hint="@string/username" /> + <EditText + android:id="@+id/password" + android:inputType="textPassword" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:layout_marginLeft="4dp" + android:layout_marginRight="4dp" + android:layout_marginBottom="16dp" + android:fontFamily="sans-serif" + android:hint="@string/password"/> +</LinearLayout> +</pre> + +<p class="note"><strong>提示:</strong>在預設情況下,如果您設定 {@link android.widget.EditText} 元素使用 {@code "textPassword"} 輸入類型,字型系列就會設為等寬字型,因此您必須將該元素的字型系列變更為 {@code "sans-serif"},讓文字欄位採用對應的字型。 + + +</p> + +<p>如要擴大 {@link android.support.v4.app.DialogFragment} 中的版面配置,請透過 {@link android.app.Activity#getLayoutInflater()} 取得 {@link android.view.LayoutInflater},然後呼叫{@link android.view.LayoutInflater#inflate inflate()} (其中的第一個參數為版面配置資源 ID,第二個參數則是版面配置的上層檢視)。接著,您可以呼叫 {@link android.app.AlertDialog#setView setView()} 來取代對話方塊中的版面配置。 + + + + + +</p> + +<pre> +@Override +public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // Get the layout inflater + LayoutInflater inflater = getActivity().getLayoutInflater(); + + // Inflate and set the layout for the dialog + // Pass null as the parent view because its going in the dialog layout + builder.setView(inflater.inflate(R.layout.dialog_signin, null)) + // Add action buttons + .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + // sign in the user ... + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + LoginDialogFragment.this.getDialog().cancel(); + } + }); + return builder.create(); +} +</pre> + +<div class="note"> +<p><strong>提示:</strong>如果您想自訂對話方塊,請改為將 {@link android.app.Activity} 顯示為對話方塊,而不是使用 {@link android.app.Dialog} API。 + +方法很簡單,只要建立 Activity 然後在 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> 宣示說明元素中將其主題設為 +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog} 即可: +</p> + +<pre> +<activity android:theme="@android:style/Theme.Holo.Dialog" > +</pre> +<p>這樣一來,Activity 就會顯示在對話方塊視窗,而不是以全螢幕模式顯示。</p> +</div> + + + +<h2 id="PassingEvents">將事件傳回對話方塊的主控件</h2> + +<p>使用者在對話方塊中輕觸任一動作按鈕或從清單中選取一個項目後,您的 {@link android.support.v4.app.DialogFragment} 可能會自行執行必要動作,不過,您通常會想將事件傳送到 Activity 或對話方塊開啟的片段。 + + +如要這麼做,請透過每個點擊事件類型適用的方法定義介面,然後在會接收對話方塊的動作事件的主機元件中實作該介面。 + +</p> + +<p>例如,以下是定義用於將事件傳回主機 Activity 的介面的 {@link android.support.v4.app.DialogFragment}: +</p> + +<pre> +public class NoticeDialogFragment extends DialogFragment { + + /* The activity that creates an instance of this dialog fragment must + * implement this interface in order to receive event callbacks. + * Each method passes the DialogFragment in case the host needs to query it. */ + public interface NoticeDialogListener { + public void onDialogPositiveClick(DialogFragment dialog); + public void onDialogNegativeClick(DialogFragment dialog); + } + + // Use this instance of the interface to deliver action events + NoticeDialogListener mListener; + + // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // Verify that the host activity implements the callback interface + try { + // Instantiate the NoticeDialogListener so we can send events to the host + mListener = (NoticeDialogListener) activity; + } catch (ClassCastException e) { + // The activity doesn't implement the interface, throw exception + throw new ClassCastException(activity.toString() + + " must implement NoticeDialogListener"); + } + } + ... +} +</pre> + +<p>代管對話方塊的 Activity 會建立包含對話方塊片段的建構函式的對話方塊執行個體,以及透過您實作的 {@code NoticeDialogListener} 介面接收對話方塊的事件: + +</p> + +<pre> +public class MainActivity extends FragmentActivity + implements NoticeDialogFragment.NoticeDialogListener{ + ... + + public void showNoticeDialog() { + // Create an instance of the dialog fragment and show it + DialogFragment dialog = new NoticeDialogFragment(); + dialog.show(getSupportFragmentManager(), "NoticeDialogFragment"); + } + + // The dialog fragment receives a reference to this Activity through the + // Fragment.onAttach() callback, which it uses to call the following methods + // defined by the NoticeDialogFragment.NoticeDialogListener interface + @Override + public void onDialogPositiveClick(DialogFragment dialog) { + // User touched the dialog's positive button + ... + } + + @Override + public void onDialogNegativeClick(DialogFragment dialog) { + // User touched the dialog's negative button + ... + } +} +</pre> + +<p>由於主機 Activity 會實作 {@code NoticeDialogListener} — 如上所述的 {@link android.support.v4.app.Fragment#onAttach onAttach()} 回呼方法會強制執行這個 Activity — 因此對話方塊片段可以使用介面回呼方法將點擊事件傳送到 Activity: + + +</p> + +<pre> +public class NoticeDialogFragment extends DialogFragment { + ... + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Build the dialog and set up the button click handlers + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.dialog_fire_missiles) + .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // Send the positive button event back to the host activity + mListener.onDialogPositiveClick(NoticeDialogFragment.this); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + // Send the negative button event back to the host activity + mListener.onDialogNegativeClick(NoticeDialogFragment.this); + } + }); + return builder.create(); + } +} +</pre> + + + +<h2 id="ShowingADialog">顯示對話方塊</h2> + +<p>如果想顯示對話方塊,請建立 {@link +android.support.v4.app.DialogFragment} 執行個體並呼叫 {@link android.support.v4.app.DialogFragment#show +show()} 來傳送對話方塊片段的 {@link android.support.v4.app.FragmentManager} 和標籤名稱。 +</p> + +<p>如要取得 {@link android.support.v4.app.FragmentManager},請呼叫 +{@link android.support.v4.app.FragmentActivity} 中的 +{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} 或 {@link +android.support.v4.app.Fragment} 中的 {@link +android.support.v4.app.Fragment#getFragmentManager()}。例如:</p> + +<pre> +public void confirmFireMissiles() { + DialogFragment newFragment = new FireMissilesDialogFragment(); + newFragment.show(getSupportFragmentManager(), "missiles"); +} +</pre> + +<p>第二個引數 {@code "missiles"} 是不重複的標籤名稱,可供系統視需要用於儲存及還原片段狀態。 +此外,該標籤還可讓您呼叫 + {@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()} 來取得片段的控點。</p> + + + + +<h2 id="FullscreenDialog">顯示全螢幕對話方塊或內嵌片段</h2> + +<p>您可能會想建立 UI 設計,讓 UI 片段在某些情況下於該設計中顯示為對話方塊,而不是其他設計中的全螢幕或內嵌片段 (例如視裝置採用螢幕大小而定)。 + +{@link android.support.v4.app.DialogFragment} 類別能為您提供這樣的彈性,這是因為該類別仍可做為可嵌入的 {@link +android.support.v4.app.Fragment} 使用。 +</p> + +<p>不過,您無法使用 {@link android.app.AlertDialog.Builder AlertDialog.Builder} 或其他 {@link android.app.Dialog} 物件來建置這種對話方塊。 +如果您想建立可嵌入的 {@link android.support.v4.app.DialogFragment},請務必在版面配置中定義對話方塊的 UI,然後透過 {@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()} 回呼載入版面配置。 + + +</p> + +<p>以下的 {@link android.support.v4.app.DialogFragment} 範例可顯示為對話方塊或可嵌入片段 (使用名為 <code>purchase_items.xml</code> 的版面配置): +</p> + +<pre> +public class CustomDialogFragment extends DialogFragment { + /** The system calls this to get the DialogFragment's layout, regardless + of whether it's being displayed as a dialog or an embedded fragment. */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout to use as dialog or embedded fragment + return inflater.inflate(R.layout.purchase_items, container, false); + } + + /** The system calls this only when creating the layout in a dialog. */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // The only reason you might override this method when using onCreateView() is + // to modify any dialog characteristics. For example, the dialog includes a + // title by default, but your custom layout might not need it. So here you can + // remove the dialog title, but you must call the superclass to get the Dialog. + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + return dialog; + } +} +</pre> + +<p>以下的程式碼可根據螢幕大小,決定要將片段顯示為對話方塊或全螢幕 UI: +</p> + +<pre> +public void showDialog() { + FragmentManager fragmentManager = getSupportFragmentManager(); + CustomDialogFragment newFragment = new CustomDialogFragment(); + + if (mIsLargeLayout) { + // The device is using a large layout, so show the fragment as a dialog + newFragment.show(fragmentManager, "dialog"); + } else { + // The device is smaller, so show the fragment fullscreen + FragmentTransaction transaction = fragmentManager.beginTransaction(); + // For a little polish, specify a transition animation + transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + // To make it fullscreen, use the 'content' root view as the container + // for the fragment, which is always the root view for the activity + transaction.add(android.R.id.content, newFragment) + .addToBackStack(null).commit(); + } +} +</pre> + +<p>如要進一步瞭解如何進行片段交易,請參閱<a href="{@docRoot}guide/components/fragments.html">片段</a>指南。 +</p> + +<p>在本範例中,<code>mIsLargeLayout</code> 布林值可指定是否要讓目前的裝置採用應用程式的大型版面配置設計 (藉此將這個片段顯示為對話方塊,而不是全螢幕)。 + +設定這種布林值的最佳做法,是使用<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">替代資源</a>值為不同裝置大小宣告<a href="{@docRoot}guide/topics/resources/more-resources.html#Bool">布林資源值</a>。 + +例如,以下是適用於不同螢幕大小的 2 種布林資源版本: +</p> + +<p class="code-caption">res/values/bools.xml</p> +<pre> +<!-- Default boolean values --> +<resources> + <bool name="large_layout">false</bool> +</resources> +</pre> + +<p class="code-caption">res/values-large/bools.xml</p> +<pre> +<!-- Large screen boolean values --> +<resources> + <bool name="large_layout">true</bool> +</resources> +</pre> + +<p>接著,您可以在呼叫 Activity 的 {@link android.app.Activity#onCreate onCreate()} 方法時初始化 {@code mIsLargeLayout} 值: +</p> + +<pre> +boolean mIsLargeLayout; + +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mIsLargeLayout = getResources().getBoolean(R.bool.large_layout); +} +</pre> + + + +<h3 id="ActivityAsDialog">針對大型螢幕將 Activity 顯示為對話方塊</h3> + +<p>您可以針對大型螢幕將 {@link android.app.Activity} 顯示為對話方塊,而不是將對話方塊顯示為全螢幕 UI,藉此達到相同結果。 + +您要採用的方法取決於您的應用程式設計,但如果您的應用程式是針對小型螢幕進行設計,將 Activity 顯示為對話方塊通常就能獲得良好效果,而如果您想針對平板電腦改善使用者體驗,請將生命週期較短的 Activity 顯示為對話方塊。 + + +</p> + +<p>如果只想針對大型螢幕將 Activity 顯示為對話方塊,請為 <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> 宣示元素套用 {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} 主題: + +</p> + +<pre> +<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" > +</pre> + +<p>如要進一步瞭解如何運用主題設定 Activity 的樣式,請參閱<a href="{@docRoot}guide/topics/ui/themes.html">樣式和主題</a>指南。</p> + + + +<h2 id="DismissingADialog">關閉對話方塊</h2> + +<p>只要使用者輕觸任何用 {@link android.app.AlertDialog.Builder} 建立的動作按鈕,系統就會為您關閉對話方塊。 +</p> + +<p>此外,系統也會在使用者輕觸對話方塊清單中的項目時,關閉對話方塊,但如果清單採用圓形按鈕或核取方塊,系統就不會關閉對話方塊。 +不過,您可以對 {@link +android.support.v4.app.DialogFragment} 呼叫 {@link android.support.v4.app.DialogFragment#dismiss()},藉此手動關閉對話方塊。 +</p> + +<p>如果您需要在對話方塊關閉時執行特定動作,請在 {@link +android.support.v4.app.DialogFragment} 中實作 {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} 方法。 +</p> + +<p>此外,您還可以「取消」<em></em>對話方塊。這種特殊事件可用於指明使用者尚未完成工作便關閉對話方塊。 +如果使用者按下 [返回]<em></em> 按鈕、輕觸對話方塊以外的螢幕畫面,或如果您對 {@link +android.app.Dialog} 呼叫 {@link android.app.Dialog#cancel()} (例如藉此回應對話方塊中的 [取消] 按鈕),就會發生這個事件。 + +</p> + +<p>如上方範例所示,您可以在 {@link +android.support.v4.app.DialogFragment} 類別中實作 {@link android.support.v4.app.DialogFragment#onCancel onCancel()} 來回應取消事件。 +</p> + +<p class="note"><strong>注意:</strong>系統會在發生會呼叫 {@link android.support.v4.app.DialogFragment#onCancel onCancel()} 回呼的事件時呼叫 +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}。 +不過,如果您呼叫 {@link android.app.Dialog#dismiss Dialog.dismiss()} 或 {@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()},則系統會呼叫 {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}「而不是」<em></em>{@link android.support.v4.app.DialogFragment#onCancel onCancel()}。 + + +因此,我們通常建議您在使用者點擊您對話方塊中的「正面」<em></em>按鈕以便將對話方塊從檢視移除時,呼叫 {@link android.support.v4.app.DialogFragment#dismiss dismiss()}。 + +</p> + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd new file mode 100644 index 000000000000..be1fa7f0dfda --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=選單 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> +<ol> + <li><a href="#xml">在 XML 中定義選單</a></li> + <li><a href="#options-menu">建立選項選單</a> + <ol> + <li><a href="#RespondingOptionsMenu">處理點擊事件</a></li> + <li><a href="#ChangingTheMenu">在執行階段變更選單項目</a></li> + </ol> + </li> + <li><a href="#context-menu">建立內容關聯選單</a> + <ol> + <li><a href="#FloatingContextMenu">建立浮動內容選單</a></li> + <li><a href="#CAB">使用內容關聯動作模式</a></li> + </ol> + </li> + <li><a href="#PopupMenu">建立彈出式選單</a> + <ol> + <li><a href="#PopupEvents">處理點擊事件</a></li> + </ol> + </li> + <li><a href="#groups">建立選單群組</a> + <ol> + <li><a href="#checkable">使用可核取的選單項目</a></li> + </ol> + </li> + <li><a href="#intents">根據意圖新增選單項目</a> + <ol> + <li><a href="#AllowingToAdd">允許將 Activity 新增至其他選單</a></li> + </ol> + </li> +</ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.view.Menu}</li> + <li>{@link android.view.MenuItem}</li> + <li>{@link android.view.ContextMenu}</li> + <li>{@link android.view.ActionMode}</li> + </ol> + + <h2>另請參閱</h2> + <ol> + <li><a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a></li> + <li><a href="{@docRoot}guide/topics/resources/menu-resource.html">選單資源</a></li> + <li><a href="http://android-developers.blogspot.com/2012/01/say-goodbye-to-menu-button.html">和選單按鈕說再見 +</a></li> + </ol> +</div> +</div> + +<p>選單在許多類型的應用程式中都是常見的使用者介面元件。為提供熟悉且一致的使用者體驗,您應該使用 +{@link android.view.Menu} API 來呈現 Activity 中的使用者動作與其他選項。 +</p> + +<p>從 Android 3.0 (API 級別 11) 開始,提供 Android 的裝置不再需要提供專屬的「選單」<em></em>按鈕。 +有此變更之後,Android 應用程式應可脫離對傳統有 6 個項目的選單面板的依賴,改為提供動作列來呈現一般的使用者動作。 + +</p> + +<p>雖然有些選單項目的設計與使用者體驗有所變更,但依然是根據 +{@link android.view.Menu} API 來定義一組動作與選項的語意。本指南說明如何在所有版本的 Android 上,建立三個基本類型的選單或動作呈現方式: + +</p> + +<dl> + <dt><strong>選項選單和動作列</strong></dt> + <dd><a href="#options-menu">選項選單</a>是 Activity 的主要選單項目集合。 +您應該將對應用程式有全域影響的動作放置在此,例如「搜尋」、「撰寫電子郵件」及「設定」。 + + <p>如果您是為 Android 2.3 以下版本進行開發,使用者可按下「選單」<em></em>按鈕來顯示選項選單面板。 +</p> + <p>在 Android 3.0 以上版本,選項選單的項目是當成螢幕上的動作項目與溢出選項組合,以<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>呈現。 +從 Android 3.0 開始,「選單」<em></em>按鈕已淘汰 (有些裝置甚至沒有),因此您應轉向使用動作列來存取動作和其他選項。 + + +</p> + <p>請參閱<a href="#options-menu">建立選項選單</a>。</p> + </dd> + + <dt><strong>內容選單和內容關聯動作模式</strong></dt> + + <dd>內容選單是會在使用者長按某元素時顯示的<a href="#FloatingContextMenu">浮動選單</a>。 +它提供的動作會影響所選取內容或內容畫面。 + + <p>針對 Android 3.0 以上版本進行開發時,您應該改為使用<a href="#CAB">關聯比對動作模式</a>,以啟用所選取內容的動作。此模式顯示的動作項目會影響畫面頂端列中選取的內容,並允許使用者選取多個項目。 + +</p> + <p>請參閱<a href="#context-menu">建立內容關聯選單</a>。</p> +</dd> + + <dt><strong>彈出式選單</strong></dt> + <dd>彈出式選單顯示的項目清單會以垂直清單的方式,錨定在呼叫該選單的檢視。 +它很適合用來提供與特定內容有關的動作溢出,或針對第二部分的命令提供選項。 +彈出式選單中的動作「不」<strong></strong>應直接影響對應內容,應由內容關聯動作直接影響。 + +彈出式選單主要用於您 Activity 中與內容區域相關的延伸動作。 + + <p>請參閱<a href="#PopupMenu">建立彈出式選單</a>。</p> +</dd> +</dl> + + + +<h2 id="xml">在 XML 中定義選單</h2> + +<p>對於所有選單類型,Android 提供標準 XML 格式來定義選單項目。 +您應該在 XML <a href="{@docRoot}guide/topics/resources/menu-resource.html">選單資源</a>中定義選單與其相關項目,而不是在您 Activity 的程式碼中建置選單。 +接著,您可以擴大 Activity 或片段中的選單資源 (當成 +{@link android.view.Menu} 物件載入)。 +</p> + +<p>建議使用選單資源的幾個理由如下:</p> +<ul> + <li>較容易視覺化 XML 中的選單結構。</li> + <li>可將選單內容和您應用程式的行為程式碼分開。</li> + <li>可讓您運用<a href="{@docRoot}guide/topics/resources/index.html">應用程式資源</a>架構,為不同的平台版本、螢幕大小及其他設定,建立替代選單設定。 +</li> +</ul> + +<p>如要定義選單,可在專案的 <code>res/menu/</code> 目錄內建立 XML 檔案,然後利用下列元素建置選單: +</p> +<dl> + <dt><code><menu></code></dt> + <dd>定義選單項目的容器 {@link android.view.Menu}。<code><menu></code> 元素必須是檔案的根節點,並可保留一或多個 +<code><item></code> 與 <code><group></code> 元素。 +</dd> + + <dt><code><item></code></dt> + <dd>建立代表選單中單一項目的 {@link android.view.MenuItem}。此元素可以包含巢狀 +<code><menu></code> 元素以建立子選單。</dd> + + <dt><code><group></code></dt> + <dd>可供 {@code <item>} 元素選用的不可見容器。它可讓您將選單項目分類,以便分享屬性,例如有效狀態與可見度。 +如需詳細資訊,請參閱<a href="#groups">建立選單群組</a>。 +</dd> +</dl> + + +<p>稱為 <code>game_menu.xml</code> 的範例選單如下:</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/new_game" + android:icon="@drawable/ic_new_game" + android:title="@string/new_game" + android:showAsAction="ifRoom"/> + <item android:id="@+id/help" + android:icon="@drawable/ic_help" + android:title="@string/help" /> +</menu> +</pre> + +<p><code><item></code> 元素支援數個屬性,您可以用來定義項目的外觀與行為。 +上述選單中的項目包括下列屬性:</p> + +<dl> + <dt>{@code android:id}</dt> + <dd>項目獨有的資源 ID,當使用者選取該項目時可讓應用程式辨識出來。 +</dd> + <dt>{@code android:icon}</dt> + <dd>要當成項目圖示使用的可繪項目參考資料。</dd> + <dt>{@code android:title}</dt> + <dd>要當成項目標題使用的字串參考資料。</dd> + <dt>{@code android:showAsAction}</dt> + <dd>指定此項目應何時且如何顯示為<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>中的動作項目。</dd> +</dl> + +<p>這些都是您應該使用的最重要屬性,但不侷限於上述這幾個。 +如需所有支援屬性的詳細資訊,請參閱<a href="{@docRoot}guide/topics/resources/menu-resource.html">選單資源</a>。</p> + +<p>您可以藉由將 {@code <menu>} 元素新增為 {@code <item>} 的子項,將子選單新增至任何選單 (子選單除外) 的項目。 +當您的應用程式有很多可按主題分類的功能,例如 PC 應用程式選單列中的項目 (檔案、編輯、檢視等等) 時,子選單相當有用。 + +例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/file" + android:title="@string/file" > + <!-- "file" submenu --> + <menu> + <item android:id="@+id/create_new" + android:title="@string/create_new" /> + <item android:id="@+id/open" + android:title="@string/open" /> + </menu> + </item> +</menu> +</pre> + +<p>如要在 Activity 中使用選單,您必須使用 {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()} 擴大選單資源 (將 XML 轉換成可程式化的物件)。 +下列各節將說明如何擴大各種 +選單類型的選單。</p> + + + +<h2 id="options-menu">建立選項選單</h2> + +<div class="figure" style="width:200px;margin:0"> + <img src="{@docRoot}images/options_menu.png" height="333" alt="" /> + <p class="img-caption"><strong>圖 1.</strong>Android 2.3 裝置上瀏覽器中的選項選單。 +</p> +</div> + +<p>您應該在選項選單中包括與目前 Activity 內容關聯動作和其他選項,例如「搜尋」、「撰寫電子郵件」及「設定」。 +</p> + +<p>選項選單中的項目會顯示在螢幕上的哪個位置,取決於您為其開發應用程式的版本。 +</p> + +<ul> + <li>如果您為「Android 2.3.x (API 級別 10) 以下版本」<strong></strong>開發應用程式,當使用者按下[選單]<em></em> 按鈕,選項選單的內容會顯示在螢幕底部,如圖 1 所示。 + +開啟時,第一個可見部分就是最多可保留六個選單項目的圖示選單。 + +如果您的選單包含六個以上的項目,Android 會將第六個與其餘的項目放入溢出選單,使用者可透過選取[更多]<em></em> 來開啟。 + +</li> + + <li>如果您為「Android 3.0 (API 級別 11) 以上版本」<strong></strong>開發應用程式,選項選單中的項目會顯示在<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>。 +在預設情況下,系統會將所有項目放入動作溢出,使用者可以利用動作列右側的動作溢出圖示來顯示 (或按裝置的「選單」<em></em>按鈕,如果有的話)。 + +為了能夠快速存取重要的動作,您可以將 {@code android:showAsAction="ifRoom"} 新增至對應的 {@code <item>} 元素,將要顯示在動作列的幾個項目升階 (請參閱圖 2)。 + + + + <p>如需動作項目與其他動作列行為的詳細資訊,請參閱<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>指南。 </p> +<p class="note"><strong>注意:</strong>即使您「並非」<em></em>為 Android 3.0 以上版本進行開發,您仍可建置自己的動作列版面配置,達到類似的效果。 +如需如何支援舊版Android 提供動作列的範例,請參閱<a href="{@docRoot}resources/samples/ActionBarCompat/index.html">動作列相容性</a>範例。 + +</p> +</li> +</ul> + +<img src="{@docRoot}images/ui/actionbar.png" alt="" /> +<p class="img-caption"><strong>圖 2.</strong><a href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb 圖片庫</a>應用程式的動作列,顯示導覽索引標籤與相機動作項目 (再加上動作溢出按鈕)。 +</p> + +<p>您可以從 {@link android.app.Activity} 子類別或 {@link android.app.Fragment} 子類別宣告選項選單的項目。 +如果您的 Activity 與片段都宣告選項選單的項目,兩者會在 UI 中結合。 + +先顯示 Activity 的項目,接著會依片段新增至 Activity 的順序顯示各片段的項目。 +您可以視需要在您想移動的 {@code <item>} 中,利用其 {@code android:orderInCategory}屬性重新排列選單項目的順序。 +</p> + +<p>如要指定 Activity 的選項選單,可覆寫 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} (片段會提供自己的 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} 回呼)。在這種方法中,您可以將選單資源(<a href="#xml">在 XML 中完成定義</a>) 擴大回呼中提供的 {@link +android.view.Menu}。 +例如:</p> + +<pre> +@Override +public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = {@link android.app.Activity#getMenuInflater()}; + inflater.inflate(R.menu.game_menu, menu); + return true; +} +</pre> + +<p>透過 {@link android.view.MenuItem} API,您也可以使用 {@link android.view.Menu#add(int,int,int,int) +add()} 來新增選單項目,以及利用 {@link android.view.Menu#findItem findItem()} 擷取項目來修訂它們的屬性。 +</p> + +<p>如果您是為 Android 2.3.x 以下版本開發應用程式,當使用者初次開啟選單時,系統會呼叫 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()},以建立選項選單。 +如果您是為 Android 3.0 以上版本開發應用程式,啟動 Activity 時,系統會呼叫 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()},以在動作列顯示項目。 +</p> + + + +<h3 id="RespondingOptionsMenu">處理點擊事件</h3> + +<p>使用者從選項選單 (包括動作列中的動作項目) 中選取項目時,系統會呼叫您 Activity 的 +{@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} 方法。此方法會傳送所選取的 {@link android.view.MenuItem}。您可以呼叫 +{@link android.view.MenuItem#getItemId()} 傳回選單項目的唯一 ID (由選單資源中的 +{@code android:id} 屬性或透過指定給 +{@link android.view.Menu#add(int,int,int,int) add()} 方法的整數來定義) 來識別該項目。您可以將此 ID 和已知選單項目比對,以執行適當動作。 +例如:</p> + +<pre> +@Override +public boolean onOptionsItemSelected(MenuItem item) { + // Handle item selection + switch (item.getItemId()) { + case R.id.new_game: + newGame(); + return true; + case R.id.help: + showHelp(); + return true; + default: + return super.onOptionsItemSelected(item); + } +} +</pre> + +<p>當您成功處理選單項目時會傳回 {@code true}。如果您不處理選單項目,應該呼叫 +{@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} 的超級類別實作 (預設實作會傳回 false)。 +</p> + +<p>如果您的 Activity 包含片段,系統會先呼叫 Activity 的 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()},再依片段的新增順序呼叫各片段,直到其中之一傳回 +{@code true} 或所有片段均已呼叫。 +</p> + +<p class="note"><strong>提示:</strong>Android 3.0 新增的功能可讓您在 XML 中使用 +{@code android:onClick} 屬性定義選單項目的點擊行為。該屬性值必須是使用選單的 Activity 所定義的方法名稱。 +它必須是公用方法且接受單一 +{@link android.view.MenuItem} 參數,當系統呼叫此方法時,它會傳送選取的選單項目。 +如需詳細資訊與範例,請參閱<a href="{@docRoot}guide/topics/resources/menu-resource.html">選單資源</a>文件。</p> + +<p class="note"><strong>提示:</strong>如果您的應用程式包含多個 Activity 且有一些提供相同的選項選單,可考慮建立只實作 +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 與 {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} 方法的 Activity。 +接著,針對應共用相同選項選單的 Activity 擴充此類別。 +如此一來,您可以管理一組處理選單動作的程式碼,以及每一個繼承選單行為的子系類別。 + + +如果您想要將選單項目新增至其中一個子系 Activity,請覆寫該 Activity 中的 {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}。呼叫 {@code super.onCreateOptionsMenu(menu)} 以建立原始的選單項目,然後利用 +{@link +android.view.Menu#add(int,int,int,int) menu.add()} 來新增選單項目。您也可以覆寫個別選單項目的超級類別行為。 +</p> + + +<h3 id="ChangingTheMenu">在執行階段變更選單項目</h3> + +<p>系統呼叫 {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 之後,會保留您填入的 {@link android.view.Menu} 執行個體,除非該選單因某種理由而變無效,否則不會再次呼叫 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}。 +不過,{@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} 應該只用來建立初始選單狀態,您不要在 Activity 生命週期間用來進行變更。 +</p> + +<p>如果您想要根據 Activity 生命週期當中發生的事件來修改選項選單,您可以使用 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} 方法這麼做。 +這個方法會將目前存在的 +{@link android.view.Menu} 物件傳送給您加以修改,例如新增、移除或停用項目 +(片段也會提供 {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()} 回呼)。</p> + +<p>在 Android 2.3.x 以下版本,每當使用者開啟選項選單 (按下 [選單]<em></em> +按鈕) 時,系統都會呼叫 {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()}。</p> + +<p>在 Android 3.0 以上版本,當選單項目顯示在動作列時會一律將選項選單視為開啟。 +當事件發生且您想要執行選單更新時,您必須呼叫 +{@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()},以要求系統呼叫 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}。</p> + +<p class="note"><strong>注意:</strong>您不應該根據目前處於焦點狀態的 +{@link android.view.View},變更選項選單中的項目。 +處於輕觸模式 (使用者未使用軌跡球或導覽用的方向鍵) 時,檢視無法成為焦點,因此您不應該使用焦點當成修改選項選單中項目時的依據。 + +如果您想要在 {@link +android.view.View} 提供具內容相關性的選單項目,請使用<a href="#context-menu">內容選單</a>。</p> + + + + +<h2 id="context-menu">建立內容關聯選單</h2> + +<div class="figure" style="width:420px;margin-top:-1em"> + <img src="{@docRoot}images/ui/menu-context.png" alt="" /> + <p class="img-caption"><strong>圖 3.</strong>浮動內容選單的螢幕擷取畫面 (左) 與內容關聯動作列 (右)。 +</p> +</div> + +<p>內容關聯選單提供的動作會影響 UI 中的特定項或內容畫面。 + +您可以為任何檢視提供內容選單,但它們通常用於使用者能直接對各項目執行動作的 {@link +android.widget.ListView}、{@link android.widget.GridView} 或其他檢視集合中的項目。</p> + +<p>您有兩種方式可提供內容關聯動作:</p> +<ul> + <li>在<a href="#FloatingContextMenu">浮動內容選單</a>中。當使用者長按 (按住不放) 某個宣告支援內容選單的檢視時,選單會顯示為浮動的選單項目清單 (類似於對話方塊)。 + +使用者一次可對一個項目執行內容關聯動作。 +</li> + + <li>在<a href="#CAB">內容關聯動作模式</a>中。此模式是 +{@link android.view.ActionMode} 的系統實作,在畫面頂端將會影響將所選項目的動作項目顯示在「內容關聯動作列」<em></em>。 +此模式處於使用中時,使用者能一次對多個項目執行某一動作 (如果您的應用程式允許的話)。 +</li> +</ul> + +<p class="note"><strong>注意:</strong>Android 3.0 (API 級別 11) 以上版本可以使用內容關聯動作模式,此模式同時是可以顯示內容關聯動作時的偏好技術。 + +如果您的應用程式支援 3.0 以下版本,您應該在這類裝置上切換為浮動內容選單。 +</p> + + +<h3 id="FloatingContextMenu">建立浮動內容選單</h3> + +<p>如何提供浮動內容選單:</p> +<ol> + <li>呼叫 {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} 並將 {@link android.view.View} 傳送給它,向應該建立關聯的內容選單註冊 {@link android.view.View}。 + + + <p>如果您的 Activity 使用 {@link android.widget.ListView} 或 {@link android.widget.GridView},且您希望每個項目都提供相同的內容選單,可將 {@link android.widget.ListView} 或 {@link android.widget.GridView} 傳送至 {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()} 來註冊內容選單的所有項目。 + +</p> +</li> + + <li>在您的 {@link android.app.Activity} 或 {@link android.app.Fragment} 中實作 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} 方法。 + + <p>當註冊的檢視收到長按事件時,系統會呼叫 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} 方法。 +您可以在這裡定義選單項目,方法通常是擴大選單資源。例如: +</p> +<pre> +@Override +public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.context_menu, menu); +} +</pre> + +<p>{@link android.view.MenuInflater} 可讓您從<a href="{@docRoot}guide/topics/resources/menu-resource.html">選單資源</a>擴大內容選單。回呼方法參數包括使用者選取的 +{@link android.view.View},以及提供其他所選取項目相關資訊的 {@link android.view.ContextMenu.ContextMenuInfo} 物件。 + +如果 Activity 有數個分別提供不同內容選單的檢視,您可以使用這些參數來決定要擴大的內容選單。 + +</p> +</li> + +<li>實作 {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}。 + <p>當使用者選取選單項目時,系統會呼叫此方法,以便您執行適當的動作。 +例如:</p> + +<pre> +@Override +public boolean onContextItemSelected(MenuItem item) { + AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { + case R.id.edit: + editNote(info.id); + return true; + case R.id.delete: + deleteNote(info.id); + return true; + default: + return super.onContextItemSelected(item); + } +} +</pre> + +<p>{@link android.view.MenuItem#getItemId()} 方法會查詢選定選單項目的 ID,使用 +{@code +android:id} 屬性在 XML 中指派給各個選單項目,如<a href="#xml">在 XML 中定義選單</a>一節所述。 +</p> + +<p>當您成功處理選單項目時會傳回 {@code true}。如果您不處理選單項目,請將選單項目傳送至超級類別實作。 +如果您的 Activity 包括片段,該 Activity 會先收到此回呼。 +在不處理時呼叫超級類別,系統就可以一次一個 (依片段的新增順序) 將事件傳送至各片段中的個別回呼方法,直到傳回 +{@code true} 或 {@code false}。 +( +{@link android.app.Activity} 與 {@code android.app.Fragment} 的預設實作會傳回 {@code +false},因此當您不處理時,務必要呼叫超級類別。)</p> +</li> +</ol> + + +<h3 id="CAB">使用內容關聯動作模式</h3> + +<p>內容關聯動作模式是 {@link android.view.ActionMode} 的系統實作,執行內容關聯動作時著重於與使用者互動。 +當使用者選取項目而啟用此模式時,「內容關聯動作列」<em></em>會出現在畫面頂端,呈現使用者可在目前所選項目上執行的動作。 + +此模式啟用時,使用者能選取多個項目 (如果您允許的話)、取消選取項目,還可以在 Activity 內繼續瀏覽 (在您允許的範圍內)。 + +當使用者取消選取所有項目、按下 [後退] 按鈕,或選取該列左側的[完成]<em></em> 按鈕時,動作模式會停用且內容 關聯 動作列也會消失。 + +</p> + +<p class="note"><strong>注意:</strong>內容關聯動作列不一定要與<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>關聯。 +即使內容相關動作列在視覺上覆蓋動作列的位置,兩者依然獨立運作。 + +</p> + +<p>如果您在為 Android 3.0 (API 級別 11) 以上版本進行開發,您應使用內容關聯動作模式來呈現內容動作,而不是<a href="#FloatingContextMenu">浮動內容選單</a>。 +</p> + +<p>針對提供內容相關動作的檢視,您應該就下列兩種事件之一 (或兩者) 呼叫內容關聯動作模式: +</p> +<ul> + <li>使用者長按檢視。</li> + <li>使用者選取檢視內的核取方塊或類似 UI 元件。</li> +</ul> + +<p>應用程式如何呼叫內容關聯動作模式以及如何定義每個動作的行為,視您的設計而定。 +基本上可分為兩種設計:</p> +<ul> + <li>在個別的任意檢視上執行內容關聯動作。</li> + <li>針對 {@link +android.widget.ListView} 或 {@link android.widget.GridView} (允許使用者選取多個項目並對全部執行同一動作) 中的項目群組批次執行內容關聯動作。 +</li> +</ul> + +<p>下列各節說明每種情況所需的設定。</p> + + +<h4 id="CABforViews">為個別的檢視啟用內容關離動作模式</h4> + +<p>如果您只有在使用者選取特定檢視時,才想要呼叫內容關聯動作模式,建議您採取以下動作: +</p> +<ol> + <li>實作 {@link android.view.ActionMode.Callback} 介面。在它的回呼方法中,您可以指定內容相關動作列的動作、回應動作項目的點擊事件,以及處理動作模式的其他生命週期事件。 + +</li> + <li>當您想要顯示該列時 (例如當使用者長按檢視時),可呼叫 {@link android.app.Activity#startActionMode startActionMode()}。 +</li> +</ol> + +<p>例如:</p> + +<ol> + <li>實作 {@link android.view.ActionMode.Callback ActionMode.Callback} 介面: +<pre> +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { + + // Called when the action mode is created; startActionMode() was called + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // Inflate a menu resource providing context menu items + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.context_menu, menu); + return true; + } + + // Called each time the action mode is shown. Always called after onCreateActionMode, but + // may be called multiple times if the mode is invalidated. + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; // Return false if nothing is done + } + + // Called when the user selects a contextual menu item + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_share: + shareCurrentItem(); + mode.finish(); // Action picked, so close the CAB + return true; + default: + return false; + } + } + + // Called when the user exits the action mode + @Override + public void onDestroyActionMode(ActionMode mode) { + mActionMode = null; + } +}; +</pre> + +<p>請注意,這些事件回呼幾乎和<a href="#options-menu">選項選單</a>的回呼一模一樣,只是每一個都會傳送與事件關聯的 {@link +android.view.ActionMode} 物件。您可以使用 {@link +android.view.ActionMode} API 對 CAB 進行各種變更,例如使用 +{@link android.view.ActionMode#setTitle setTitle()} 與 {@link +android.view.ActionMode#setSubtitle setSubtitle()} 修改標題與子標題 (指出已選取的項目數量相當有用)。 +</p> + +<p>同時請注意,當動作模式終結時,上述範例會將 {@code mActionMode} 變數設為 null。 +在下個步驟中,您將看到它是如何初始化,以及如何儲存 Activity 或片段中的成員變數,非常實用。 +</p> +</li> + + <li>呼叫 {@link android.app.Activity#startActionMode startActionMode()} (可以的話) 以啟用內容關聯動作模式,例如回應長按 {@link +android.view.View}。 +</p> + +<pre> +someView.setOnLongClickListener(new View.OnLongClickListener() { + // Called when the user long-clicks on someView + public boolean onLongClick(View view) { + if (mActionMode != null) { + return false; + } + + // Start the CAB using the ActionMode.Callback defined above + mActionMode = getActivity().startActionMode(mActionModeCallback); + view.setSelected(true); + return true; + } +}); +</pre> + +<p>當您呼叫 {@link android.app.Activity#startActionMode startActionMode()} 時,系統會傳回建立的 +{@link android.view.ActionMode}。只要將此儲存在成員變數中,您就可以變更內容關聯動作列以回應其他事件。 +在上述範例中, +{@link android.view.ActionMode} 是用來確保不會重新建立已在使用中的 {@link android.view.ActionMode} 執行個體,方法是在將動作模式啟動之前,檢查成員是否為 null。 + +</p> +</li> +</ol> + + + +<h4 id="CABforListView">啟用 ListView 或 GridView 中的批次內容相關動作</h4> + +<p>如果您在 {@link android.widget.ListView} 或 {@link +android.widget.GridView} (或 {@link android.widget.AbsListView} 的另一個延伸) 中有一系列項目,且想要允許使用者執行批次動作,請進行以下動作: +</p> + +<ul> + <li>實作 {@link android.widget.AbsListView.MultiChoiceModeListener} 介面並使用 +{@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()} 加以設定以供檢視群組使用。在接聽器的回呼方法中,您可以指定內容相關動作列的動作、回應動作項目的點擊事件,以及處理從 {@link android.view.ActionMode.Callback} 介面繼承的其他回呼。 + +</li> + + <li>使用 {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL} 引數呼叫 {@link android.widget.AbsListView#setChoiceMode setChoiceMode()}。</li> +</ul> + +<p>例如:</p> + +<pre> +ListView listView = getListView(); +listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); +listView.setMultiChoiceModeListener(new MultiChoiceModeListener() { + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, + long id, boolean checked) { + // Here you can do something when items are selected/de-selected, + // such as update the title in the CAB + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + // Respond to clicks on the actions in the CAB + switch (item.getItemId()) { + case R.id.menu_delete: + deleteSelectedItems(); + mode.finish(); // Action picked, so close the CAB + return true; + default: + return false; + } + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // Inflate the menu for the CAB + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.context, menu); + return true; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + // Here you can make any necessary updates to the activity when + // the CAB is removed. By default, selected items are deselected/unchecked. + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // Here you can perform updates to the CAB due to + // an {@link android.view.ActionMode#invalidate} request + return false; + } +}); +</pre> + +<p>這樣一來,當使用者以長按的方式選取項目時,系統就會呼叫 {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +方法,並顯示包含指定動作的內容相關動作列。可以看見內容關聯動作列時,使用者即可選取其他項目。 +</p> + +<p>在某些情況下,內容關聯動作提供一般動作項目,因為使用者可能未發現有長按行為,所以您可能希望新增核取方塊或類似的 UI 元素,讓他們選取項目。 + +當使用者選取核取方塊時,您可以使用 {@link android.widget.AbsListView#setItemChecked setItemChecked()} 將個別的清單項目設定成已核取狀態,以呼叫內容關聯動作模式。 + +</p> + + + + +<h2 id="PopupMenu">建立彈出式選單</h2> + +<div class="figure" style="width:220px"> +<img src="{@docRoot}images/ui/popupmenu.png" alt="" /> +<p><strong>圖 4.</strong>Gmail 應用程式中的彈出式選單錨定於右上角的溢出按鈕。 +</p> +</div> + +<p>{@link android.widget.PopupMenu} 是錨定於 {@link android.view.View} 的強制回應選單。 +有空間的畫會顯示在錨定檢視下方或檢視上方。適合用途:</p> +<ul> + <li>為與特定內容關聯動作提供溢出樣式選單 (例如 Gmail 的電子郵件標頭,如圖 4 所示)。<em></em> + + <p class="note"><strong>注意:</strong>內容選單一般用於會「影響」<em></em>所選取內容的動作,與這並不相同。 +對於會影響所選取內容的動作,請使用<a href="#CAB">內容關聯動作模式</a>或<a href="#FloatingContextMenu">浮動內容選單</a>。 +</p></li> + <li>提供命令句的第二部分 (例如,標示為「新增」的按鈕會產生包含不同「新增」選項的彈出式選單)。 +</li> + <li>提供類似於 {@link android.widget.Spinner} (不保留永續性選擇) 的下拉式清單。 +</li> +</ul> + + +<p class="note"><strong>注意:</strong> API 級別 11 以上版本才可以使用 {@link android.widget.PopupMenu}。 +</p> + +<p>如果您<a href="#xml">在 XML 中定義選單</a>,以下說明如何顯示彈出式選單:</p> +<ol> + <li>透過其建構函式將 {@link android.widget.PopupMenu} 具現化,使用應錨定選單的目前應用程式 +{@link android.content.Context} 與 {@link android.view.View}。 +</li> + <li>使用 {@link android.view.MenuInflater} 將您的選單資源擴大成 +{@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()} 所傳回的 {@link android.view.Menu} 物件。針對 14 以上的 API 級別,您可以改用 +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()}。</li> + <li>呼叫 {@link android.widget.PopupMenu#show() PopupMenu.show()}。</li> +</ol> + +<p>例如,以下是含有 {@link android.R.attr#onClick android:onClick} 屬性可顯示彈出式選單的按鈕。 +</p> + +<pre> +<ImageButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_overflow_holo_dark" + android:contentDescription="@string/descr_overflow_button" + android:onClick="showPopup" /> +</pre> + +<p>接著,該 Activity 可以顯示彈出式選單,如下所示:</p> + +<pre> +public void showPopup(View v) { + PopupMenu popup = new PopupMenu(this, v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.actions, popup.getMenu()); + popup.show(); +} +</pre> + +<p>在 14 以上的 API 級別中,您可以利用 {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()} 將兩行結合來擴大選單。</p> + +<p>當使用者選取某項目或輕觸選單區域外時會關閉選單。 +您可以使用 {@link +android.widget.PopupMenu.OnDismissListener} 來接聽關閉事件。</p> + +<h3 id="PopupEvents">處理點擊事件</h3> + +<p>如要在使用者選取選單項目時執行動作,您必須呼叫 {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()} 來實作 {@link +android.widget.PopupMenu.OnMenuItemClickListener} 介面並向您的 {@link +android.widget.PopupMenu} 註冊。 +當使用者選取項目時,系統會在您的介面中呼叫 {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} 回呼。 +</p> + +<p>例如:</p> + +<pre> +public void showMenu(View v) { + PopupMenu popup = new PopupMenu(this, v); + + // This activity implements OnMenuItemClickListener + popup.setOnMenuItemClickListener(this); + popup.inflate(R.menu.actions); + popup.show(); +} + +@Override +public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + case R.id.archive: + archive(item); + return true; + case R.id.delete: + delete(item); + return true; + default: + return false; + } +} +</pre> + + +<h2 id="groups">建立選單群組</h2> + +<p>選單群組是指某些特性相同的選單項目集合。您可以利用群組: +</p> +<ul> + <li>透過 {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()} 來顯示或隱藏所有項目</li> + <li>透過 {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()} 來啟用或停用所有項目</li> + <li>透過 {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()} 指定是否可核取所有項目</li> +</ul> + +<p>您可以將 {@code <item>} 元素堆疊在選單資源中的 {@code <group>}元素內,或使用 {@link +android.view.Menu#add(int,int,int,int) add()} 方法指定群組 ID 來建立群組。 +</p> + +<p>以下是包含群組的範例選單資源:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_save" + android:icon="@drawable/menu_save" + android:title="@string/menu_save" /> + <!-- menu group --> + <group android:id="@+id/group_delete"> + <item android:id="@+id/menu_archive" + android:title="@string/menu_archive" /> + <item android:id="@+id/menu_delete" + android:title="@string/menu_delete" /> + </group> +</menu> +</pre> + +<p>群組中的項目和第一個項目都會顯示在同一層,選單中的這三個項目都屬於同層級。 +不過,您可以參考群組 ID 並使用上方所述的方法,修改群組當中兩個項目的特性。 +系統也絕不會將群組的項目分離。 +例如,如果您針對每個項目宣告 {@code +android:showAsAction="ifRoom"},它們都會顯示在動作列中或顯示在動作溢出中。 +</p> + + +<h3 id="checkable">使用可勾選的選單項目</h3> + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/radio_buttons.png" height="333" alt="" /> + <p class="img-caption"><strong>圖 5.</strong>包含可勾選項目的子選單螢幕擷取畫面。 +</p> +</div> + +<p>對於獨立選項使用核取方塊,或對互斥選項群組使用選項按鈕,將選單當成開啟或關閉選項的介面,相當實用。 + +圖 5 顯示的子選單包含能以圓形按鈕勾選的項目。 +</p> + +<p class="note"><strong>注意:</strong>圖示選單 (出自選項選單) 中的選單項目無法顯示核取方塊或選項按鈕。 +如果您選擇要將圖示選單中的項目設為可勾選,每當狀態變更時,您都必須切換圖示和/或文字,手動指出勾選狀態。 + +</p> + +<p>您可以使用 {@code <item>} 元素中的 {@code +android:checkable} 屬性,為個別的選單項目定義可勾選行為,或利用 {@code <group>} 元素中的 {@code android:checkableBehavior} 屬性來為整個群組定義。 +例如,此選單群組中的所有項目都可以利用選項按鈕勾選。 +</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <group android:checkableBehavior="single"> + <item android:id="@+id/red" + android:title="@string/red" /> + <item android:id="@+id/blue" + android:title="@string/blue" /> + </group> +</menu> +</pre> + +<p>{@code android:checkableBehavior} 屬性可接受其中一項: +<dl> + <dt>{@code single}</dt> + <dd>只能勾選群組中的一個項目 (圓形按鈕)</dd> + <dt>{@code all}</dt> + <dd>可勾選所有項目 (核取方塊)</dd> + <dt>{@code none}</dt> + <dd>沒有任何項目可供勾選</dd> +</dl> + +<p>您可以使用 +{@code <item>} 元素中的 {@code android:checked} 屬性將預設的勾選狀態套用至項目,並使用 {@link +android.view.MenuItem#setChecked(boolean) setChecked()} 方法在程式碼中變更它。</p> + +<p>已選取可勾選項目時,系統會呼叫所選取個別項目的回呼方法 (例如 {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()})。 +您必須在這裡設定核取方塊的狀態,原因是核取方塊或選項按鈕並不會自動變更其狀態。 + +您可以利用 +{@link android.view.MenuItem#isChecked()} 來查詢項目的目前狀態 (使用者選取它之前的狀態),然後利用 +{@link android.view.MenuItem#setChecked(boolean) setChecked()} 來設定勾選狀態。例如:</p> + +<pre> +@Override +public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.vibrate: + case R.id.dont_vibrate: + if (item.isChecked()) item.setChecked(false); + else item.setChecked(true); + return true; + default: + return super.onOptionsItemSelected(item); + } +} +</pre> + +<p>如果您不以這種方式設定已勾選狀態,將無法在使用者選取時變更項目 (核取方塊或選項按鈕) 的可見狀態。 + +當您確實設定狀態,Activity 會保留項目的已勾選狀態,當使用者稍後開啟選單時,即可看見您設定的已勾選狀態。 + +</p> + +<p class="note"><strong>注意:</strong>可勾選的選單項目只能在各工作階段上使用,並不會在應用程式終結後儲存。 + +如果想為使用者儲存特定應用程式設定,請使用<a href="{@docRoot}guide/topics/data/data-storage.html#pref">共用偏好設定</a>。 +</p> + + + +<h2 id="intents">根據意圖新增選單項目</h2> + +<p>有時候您會希望選單項目使用 {@link android.content.Intent}來啟動 Activity (不論是您的應用程式或另一應用程式中的 Activity)。 +當您知道想要使用的意圖,同時有應繼承該意圖的特定選單項目時,您可在使用者選取項目時才回呼的適當方法實作期間使用 +{@link android.app.Activity#startActivity(Intent) startActivity()} 執行該意圖 (例如 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} callback) 回呼)。 + +</p> + +<p>不過,如果您不確定使用者的裝置是否包含可處理該意圖的應用程式,而新增可呼叫它的選單項目會導致選單項目無法運作,原因可能是該意圖不會解析成 Activity。 + + +為解決這個問題,當 Android 在處理您意圖的裝置上找到 Activity 時,Android 會讓您將選單項目動態新增至選單。 +</p> + +<p>如何根據接受意圖的可用 Activity 來新增選單項目:</p> +<ol> + <li>定義類別為 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} 和/或 +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 的意圖,再加上其他需求。</li> + <li>呼叫 {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}。Android 接著會搜尋可執行該意圖的任何應用程式,並將其新增至您的選單。 +</li> +</ol> + +<p>如果未安裝可滿足該意圖的應用程式,則不會新增任何選單項目。 +</p> + +<p class="note"><strong>注意:</strong> +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 是用來處理畫面上目前選取的元素。 +因此,請只在 {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()} 中建立選單的情況下才使用。</p> + +<p>例如:</p> + +<pre> +@Override +public boolean onCreateOptionsMenu(Menu menu){ + super.onCreateOptionsMenu(menu); + + // Create an Intent that describes the requirements to fulfill, to be included + // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE. + Intent intent = new Intent(null, dataUri); + intent.addCategory(Intent.CATEGORY_ALTERNATIVE); + + // Search and populate the menu with acceptable offering applications. + menu.addIntentOptions( + R.id.intent_group, // Menu group to which new items will be added + 0, // Unique item ID (none) + 0, // Order for the items (none) + this.getComponentName(), // The current activity name + null, // Specific items to place first (none) + intent, // Intent created above that describes our requirements + 0, // Additional flags to control items (none) + null); // Array of MenuItems that correlate to specific items (none) + + return true; +}</pre> + +<p>找到的各個 Activity 提供的意圖篩選器如果與定義的意圖相符,就會新增選單項目,使用意圖篩選器的 <code>android:label</code> 中的值當成選單項目標題,並將應用程式圖示當成選單項目圖示。 + +此外, +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} 方法還會傳回新增的選單項目數量。</p> + +<p class="note"><strong>注意:</strong>當您呼叫 {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} 時,它會覆寫第一個引數中所指定選單群組中的任何或所有選單項目。 +</p> + + +<h3 id="AllowingToAdd">允許將 Activity 新增至其他選單</h3> + +<p>您也能向其他應用程式提供 Activity 的服務,這樣即可在其他應用程式的選單中包含您的應用程式 (與上述的角色顛倒)。 +</p> + +<p>如要包含在其他應用程式選單中,您必須照常定義意圖篩選器,但務必為意圖篩選器類別納入 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} 和/或 {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 值。 + +例如:</p> +<pre> +<intent-filter label="@string/resize_image"> + ... + <category android:name="android.intent.category.ALTERNATIVE" /> + <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> + ... +</intent-filter> +</pre> + +<p>如要進一步瞭解如何編寫意圖篩選器,請參閱<a href="/guide/components/intents-filters.html">意圖和意圖篩選器</a>。 +</p> + +<p>如需採用此技術的範例應用程式,請參閱 <a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">NotePad</a> 範例程式碼。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 000000000000..b8537445cff0 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=通知 +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>本文件內容</h2> +<ol> + <li><a href="#Design">設計注意事項</a></li> + <li><a href="#CreateNotification">建立通知</a> + <ol> + <li><a href="#Required">必要的通知內容</a></li> + <li><a href="#Optional">選用的通知內容和設定</a></li> + <li><a href="#Actions">通知動作</a></li> + <li><a href="#Priority">通知優先順序</a></li> + <li><a href="#SimpleNotification">建立簡易通知</a></li> + <li><a href="#ApplyStyle">將擴充版面配置套用至通知</a></li> + <li><a href="#Compatibility">處理相容性</a></li> + </ol> + </li> + <li><a href="#Managing">管理通知</a> + <ol> + <li><a href="#Updating">更新通知</a></li> + <li><a href="#Removing">移除通知</a></li> + </ol> + </li> + <li><a href="#NotificationResponse">啟動 Activity 時保留導覽</a> + <ol> + <li><a href="#DirectEntry">設定一般 Activity PendingIntent</a></li> + <li><a href="#ExtendedNotification">設定特殊 Activity PendingIntent</a></li> + </ol> + </li> + <li><a href="#Progress">在通知中顯示進度</a> + <ol> + <li><a href="#FixedProgress">顯示時間長度固定的進度指示器</a></li> + <li><a href="#ActivityIndicator">顯示持續性 Activity 指示器</a></li> + </ol> + </li> + <li><a href="#metadata">通知中繼資料</a></li> + <li><a href="#Heads-up">抬頭通知</a></li> + <li><a href="#lockscreenNotification">鎖定螢幕通知</a></li> + <ol> + <li><a href="#visibility">設定可見度</a></li> + <li><a href="#controllingMedia">在鎖定螢幕上控制媒體播放</a></li> + </ol> + <li><a href="#CustomNotification">自訂通知版面配置</a></li> +</ol> + + <h2>重要類別</h2> + <ol> + <li>{@link android.app.NotificationManager}</li> + <li>{@link android.support.v4.app.NotificationCompat}</li> + </ol> + <h2>相關影片</h2> + <ol> + <li> + <a href="http://www.youtube.com/watch?v=Yc8YrVc47TI&feature=player_detailpage#t=1672s">4.1 版中的通知 +</a> + </li> + </ol> +<h2>另請參閱</h2> +<ol> + <li> + <a href="{@docRoot}design/patterns/notifications.html">Android 設計:通知</a> + </li> +</ol> +</div> +</div> +<p> + 通知是您應用程式的一般 UI 以外,可以向使用者顯示的訊息。當您告訴系統發出通知時,它會先在「通知區域」<strong></strong>顯示為圖示。 + +使用者可開啟「通知匣」<strong></strong>來查看通知的詳細資料。 +通知區域與通知匣都是使用者可隨時查看的系統控制區域。 + +</p> +<img id="figure1" src="{@docRoot}images/ui/notifications/notification_area.png" height="" alt="" /> +<p class="img-caption"> + <strong>圖 1.</strong>通知區域中的通知。 +</p> +<img id="figure2" src="{@docRoot}images/ui/notifications/notification_drawer.png" width="280px" alt="" /> +<p class="img-caption"> + <strong>圖 2.</strong>通知匣中的通知。 +</p> + +<p class="note"><strong>注意:</strong>除非另外註明,否則本指南參照<a href="{@docRoot}tools/support-library/index.html">支援程式庫</a> 4 版中的 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 類別。類別 {@link android.app.Notification.Builder Notification.Builder} 是在 Android 3.0 (API 級別 11) 新增。 + + +</p> + +<h2 id="Design">設計注意事項</h2> + +<p>做為 Android 使用者介面重要部分,通知有自己的設計指導方針。 +在 Android 5.0 (API 級別 21) 導入的質感設計變更,更是特別重要。 +如需詳情,請參閱<a href="{@docRoot}training/material/index.html">質感設計</a>。 +如要瞭解如何設計通知與其互動,請參閱<a href="{@docRoot}design/patterns/notifications.html">通知</a>設計指南。 +</p> + +<h2 id="CreateNotification">建立通知</h2> + +<p>您會為 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 物件中的通知指定 UI 資訊與動作。 +如要建立通知本身,您可以呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()},其傳回的 +{@link android.app.Notification} 物件會包含您的規格。如要發出通知,您可以呼叫 {@link android.app.NotificationManager#notify NotificationManager.notify()} 將 {@link android.app.Notification} 物件傳送至系統。 + +</p> + +<h3 id="Required">必要的通知內容</h3> +<p> + {@link android.app.Notification} 物件「必須」<em></em>包含下列項目: +</p> +<ul> + <li> + 小圖示,由 +{@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} 設定 + </li> + <li> + 標題,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} 設定 + </li> + <li> + 詳情文字,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} 設定 + </li> +</ul> +<h3 id="Optional">選用的通知內容和設定</h3> +<p> + 所有其他的通知設定與內容均為選用。如需詳情,請參閱 +{@link android.support.v4.app.NotificationCompat.Builder} 的參考文件。 +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Actions">通知動作</h3> +<p> + 動作雖屬選用性質,但您至少必須將一個動作新增至通知。 + 動作可讓使用者從通知直接移至您應用程式中的 +{@link android.app.Activity},他們能在那裡查看一或多個事件或執行進一步工作。 + +</p> +<p> + 通知可以提供多個動作。您應該一律定義會在使用者按一下通知時觸發的動作。 +這個動作通常會開啟您應用程式中的 +{@link android.app.Activity}。您也可以將按鈕新增至通知來執行其他動作,例如延遲鬧鐘或立即回應文字訊息。自 Android 4.1 起才提供此功能。 + +如果您使用其他動作按鈕,也務必要在您應用程式的 {@link android.app.Activity} 中提供此功能。 + +如需詳情,請參閱<a href="#Compatibility">處理相容性</a>。 +</p> +<p> + 在 {@link android.app.Notification} 內,動作本身是由 +{@link android.app.PendingIntent} 完成定義,其中包含的 +{@link android.content.Intent} 會啟動您應用程式中的 +{@link android.app.Activity}。如要將 +{@link android.app.PendingIntent} 與手勢關聯,可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder} 的適當方法。例如,如果當使用者按一下通知匣中的通知文字時,您希望啟動 +{@link android.app.Activity},可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()} 來新增 {@link android.app.PendingIntent}。 + +</p> +<p> + 最常見的動作情況就是在使用者按一下通知時啟動 {@link android.app.Activity}。 +您也可以在使用者關閉通知時啟動 {@link android.app.Activity}。 +在 Android 4.1 以上版本中,您可以透過動作按鈕啟動 +{@link android.app.Activity}。如需詳情,請參閱 +{@link android.support.v4.app.NotificationCompat.Builder} 的參考指南。 +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Priority">通知優先順序</h3> +<p> + 如有需要,您可以設定通知的優先順序。優先順序就像是提示一樣,可讓裝置 UI 知道該如何顯示通知。 + + 如要設定通知的優先順序,可呼叫 {@link +android.support.v4.app.NotificationCompat.Builder#setPriority(int) +NotificationCompat.Builder.setPriority()},然後傳入其中一個 {@link +android.support.v4.app.NotificationCompat} 優先順序常數。共有五個優先順序級別,範圍從 +{@link +android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) 到 {@link +android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2)。如未加以設定,優先順序會預設為 +{@link +android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0)。 +</p> +<p> 如要瞭解如何設定適當的優先順序級別,請參閱<a href="{@docRoot}design/patterns/notifications.html">通知</a>設計指南中的「正確地設定及管理通知優先順序」。 + + +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="SimpleNotification">建立簡易通知</h3> +<p> + 下列程式碼片段說明簡易的通知,指定要在使用者按一下通知時開啟的 Activity。 +請注意,以下程式碼會建立 +{@link android.support.v4.app.TaskStackBuilder} 物件,並使用此物件建立動作的 +{@link android.app.PendingIntent}。如要進一步瞭解這種模式,請參閱<a href="#NotificationResponse">啟動 Activity 時保留導覽</a>: + + +</p> +<pre> +NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle("My notification") + .setContentText("Hello World!"); +// Creates an explicit intent for an Activity in your app +Intent resultIntent = new Intent(this, ResultActivity.class); + +// The stack builder object will contain an artificial back stack for the +// started Activity. +// This ensures that navigating backward from the Activity leads out of +// your application to the Home screen. +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); +// Adds the back stack for the Intent (but not the Intent itself) +stackBuilder.addParentStack(ResultActivity.class); +// Adds the Intent that starts the Activity to the top of the stack +stackBuilder.addNextIntent(resultIntent); +PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent( + 0, + PendingIntent.FLAG_UPDATE_CURRENT + ); +mBuilder.setContentIntent(resultPendingIntent); +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// mId allows you to update the notification later on. +mNotificationManager.notify(mId, mBuilder.build()); +</pre> +<p>這樣一來,您的使用者就會收到通知。</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ApplyStyle">將擴充版面配置套用至通知</h3> +<p> + 如要讓通知顯示在擴充的檢視中,請先建立含有您所需一般檢視選項的 +{@link android.support.v4.app.NotificationCompat.Builder} 物件。 +接著,將擴充版面配置物件當成引數,呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setStyle +Builder.setStyle()}。 +</p> +<p> + 請記住,Android 4.1 以下版本平台不支援擴充通知。如要進一步瞭解如何處理 Android 4.1 以下版本平台的通知,請參閱<a href="#Compatibility">處理相容性</a>。 + + +</p> +<p> + 例如,下列程式碼片段示範如何更改上述程式碼片段中建立的通知,以使用擴充版面配置: + +</p> +<pre> +NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle("Event tracker") + .setContentText("Events received") +NotificationCompat.InboxStyle inboxStyle = + new NotificationCompat.InboxStyle(); +String[] events = new String[6]; +// Sets a title for the Inbox in expanded layout +inboxStyle.setBigContentTitle("Event tracker details:"); +... +// Moves events into the expanded layout +for (int i=0; i < events.length; i++) { + + inboxStyle.addLine(events[i]); +} +// Moves the expanded layout object into the notification object. +mBuilder.setStyle(inBoxStyle); +... +// Issue the notification here. +</pre> + +<h3 id="Compatibility">處理相容性</h3> + +<p> + 即使支援程式庫類別 {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 中有設定這些功能的方法,特定版本也不一定能使用所有通知功能。 + + + 例如,取決於擴充通知的動作按鈕,只能在 Android 4.1 以上版本中顯示,原因是擴充通知本身只能在 Android 4.1 以上版本中使用。 + + +</p> +<p> + 為確保能有最佳相容性,可利用 +{@link android.support.v4.app.NotificationCompat NotificationCompat} 與其子類別來建立通知,特別是 +{@link android.support.v4.app.NotificationCompat.Builder +NotificationCompat.Builder}。此外,實作通知時,請按照下列程序操作: +</p> +<ol> + <li> + 不論使用者所用的版本為何,請向所有使用者提供所有所有通知功能。 +如要這麼做,請驗證可從您應用程式的 +{@link android.app.Activity} 使用所有功能。您可能會想要新增 +{@link android.app.Activity} 來執行此動作。 + <p> + 例如,如果您想要使用 +{@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} + 以提供可停止和開始媒體播放的控制項,可先在您應用程式的 +{@link android.app.Activity} 中實作此控制項。 + </p> + </li> + <li> + 藉由在使用者按一下通知時啟動功能,可確保所有使用者都能使用 {@link android.app.Activity} 中的功能。 +如要這樣做,請為 {@link android.app.Activity} 建立 {@link android.app.PendingIntent}。 + +呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 以將 {@link android.app.PendingIntent} 新增至通知。 + </li> + <li> + 現在,可以將您想要使用的擴充通知功能新增至通知。請記住,您新增的任何功能也必須能在使用者點選通知時所啟動的 +{@link android.app.Activity} 中使用。 + + </li> +</ol> + + +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="Managing">管理通知</h2> +<p> + 當您必須對相同類型的事件發出多次通知時,請避免建立全新的通知。 +您應該考慮變更或新增 (或兩者) 就有通知的某些值來加以更新。 + +</p> +<p> + 例如,Gmail 藉由新增其未讀訊息計數並將每封電子郵件的摘要新增至通知,藉此通知使用者新電子郵件已送達。 +而這稱為「堆疊」通知。詳情請參閱<a href="{@docRoot}design/patterns/notifications.html">通知</a>設計指南。 + + +</p> +<p class="note"> + <strong>注意:</strong>此 Gmail 功能需要「收件匣」擴充版面配置,也屬於 Android 4.1 以上版本才能使用的擴充通知功能。 + +</p> +<p> + 以下各節說明如何更新及移除通知。 +</p> +<h3 id="Updating">更新通知</h3> +<p> + 藉由呼叫 +{@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()} 連同通知 ID 一起發出,即可將通知設定成可以更新。 + 如要在發出此通知後加以更新,可以更新或建立 +{@link android.support.v4.app.NotificationCompat.Builder} 物件,從中建置 +{@link android.app.Notification} 物件,並將 +{@link android.app.Notification} 連同您先前使用的相同 ID 一起發出。如果仍可看見先前的通知,系統會更新 {@link android.app.Notification} 物件內容中的通知。 + +如果已關閉先前的通知,會改為建立新的通知。 + +</p> +<p> + 以下程式碼片段示範如何更新通知以反映發生的事件數目。 +以堆疊通知的方式顯示摘要: +</p> +<pre> +mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// Sets an ID for the notification, so it can be updated +int notifyID = 1; +mNotifyBuilder = new NotificationCompat.Builder(this) + .setContentTitle("New Message") + .setContentText("You've received new messages.") + .setSmallIcon(R.drawable.ic_notify_status) +numMessages = 0; +// Start of a loop that processes data and then notifies the user +... + mNotifyBuilder.setContentText(currentText) + .setNumber(++numMessages); + // Because the ID remains unchanged, the existing notification is + // updated. + mNotificationManager.notify( + notifyID, + mNotifyBuilder.build()); +... +</pre> + +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="Removing">移除通知</h3> +<p> + 下列其中一個情況發生之前,都會看見通知: +</p> +<ul> + <li> + 使用者個別關閉通知,或透過「全部清除」的方式關閉通知 (如果可以清除通知的話)。 + + </li> + <li> + 使用者按一下通知,而您在建立通知時呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()}。 + + </li> + <li> + 您對特定通知 ID 呼叫 {@link android.app.NotificationManager#cancel(int) cancel()}。這種方法也會刪除進行中的通知。 + + </li> + <li> + 您呼叫 {@link android.app.NotificationManager#cancelAll() cancelAll()},將先前發出的所有通知移除。 + + </li> +</ul> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="NotificationResponse">啟動 Activity 時保留導覽</h2> +<p> + 當您從通知啟動 {@link android.app.Activity} 時,您必須保留使用者預期的導覽體驗。 +按一下 <i>[返回]</i> 應可將使用者從應用程式的一般工作流程帶回主螢幕,而按一下 + <i>[近期記錄]</i> 應會將 +{@link android.app.Activity} 顯示為個別的工作。如要保留導覽體驗,請以全新的工作啟動 +{@link android.app.Activity}。您該如何設定 +{@link android.app.PendingIntent} 以取得全新工作,取決於即將啟動的 +{@link android.app.Activity} 屬性。一般有兩種情況: +</p> +<dl> + <dt> + 一般 Activity + </dt> + <dd> + 您即將啟動的 {@link android.app.Activity} 屬於應用程式的一般工作流程。 +在這種情況下,設定 {@link android.app.PendingIntent}以啟動全新工作,再連同返回堆疊一起提供 {@link android.app.PendingIntent},可再次產生應用程式的一般 + + <i>返回</i> 行為。 + <p> + Gmail 應用程式的通知可示範這種情況。當您點選某一封電子郵件訊息的通知後,您會看到訊息本身。 +輕觸 [返回]<b></b>可將您從 Gmail 帶回主螢幕,就像您剛才從主螢幕進入 Gmail,而不是從通知進入。 + + + </p> + <p> + 不論您所在的應用程式為何,當您輕觸通知時,都會發生這種情況。 +例如,如果您在 Gmail 撰寫訊息,而您點選某一封電子郵件的通知,就會立即前往該封電子郵件。 +輕觸 <i>[返回]</i> + 會將您帶往收件匣,然後再到主螢幕,而不是到您正在撰寫的訊息。 + + </p> + </dd> + <dt> + 特殊 Activity + </dt> + <dd> + 只有從通知啟動這個 {@link android.app.Activity} 時,使用者才會看到它。 + 在某種意義上,{@link android.app.Activity}藉由提供難以在通知本身顯示的資訊來擴充通知。 +針對這種情況,請設定 +{@link android.app.PendingIntent} 以全新工作啟動。您不必建立返回堆疊,原因是啟動的 +{@link android.app.Activity} 不屬於應用程式的 Activity 流程。 +按一下 <i>[返回]</i> 還是會將使用者帶回主螢幕。 + + </dd> +</dl> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="DirectEntry">設定一般 Activity PendingIntent</h3> +<p> + 如要設定會啟動直接項目 +{@link android.app.Activity} 的 {@link android.app.PendingIntent},請按照下列步驟操作: +</p> +<ol> + <li> + 在宣示說明中定義您應用程式的 {@link android.app.Activity} 階層。 + <ol style="list-style-type: lower-alpha;"> + <li> + 新增 Android 4.0.3 以下版本的支援。如要這樣做,請將 +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a></code> 元素新增為 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 的下層物件,以指定您要啟動 +{@link android.app.Activity} 的上層物件。 + + <p> + 針對此元素設定 +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a>="android.support.PARENT_ACTIVITY"</code>。 + 設定 +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#val">android:value</a>="<parent_activity_name>"</code>,其中 <code><parent_activity_name></code> 是上層 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素的 +<code><a href="{@docRoot}guide/topics/manifest/meta-data-element.html#nm">android:name</a></code> 值。 + + +如需範例,請參閱下列 XML。 + </p> + </li> + <li> + 此外您還需要新增 Android 4.1 以上版本的支援。為此,請將 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#parent">android:parentActivityName</a></code> 屬性新增到您要啟動 {@link android.app.Activity} 的 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素。 + + + </li> + </ol> + <p> + 最後的 XML 看起來會如下所示: + </p> +<pre> +<activity + android:name=".MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +<activity + android:name=".ResultActivity" + android:parentActivityName=".MainActivity"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".MainActivity"/> +</activity> +</pre> + </li> + <li> + 根據啟動 +{@link android.app.Activity} 的 {@link android.content.Intent} 建立返回堆疊: + <ol style="list-style-type: lower-alpha;"> + <li> + 建立 {@link android.content.Intent} 以啟動 {@link android.app.Activity}。 + </li> + <li> + 呼叫 {@link android.app.TaskStackBuilder#create +TaskStackBuilder.create()} 來建立堆疊建立器。 + </li> + <li> + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} 將返回堆疊新增至堆疊建立器。 + 對於您在宣示說明中所定義階層中的每個 {@link android.app.Activity} +而言,返回堆疊包含會啟動 +{@link android.app.Activity} 的 {@link android.content.Intent} 物件。此方法也會新增以全新工作啟動堆疊的旗標。 + + <p class="note"> + <strong>注意:</strong>雖然 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}的引數是參考啟動的 {@link android.app.Activity},但呼叫的方法不會新增會啟動 +{@link android.app.Activity} 的 +{@link android.content.Intent}, +這項工作是在下個步驟中完成。 + </p> + </li> + <li> + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()} 來新增會從通知啟動 {@link android.app.Activity} 的 {@link android.content.Intent}。 + + 將您在第一個步驟中建立的 {@link android.content.Intent} 當成 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()} 的引數傳送。 + + </li> + <li> + 如有必要,呼叫 +{@link android.support.v4.app.TaskStackBuilder#editIntentAt +TaskStackBuilder.editIntentAt()} 在堆疊上將引數新增至 {@link android.content.Intent} 物件。當使用者使用以下方式導覽時,有必要確保目標 +{@link android.app.Activity} 可顯示有意義的資料: + <i>返回</i>。 + </li> + <li> + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()} 來取得 {@link android.app.PendingIntent} 以用於此返回堆疊。 + 接著,您可以使用這個 {@link android.app.PendingIntent} 當成 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 的引數。 + </li> + </ol> + </li> +</ol> +<p> + 以下程式碼片段會示範程序: +</p> +<pre> +... +Intent resultIntent = new Intent(this, ResultActivity.class); +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); +// Adds the back stack +stackBuilder.addParentStack(ResultActivity.class); +// Adds the Intent to the top of the stack +stackBuilder.addNextIntent(resultIntent); +// Gets a PendingIntent containing the entire back stack +PendingIntent resultPendingIntent = + stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); +... +NotificationCompat.Builder builder = new NotificationCompat.Builder(this); +builder.setContentIntent(resultPendingIntent); +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +mNotificationManager.notify(id, builder.build()); +</pre> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ExtendedNotification">設定特殊 Activity PendingIntent</h3> +<p> + 下一節說明如何設定特殊 Activity +{@link android.app.PendingIntent}。 +</p> +<p> + 特殊 {@link android.app.Activity} 不需要返回堆疊,因此您不必在宣示說明中定義其 +{@link android.app.Activity} 階層,也不用呼叫 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}來建置返回堆疊。 + +而是使用宣示說明設定 {@link android.app.Activity} 工作選項,並呼叫 +{@link android.app.PendingIntent#getActivity getActivity()} 來建立 {@link android.app.PendingIntent}: + +</p> +<ol> + <li> + 在您的宣示說明中,將下列屬性新增至 {@link android.app.Activity} 的 +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> 元素 + + <dl> + <dt> +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">android:name</a>="<i>activityclass</i>"</code> + </dt> + <dd> + Activity 的完整類別名稱。 + </dd> + <dt> +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">android:taskAffinity</a>=""</code> + </dt> + <dd> + 結合您在程式碼中設定的 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} 旗標,可確保此 {@link android.app.Activity} 不會前往應用程式的預設工作。 + +具有應用程式預設親和性的任何現有工作均不受影響。 + + </dd> + <dt> +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude">android:excludeFromRecents</a>="true"</code> + </dt> + <dd> + 從 [近期記錄] 中排除新的工作,<i></i>,避免使用者意外返回瀏覽。 + + </dd> + </dl> + <p> + 以下程式碼片段顯示該元素: + </p> +<pre> +<activity + android:name=".ResultActivity" +... + android:launchMode="singleTask" + android:taskAffinity="" + android:excludeFromRecents="true"> +</activity> +... +</pre> + </li> + <li> + 建置並發出通知: + <ol style="list-style-type: lower-alpha;"> + <li> + 建立會啟動 {@link android.app.Activity} 的{@link android.content.Intent}。 + + </li> + <li> + 使用旗標 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} 與 +{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK} 呼叫 +{@link android.content.Intent#setFlags setFlags()} 來設定 {@link android.app.Activity} 以全新的空白工作啟動。 + + </li> + <li> + 為 {@link android.content.Intent} 設定您需要的任何其他選項。 + </li> + <li> + 呼叫 {@link android.app.PendingIntent#getActivity getActivity()} 從{@link android.content.Intent} 中建立 {@link android.app.PendingIntent}。 + + 接著,您可以使用這個 {@link android.app.PendingIntent} 當成 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 的引數。 + </li> + </ol> + <p> + 以下程式碼片段會示範程序: + </p> +<pre> +// Instantiate a Builder object. +NotificationCompat.Builder builder = new NotificationCompat.Builder(this); +// Creates an Intent for the Activity +Intent notifyIntent = + new Intent(this, ResultActivity.class); +// Sets the Activity to start in a new, empty task +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); +// Creates the PendingIntent +PendingIntent notifyPendingIntent = + PendingIntent.getActivity( + this, + 0, + notifyIntent, + PendingIntent.FLAG_UPDATE_CURRENT +); + +// Puts the PendingIntent into the notification builder +builder.setContentIntent(notifyPendingIntent); +// Notifications are issued by sending them to the +// NotificationManager system service. +NotificationManager mNotificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// Builds an anonymous Notification object from the builder, and +// passes it to the NotificationManager +mNotificationManager.notify(id, builder.build()); +</pre> + </li> +</ol> +<!-- ------------------------------------------------------------------------------------------ --> +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="Progress">在通知中顯示進度</h2> +<p> + 通知可包含動畫進度指示器,向使用者顯示進行中操作的狀態。 +如果您隨時可預估操作花費的時間以及完成程度,可以使用形式「明確」的指示器 (進度列)。 + +如果您無法預估操作的時間長度,請使用形式「不明確」的指示器 (Activity 指示器)。 + +</p> +<p> + 進度指示器是使用平台的 +{@link android.widget.ProgressBar} 類別實作來顯示。 +</p> +<p> + 從 Android 4.0 開始,只要呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 即可在平台上使用進度指示器。對於先前的版本,您必須建立自己的自訂通知版面配置,當中還要包含 {@link android.widget.ProgressBar} 檢視。 + + +</p> +<p> + 下列各節說明如何使用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 在通知中顯示進度。 +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="FixedProgress">顯示時間長度固定的進度指示器</h3> +<p> + 如要顯示明確的進度列,請呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress +setProgress(max, progress, false)} 將該列新增至您的通知,然後發出通知。隨著您的操作進行, +<code>progress</code> 會增加並更新通知。操作結束時, +<code>progress</code> 應該等於 <code>max</code>。呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 的常見方法是將 <code>max</code> 設定為 100,然後新增 <code>progress</code> 做為操作的「完成百分比」值。 + + +</p> +<p> + 當操作完成時,您可以繼續顯示進度列或將其移除。不論是任何一種情況,都務必更新通知文字來指出操作已完成。 + + 如要移除進度列,請呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress +setProgress(0, 0, false)}。例如: +</p> +<pre> +... +mNotifyManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +mBuilder = new NotificationCompat.Builder(this); +mBuilder.setContentTitle("Picture Download") + .setContentText("Download in progress") + .setSmallIcon(R.drawable.ic_notification); +// Start a lengthy operation in a background thread +new Thread( + new Runnable() { + @Override + public void run() { + int incr; + // Do the "lengthy" operation 20 times + for (incr = 0; incr <= 100; incr+=5) { + // Sets the progress indicator to a max value, the + // current completion percentage, and "determinate" + // state + mBuilder.setProgress(100, incr, false); + // Displays the progress bar for the first time. + mNotifyManager.notify(0, mBuilder.build()); + // Sleeps the thread, simulating an operation + // that takes time + try { + // Sleep for 5 seconds + Thread.sleep(5*1000); + } catch (InterruptedException e) { + Log.d(TAG, "sleep failure"); + } + } + // When the loop is finished, updates the notification + mBuilder.setContentText("Download complete") + // Removes the progress bar + .setProgress(0,0,false); + mNotifyManager.notify(ID, mBuilder.build()); + } + } +// Starts the thread by calling the run() method in its Runnable +).start(); +</pre> + +<!-- ------------------------------------------------------------------------------------------ --> +<h3 id="ActivityIndicator">顯示持續性 Activity 指示器</h3> +<p> + 如要顯示不明確的 Activity 指示器,可利用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)}(可忽略前兩個引數) 將這類指示器新增至通知,然後發出通知。 +結果是指示器會有和進度列相同的樣式,只不過其中的動畫會持續播放。 + +</p> +<p> + 在操作一開始發出通知。動畫會持續播放,直到您修改通知為止。 +操作完成時,可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)},然後更新通知以移除 Activity 指示器。 + + 請務必這樣做,不然即使操作完成後,動畫還是會繼續播放。此外,請務必變更通知文字來指出操作已完成。 + +</p> +<p> + 如要查看 Activity 指示器如何運作,請參考上方的程式碼片段。找出下列幾行程式碼: +</p> +<pre> +// Sets the progress indicator to a max value, the current completion +// percentage, and "determinate" state +mBuilder.setProgress(100, incr, false); +// Issues the notification +mNotifyManager.notify(0, mBuilder.build()); +</pre> +<p> + 用下列幾行取代您找到的程式碼: +</p> +<pre> + // Sets an activity indicator for an operation of indeterminate length +mBuilder.setProgress(0, 0, true); +// Issues the notification +mNotifyManager.notify(0, mBuilder.build()); +</pre> + +<h2 id="metadata">通知中繼資料</h2> + +<p>通知會按照您以下列 +{@link android.support.v4.app.NotificationCompat.Builder} 方法指派的中繼資料加以排序:</p> + +<ul> + <li>當裝置處於「優先順序」模式時 (例如,如果您的通知代表來電、即時訊息或鬧鐘),{@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} 會指示系統如何處理您的應用程式通知。 + +</li> + <li>{@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} 會將具有優先順序欄位的通知設定為 {@code PRIORITY_MAX} 或 {@code PRIORITY_HIGH},而如果通知也有聲音或振動,就會以小型浮動視窗顯示。 + +</li> + <li>{@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} 可讓您將人員名單新增至通知。 +您的應用程式可用這種方式通知系統,應該將來自指定人員的通知歸在同一組,或是將較重要人員發出的通知排序。 + +</li> +</ul> + +<div class="figure" style="width:230px"> + <img src="{@docRoot}images/ui/notifications/heads-up.png" alt="" width="" height="" id="figure3" /> + <p class="img-caption"> + <strong>圖 3.</strong>顯示抬頭通知的全螢幕 Activity + </p> +</div> + +<h2 id="Heads-up">抬頭通知</h2> + +<p>針對 Android 5.0 (API 級別 21),當裝置處於使用中 (也就是裝置已解鎖且螢幕處於開啟) 時,通知能以小型浮動視窗顯示 (也稱為「抬頭」<em></em>通知)。 + +這些通知類似於精簡版的通知,只不過抬頭通知也會顯示動作按鈕。 + +使用者不需要離開目前的應用程式,就可以執行動作或關閉抬頭通知。 +</p> + +<p>可能觸發抬頭通知的範例情況包括:</p> + +<ul> + <li>使用者的 Activity 處於全螢幕模式 (應用程式使用 +{@link android.app.Notification#fullScreenIntent});或者</li> + <li>通知擁有高優先順序,並使用鈴聲或振動 +</li> +</ul> + +<h2 id="lockscreenNotification">鎖定螢幕通知</h2> + +<p>隨著 Android 5.0 (API 級別 21) 版發行,通知現在可以顯示在鎖定螢幕上。 +您的應用程式可使用此功能,提供媒體播放控制項和其他常見動作。 +使用者可以選擇是否透過 [設定] 在鎖定螢幕上顯示通知,您還可以指定是否能在鎖定螢幕上看見來自您應用程式的通知。 +</p> + +<h3 id="visibility">設定可見度</h3> + +<p>您的應用程式可控制安全鎖定螢幕上所顯示通知的詳細資料可見程度。 +您可以呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()},然後指定下列其中一個值: +</p> + +<ul> + <li>{@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC}:顯示通知的完整內容。 +</li> + <li>{@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET}:不在鎖定螢幕上顯示此通知的任何部分。 +</li> + <li>{@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE}:顯示基本資訊,例如通知的圖示與內容標題,但隱藏通知的完整內容。 +</li> +</ul> + +<p>已設定 {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} 時,您還可提供可隱藏特定詳細資料的通知內容替代版本。 +例如,SMS 應用程式可能會顯示「您有 3 則簡訊」<em></em>,但隱藏訊息內容與寄件者。 + +如要提供此替代通知,可先使用 {@link android.support.v4.app.NotificationCompat.Builder} 來建立替代通知。 +建立私人通知物件時,透過 {@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} 方法附加上替代通知。 + + +</p> + +<h3 id="controllingMedia">在鎖定螢幕上控制媒體播放</h3> + +<p>在 Android 5.0 (API 級別 21) 中,鎖定螢幕不會再依據 {@link android.media.RemoteControlClient} (現已淘汰) 顯示媒體控制項。 +利用 {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} 方法使用 {@link android.app.Notification.MediaStyle} 範本,將動作轉換成可點擊的圖示。 + + +</p> + +<p class="note"><strong>注意:</strong>支援程式庫不包含範本與 {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} 方法,因此這些功能只能在 Android 5.0 以上版本中執行。 + +</p> + +<p>如要在 Android 5.0 的鎖定螢幕上顯示媒體播放控制項,請將可見度設定為 {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC},如上所述。 +接著,新增動作並設定 {@link android.app.Notification.MediaStyle} 範本,如以下程式碼範例所示: + +</p> + +<pre> +Notification notification = new Notification.Builder(context) + // Show controls on lock screen even when user hides sensitive content. + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_stat_player) + // Add media control buttons that invoke intents in your media service + .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 + .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 + .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 + // Apply the media style template + .setStyle(new Notification.MediaStyle() + .setShowActionsInCompactView(1 /* #1: pause button */) + .setMediaSession(mMediaSession.getSessionToken()) + .setContentTitle("Wonderful music") + .setContentText("My Awesome Band") + .setLargeIcon(albumArtBitmap) + .build(); +</pre> + +<p class="note"><strong>注意:</strong>進一步實作控制媒體的 {@link android.media.RemoteControlClient} 已失效。 +如要進一步瞭解管理媒體工作階段與控制播放的新 API,請參閱<a href="{@docRoot}about/versions/android-5.0.html#MediaPlaybackControl">媒體播放控制項</a>。 + +</p> + + +<!-- ------------------------------------------------------------------------------------------ --> +<h2 id="CustomNotification">自訂通知版面配置</h2> +<p> + 通知架構可讓您定義自訂通知版面配置,藉此定義 {@link android.widget.RemoteViews} 物件中的通知外觀。 + + 自訂配置通知類似於一般通知,只不過這類通知是以 XML 版面配置檔案中定義的 {@link android.widget.RemoteViews} 為基礎。 + +</p> +<p> + 自訂通知版面配置的可用高度取決於通知檢視。一般檢視的版面配置以 64 dp 為限,而擴充檢視的版面配置以 256 dp 為限。 + +</p> +<p> + 如要定義自訂通知版面配置,請從具現化可擴大 XML 配置檔案的 +{@link android.widget.RemoteViews} 物件著手。接著,改為呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()},而不是呼叫像是 {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} 的方法。 + + +如要設定自訂通知中的內容詳細資料,可使用 {@link android.widget.RemoteViews} 中的方法來設定檢視下層物件的值: + + +</p> +<ol> + <li> + 以個別檔案建立通知的 XML 版面配置。您可以使用任何檔案名稱,但副檔名必須為 <code>.xml</code>。 + + </li> + <li> + 在您的應用程式中,使用 {@link android.widget.RemoteViews} 方法來定義通知的圖示與文字。 +藉由呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()} 將這個 {@link android.widget.RemoteViews} 物件放入您的 {@link android.support.v4.app.NotificationCompat.Builder}。 + +避免在您的 {@link android.widget.RemoteViews} 物件上設定背景 {@link android.graphics.drawable.Drawable},這是因為這樣做可能會使您的文字色彩變得無法閱讀。 + + + </li> +</ol> +<p> + {@link android.widget.RemoteViews} 類別還包含您可用來將 {@link android.widget.Chronometer} 或 {@link android.widget.ProgressBar} 輕鬆新增至通知版面配置的方法。 + +如要進一步瞭解如何為您的通知建立自訂版面配置,請參閱 {@link android.widget.RemoteViews} 參考文件。 + +</p> +<p class="caution"> + <strong>注意:</strong>當您使用自訂通知版面配置時,請特別注意,確保您的自訂版面配置適用於不同的裝置方向或解析度。 +雖然此建議適用於所有檢視版面配置,但對於通知而言特別重要,原因是通知匣的空間十分有限。 + +不要建立過於複雜的自訂版面配置,而且一定要在不同的設定中加以測試。 + +</p> +<!-- ------------------------------------------------------------------------------------------ --> +<h4>針對自訂通知文字使用樣式資源</h4> +<p> + 自訂通知文字一律使用樣式資源。在不同的裝置與版本間可能會有不同的通知背景色彩,因此使用樣式資源可協助您將這點列入考量。 + +從 Android 2.3 開始,系統已定義了標準通知版面配置資料的樣式。 +如果您在適用於 Android 2.3 以上版本的應用程式中使用相同的樣式,將可確保能在顯示背景上看見您的文字。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd new file mode 100644 index 000000000000..44d05a82ebc2 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=UI 總覽 +@jd:body + + +<p>Android 應用程式中的所有使用者介面元素都是使用 {@link android.view.View} 與 +{@link android.view.ViewGroup} 物件。{@link android.view.View} 物件可在畫面上繪製使用者能與之互動的項目。 +{@link android.view.ViewGroup} 物件可保留其他 +{@link android.view.View} (與 {@link android.view.ViewGroup}) 物件,以定義介面的版面配置。 +</p> + +<p>Android 提供 {@link android.view.View} 與{@link +android.view.ViewGroup} 子類別集合,提供一般輸入控制項 (例如按鈕與文字欄位) 及各種版面配置模型 (例如線性或相對)。 +</p> + + +<h2 id="Layout">使用者介面版面配置</h2> + +<p>應用程式每個元件的使用者介面是使用 {@link +android.view.View} 與 {@link android.view.ViewGroup} 物件的階層來定義,如圖 1 所示。每個檢視群組都是不可見容器,用以組織子檢視,而子檢視可能是輸入控制項或其他繪製部分 UI 的小工具。這個階層樹狀結構可依您的需求簡單或複雜化 (但簡化才會有最佳效能)。 + + + +</p> + +<img src="{@docRoot}images/viewgroup.png" alt="" /> +<p class="img-caption"><strong>圖 1.</strong>定義 UI 版面配置的檢視階層圖例 +</p> + +<p>如要宣告版面配置,您可以在程式碼中將 {@link android.view.View} 物件具現化,然後開始建置樹狀結構,但最簡單也最有效的方法是使用 XML 檔案來定義您的版面配置。 + +XML 提供類似於 HTML 且人類看得懂的版面配置結構。</p> + +<p>檢視的 XML 元素名稱相當於它個別代表的 Android 類別。因此, +<code><TextView></code> 元素可在您的 UI 中建立 {@link android.widget.TextView} 小工具,而 +<code><LinearLayout></code> 元素可以建立 {@link android.widget.LinearLayout} 檢視群組。 + </p> + +<p>例如,上面有一個文字檢視與按鈕的簡單垂直版面配置,看起來像這樣:</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical" > + <TextView android:id="@+id/text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="I am a TextView" /> + <Button android:id="@+id/button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="I am a Button" /> +</LinearLayout> +</pre> + +<p>當您載入應用程式中的版面配置資源時,Android 會將每個版面配置節點初始化成執行階段物件,您再用來定義其他行為、查詢物件狀態或修改版面配置。 + +</p> + +<p>如需建立 UI 版面配置的完整指南,請參閱 <a href="declaring-layout.html">XML 版面配置</a>。 + + + +<h2 id="UIComponents">使用者介面元件</h2> + +<p>您不必使用 {@link android.view.View} 與 {@link +android.view.ViewGroup} 物件來建置您的所有 UI。Android 提供的數個應用程式元件會提供標準 UI 版面配置,您只需要定義內容即可。 +這些 UI 元件各自有一組獨特的 API,如其各自的文件中所述,例如<a href="{@docRoot}guide/topics/ui/actionbar.html">動作列</a>、<a href="{@docRoot}guide/topics/ui/dialogs.html">對話方塊</a>及<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">狀態通知</a>。 +</p> + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd new file mode 100644 index 000000000000..91ac929e0fa1 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=設定 +page.tags=偏好設定、偏好設定 Activity、偏好設定片段 + +@jd:body + + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>本文件內容</h2> +<ol> + <li><a href="#Overview">總覽</a> + <ol> + <li><a href="#SettingTypes">偏好設定</a></li> + </ol> + </li> + <li><a href="#DefiningPrefs">在 XML 中定義偏好設定</a> + <ol> + <li><a href="#Groups">建立設定群組</a></li> + <li><a href="#Intents">使用意圖</a></li> + </ol> + </li> + <li><a href="#Activity">建立偏好設定 Activity</a></li> + <li><a href="#Fragment">使用偏好設定片段</a></li> + <li><a href="#Defaults">設定預設值</a></li> + <li><a href="#PreferenceHeaders">使用偏好設定標頭</a> + <ol> + <li><a href="#CreateHeaders">建立標頭檔案</a></li> + <li><a href="#DisplayHeaders">顯示標頭</a></li> + <li><a href="#BackCompatHeaders">使用偏好設定標頭支援舊版</a></li> + </ol> + </li> + <li><a href="#ReadingPrefs">讀取偏好設定</a> + <ol> + <li><a href="#Listening">接聽偏好設定變更</a></li> + </ol> + </li> + <li><a href="#NetworkUsage">管理網路使用量</a></li> + <li><a href="#Custom">建置自訂偏好設定</a> + <ol> + <li><a href="#CustomSelected">指定使用者介面</a></li> + <li><a href="#CustomSave">儲存設定值</a></li> + <li><a href="#CustomInitialize">初始化目前值</a></li> + <li><a href="#CustomDefault">提供預設值</a></li> + <li><a href="#CustomSaveState">儲存和還原偏好設定狀態</a></li> + </ol> + </li> +</ol> + +<h2>重要類別</h2> +<ol> + <li>{@link android.preference.Preference}</li> + <li>{@link android.preference.PreferenceActivity}</li> + <li>{@link android.preference.PreferenceFragment}</li> +</ol> + + +<h2>另請參閱</h2> +<ol> + <li><a href="{@docRoot}design/patterns/settings.html">設定設計指南</a></li> +</ol> +</div> +</div> + + + + +<p>應用程式通常會包含可讓使用者修改應用程式功能和行為的設定。例如,有些應用程式可讓使用者指定是否啟用通知,或指定應用程式與雲端同步資料的頻率。 + +</p> + +<p>如果您想要為應用程式提供設定,應該使用 Android {@link android.preference.Preference} API 建置與其他 Android 應用程式使用者體驗一致的介面 (包含系統設定)。 + +本文件說明如何使用 {@link android.preference.Preference} API 建置應用程式設定。 +</p> + +<div class="note design"> +<p><strong>設定設計</strong></p> + <p>如要瞭解如何設計設定,請參閱<a href="{@docRoot}design/patterns/settings.html">設定</a>設計指南。</p> +</div> + + +<img src="{@docRoot}images/ui/settings/settings.png" alt="" width="435" /> +<p class="img-caption"><strong>圖 1.</strong>Android 簡訊應用程式設定的螢幕擷取畫面。 +選取一個 {@link android.preference.Preference} 定義的項目可以開啟介面變更設定。 +</p> + + + + +<h2 id="Overview">總覽</h2> + +<p>現在不使用 {@link android.view.View} 物件建置使用者介面,而是使用您在 XML 檔案中所宣告 {@link android.preference.Preference} 類別的各種子類別來建置設定。 + +</p> + +<p>一個 {@link android.preference.Preference} 物件是單一設定的建置區塊。 +每個 {@link android.preference.Preference} 都以項目的形式在清單中顯示,並為使用者提供適當的 UI 以修改設定。 +例如,{@link +android.preference.CheckBoxPreference} 可以建立顯示核取方塊的清單項目,而 {@link +android.preference.ListPreference} 可以建立開啟對話方塊 (其中包含選擇清單) 的項目。</p> + +<p>每個您新增的 {@link android.preference.Preference} 都會有一個對應的鍵值配對,系統可使用此鍵值對將設定儲存到您應用程式設定的預設 {@link android.content.SharedPreferences} 檔案。 + +當使用者變更設定,系統會為您更新 {@link android.content.SharedPreferences} 檔案中的對應值。 +您唯一需要與關聯的 {@link android.content.SharedPreferences} 檔案直接互動的時候,是當您需要讀取值才能根據使用者設定判斷應用程式行為時。 + +</p> + +<p>儲存在 {@link android.content.SharedPreferences} 的每個設定值可以是下列其中一個資料類型: +</p> + +<ul> + <li>布林值</li> + <li>浮動</li> + <li>整數</li> + <li>長整數</li> + <li>字串</li> + <li>字串 {@link java.util.Set}</li> +</ul> + +<p>由於您的應用程式設定 UI 是使用 {@link android.preference.Preference} 物件,而不是 {@link android.view.View} 物件建置,因此您需要使用專門的 {@link android.app.Activity} 或 {@link android.app.Fragment} 子類別來顯示清單設定: + + +</p> + +<ul> + <li>如果應用程式支援的 Android 版本早於 3.0 (API 級別 10 及較早版本),您必須以 {@link android.preference.PreferenceActivity} 類別延伸的形式建置 Activity。 +</li> + <li>在 Android 3.0 及更新版本上,您應該使用傳統 {@link android.app.Activity},它託管了顯示應用程式設定的 {@link android.preference.PreferenceFragment}。 + +然而,當您有多個設定群組時,還可以使用 {@link android.preference.PreferenceActivity} 在大螢幕建立兩個面板的版面配置。 +</li> +</ul> + +<p>如何設定 {@link android.preference.PreferenceActivity} 和 {@link +android.preference.PreferenceFragment} 執行個體在<a href="#Activity">建立偏好設定 Activity</a> 和<a href="#Fragment">使用偏好設定片段</a>小節中有相關說明。 +</p> + + +<h3 id="SettingTypes">偏好設定</h3> + +<p>應用程式的每個設定都會以 {@link +android.preference.Preference} 類別的特定子類別代表。每個子類別包含一組核心屬性,可讓您為設定指定標題等項目和預設值。 +每個子類別還提供自己專屬的屬性和使用者介面。 +例如,圖 1 顯示簡訊應用程式設定的螢幕擷取畫面。 +設定畫面中的每個清單項目都由不同的 {@link +android.preference.Preference} 物件支援。</p> + +<p>下列為幾個最常見的偏好設定:</p> + +<dl> + <dt>{@link android.preference.CheckBoxPreference}</dt> + <dd>為啟用或停用的設定顯示含有核取方塊的項目。儲存的值是布林值 (如果選取 <code>true</code>)。 +</dd> + + <dt>{@link android.preference.ListPreference}</dt> + <dd>開啟含有選項按鈕清單的對話方塊。儲存的值可以是任何支援的值類型 (如上所列)。 +</dd> + + <dt>{@link android.preference.EditTextPreference}</dt> + <dd>開啟含有 {@link android.widget.EditText} 小工具的對話方塊。儲存的值是 {@link +java.lang.String}。</dd> +</dl> + +<p>請參閱 {@link android.preference.Preference} 類別,以取得所有其他子類別及其對應屬性的清單。 +</p> + +<p>當然,內建類別無法滿足所有需要,您的應用程式可能需要更專門的功能。 +例如,平台目前無法提供 {@link +android.preference.Preference} 類別以選取數字或日期。因此,您可能需要定義自己的 {@link android.preference.Preference} 子類別。 +如需執行此操作的協助,請參閱<a href="#Custom">建置自訂偏好設定</a>。</p> + + + +<h2 id="DefiningPrefs">在 XML 中定義偏好設定</h2> + +<p>雖然您可以在執行階段將新的 {@link android.preference.Preference} 物件具現化,但您仍應該在 XML 中定義設定清單,並在其中包含 {@link android.preference.Preference} 物件的階層。 + +使用 XML 檔案定義設定集合是較建議的做法,因為該檔案提供的易讀結構很容易更新。 +而且,雖然您仍然可以在執行階段修改集合,但您的應用程式設定通常是預先定義好的。 +</p> + +<p>每個 {@link android.preference.Preference} 子類別都可透過與類別名稱相符的 XML 元素進行宣告,例如 {@code <CheckBoxPreference>}。 +</p> + +<p>您必須將 XML 檔案儲存在 {@code res/xml/} 目錄中。雖然您可以自由命名檔案,但在傳統上會命名為 {@code preferences.xml}。 +您通常只需要一個檔案,因為階層 (會開啟自己的設定清單) 中的子目錄是透過 {@link android.preference.PreferenceScreen} 的巢狀執行個體進行宣告。 + +</p> + +<p class="note"><strong>注意:</strong>如果您想要為設定建立多個面板的版面配置,則您需要為每個片段準備個別的 XML 檔案。 +</p> + +<p>XML 檔案的根節點必須是 {@link android.preference.PreferenceScreen +<PreferenceScreen>} 元素。您要將每個 {@link +android.preference.Preference} 新增到此元素內。您在 {@link android.preference.PreferenceScreen <PreferenceScreen>} 元素內新增的每個子項會在設定清單中顯示為單一項目。 + +</p> + +<p>例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <CheckBoxPreference + android:key="pref_sync" + android:title="@string/pref_sync" + android:summary="@string/pref_sync_summ" + android:defaultValue="true" /> + <ListPreference + android:dependency="pref_sync" + android:key="pref_syncConnectionType" + android:title="@string/pref_syncConnectionType" + android:dialogTitle="@string/pref_syncConnectionType" + android:entries="@array/pref_syncConnectionTypes_entries" + android:entryValues="@array/pref_syncConnectionTypes_values" + android:defaultValue="@string/pref_syncConnectionTypes_default" /> +</PreferenceScreen> +</pre> + +<p>此範例中有一個 {@link android.preference.CheckBoxPreference} 和一個 {@link +android.preference.ListPreference}。這兩個項目都包含下列三個屬性:</p> + +<dl> + <dt>{@code android:key}</dt> + <dd>需要這個屬性才能保留資料值的偏好設定。它會指定當系統將此設定值儲存於 {@link +android.content.SharedPreferences} 時要使用的唯一索引鍵 (字串)。 + + <p>只有在下列情況下不需要此屬性:<em></em>偏好設定為 {@link android.preference.PreferenceCategory} 或 {@link android.preference.PreferenceScreen},或者偏好設定指定 {@link android.content.Intent} 進行呼叫 (搭配 <a href="#Intents">{@code <intent>}</a> 元素) 或 {@link android.app.Fragment} 進行顯示 (搭配 <a href="{@docRoot}reference/android/preference/Preference.html#attr_android:fragment">{@code +android:fragment}</a> 屬性)。 + +</p> + </dd> + <dt>{@code android:title}</dt> + <dd>這可為設定提供使用者可見的名稱。</dd> + <dt>{@code android:defaultValue}</dt> + <dd>這可指定系統在 {@link +android.content.SharedPreferences} 檔案中應設定的初始值。您應該為所有設定提供預設值。 +</dd> +</dl> + +<p>如需所有其他支援的屬性相關資訊,請參閱 {@link +android.preference.Preference} (及個別子類別) 文件。</p> + + +<div class="figure" style="width:300px"> + <img src="{@docRoot}images/ui/settings/settings-titles.png" alt="" /> + <p class="img-caption"><strong>圖 2.</strong>設定含有標題的類別。 + <br/><b>1.</b>類別以 {@link +android.preference.PreferenceCategory <PreferenceCategory>} 元素指定。 <br/><b>2.</b>標題以 {@code android:title} 屬性指定。 +</p> +</div> + + +<p>如果您的設定清單超過約 10 個項目,您可以新增標題來定義設定群組,或在分開的畫面顯示這些群組。 + +這些選項在下列幾節有詳細的描述。</p> + + +<h3 id="Groups">建立設定群組</h3> + +<p>如果您的清單有 10 個以上的項目,掃描、理解和處理這些項目對使用者而言可能會很困難。 +如要解決這個問題,可將部分或所有設定分成不同的群組,有效地將一長串清單分成多個簡短的清單。 + +一組包含相關設定的群組可以下列其中一種方式顯示:</p> + +<ul> + <li><a href="#Titles">使用標題</a></li> + <li><a href="#Subscreens">使用子畫面</a></li> +</ul> + +<p>您可以使用上述一種或兩種分組技巧來整理您的應用程式設定。決定要使用哪種方法或如何分割設定時,您應該要依照 Android 設計<a href="{@docRoot}design/patterns/settings.html">設定</a>指南中的指導方針進行。 + +</p> + + +<h4 id="Titles">使用標題</h4> + +<p>如果您想要在設定群組間使用標題區分 (如圖 2 所示),將 {@link android.preference.Preference} 物件的每個群組放入 {@link +android.preference.PreferenceCategory}。 +</p> + +<p>例如:</p> + +<pre> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <PreferenceCategory + android:title="@string/pref_sms_storage_title" + android:key="pref_key_storage_settings"> + <CheckBoxPreference + android:key="pref_key_auto_delete" + android:summary="@string/pref_summary_auto_delete" + android:title="@string/pref_title_auto_delete" + android:defaultValue="false"... /> + <Preference + android:key="pref_key_sms_delete_limit" + android:dependency="pref_key_auto_delete" + android:summary="@string/pref_summary_delete_limit" + android:title="@string/pref_title_sms_delete"... /> + <Preference + android:key="pref_key_mms_delete_limit" + android:dependency="pref_key_auto_delete" + android:summary="@string/pref_summary_delete_limit" + android:title="@string/pref_title_mms_delete" ... /> + </PreferenceCategory> + ... +</PreferenceScreen> +</pre> + + +<h4 id="Subscreens">使用子畫面</h4> + +<p>如果您想將設定群組放入子畫面 (如圖 3 所示),將 {@link android.preference.Preference} 物件的群組放入 {@link +android.preference.PreferenceScreen}。 +</p> + +<img src="{@docRoot}images/ui/settings/settings-subscreen.png" alt="" /> +<p class="img-caption"><strong>圖 3.</strong>設定子畫面。{@code +<PreferenceScreen>} 元素建立的項目會在選取時開啟獨立的清單以顯示巢狀設定。 +</p> + +<p>例如:</p> + +<pre> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- opens a subscreen of settings --> + <PreferenceScreen + android:key="button_voicemail_category_key" + android:title="@string/voicemail" + android:persistent="false"> + <ListPreference + android:key="button_voicemail_provider_key" + android:title="@string/voicemail_provider" ... /> + <!-- opens another nested subscreen --> + <PreferenceScreen + android:key="button_voicemail_setting_key" + android:title="@string/voicemail_settings" + android:persistent="false"> + ... + </PreferenceScreen> + <RingtonePreference + android:key="button_voicemail_ringtone_key" + android:title="@string/voicemail_ringtone_title" + android:ringtoneType="notification" ... /> + ... + </PreferenceScreen> + ... +</PreferenceScreen> +</pre> + + +<h3 id="Intents">使用意圖</h3> + +<p>在某些情況下,您可能會希望偏好設定項目開啟不同的 Activity 而不是設定畫面,例如,開啟網路瀏覽器以檢視網頁。 +如要在使用者選取偏好設定項目時呼叫 {@link +android.content.Intent},請新增 {@code <intent>} 元素做為對應 {@code <Preference>} 元素的子項。 +</p> + +<p>例如,您可以使用下列方法透過偏好設定項目開啟網頁:</p> + +<pre> +<Preference android:title="@string/prefs_web_page" > + <intent android:action="android.intent.action.VIEW" + android:data="http://www.example.com" /> +</Preference> +</pre> + +<p>您可以使用下列屬性建立隱含和明確意圖:</p> + +<dl> + <dt>{@code android:action}</dt> + <dd>根據 {@link android.content.Intent#setAction setAction()} 方法指派的動作。 +</dd> + <dt>{@code android:data}</dt> + <dd>根據 {@link android.content.Intent#setData setData()} 方法指派的資料。</dd> + <dt>{@code android:mimeType}</dt> + <dd>根據 {@link android.content.Intent#setType setType()} 方法指派的 MIME 類型。 +</dd> + <dt>{@code android:targetClass}</dt> + <dd>根據 {@link android.content.Intent#setComponent +setComponent()} 方法指派的元件名稱類別部分。</dd> + <dt>{@code android:targetPackage}</dt> + <dd>根據 {@link +android.content.Intent#setComponent setComponent()} 方法指派的元件名稱封裝部分。</dd> +</dl> + + + +<h2 id="Activity">建立偏好設定 Activity</h2> + +<p>如要在 Activity 中顯示設定,延伸 {@link +android.preference.PreferenceActivity} 類別。這是傳統 {@link +android.app.Activity} 類別的延伸,可根據 {@link +android.preference.Preference} 物件的階層顯示設定清單。當使用者進行變更時,{@link android.preference.PreferenceActivity} 會自動保留與每個 {@link +android.preference.Preference} 關聯的設定。 +</p> + +<p class="note"><strong>注意:</strong>如果您針對 Android 3.0 及更新版本開發應用程式,您應該改為使用 {@link android.preference.PreferenceFragment}。 +前往下一個<a href="#Fragment">使用偏好設定片段</a>章節。 +</p> + +<p>最需要注意的一件事就是,不要在 {@link +android.preference.PreferenceActivity#onCreate onCreate()} 呼叫期間載入檢視的版面配置。您應該要呼叫 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()},將您在 XML 檔案中宣告的偏好設定新增到 Activity 中。 +例如,下列為功能 {@link android.preference.PreferenceActivity} 所需的基本程式碼: +</p> + +<pre> +public class SettingsActivity extends PreferenceActivity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); + } +} +</pre> + +<p>事實上,這個程式碼對某些應用程式而言已經足夠,因為使用者修改偏好設定後,系統會將變更儲存到預設 {@link android.content.SharedPreferences} 檔案,當您需要檢查使用者設定時,您的其他應用程式元件就能進行讀取。 + +但是,許多應用程式需要更多一點程式碼,以接聽對偏好設定進行的變更。 +如需在 {@link android.content.SharedPreferences} 檔案接聽變更的相關資訊,請參閱<a href="#ReadingPrefs">讀取偏好設定</a>。 + +</p> + + + + +<h2 id="Fragment">使用偏好設定片段</h2> + +<p>如果您針對 Android 3.0 (API 級別 11) 及更新版本進行開發,您應該使用 {@link +android.preference.PreferenceFragment} 顯示 {@link android.preference.Preference} 物件清單。 +您可以將 {@link android.preference.PreferenceFragment} 新增到任何 Activity — 您不需要使用 {@link android.preference.PreferenceActivity}。 +</p> + +<p><a href="{@docRoot}guide/components/fragments.html">片段</a>單就 Activity 而言,可為您的應用程式提供更有彈性的架構,無論您建置哪一種 Activity 都一樣。 + +因此,我們建議您盡可能使用 {@link +android.preference.PreferenceFragment} 來控制設定的顯示,而不要使用 {@link +android.preference.PreferenceActivity}。</p> + +<p>實作 {@link android.preference.PreferenceFragment} 可以很簡單,只要定義 {@link android.preference.PreferenceFragment#onCreate onCreate()} 方法,透過 {@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()} 載入偏好設定檔案。 + +例如:</p> + +<pre> +public static class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.preferences); + } + ... +} +</pre> + +<p>之後,您可以將此片段新增至 {@link android.app.Activity},做法與任何其他 +{@link android.app.Fragment} 一樣。例如:</p> + +<pre> +public class SettingsActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new SettingsFragment()) + .commit(); + } +} +</pre> + +<p class="note"><strong>注意:</strong>{@link android.preference.PreferenceFragment} 沒有自己的 {@link android.content.Context} 物件。 +如果您需要 {@link android.content.Context} 物件,可以呼叫 {@link android.app.Fragment#getActivity()}。 +不過,必須小心,只能在片段附加到 Activity 時才能呼叫 {@link android.app.Fragment#getActivity()}。 +如果未附加片段,或是在生命週期期間中斷連結,{@link +android.app.Fragment#getActivity()} 將傳回 null。 +</p> + + +<h2 id="Defaults">設定預設值</h2> + +<p>您建立的偏好設定可能為應用程式定義了一些重要的行為,因此當使用者第一次開啟您的應用程式時,務必使用每個 {@link android.preference.Preference} 預設值來初始化關聯的 {@link android.content.SharedPreferences} 檔案。 + + +</p> + +<p>您必須要做的第一件事,就是使用 {@code android:defaultValue} 屬性指定 XML 檔案中每個 {@link +android.preference.Preference} 物件的預設值。 +值可以是適用於對應 {@link android.preference.Preference} 物件的任何資料類型。 +例如: +</p> + +<pre> +<!-- default value is a boolean --> +<CheckBoxPreference + android:defaultValue="true" + ... /> + +<!-- default value is a string --> +<ListPreference + android:defaultValue="@string/pref_syncConnectionTypes_default" + ... /> +</pre> + +<p>之後,從您應用程式主 Activity — 以及使用者第一次進入您應用程式所使用的任何其他 Activity — 的 {@link android.app.Activity#onCreate onCreate()} 方法呼叫 {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}: + +</p> + +<pre> +PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false); +</pre> + +<p>在 {@link android.app.Activity#onCreate onCreate()} 期間呼叫它,可確保您的應用程式正確地以預設值初始化,您的應用程式可能需要讀取這些預設值,才能判斷一些行為 (例如,使用行動網路時是否可下載資料)。 + + +</p> + +<p>這個方法採用三種引數:</p> +<ul> + <li>您的應用程式 {@link android.content.Context}。</li> + <li>您要設定預設值之偏好設定 XML 檔案的資源 ID。</li> + <li>布林值指出預設值是否要設定一次以上。 +<p>如果為 <code>false</code>,系統只會在過去從未呼叫此方法時設定預設值 (或者預設值共用偏好設定檔案的 {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} 為 false)。 + +</p></li> +</ul> + +<p>只要將第三個引數設為 <code>false</code>,您可以在每次 Activity 啟動時很安全地呼叫此方法,而不會將使用者儲存的偏好設定重設為預設值。 + +不過,如果您將它設為 <code>true</code>,會將任何之前的值覆寫為預設值。 +</p> + + + +<h2 id="PreferenceHeaders">使用偏好設定標頭</h2> + +<p>在少數情況下,您可能會想將設定設計為只在第一個畫面顯示<a href="#Subscreens">子畫面</a>清單(與系統設定應用程式一樣,如圖 4 和 5 所示)。 + +當您為 Android 3.0 及更新版本開發這類設計時,您應該使用 Android 3.0 的新「標頭」功能,而不是使用巢狀 {@link android.preference.PreferenceScreen} 元素建置子畫面。 + +</p> + +<p>如要使用標頭建置設定,您必須:</p> +<ol> + <li>將每個設定群組分成不同的 {@link +android.preference.PreferenceFragment} 執行個體。也就是說,每個設定群組需要一個獨立的 XML 檔案。 +</li> + <li>建立一個 XML 標頭檔案,其中列出每個設定群組以及宣告哪些片段包含對應的設定清單。 +</li> + <li>延伸 {@link android.preference.PreferenceActivity} 類別以託管您的設定。</li> + <li>實作 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回呼以指定標頭檔案。 +</li> +</ol> + +<p>使用這個設計最大的好處在於在大螢幕執行時, +{@link android.preference.PreferenceActivity} 會自動顯示兩個面板的版面配置,如圖 4 所示。</p> + +<p>即使應用程式支援的 Android 版本較 3.0 舊,您還是可以建置應用程式在新版的裝置上使用 {@link android.preference.PreferenceFragment} 顯示兩個面板,同時仍然支援舊版裝置上的傳統多畫面階層 (請參閱<a href="#BackCompatHeaders">使用偏好設定標頭支援舊版</a>)。 + + + +</p> + +<img src="{@docRoot}images/ui/settings/settings-headers-tablet.png" alt="" /> +<p class="img-caption"><strong>圖 4.</strong>含標頭的兩個面板版面配置。 <br/><b>1.</b>標頭以 XML 標頭檔案定義。 + <br/><b>2.</b>每個設定群組是由標頭檔案 {@code <header>} 元素指定的{@link android.preference.PreferenceFragment} 所定義。 + +</p> + +<img src="{@docRoot}images/ui/settings/settings-headers-handset.png" alt="" /> +<p class="img-caption"><strong>圖 5.</strong>含設定標頭的手機裝置。選取項目後,關聯的 {@link android.preference.PreferenceFragment} 會取代標頭。 + +</p> + + +<h3 id="CreateHeaders" style="clear:left">建立標頭檔案</h3> + +<p>您標頭清單中的每個設定群組是由根 {@code <preference-headers>} 元素中的單一 {@code <header>} 元素指定。 +例如:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> + <header + android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne" + android:title="@string/prefs_category_one" + android:summary="@string/prefs_summ_category_one" /> + <header + android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo" + android:title="@string/prefs_category_two" + android:summary="@string/prefs_summ_category_two" > + <!-- key/value pairs can be included as arguments for the fragment. --> + <extra android:name="someKey" android:value="someHeaderValue" /> + </header> +</preference-headers> +</pre> + +<p>有了 {@code android:fragment} 屬性,每個標頭會宣告當使用者選取標頭時,應該開啟的 {@link +android.preference.PreferenceFragment} 執行個體。</p> + +<p>{@code <extras>} 元素可讓您將鍵值配對傳送到 {@link +android.os.Bundle} 中的片段。片段可以透過呼叫 {@link +android.app.Fragment#getArguments()} 擷取引數。您可以因各種理由將引數傳送到片段,但其中一個最好的理由是針對每個群組重複使用 {@link +android.preference.PreferenceFragment} 中相同的子類別,並使用引數指定應載入片段的偏好設定 XML 檔案。 + +</p> + +<p>例如,當每個標頭使用 {@code "settings"} 索引鍵定義 {@code <extra>} 引數時,下列片段可在多個設定群組重複使用: +</p> + +<pre> +public static class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String settings = getArguments().getString("settings"); + if ("notifications".equals(settings)) { + addPreferencesFromResource(R.xml.settings_wifi); + } else if ("sync".equals(settings)) { + addPreferencesFromResource(R.xml.settings_sync); + } + } +} +</pre> + + + +<h3 id="DisplayHeaders">顯示標頭</h3> + +<p>如要顯示偏好設定標頭,必須實作 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回呼方法,並呼叫 {@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}。 +例如:</p> + +<pre> +public class SettingsActivity extends PreferenceActivity { + @Override + public void onBuildHeaders(List<Header> target) { + loadHeadersFromResource(R.xml.preference_headers, target); + } +} +</pre> + +<p>當使用者從標頭清單選取項目時,系統會開啟關聯的 {@link +android.preference.PreferenceFragment}。</p> + +<p class="note"><strong>注意:</strong>使用偏好設定標頭時,{@link +android.preference.PreferenceActivity} 的子類別不需要實作 {@link +android.preference.PreferenceActivity#onCreate onCreate()} 方法,這是因為 Activity 唯一需要做的工作就是載入標頭。 +</p> + + +<h3 id="BackCompatHeaders">使用偏好設定標頭支援舊版</h3> + +<p>如果您應用程式支援的 Android 版本比 3.0 舊,您仍然可以在 Android 3.0 及更新版本執行時,使用標頭提供兩個面板的版面配置。 +您只需要建立一個額外的偏好設定 XML 檔案,該檔案要使用行為與標頭項目 (供舊版 Android 使用) 一樣的基本 {@link android.preference.Preference +<Preference>} 元素。 + +</p> + +<p>但是,不會開啟新的 {@link android.preference.PreferenceScreen},每個 {@link +android.preference.Preference <Preference>} 元素會傳送一個 {@link android.content.Intent} 到 {@link android.preference.PreferenceActivity},以指定要載入的偏好設定 XML 檔案。 + +</p> + +<p>例如,下列為使用 Android 3.0及更新版本的偏好設定標頭 XML 檔案 ({@code res/xml/preference_headers.xml}): +</p> + +<pre> +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"> + <header + android:fragment="com.example.prefs.SettingsFragmentOne" + android:title="@string/prefs_category_one" + android:summary="@string/prefs_summ_category_one" /> + <header + android:fragment="com.example.prefs.SettingsFragmentTwo" + android:title="@string/prefs_category_two" + android:summary="@string/prefs_summ_category_two" /> +</preference-headers> +</pre> + +<p>而,這裡有個偏好設定檔案為 Android 3.0 以下版本提供相同的標頭 ({@code res/xml/preference_headers_legacy.xml}): +</p> + +<pre> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + <Preference + android:title="@string/prefs_category_one" + android:summary="@string/prefs_summ_category_one" > + <intent + android:targetPackage="com.example.prefs" + android:targetClass="com.example.prefs.SettingsActivity" + android:action="com.example.prefs.PREFS_ONE" /> + </Preference> + <Preference + android:title="@string/prefs_category_two" + android:summary="@string/prefs_summ_category_two" > + <intent + android:targetPackage="com.example.prefs" + android:targetClass="com.example.prefs.SettingsActivity" + android:action="com.example.prefs.PREFS_TWO" /> + </Preference> +</PreferenceScreen> +</pre> + +<p>因為 Android 3.0 已加入對 {@code <preference-headers>} 的支援,系統只會在 Androd 3.0 或更新版本執行時,才會呼叫 {@link +android.preference.PreferenceActivity} 中的 {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}。 +如要載入「舊版」標頭檔案 ({@code preference_headers_legacy.xml}),您必須檢查 Android 版本,如果版本比 Android 3.0 更舊 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}),呼叫 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 以載入舊版標頭檔案。 + + +例如:</p> + +<pre> +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ... + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // Load the legacy preferences headers + addPreferencesFromResource(R.xml.preference_headers_legacy); + } +} + +// Called only on Honeycomb and later +@Override +public void onBuildHeaders(List<Header> target) { + loadHeadersFromResource(R.xml.preference_headers, target); +} +</pre> + +<p>最後一件要做的事,是處理傳送到 Activity 的 {@link android.content.Intent},以識別要載入的偏好設定檔案。 +擷取意圖的動作,並將它與偏好設定 XML {@code <intent>} 標籤中使用的已知動作字串進行比對: +</p> + +<pre> +final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE"; +... + +@Override +public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String action = getIntent().getAction(); + if (action != null && action.equals(ACTION_PREFS_ONE)) { + addPreferencesFromResource(R.xml.preferences); + } + ... + + else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + // Load the legacy preferences headers + addPreferencesFromResource(R.xml.preference_headers_legacy); + } +} +</pre> + +<p>請注意,{@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 的連續呼叫會將所有偏好設定堆疊在單一清單中,因此請確定將條件鏈結到 else-if 陳述式時只會呼叫它一次。 + +</p> + + + + + +<h2 id="ReadingPrefs">讀取偏好設定</h2> + +<p>根據預設,透過呼叫靜態方法 {@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()},您應用程式中的所有偏好設定會儲存可從應用程式內任何地方存取的檔案中。 +這會傳回 {@link +android.content.SharedPreferences} 物件,其中包含與您 {@link +android.preference.PreferenceActivity} 使用之 {@link android.preference.Preference} 物件關聯的所有鍵值配對。 +</p> + +<p>例如,下列說明如何從應用程式中的任何其他 Activity 讀取其中一個偏好設定值: +</p> + +<pre> +SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); +String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, ""); +</pre> + + + +<h3 id="Listening">接聽偏好設定變更</h3> + +<p>使用者變更其中一個偏好設定後,您想要立即收到通知的原因有好幾個。 +如要在任何偏好設定發生變更時收到回呼,實作 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} 介面,並呼叫 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} 為 {@link android.content.SharedPreferences} 物件註冊接聽器。 + +</p> + +<p>介面只有一個回呼方式 {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()},而且在 Activity 中實作介面可能對您來說會更為容易。 +例如:</p> + +<pre> +public class SettingsActivity extends PreferenceActivity + implements OnSharedPreferenceChangeListener { + public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType"; + ... + + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(KEY_PREF_SYNC_CONN)) { + Preference connectionPref = findPreference(key); + // Set summary to be the user-description for the selected value + connectionPref.setSummary(sharedPreferences.getString(key, "")); + } + } +} +</pre> + +<p>在此範例中,方法會檢查是否是針對已知的偏好設定索引鍵設定進行變更。它會呼叫 {@link android.preference.PreferenceActivity#findPreference findPreference()} 以取得變更的 {@link android.preference.Preference} 物件,以便將項目摘要修改為使用者選取的描述。 + + +也就是說,當設定為 {@link +android.preference.ListPreference} 或其他多選擇設定時,如果設定變更為顯示目前狀態 (如圖 5 顯示的休眠設定),您應該呼叫 {@link +android.preference.Preference#setSummary setSummary()}。 +</p> + +<p class="note"><strong>注意:</strong>如 Android 設計文件中有關<a href="{@docRoot}design/patterns/settings.html">設定</a>的說明所述,我們建議您在每次使用者變更偏好設定時更新 {@link android.preference.ListPreference} 的摘要,以描述目前的設定。 + +</p> + +<p>為了在 Activity 中正確管理生命週期,我們建議您分別在 {@link +android.app.Activity#onResume} 和 {@link android.app.Activity#onPause} 回呼期間,註冊和解決註冊您的 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener}: +</p> + +<pre> +@Override +protected void onResume() { + super.onResume(); + getPreferenceScreen().getSharedPreferences() + .registerOnSharedPreferenceChangeListener(this); +} + +@Override +protected void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences() + .unregisterOnSharedPreferenceChangeListener(this); +} +</pre> + +<p class="caution"><strong>注意:</strong>當您呼叫 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} 時,偏好設定管理員目前不會在接聽器儲存強式參照。 +您必須將強式參照儲存到接聽器,否則它會很容易受記憶體回收的影響。 +我們建議您在物件的執行個體資料中保留接聽器參照,如此一來您可以在需要接聽器時隨時使用。 + +</p> + +<p>例如,在下列程式碼中,呼叫器沒有保留接聽器參照。 +因此,接聽器將受到記憶體回收的支配,而且會在未來不明確的時間發生失敗: +</p> + +<pre> +prefs.registerOnSharedPreferenceChangeListener( + // Bad! The listener is subject to garbage collection! + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}); +</pre> + +<p>將接聽器參照儲存在物件的執行個體資料欄位中,可以在需要接聽器時隨時使用: +</p> + +<pre> +SharedPreferences.OnSharedPreferenceChangeListener listener = + new SharedPreferences.OnSharedPreferenceChangeListener() { + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + // listener implementation + } +}; +prefs.registerOnSharedPreferenceChangeListener(listener); +</pre> + +<h2 id="NetworkUsage">管理網路使用量</h2> + + +<p>從 Android 4.0 開始,系統的設定應用程式允許使用者查看應用程式在前景和背景時使用了多少網路資料。 +然後,使用者可以停用個別應用程式的背景資料。 +如要避免使用者停用您的應用程式從背景存取資料,您應該有效率地使用資料連線,並允許使用者透過應用程式設定精簡您的應用程式資料使用量。 + +<p> + +<p>例如,您可以讓使用者控制同步資料的頻率,無論您的應用程式只在 Wi-Fi 上執行上傳/下載、應用程式在漫遊使用資料等。 +使用者有了這些控制項,就比較不會在快要達到在系統設定中設定的限制時停用您應用程式的資料存取,因為他們可以準確地控制您應用程式使用的資料量。 + + +</p> + +<p>您在 {@link android.preference.PreferenceActivity} 新增所需的偏好設定以控制應用程式的資料習慣後,您應該在宣示說明檔新增 {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} 的意圖篩選器。 +例如:</p> + +<pre> +<activity android:name="SettingsActivity" ... > + <intent-filter> + <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> +</activity> +</pre> + +<p>這個意圖篩選器向系統指出,是 Activity 在控制您的應用程式資料使用量。 +因此,當使用者從系統設定應用程式檢查您的應用程式使用了多少資料量時,可以使用 [檢視應用程式設定] 按鈕啟動您的<em></em> {@link android.preference.PreferenceActivity},讓使用者精簡您應用程式使用的資料量。 + + +</p> + + + + + + + +<h2 id="Custom">建置自訂偏好設定</h2> + +<p>Android 架構包含各式各樣的 {@link android.preference.Preference} 子類別,可讓您建置所種不同設定類型的 UI。不過,您可能會發現內建解決方案中可能沒有您所需的設定,例如數字挑選器或日期挑選器。 + + +在這類情況下,您需要延伸 {@link android.preference.Preference} 類別或其中一個其他子類別,以建立自訂的偏好設定。 +</p> + +<p>當您延伸 {@link android.preference.Preference} 類別時,需要執行幾個重要工作: +</p> + +<ul> + <li>指定使用者選取設定時要顯示的使用者介面。</li> + <li>視需要儲存設定值。</li> + <li>顯示 {@link android.preference.Preference} 時,使用目前 (或預設) 值加以初始化。 +</li> + <li>在系統要求時提供預設值。</li> + <li>如果 {@link android.preference.Preference} 提供自己的 UI (例如對話方塊),儲存並還原狀態以處理生命週期變更 (例如,當使用者旋轉螢幕時)。 +</li> +</ul> + +<p>以下各節說明如何完成這些工作。</p> + + + +<h3 id="CustomSelected">指定使用者介面</h3> + + <p>如果您直接延伸 {@link android.preference.Preference} 類別,必須實作 {@link android.preference.Preference#onClick()} 以定義使用者選取項目時的動作。 + +不過,大多數自訂設定會延伸 {@link android.preference.DialogPreference} 來顯示對話方塊,以簡化程序。 +延伸 {@link +android.preference.DialogPreference} 時,您必須在類別建構函式期間呼叫 {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()},以指定對話方塊的版面配置。 +</p> + + <p>例如,下列為自訂 {@link +android.preference.DialogPreference} 的建構函式,該偏好設定宣告版面配置並指定預設正值和負值對話方塊按鈕的文字: +</p> + +<pre> +public class NumberPickerPreference extends DialogPreference { + public NumberPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + setDialogLayoutResource(R.layout.numberpicker_dialog); + setPositiveButtonText(android.R.string.ok); + setNegativeButtonText(android.R.string.cancel); + + setDialogIcon(null); + } + ... +} +</pre> + + + +<h3 id="CustomSave">儲存設定值</h3> + +<p>您可以呼叫其中一個 {@link +android.preference.Preference} 類別的 {@code persist*()} 方法隨時儲存設定值,例如,如果設定值是整數,使用 {@link +android.preference.Preference#persistInt persistInt()},或者使用 {@link android.preference.Preference#persistBoolean persistBoolean()} 儲存布林值。 +</p> + +<p class="note"><strong>注意:</strong>每個 {@link android.preference.Preference} 只能儲存一個資料類型,因此您必須使用適合您自訂 {@link android.preference.Preference} 使用之資料類型的 {@code persist*()} 方法。 + +</p> + +<p>當您選擇保留設定時,可以依據您延伸的 {@link +android.preference.Preference} 類別加以設定。如果您延伸 {@link +android.preference.DialogPreference},則應該只在對話方塊因正值結果 (使用者選取 [確定] 按鈕) 關閉時保留該值。 +</p> + +<p>當 {@link android.preference.DialogPreference} 關閉時,系統會呼叫 {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()} 方法。這個方法包括一個布林值引數,指定使用者結果是否為「正值」— 如果值為 +<code>true</code>,則表示使用者選取正值按鈕,而您應該儲存新值。 +例如: +</p> + +<pre> +@Override +protected void onDialogClosed(boolean positiveResult) { + // When the user selects "OK", persist the new value + if (positiveResult) { + persistInt(mNewValue); + } +} +</pre> + +<p>在此範例中,<code>mNewValue</code> 是保留設定目前值的類別成員。 +呼叫 {@link android.preference.Preference#persistInt persistInt()} 可將值儲存到 {@link android.content.SharedPreferences} 檔案 (自動使用 XML 檔案中為此 {@link android.preference.Preference} 指定的索引鍵)。 + +</p> + + +<h3 id="CustomInitialize">初始化目前值</h3> + +<p>當系統將您的 {@link android.preference.Preference} 新增到畫面時,會呼叫 {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} 以通知您設定是否有持續值。 + +如果沒有持續值,這個呼叫會提供您預設值。 +</p> + +<p>{@link android.preference.Preference#onSetInitialValue onSetInitialValue()} 方法會傳送布林值 <code>restorePersistedValue</code>,以指出該設定的值是否已經持續。 + +如果是 <code>true</code>,則您應該呼叫其中一個 {@link +android.preference.Preference} 類別的 {@code getPersisted*()} 方法以擷取持續值,例如,如果為整數值,可使用 {@link +android.preference.Preference#getPersistedInt getPersistedInt()}。 +您通常都會想要擷取持續值,如此您可以正確的更新 UI 以反映之前儲存的值。 + +</p> + +<p>如果 <code>restorePersistedValue</code> 是 <code>false</code>,則您應該使用在第二引數中傳送的預設值。 +</p> + +<pre> +@Override +protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { + if (restorePersistedValue) { + // Restore existing state + mCurrentValue = this.getPersistedInt(DEFAULT_VALUE); + } else { + // Set default state from the XML attribute + mCurrentValue = (Integer) defaultValue; + persistInt(mCurrentValue); + } +} +</pre> + +<p>在實際上沒有持續值或沒有索引鍵的情況下,每個 {@code getPersisted*()} 方法會採用指定要使用之預設值的引數。 +在上述範例中,會使用本機常數來指定預設值,以避免 {@link +android.preference.Preference#getPersistedInt getPersistedInt()} 無法傳回持續值。 +</p> + +<p class="caution"><strong>注意:</strong>您<strong>無法</strong>使用 <code>defaultValue</code> 作為 {@code getPersisted*()} 方法中的預設值,因為當 <code>restorePersistedValue</code> 為 <code>true</code> 時,其值永遠是 null。 + +</p> + + +<h3 id="CustomDefault">提供預設值</h3> + +<p>如果 {@link android.preference.Preference} 類別的執行個體指定預設值 (包含 {@code android:defaultValue} 屬性),則系統會在將物件具現化時呼叫 {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()},以擷取值。 + +您必須實作此方法,才能讓系統將預設值儲存在 {@link +android.content.SharedPreferences} 中。 +例如:</p> + +<pre> +@Override +protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInteger(index, DEFAULT_VALUE); +} +</pre> + +<p>方法引數可以提供您所需的所有物件:屬性陣列,以及您必須擷取之 {@code android:defaultValue} 的索引位置。 +您必須實作此方法以便從屬性擷取預設值的原因,是因為您必須在未定義值的情況為屬性指定一個本機預設值。 + +</p> + + + +<h3 id="CustomSaveState">儲存和還原偏好設定狀態</h3> + +<p>如同版面配置中的 {@link android.view.View},您的 {@link android.preference.Preference} 子類別負責在 Activity 或片段重新啟動 (例如,當使用者旋轉螢幕時) 時,儲存和還原其狀態。 + +如要正確的儲存和還原 {@link android.preference.Preference} 類別的狀態,您必須實作生命週期回呼方法 {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}。 + +</p> + +<p>您 {@link android.preference.Preference} 的狀態由實作 {@link android.os.Parcelable} 介面的物件定義。 +Android 架構為您提供這類物件作為定義狀態物件的起始點:{@link +android.preference.Preference.BaseSavedState} 類別。 +</p> + +<p>如要定義 {@link android.preference.Preference} 類別儲存狀態的方法,您應該延伸 {@link android.preference.Preference.BaseSavedState} 類別。 +您只需要覆寫幾個方法,然後定義 {@link android.preference.Preference.BaseSavedState#CREATOR}物件。 + +</p> + +<p>對於大多數應用程式而言,如果您的 {@link android.preference.Preference} 子類別儲存整數以外的資料類型,您可以複製下列實作,然後變更處理 {@code value} 的行即可。 + +</p> + +<pre> +private static class SavedState extends BaseSavedState { + // Member that holds the setting's value + // Change this data type to match the type saved by your Preference + int value; + + public SavedState(Parcelable superState) { + super(superState); + } + + public SavedState(Parcel source) { + super(source); + // Get the current preference's value + value = source.readInt(); // Change this to read the appropriate data type + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + // Write the preference's value + dest.writeInt(value); // Change this to write the appropriate data type + } + + // Standard creator object using an instance of this class + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; +} +</pre> + +<p>將上述 {@link android.preference.Preference.BaseSavedState} 實作新增到您的應用程式 (通常做為 {@link android.preference.Preference} 子類別的子類別) 之後,您需要針對 {@link android.preference.Preference} 子類別實作 {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} 方法。 + + +</p> + +<p>例如:</p> + +<pre> +@Override +protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + // Check whether this Preference is persistent (continually saved) + if (isPersistent()) { + // No need to save instance state since it's persistent, + // use superclass state + return superState; + } + + // Create instance of custom BaseSavedState + final SavedState myState = new SavedState(superState); + // Set the state's value with the class member that holds current + // setting value + myState.value = mNewValue; + return myState; +} + +@Override +protected void onRestoreInstanceState(Parcelable state) { + // Check whether we saved the state in onSaveInstanceState + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save the state, so call superclass + super.onRestoreInstanceState(state); + return; + } + + // Cast state to custom BaseSavedState and pass to superclass + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + + // Set this Preference's widget to reflect the restored state + mNumberPicker.setValue(myState.value); +} +</pre> + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd new file mode 100644 index 000000000000..68714e8b2b59 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=輸入事件 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>本文件內容</h2> + <ol> + <li><a href="#EventListeners">事件接聽器</a></li> + <li><a href="#EventHandlers">事件處理常式</a></li> + <li><a href="#TouchMode">輕觸模式</a></li> + <li><a href="#HandlingFocus">處理焦點</a></li> + </ol> + +</div> +</div> + +<p>在 Android,有多種方法可以攔截使用者與應用程式互動的事件。 +如果考慮的是使用者介面內的事件,方法就是從與使用者互動的特定檢視物件中擷取事件。 +檢視類別提供執行此動作的方法。</p> + +<p>在您用來撰寫版面配置的各種檢視類別中,您會發現多種公用回呼方法,對 UI 事件似乎相當實用。 +當該物件發生個別動作時,Android 架構會呼叫這些方法。 +例如,輕觸檢視 (例如按鈕) 時,會在該物件上呼叫 <code>onTouchEvent()</code> 方法。 +不過,為了攔截這個事件,您必須延伸類別並覆寫方法。 +然而,延伸每個檢視物件以便處理這類事件並不實際。 +這就是檢視類別也包含巢狀介面與回呼的集合的原因,這樣您就能更輕鬆地定義。 +這些介面稱為<a href="#EventListeners">事件接聽器</a>,就是用來擷取使用者與您 UI 互動的票券。 +</p> + +<p>雖然您更常使用事件接聽器來接聽使用者互動,未來您可能會想要延伸檢視類別以建置自訂元件。 +或許您想要延伸 {@link android.widget.Button} 類別讓一些項目更為出色。 + +在這種情況下,您能夠使用類別<a href="#EventHandlers">事件處理常式</a>來為您的類別定義預設事件行為。 +</p> + + +<h2 id="EventListeners">事件接聽器</h2> + +<p>事件接聽器是 {@link android.view.View} 類別中的一個介面,該類別包含單一回呼方法。 +當使用者與 UI 中的項目互動並觸發接聽器所註冊的檢視時,Android 架構就會呼叫這些方法。 +</p> + +<p>事件接聽器介面包含下列回呼方法:</p> + +<dl> + <dt><code>onClick()</code></dt> + <dd>從 {@link android.view.View.OnClickListener}。使用者輕觸項目 (處於輕觸模式時),或將焦點放在具有導覽鍵或軌跡球的項目上,然後按適當的 "enter" 鍵或按下軌跡球時,就會呼叫。 + + +</dd> + <dt><code>onLongClick()</code></dt> + <dd>從 {@link android.view.View.OnLongClickListener}。使用者輕觸並按住項目 (處於輕觸模式時),或將焦點放在具有導覽鍵或軌跡球的項目上,然後按住適當的 "enter" 鍵或按住軌跡球 (一秒) 時,就會呼叫。 + + +</dd> + <dt><code>onFocusChange()</code></dt> + <dd>從 {@link android.view.View.OnFocusChangeListener}。使用者使用導覽鍵或軌跡球導覽至項目或離開項目時,就會呼叫。 +</dd> + <dt><code>onKey()</code></dt> + <dd>從 {@link android.view.View.OnKeyListener}。使用者將焦點放在項目上,然後按下或放開裝置上的硬體鍵時,就會呼叫。 +</dd> + <dt><code>onTouch()</code></dt> + <dd>從 {@link android.view.View.OnTouchListener}。當使用者執行符合輕觸事件資格的動作時,包括按下、放開或在螢幕上的任何移動手勢 (在項目邊界內),就會呼叫。 + +</dd> + <dt><code>onCreateContextMenu()</code></dt> + <dd>從 {@link android.view.View.OnCreateContextMenuListener}。建置操作選單時 (產生持續「長按」的效果),就會呼叫。 +請參閱<a href="{@docRoot}guide/topics/ui/menus.html#context-menu">選單</a>開發人員指南的操作選單相關討論。 + +</dd> +</dl> + +<p>這些方法是其個別介面的唯一要素。如要定義其中一種方法並處理您的事件,可在您的 Activity 中實作巢狀介面或將它定義為匿名類別。然後,將您的實作執行個體傳送至個別的 <code>View.set...Listener()</code> 方法。 + + +(例如,呼叫 +<code>{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()}</code>,將您的 {@link android.view.View.OnClickListener OnClickListener} 實作傳送給它。) +</p> + +<p>下列範例說明如何為某個按鈕註冊 on-click 接聽器。 </p> + +<pre> +// Create an anonymous implementation of OnClickListener +private OnClickListener mCorkyListener = new OnClickListener() { + public void onClick(View v) { + // do something when the button is clicked + } +}; + +protected void onCreate(Bundle savedValues) { + ... + // Capture our button from layout + Button button = (Button)findViewById(R.id.corky); + // Register the onClick listener with the implementation above + button.setOnClickListener(mCorkyListener); + ... +} +</pre> + +<p>您也會發現在 Activity 中實作 OnClickListener 會更為方便。這樣可避免額外的類別載入和物件配置。 +例如:</p> +<pre> +public class ExampleActivity extends Activity implements OnClickListener { + protected void onCreate(Bundle savedValues) { + ... + Button button = (Button)findViewById(R.id.corky); + button.setOnClickListener(this); + } + + // Implement the OnClickListener callback + public void onClick(View v) { + // do something when the button is clicked + } + ... +} +</pre> + +<p>請注意,上述範例的 <code>onClick()</code> 回呼不會傳回任何值,但某些其他事件接聽器方法必須傳回布林值。 +這個原因取決於事件。 +少數執行此事件的原因如下:</p> +<ul> + <li><code>{@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()}</code> - 這會傳回一個布林值,指出您是否已使用此事件,未來不應該繼續執行。 +亦即,傳回 <em>true</em> 指出您已處理事件,應該在這裡停止;如果您未處理事件和/或事件應該繼續在任何其他 on-click 接聽器上執行,則會傳回 <em>false</em>。 + + +</li> + <li><code>{@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()}</code> - 這會傳回一個布林值,指出您是否已使用此事件,未來不應該繼續執行。 + + 亦即,傳回 <em>true</em> 指出您已處理事件,應該在這裡停止;如果您未處理事件和/或事件應該繼續在任何其他 on-key 接聽器上執行,則會傳回 <em>false</em>。 + +</li> + <li><code>{@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()}</code> - 這會傳回一個布林值,指出您的接聽器是否已使用此事件。 +最重要的是,這個事件可以處理彼此接續的多個動作。 +因此,收到向下動作事件時如果您傳回 <em>false</em>,就表示您尚未使用事件,而且對於這個事件的後續動作也不感興趣。 + +因此,您不需要在事件內執行任何其他動作,例如手指手勢或最終動作事件。 +</li> +</ul> + +<p>請記住,硬體按鍵事件一律會傳送至目前焦點中的檢視。它們會從檢視階層的最上層開始發送,然後往下直到到達適當的目的地為止。 +如果焦點現在位於您的檢視 (或檢視的子項),則您可以透過 <code>{@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()}</code> 方法查看事件過程。 +作為透過您的檢視擷取按鍵事件的替代方法,您也可以使用 <code>{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}</code> 和 <code>{@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}</code> 接收您 Activity 內的所有事件。 + +</p> + +<p>此外,當您思考應用程式的文字輸入時,請記住,許多裝置只有軟體輸入方法。 +這類方法不需要以按鍵為基礎;有些可能會使用語音輸入、手寫等方式。即使輸入方法出現類似鍵盤的介面,也通常<strong>不會</strong>觸發事件的 +<code>{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}</code> 系列。 +您不應該建置需要按下特定按鍵才能控制的 UI,除非您想將應用程式限制為使用硬體鍵盤的裝置。 + +尤其是當使用者按 Return 鍵時,不要藉助這些方法來驗證輸入;改為使用像 {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} 的動作向輸入方法示意您應用程式預期的反應方式,讓它能夠以有意義的方式變更其 UI。 + +避免假設軟體輸入方法應該會如何運作,只要相信它能為您的應用程式提供既有的格式化文字。 +</p> + +<p class="note"><strong>注意:</strong>Android 會先呼叫事件處理常式,然後再從類別定義呼叫適當的預設處理常式。 +因此,從這些事件接聽器傳回 <em>true</em> 將會停止將事件傳播到其他事件接聽器,也會封鎖對檢視中預設事件處理常式的回呼。 + +因此,要確定您傳回 <em>true</em> 時要終止事件。</p> + + +<h2 id="EventHandlers">事件處理常式</h2> + +<p>如果您正從檢視建置自訂元件,則您就能定義多種回呼方法做為預設事件處理常式。在<a href="{@docRoot}guide/topics/ui/custom-components.html">自訂元件</a>的相關文件中,您將瞭解事件處理常式的一些常見回呼,包括: + + + +</p> +<ul> + <li><code>{@link android.view.View#onKeyDown}</code> - 當發生新的按鍵事件時就會進行呼叫。</li> + <li><code>{@link android.view.View#onKeyUp}</code> - 當發生向上鍵事件時就會進行呼叫。</li> + <li><code>{@link android.view.View#onTrackballEvent}</code> - 當發生軌跡球動作事件時就會進行呼叫。</li> + <li><code>{@link android.view.View#onTouchEvent}</code> - 當發生觸控螢幕動作事件時就會進行呼叫。</li> + <li><code>{@link android.view.View#onFocusChanged}</code> - 當檢視獲得或失去焦點時就會進行呼叫。</li> +</ul> +<p>您還必須注意其他一些方法,這些方法不屬於檢視類別,但會直接影響您能夠處理事件的方式。 +因此,在版面配置中管理更複雜的事件時,請考量下列其他方法: +</p> +<ul> + <li><code>{@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)}</code> - 這讓您的 {@link +android.app.Activity} 能在發送至視窗之前攔截所有輕觸事件。</li> + <li><code>{@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)}</code> - 這讓 {@link +android.view.ViewGroup} 在發送至子檢視時能監控事件。</li> + <li><code>{@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)}</code> - 呼叫這個父檢視以指出不應該使用 <code>{@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)}</code> 攔截輕觸事件。 +</li> +</ul> + +<h2 id="TouchMode">輕觸模式</h2> +<p> +使用者以方向鍵或軌跡球瀏覽使用者介面時,必須對可動作的項目 (例如按鈕) 提供焦點,這樣使用者就能看到什麼項目將接受輸入。 + +不過,如果裝置具有輕觸功能,而且使用者透過輕觸方式開始與介面互動,就不需要再將項目反白顯示,或是對特定檢視提供焦點。 + +因此,這就是名稱為「輕觸模式」的互動模式。 + +</p> +<p> +如果是具備輕觸功能的裝置,使用者輕觸螢幕之後,裝置就會進入輕觸模式。 +從這點以此類推,只有 +{@link android.view.View#isFocusableInTouchMode} 為 true 的檢視才可設定焦點,例如文字編輯小工具。 +其他可輕觸的檢視,例如按鈕,在輕觸時不會成為焦點;按下時,只會觸發 on-click 接聽器。 + +</p> +<p> +使用者在任何時候點擊方向鍵或使用軌跡球捲動時,裝置會結束輕觸模式,然後找尋要成為焦點的檢視。 +現在,使用者可繼續與使用者介面互動,而不必輕觸螢幕。 + +</p> +<p> +整個系統 (所有視窗和 Activity) 都會保留輕觸模式的狀態。如要查詢目前的狀態,您可以呼叫 +{@link android.view.View#isInTouchMode} 以查看裝置目前是否處於輕觸模式。 + +</p> + + +<h2 id="HandlingFocus">處理焦點</h2> + +<p>架構會處理慣例焦點移動以回應使用者輸入。 +這包括在移除或隱藏檢視時變更焦點,或是新檢視可用時。 +檢視可透過 <code>{@link android.view.View#isFocusable()}</code> 方法指出它們成為焦點的意願。 +如要變更檢視是否可成為焦點,可呼叫 <code>{@link android.view.View#setFocusable(boolean) setFocusable()}</code>。 +處於輕觸模式時,您可使用 <code>{@link android.view.View#isFocusableInTouchMode()}</code> 查詢檢視是否允許成為焦點。 + +您可以使用 <code>{@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()}</code> 進行變更。 +</p> + +<p>焦點移動是以演算法為依據,在指定的方向尋找最接近的項目。 +在少數情況下,預設的演算法與開發人員預期的行為可能不符。 +在這些情況下,您可以在版面配置檔案使用下列 XML 屬性進行明確的覆寫: + +<var>nextFocusDown</var>、 <var>nextFocusLeft</var>、 <var>nextFocusRight</var>和 +<var>nextFocusUp</var>。在移出焦點的檢視中,新增下列其中一個屬性<em></em>。 +將屬性的值定義為應該成為焦點之檢視的 ID<em></em>。 +例如:</p> +<pre> +<LinearLayout + android:orientation="vertical" + ... > + <Button android:id="@+id/top" + android:nextFocusUp="@+id/bottom" + ... /> + <Button android:id="@+id/bottom" + android:nextFocusDown="@+id/top" + ... /> +</LinearLayout> +</pre> + +<p>一般來說,在這個直式版面配置中,從第一個按鈕往上瀏覽或從第二個按鈕往下瀏覽都不會到任何地方。 +現在,上方按鈕已將下方按鈕定義為 + <var>nextFocusUp</var> (反之亦然),導覽焦點會從上到下以及從下到上循環。 +</p> + +<p>如果您想在 UI 中將檢視宣告為可設定焦點 (傳統上不行),可在您的版面配置宣告中將 <code>android:focusable</code> XML 屬性新增至檢視。 + +將值設定為 <var>true</var>。您也可以使用 <code>android:focusableInTouchMode</code> 在輕觸模式中將檢視宣告為可設定焦點。 +</p> +<p>如要要求特定檢視成為焦點,可呼叫 <code>{@link android.view.View#requestFocus()}</code>。</p> +<p>如要接聽焦點事件 (在檢視獲得或失去焦點時收到通知),可使用 +<code>{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}</code> (如上述<a href="#EventListeners">事件接聽器</a>中所討論)。 +</p> + + + +<!-- +<h2 is="EventCycle">Event Cycle</h2> + <p>The basic cycle of a View is as follows:</p> + <ol> + <li>An event comes in and is dispatched to the appropriate View. The View + handles the event and notifies any listeners.</li> + <li>If, in the course of processing the event, the View's bounds may need + to be changed, the View will call {@link android.view.View#requestLayout()}.</li> + <li>Similarly, if in the course of processing the event the View's appearance + may need to be changed, the View will call {@link android.view.View#invalidate()}.</li> + <li>If either {@link android.view.View#requestLayout()} or {@link android.view.View#invalidate()} were called, + the framework will take care of measuring, laying out, and drawing the tree + as appropriate.</li> + </ol> + + <p class="note"><strong>Note:</strong> The entire View tree is single threaded. You must always be on + the UI thread when calling any method on any View. + If you are doing work on other threads and want to update the state of a View + from that thread, you should use a {@link android.os.Handler}. + </p> +--> diff --git a/docs/html-intl/intl/zh-tw/index.jd b/docs/html-intl/intl/zh-tw/index.jd index cf1376facee7..a5772ef0fab0 100644 --- a/docs/html-intl/intl/zh-tw/index.jd +++ b/docs/html-intl/intl/zh-tw/index.jd @@ -5,42 +5,81 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 @jd:body -<!-- <div class="dac-hero-carousel" data-carousel-query="collection:index/carousel"> -</div> --> -<section class="dac-hero-carousel"> +<script> + $(document).ready(function() { + if (useUpdatedTemplates) { + $("#useUpdatedTemplates").css("display","block"); + } else { + $("#useOldTemplates").css("display","block"); + } + }) +</script> -<!-- <article class="dac-expand dac-hero dac-invert active" style="background-color: rgb(38, 50, 56);"> --> -<article class="dac-expand dac-hero dac-invert dac-darken mprev active" style="background-color: #75d1ff;"> -<a href="/preview/index.html"> +<section class="dac-expand dac-hero dac-invert" style="background-color:#455A64"> <div class="wrap" style="max-width:1100px;margin-top:0"> - <div class="cols dac-hero-content"> - <div class="col-8of16 col-push-6of16 dac-hero-figure mprev"> - </div> - <div class="col-8of16 col-pull-7of16"> - <div class="dac-hero-tag"></div> - - <h1 class="dac-hero-title" style="white-space:nowrap;">Android 6.0 Marshmallow</h1> - <p class="dac-hero-description">準備使用新版 Android。在 Nexus 5、6、9 和 - Player 上測試您的應用程式。 </p> - - <a class="dac-hero-cta" href="{@docRoot}preview/index.html"> + <div class="col-7of16 col-push-9of16" style="padding-left:2em;"> + <a href="{@docRoot}preview/index.html"> + <h1 class="dac-hero-title">Android N Developer Preview</h1> + <p class="dac-hero-description"> + Get ready for the next version of Android! + <strong>Test your apps</strong> on Nexus and other devices. Support new system + behaviors to <strong>save power and memory</strong>. + Extend your apps with <strong>multi-window UI</strong>, + <strong>direct reply notifications</strong> and more. + </p> + <a class="dac-hero-cta" href="/preview/index.html"> <span class="dac-sprite dac-auto-chevron"></span> - 開始使用! - </a><br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> + Learn more + </a><!--<br> + <a class="dac-hero-cta" href="/preview/support.html"> <span class="dac-sprite dac-auto-chevron"></span> - Developer Preview 3 (final SDK)</a> - </div> + Update to Developer Preview (final SDK) + </a><br>--> + </a> + </div> + <div class="col-9of16 col-pull-7of16 dac-hero-figure" style="margin-top:0em;padding-right:1.5em;"> + <a href="{@docRoot}preview/index.html"> + <img style="" class="dac-hero-image" src="/images/home/n-preview-hero.png" + srcset="/images/home/n-preview-hero.png 1x, + /images/home/n-preview-hero_2x.png 2x"> + </a> </div> </div> -</a> -</article></section> +</section> -<div class="actions-bar dac-expand dac-invert"> +<div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand"> <div class="wrap dac-offset-parent"> <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps"> <i class="dac-sprite dac-arrow-down-gray"></i> </a> + <ul class="dac-actions"> + <li class="dac-action"> + <a class="dac-action-link" href="{@docRoot}sdk/index.html"> + <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> + Get the SDK + </a> + </li> + <li class="dac-action"> + <a class="dac-action-link" href="{@docRoot}samples/index.html"> + <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> + Browse sample code + </a> + </li> + <li class="dac-action"> + <a class="dac-action-link" href="{@docRoot}distribute/stories/index.html"> + <i class="dac-action-sprite dac-sprite dac-auto-chevron-large"></i> + Watch stories + </a> + </li> + </ul> + </div><!-- end .wrap --> +</div><!-- end .dac-actions --> + +<div id="useOldTemplates" style="display:none" class="actions-bar dac-expand dac-invert"> + <div class="wrap dac-offset-parent"> + <a class="dac-fab dac-scroll-button" data-scroll-button="" href="#build-apps"> + <i class="dac-sprite dac-arrow-down-gray"></i> + </a> <div class="actions"> <div><a href="{@docRoot}sdk/index.html"> <span class="dac-sprite dac-auto-chevron-large"></span> @@ -50,17 +89,15 @@ page.customHeadTag=<meta name="google-site-verification" content="sa-bIAI6GKvct3 <span class="dac-sprite dac-auto-chevron-large"></span> Browse Samples </a></div> - <div><a href="//www.youtube.com/user/androiddevelopers"> + <div><a href="{@docRoot}distribute/stories/index.html"> <span class="dac-sprite dac-auto-chevron-large"></span> - Watch Videos + Watch Stories </a></div> </div><!-- end .actions --> </div><!-- end .wrap --> -</div><!-- end .actions-bar --> - - +</div> -<section class="dac-section dac-section-light" id="build-apps"><div class="wrap"> +<section class="dac-section dac-light" id="build-apps"><div class="wrap"> <h1 class="dac-section-title">Build Beautiful Apps</h1> <div class="dac-section-subtitle"> Resources to get you started with designing and developing for Android. diff --git a/docs/html-intl/intl/zh-tw/preview/api-overview.jd b/docs/html-intl/intl/zh-tw/preview/api-overview.jd deleted file mode 100644 index f6c56962f77b..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/api-overview.jd +++ /dev/null @@ -1,521 +0,0 @@ -page.title=API 總覽 -page.keywords=預覽,sdk,相容性 -page.tags=previewresources, androidm -sdk。platform.apiLevel=22-mnc -page.image=images/cards/card-api-overview_16-9_2x.png -@jd:body - - -<div id="qv-wrapper"> -<div id="qv"> - -<h2>本文件內容 -<a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle"> - <span class="more">顯示更多內容</span> - <span class="less" style="display:none">顯示較少內容</span></a></h2> - -<ol id="toc44" class="hide-nested"> - <li><a href="#app-linking">應用程式連結</a></li> - <li><a href="#backup">針對應用程式進行自動備份</a></li> - <li><a href="#authentication">驗證</a> - <ol> - <li><a href="#fingerprint-authentication">指紋驗證</a></li> - <li><a href="#confirm-credential">確認認證</a></li> - </ol> - </li> - <li><a href="#direct-share">直接分享</a></li> - <li><a href="#voice-interactions">語音互動</a></li> - <li><a href="#assist">協助 API</a></li> - <li><a href="#notifications">通知</a></li> - <li><a href="#bluetooth-stylus">藍牙手寫筆支援</a></li> - <li><a href="#ble-scanning">已改進藍牙低電量掃描</a></li> - <li><a href="#hotspot">無線基地台 2.0 版本 1 支援</a></li> - <li><a href="#4K-display">4K 顯示模式</a></li> - <li><a href="#behavior-themeable-colorstatelists">具備設計風格的 ColorStateList</a></li> - <li><a href="#audio">音訊功能</a></li> - <li><a href="#video">影片功能</a></li> - <li><a href="#camera">相機功能</a> - <ol> - <li><a href="#flashlight">閃光燈 API</a></li> - <li><a href="#reprocessing">相機重新處理</a></li> - </ol> - </li> - <li><a href="#afw">Android for Work 功能</a></li> -</ol> - -<h2>API 差異</h2> -<ol> -<li><a href="{@docRoot}preview/download.html">API 級別 22 到 M 預覽版»</a> </li> -</ol> - -</div> -</div> - -<p>M 開發人員預覽版讓您能夠搶先查看即將發行的 Android 平台版本,這個版本將提供可供使用者和應用程式開發人員使用的新功能。 - -本文件提供最值得受到矚目的 API 簡介。</p> - -<p>M 開發人員預覽版的適用對象是<strong>早期採用的開發人員</strong>和<strong>測試者</strong>。 -如果您對於如何影響 Android 架構方向深感興趣,請<a href="{@docRoot}preview/setup-sdk.html">嘗試使用 M 開發人員預覽版</a>,然後將您的意見反應傳送給我們! - - -</p> - -<p class="caution"><strong>注意:</strong>請勿將使用 M 開發人員預覽版的應用程式發行到 Google Play 商店。 -</p> - -<p class="note"><strong>注意:</strong>本文件通常會參考 <a href="{@docRoot}">developer.android.com</a> 上尚未提供可用參考資料的類別和方法。 -這些 API 元素在本文件中的格式是 {@code code style} (不含超連結)。 -如需這些元素的 API 初稿文件,請下載<a href="{@docRoot}preview/download.html#docs">預覽版參考資料</a>。 -</p> - -<h3>重要行為變更</h3> - -<p>如果您先前曾發行過適用於 Android 的應用程式,請注意,您的應用程式會受到平台中的變更所影響。 -</p> - -<p>如需完整資訊,請參閱<a href="behavior-changes.html">行為變更</a>。</p> - -<h2 id="app-linking">應用程式連結</h2> -<p>這個預覽版提供功能更強大的應用程式連結來增強 Android 的意圖系統。此功能讓您能夠將應用程式關聯到您自己的 Web 網域。 -根據這個關聯,平台可以判斷要用來處理特定 Web 連結的預設應用程式,並略過提示使用者選取應用程式的程序。如要深入瞭解如何實作此功能,請參閱<a href="{@docRoot}preview/features/app-linking.html">應用程式連結</a>。 - - - - -<h2 id="backup">針對應用程式進行自動備份</h2> -<p>系統現在會針對應用程式執行自動完整資料備份與還原。預設會針對目標為 M 預覽版的應用程式啟用這個行為;您不需要新增任何其他的程式碼。 -如果使用者刪除他們的 Google 帳戶,也會同時刪除他們的備份資料。 -如要深入瞭解此功能的運作方式以及如何在檔案系統上設定要備份的內容,請參閱<a href="{@docRoot}preview/backup/index.html">針對應用程式進行自動備份</a>。 - -</p> - -<h2 id="authentication">驗證</h2> -<p>這個預覽版提供新的 API,讓您能夠在支援的裝置上利用使用者的指紋掃描來驗證他們,並使用裝置解鎖機制 (例如鎖定螢幕密碼) 來檢查距離最後一次驗證該使用者的時間有多接近。 - -將這些 API 與 <a href="{@docRoot}training/articles/keystore.html">Android 金鑰存放區系統</a>搭配使用。 -</p> - -<h3 id="fingerprint-authentication">指紋驗證</h3> - -<p>如要透過指紋掃描驗證使用者,請取得新的 -{@code android.hardware.fingerprint.FingerprintManager} 類別的執行個體,然後呼叫 -{@code FingerprintManager.authenticate()} 方法。您的應用程式必須在配備指紋感應器的相容裝置上執行。 -您必須在應用程式上實作適用於指紋驗證流程的使用者介面,並在您的 UI 中使用標準的 Android 指紋圖示。Android 指紋圖示 ({@code c_fp_40px.png}) 隨附於<a href="https://github.com/googlesamples/android-FingerprintDialog" class="external-link">範例應用程式</a>中。如果您正在開發多個使用指紋驗證的應用程式,請注意,每個應用程式都必須個別驗證使用者的指紋。 - - - - -</p> - -<p>如要在您的應用程式中使用此功能,請先在您的宣示說明中新增 {@code USE_FINGERPRINT} 權限。 -</p> - -<pre> -<uses-permission - android:name="android.permission.USE_FINGERPRINT" /> -</pre> - -<img src="{@docRoot}preview/images/fingerprint-screen.png" srcset="{@docRoot}preview/images/fingerprint-screen.png 1x, {@docRoot}preview/images/fingerprint-screen_2x.png 2x" style="float:right; margin:0 0 10px 20px" width="282" height="476" /> - -<p>如要查看指紋驗證的應用程式實作,請參閱<a href="https://github.com/googlesamples/android-FingerprintDialog" class="external-link">指紋對話方塊範例</a>。 - -</p> - -<p>如果您正在測試此功能,請依照下列步驟執行:</p> -<ol> -<li>安裝 Android SDK 工具修訂版 24.3 (如果您尚未安裝)。</li> -<li>前往 [設定] > [安全性] > [指紋]<strong></strong>,然後依照註冊指示,在模擬器中註冊新的指紋。 -</li> -<li>使用模擬器,利用下列命令來模擬指紋輕觸事件。 -使用同一個命令,在鎖定螢幕上或您的應用程式中模擬指紋輕觸事件。 - -<pre class="no-prettyprint"> -adb -e emu finger touch <finger_id> -</pre> -<p>在 Windows 上,您可能必須執行 {@code telnet 127.0.0.1 <emulator-id>},後面接著 -{@code finger touch <finger_id>}。 -</p> -</li> -</ol> - -<h3 id="confirm-credential">確認認證</h3> -<p>您的應用程式可以根據使用者最近一次將裝置解鎖的時間有多接近來驗證他們。此功能讓使用者不需記住其他應用程式特定的密碼,並且讓您不需要實作自己的驗證使用者介面。 - -您的應用程式應該將此功能與公用或秘密金鑰實作搭配使用,來進行使用者驗證。 -</p> - -<p>如要設定在成功驗證使用者之後,同一個金鑰可重複使用的逾時時間長度,可在您設定 {@link javax.crypto.KeyGenerator} 或 -{@link java.security.KeyPairGenerator} 時,呼叫新的 -{@code android.security.keystore.KeyGenParameterSpec.setUserAuthenticationValidityDurationSeconds()} -方法。 -此功能目前適用於對稱式密碼編譯操作。 -</p> - -<p>避免過度顯示重新驗證對話方塊 -- 您的應用程式應該先嘗試使用密碼編譯物件,如果逾時過期,才使用 -{@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence) createConfirmDeviceCredentialIntent()} -方法在您的應用程式內重新驗證使用者。 - -</p> - -<p>如要查看此功能的應用程式實作,請參閱<a href="https://github.com/googlesamples/android-ConfirmCredential" class="external-link">確認認證範例</a>。 - -</p> - -<h2 id="direct-share">直接分享</h2> - -<img src="{@docRoot}preview/images/direct-share-screen.png" srcset="{@docRoot}preview/images/direct-share-screen.png 1x, {@docRoot}preview/images/direct-share-screen_2x.png 2x" style="float:right; margin:0 0 20px 30px" width="312" height="329" /> - -<p>這個預覽版提供 API,讓使用者能夠以直覺且快速的方式進行分享。您現在可以定義「直接分享目標」<em></em>,在您的應用程式中啟動特定的行為。這些直接分享目標是透過 [分享]<em></em> 選單來向使用者公開。 - -此功能讓使用者能夠將內容分享到其他應用程式內的目標,例如聯絡人。 -例如,直接分享目標可以在其他社交網路應用程式中啟動某個活動,讓使用者能夠在該應用程式中,直接與特定的朋友或社群分享內容。 - -</p> - -<p>如要啟用直接分享目標,您必須定義一個類別來擴充 -{@code android.service.} <br> -{@code chooser.ChooserTargetService} 類別。在宣示說明中宣告您的 -{@code ChooserTargetService}。在該宣告中,指定 -{@code BIND_CHOOSER_TARGET_SERVICE} 權限以及含有 -{@code SERVICE_INTERFACE} 動作的意圖篩選條件。</p> -<p>下列範例示範如何在您的宣示說明中宣告 {@code ChooserTargetService}。 -</p> -<pre> -<service android:name=".ChooserTargetService" - android:label="@string/service_name" - android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE"> - <intent-filter> - <action android:name="android.service.chooser.ChooserTargetService" /> - </intent-filter> -</service> -</pre> - -<p>針對您要向 {@code ChooserTargetService} 公開的每個活動,在您的應用程式宣示說明中,新增名稱為 -{@code "android.service.chooser.chooser_target_service"} 的 -{@code <meta-data>} 元素。 -</p> - -<pre> -<activity android:name=".MyShareActivity” - android:label="@string/share_activity_label"> - <intent-filter> - <action android:name="android.intent.action.SEND" /> - </intent-filter> -<meta-data - android:name="android.service.chooser.chooser_target_service" - android:value=".ChooserTargetService" /> -</activity> -</pre> - -<h2 id="voice-interactions">語音互動</h2> -<p> -這個預覽版提供新的語音互動 API,可與<a href="https://developers.google.com/voice-actions/" class="external-link">語音操作</a>搭配使用,讓您能夠在應用程式內建置交談式語音體驗。 - -呼叫 -{@code android.app.Activity.isVoiceInteraction()} 方法,來判斷是否已啟動您的活動來回應語音操作。 -如果是,則您的應用程式可以使用 -{@code android.app.VoiceInteractor} 類別,來要求使用者進行語音確認、從選項清單中選取,以及其他更多動作。 -如要深入瞭解如何實作語音操作,請參閱<a href="https://developers.google.com/voice-actions/interaction/" class="external-link">語音操作開發人員網站</a>。 - -</p> - -<h2 id="assist">協助 API</h2> -<p> -這個預覽版提供一種新方式,可透過小幫手吸引使用者來使用您的應用程式。如要使用此功能,使用者必須啟用小幫手來使用目前的內容。 -啟用之後,使用者就能夠在所有應用程式內,按住 <strong>Home</strong> 按鈕不放來啟用小幫手。 -</p> -<p>您的應用程式可以設定 -{@link android.view.WindowManager.LayoutParams#FLAG_SECURE} 旗標,選擇不要與小幫手分享目前的內容。除了平台傳遞給小幫手的一組標準資訊之外,您的應用程式也可以使用新的 {@code android.app.Activity.AssistContent} 類別來分享其他資訊。 - -</p> - -<p>如要將您應用程式的其他內容提供給小幫手,請依照下列步驟執行:</p> - -<ol> -<li>實作 {@link android.app.Application.OnProvideAssistDataListener} 介面。</li> -<li>使用 -{@link android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()} 來註冊這個監聽器。</li> -<li>如要提供特定活動的內容資訊,請覆寫 - {@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} -回呼,然後選擇性地覆寫新的 {@code Activity.onProvideAssistContent()} 回呼。 -</ol> - -<h2 id="notifications">通知</h2> -<p>這個預覽版新增了下列適用於通知的 API 變更:</p> -<ul> - <li>新的 {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} 篩選條件級別,會對應至新的「僅允許鬧鐘」<em></em>的請勿打擾模式。 -</li> - <li>新的 {@code Notification.CATEGORY_REMINDER} 類別值,可用來分辨來自其他事件 ({@link android.app.Notification#CATEGORY_EVENT}) 與鬧鐘 ({@link android.app.Notification#CATEGORY_ALARM}) 的使用者排程提醒。 - - -</li> - <li>新的 {@code android.graphics.drawable.Icon} 類別,可以透過 {@code Notification.Builder.setSmallIcon(Icon)} 和 -{@code Notification.Builder.setLargeIcon(Icon)} 方法來附加您的通知。 -</li> - <li>新的 {@code NotificationManager.getActiveNotifications()} 方法,讓您的應用程式能夠找出它們目前有哪些通知仍處於有效狀態。 -如要查看使用此功能的應用程式實作,請參閱<a href="https://github.com/googlesamples/android-ActiveNotifications" class="external-link">使用中通知範例</a>。 -</li> -</ul> - -<h2 id="bluetooth-stylus">藍牙手寫筆支援</h2> -<p>這個預覽版提供對於使用者使用藍牙手寫筆進行輸入的改良支援。使用者可以將相容的藍芽手寫筆與他們的手機或平板電腦配對並連線。 -連線之後,來自觸控螢幕的位置資訊會與來自手寫筆的壓力和按鈕資訊結合,比起單獨使用觸控螢幕,這樣能夠提供更大範圍的表達方式。 - -您的應用程式可以藉由在您的活動中註冊新的 -{@code View.onStylusButtonPressListener} 和 {@code GestureDetector.OnStylusButtonPressListener} -回呼,來監聽手寫筆按鈕的按下動作,並執行次要動作。 -</p> - -<p>使用 {@link android.view.MotionEvent} 方法和常數來偵測手寫筆按鈕互動: -</p> -<ul> -<li>如果使用者使用具有一個按鈕的手寫筆輕觸應用程式的螢幕,則 -{@link android.view.MotionEvent#getToolType(int) getTooltype()} 方法會傳回 -{@link android.view.MotionEvent#TOOL_TYPE_STYLUS}。</li> -<li>針對目標為 M 預覽版的應用程式, -{@link android.view.MotionEvent#getButtonState() getButtonState()} -方法會在使用者按下主要手寫筆按鈕時傳回 {@code MotionEvent.STYLUS_BUTTON_PRIMARY}。 -如果手寫筆有第二個按鈕,則當使用者按下該按鈕時,同一個方法會傳回 -{@code MotionEvent.STYLUS_BUTTON_SECONDARY}。如果使用者同時按下這兩個按鈕,此方法即會使用 OR 連結,一併傳回這兩個值 ({@code STYLUS_BUTTON_PRIMARY|STYLUS_BUTTON_SECONDARY})。 - -</li> -<li> -針對目標為較低平台版本的應用程式, -{@link android.view.MotionEvent#getButtonState() getButtonState()} 方法會傳回 -{@link android.view.MotionEvent#BUTTON_SECONDARY} (針對主要手寫筆按鈕的按下動作)、 -{@link android.view.MotionEvent#BUTTON_TERTIARY} (針對次要手寫筆按鈕的按下動作),或兩者。 -</li> -</ul> - -<h2 id="ble-scanning">已改進藍牙低電量掃描</h2> -<p> -如果您的應用程式會執行藍芽低電量掃描,就可以使用新的 -{@code android.bluetooth.le.ScanSettings.Builder.setCallbackType()} 方法,來指定如果先找到符合 -{@link android.bluetooth.le.ScanFilter} 組合的廣告封包,以及在某個時段中找不到它時,只需通知回呼。 - -比起先前平台版本中所提供的功能,這個掃描方法功能更強大且更有效率。 - -</p> - -<h2 id="hotspot">無線基地台 2.0 版本 1 支援</h2> -<p> -這個預覽版在 Nexus 6 和 Nexus 9 裝置上新增對於無線基地台 2.0 版本 1 規格的支援。如要在您的應用程式中佈建無線基地台 2.0 認證,請使用 -{@link android.net.wifi.WifiEnterpriseConfig} 類別的新方法,例如 {@code setPlmn()} 和 -{@code setRealm()}。 -在 {@link android.net.wifi.WifiConfiguration} 物件中,您可以設定 -{@link android.net.wifi.WifiConfiguration#FQDN} 和 {@code providerFriendlyName} 欄位。新的 {@code ScanResult.PasspointNetwork} 屬性指出偵測到的網路是否代表無線基地台 2.0 存取點。 - - -</p> - -<h2 id="4K-display">4K 顯示模式</h2> -<p>此平台現在允許應用程式能夠要求將在相容硬體中呈現的顯示解析度升級到 4K。 -如要查詢目前的實際解析度,請使用新的 -{@code android.view.Display.Mode} API。如果 UI 是使用較低的邏輯解析度來繪製,並向上升級為較高的實際解析度,請注意, -{@code Display.Mode.getPhysicalWidth()} 方法傳回的實際解析度可能會與 {@link android.view.Display#getSize(android.graphics.Point) getSize()} 報告的邏輯解析度不同。 - -</p> - -<p>您可以在應用程式執行時,藉由設定應用程式視窗的 {@code WindowManager.LayoutParams.preferredDisplayModeId} 屬性,來要求系統變更該應用程式中的實際解析度。 -如果您想要切換為 4K 顯示解析度,這個功能非常實用。 -儘管在 4K 顯示模式中,UI 會持續使用原始解析度 (例如 1080p) 來呈現並向上升級為 4K,但是 -{@link android.view.SurfaceView} 物件可能會以原生解析度來顯示內容。 -</p> - -<h2 id="behavior-themeable-colorstatelists">具備設計風格的 ColorStateList</h2> -<p>針對執行 M 預覽版的裝置, -{@link android.content.res.ColorStateList} 中目前支援設計風格屬性。 -{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} 和 -{@link android.content.res.Resources#getColor(int) getColor()} 方法已過時。如果您正在呼叫這些 API,請改為呼叫新的 {@code Context.getColorStateList()} 或 -{@code Context.getColor()} 方法。 -您也可以透過 {@link android.support.v4.content.ContextCompat},在 v4 appcompat 程式庫中取得這些方法。 -</p> - -<h2 id="audio">音訊功能</h2> - -<p>這個預覽版在 Android 上新增了音訊處理的增強功能,包括: </p> -<ul> - <li>利用新的 {@code android.media.midi} API,來支援 <a href="http://en.wikipedia.org/wiki/MIDI" class="external-link">MIDI</a> 通訊協定。 -使用這些 API 來傳送與接收 MIDI 事件。 -</li> - <li>新的 {@code android.media.AudioRecord.Builder} 和 {@code android.media.AudioTrack.Builder} -類別,可分別建立數位音訊擷取和播放物件,並設定音訊來源和接收屬性來覆寫系統預設值。 -</li> - <li>API 勾點,適合用來關聯音訊與輸入裝置。如果您的應用程式允許使用者從連接到 Android TV 的遊戲控制器或遙控器啟動音訊搜尋,則這特別有用。系統會在使用者啟動搜尋時,叫用新的 {@code android.app.Activity.onSearchRequested()} 回呼。 - - -如要判斷使用者的輸入裝置是否有內建的麥克風,請從該回呼中擷取 {@link android.view.InputDevice} 物件,然後呼叫新的 -{@code InputDevice.hasMic()} 方法。 -</li> - <li>新的 {@code android.media.AudioDevicesManager} 類別,讓您能夠擷取所有已連接的來源與接收音訊裝置的清單。 -如果您想要讓應用程式在連接或中斷連接音訊裝置時收到通知,也可以指定 -{@code android.media.OnAudioDeviceConnectionListener} 物件。 -</li> -</ul> - -<h2 id="video">影片功能</h2> -<p>這個預覽版在影片處理 API 中增加了新功能,包括:</p> -<ul> -<li>新的 {@code android.media.MediaSync} 類別,可協助應用程式同步轉譯音訊和影片串流。 -音訊緩衝區是利用非封鎖的方式來提交,並透過回呼來傳回。 -它也支援動態播放速率。 -</li> -<li>新的 {@code MediaDrm.EVENT_SESSION_RECLAIMED} 事件,指出應用程式開啟的工作階段已由資源管理員所回收。 -如果您的應用程式使用 DRM 工作階段,就應該處理這個事件,並確定不會使用回收的工作階段。 - -</li> -<li>新的 {@code MediaCodec.CodecException.ERROR_RECLAIMED} 錯誤碼,表示資源管理員已回收轉碼器所使用的媒體資源。 -如果發生這個例外狀況,就必須釋放轉碼器,就如同它已進入終止狀態。 - -</li> -<li>新的 {@code MediaCodecInfo.CodecCapabilities.getMaxSupportedInstances()} 介面,可取得支援並行轉碼器執行個體數目上限的提示。 - -</li> -<li>新的 {@code MediaPlayer.setPlaybackParams()} 方法,可將媒體播放速率設定為快速或慢速播放。 -這也可以和影片一起自動延伸或加速音訊播放。 -</li> -</ul> - -<h2 id="camera">相機功能</h2> -<p>這個預覽版包含下列可用來存取相機閃光燈和相機重新處理影像的 API: -</p> - -<h3 id="flashlight">閃光燈 API</h3> -<p>如果相機裝置具有閃光裝置,則您可以呼叫 {@code CameraManager.setTorchMode()} -方法,在不開啟相機裝置的情況下,開啟或關閉閃光裝置的閃光模式。應用程式不具備閃光裝置或相機裝置的獨佔擁有權。 -每當相機裝置變成無法使用時,或者,當其他相機資源讓閃光變成無法使用時,閃光模式也會關閉且變成無法使用。 - -其他應用程式也會呼叫 {@code setTorchMode()} -來關閉閃光模式。關閉最後一個開啟閃光模式的應用程式時,閃光模式即會關閉。 -</p> - -<p>您可以呼叫 -{@code CameraManager.registerTorchCallback()} 方法,來註冊回呼要收到有關閃光模式狀態的通知。第一次註冊回呼時,會立即使用所有目前已知具有閃光裝置之相機裝置的閃光模式來呼叫它。 - -如果成功開啟或關閉閃光模式,即會叫用 -{@code CameraManager.TorchCallback.onTorchModeChanged()} 方法。</p> - -<h3 id="reprocessing">重新處理 API</h3> -<p>{@link android.hardware.camera2 Camera2} API 已擴充,支援重新處理 YUV 和私人不透明格式的影像。 -您的應用程式會判斷是否可透過 {@code CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES} 使用重新處理功能。 -如果裝置支援重新處理,您就可以呼叫 -{@code CameraDevice.createReprocessableCaptureSession()} 來建立可重新處理的相機拍攝工作階段,然後建立適用於重新處理輸入緩衝區的要求。 - -</p> - -<p>使用 {@code ImageWriter} 類別,將輸入緩衝區流程連接到相機重新處理輸入。 -如要取得空的緩衝區,請依照這個程式設計模型執行:</p> - -<ol> -<li>呼叫 {@code ImageWriter.dequeueInputImage()} 方法。</li> -<li>在輸入緩衝區中填入資料。</li> -<li>呼叫 {@code ImageWriter.queueInputImage()} 方法,將緩衝區傳送到相機。</li> -</ol> - -<p>如果您將 {@code ImageWriter} 物件和 -{@code android.graphics.ImageFormat.PRIVATE} 影像一起使用,您的應用程式就無法直接存取影像資料。 -請改為呼叫 {@code ImageWriter.queueInputImage()} 方法但不含任何緩衝區複本,直接將 {@code ImageFormat.PRIVATE} 影像傳遞到 -{@code ImageWriter}。 -</p> - -<p>{@code ImageReader} 類別現在支援 {@code android.graphics.ImageFormat.PRIVATE} 格式的影像串流。 -這個支援讓您的應用程式能夠保留 -{@code ImageReader} 輸出影像的循環影像佇列、選取一或多個影像,然後將它們傳送到 -{@code ImageWriter} 以進行相機重新處理。</p> - -<h2 id="afw">Android for Work 功能</h2> -<p>這個預覽版包含下列適用於 Android for Work 功能的新 API:</p> -<ul> - <li><strong>已增強適用於公司擁有、單一用途裝置的控制項:</strong>裝置擁有者現在可以控制下列設定,來改進公司擁有、單一用途 (COSU) 裝置的管理: - - - <ul> - <li>使用 -{@code DevicePolicyManager.setKeyguardEnabledState()} 方法來停用或重新啟用滑動解鎖。</li> - <li>使用 -{@code DevicePolicyManager.setStatusBarEnabledState()} 方法,來停用或重新啟用狀態列 (包括快速設定、通知,以及啟動 Google 即時資訊的導覽向上滑動手勢)。 -</li> - <li>使用 {@link android.os.UserManager} 常數 -{@code DISALLOW_SAFE_BOOT} 來停用或重新啟用安全開機。</li> - <li>使用 - {@link android.provider.Settings.Global} 常數 {@code STAY_ON_WHILE_PLUGGED_IN},防止螢幕在使用者為裝置連接電源時關閉。</li> - </ul> - </li> - <li><strong>透過裝置擁有者自動安裝與解除安裝應用程式:</strong>裝置擁有者現在可以使用 {@link android.content.pm.PackageInstaller} -API (與 Google Play for Work 無關) 自動安裝與解除安裝應用程式。 -您現在可以透過裝置擁有者佈建裝置,該裝置擁有者可在不與使用者互動的情況下擷取並安裝應用程式。 -如要在不啟用 Google 帳戶的情況下輕觸一次就能佈建 Kiosk 或其他這類裝置,這個功能非常有用。 -</li> -<li><strong>自動存取企業憑證:</strong>當應用程式呼叫 -{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()} 時,在系統提示使用者選取憑證之前,設定檔或裝置擁有者現在會呼叫 {@code DeviceAdminReceiver.onChoosePrivateKeyAlias()} 方法,為提出要求的應用程式自動提供別名。 - - -這個功能讓您能夠在不與使用者互動的情況下,為受管理的應用程式授與存取憑證的權限。 -</li> -<li><strong>自動接受系統更新。</strong>藉由使用 -{@code DevicePolicyManager.setSystemUpdatePolicy()} 來設定系統更新原則,裝置擁有者現在可以自動接受系統更新 (例如,在 Kiosk 裝置的案例中),或者延後更新,並防止使用者進行更新,最多 30 天。 - -因此,系統管理員可以設定必須取得更新的每日時間範圍 ,例如,在 Kiosk 裝置處於未使用狀態時。 -在系統更新可供使用時,系統就會檢查工作原則控制器應用程式是否已設定系統更新原則,並據以運作。 - - -</li> -<li> -<strong>委派的憑證安裝:</strong>設定檔或裝置擁有者現在可以授與第三方廠商的應用程式呼叫這些 {@link android.app.admin.DevicePolicyManager} 憑證管理 API 的能力: - - -<ul> - <li>{@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName) -getInstalledCaCerts()}</li> - <li>{@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[]) -hasCaCertInstalled()}</li> - <li>{@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[]) -installCaCert()}</li> - <li>{@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[]) -uninstallCaCert()}</li> - <li>{@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName) -uninstallAllUserCaCerts()}</li> - <li>{@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String) -installKeyPair()}</li> -</ul> -</li> -<li><strong>企業原廠重設保護:</strong>佈建裝置擁有者時,您現在可以藉由設定 -{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS} 套件組合,來設定參數以解除鎖定原廠重設保護 (FRP)。 -NFC 程式設計人員應用程式可以在已重設裝置來解除鎖定 FRP 並佈建裝置之後提供這些參數,而不需使用先前設定的 Google 帳戶。 - -如果您並未修改這些參數,FRP 會就地保留,並防止裝置在沒有先前啟用的 Google 認證的情況下啟用。 - - -<p>此外,裝置擁有者可以在 Google Play 服務上設定應用程式限制,來指定可用來解除鎖定 FRP 的替代 Google 帳戶,以取代已在裝置上啟用的帳戶。 -</p> -</li> -<img src="{@docRoot}preview/images/work-profile-screen.png" srcset="{@docRoot}preview/images/work-profile-screen.png 1x, {@docRoot}preview/images/work-profile-screen_2x.png 2x" style="float:right; margin:0 0 10px 20px" width="282" height="476" /> -<li><strong>資料使用量追蹤。</strong>設定檔或裝置擁有者現在可以使用新的 -{@code android.app.usage.NetworkStatsManager} 方法,針對可在 [設定] > [資料]<strong></strong> 使用量中看見的資料使用量統計資料進行查詢。 -系統會自動授與設定檔擁有者權限來查詢他們所管理之設定檔上的資料,在此同時,裝置擁有者會取得受管理的主要使用者之使用量資料的存取權限。 - -</li> -<li><strong>執行階段權限管理:</strong> -<p>設定檔或裝置擁有者可以使用 -{@code DevicePolicyManager.setPermissionPolicy()},針對所有應用程式的所有執行階段要求設定權限原則,以提示使用者授與一般權限,或者以無訊息方式自動授與或拒絕該權限。 - -如果設定了後項原則,使用者就無法在應用程式權限畫面的 [設定]<strong></strong> 中,修改設定檔或裝置擁有者所做的選項。 - -</p></li> -<li><strong>設定中的 VPN:</strong>VPN 應用程式現在可以在 [設定] > [更多] > [VPN]<strong></strong> 中看見。此外,伴隨 VPN 使用量出現的通知是該 VPN 設定方式的特定通知。 - - -針對設定檔擁有者,通知是專門用來通知是否已針對受管理的設定檔、個人設定檔或兩者設定了 VPN。 -針對裝置擁有者,通知是專門用來通知是否已針對整個裝置設定了 VPN。 -</li> -<li><strong>工作狀態通知:</strong>每當來自受管理設定檔的應用程式在前景中有活動時,狀態列上就會出現公事包圖示。 -因此,如果直接將裝置解除鎖定至受管理設定檔中應用程式的活動,即會顯示一個快顯通知,通知使用者他們正處於工作設定檔內。 - - -</li> -</ul> - -<p class="note"> - 如需 M 開發人員預覽版中所有 API 變更的詳細檢視,請參閱 <a href="{@docRoot}preview/download.html">API 差異報告</a>。 -</p> diff --git a/docs/html-intl/intl/zh-tw/preview/backup/index.jd b/docs/html-intl/intl/zh-tw/preview/backup/index.jd deleted file mode 100644 index edb8b98fb63c..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/backup/index.jd +++ /dev/null @@ -1,327 +0,0 @@ -page.title=針對應用程式進行自動備份 -page.tags=backup, previewresources, androidm -page.keywords=backup, autobackup, preview -page.image=images/cards/card-auto-backup_2x.png -@jd:body - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#overview">總覽</a></li> - <li><a href="#configuring">設定資料備份</a></li> - <li><a href="#testing">測試備份設定</a></li> - <li><a href="#issues">已知問題</a></li> - </ol> - </div> -</div> - -<p> - 使用者通常會在應用程式中花費很多時間精力建立資料和設定偏好設定。 -如果使用者換掉壞掉的裝置或升級到新的裝置時,能夠保存裝置上的資料,就能夠讓使用者獲得良好的體驗。 -執行 Android M 預覽版系統的裝置可以協助在上述情況自動將應用程式資料備份到 Google 雲端硬碟,讓使用者獲得良好的體驗。 - -如果使用者變更或升級裝置,應用程式資料會自動儲存。 - -</p> - -<p> - 在執行 Android M 預覽版的裝置上安裝的所有應用程式,預設都會啟用自動備份。不需要任何額外的應用程式程式碼。 -系統則提供使用者不進行自動備份資料的選項。 -您也可以選擇限制要備份應用程式的哪些資料。 -</p> - -<p> - 本文件說明新的系統行為,以及如何指定備份應用程式的哪些資料。 - -</p> - -<h2 id="overview">總覽</h2> - -<p> - 自動備份功能保存應用程式資料的方法,是將使用者裝置上建立的資料上傳到使用者的 Google 雲端硬碟帳戶,然後將資料加密。 -儲存資料不會向您或使用者收費,儲存的資料也不會計入使用者個人雲端硬碟的容量配額。 -在 M 預覽版期間,使用者最多可以為每個 Android 應用程式儲存 25 MB 的資料。 - -</p> - -<p> - 當裝置閒置、正在充電且連線到 Wi-Fi 網路時,每 24 小時會執行一次自動備份。 -當符合上述情況時,備份管理員服務就會將所有可用的備份資料上傳到雲端。 -當使用者轉換到新的裝置,或者解除安裝又重新安裝已備份的應用程式時,系統會進行一項還原作業,將備份的資料複製到新安裝之應用程式的資料目錄。 - - -</p> - -<p class="note"> - <strong>注意:</strong>如果您的應用程式使用舊版 - <a href="{@docRoot}google/backup/index.html">Android 備份服務</a>,這個新的行為就不適用,現有的備份行為還是如往常運作。 - -</p> - - -<h3 id="auto-exclude">自動排除的資料檔案</h3> - -<p> - 並非所有應用程式資料都需要備份,例如暫存檔案和快取,因此自動備份服務預設會排除某些資料檔案: - -</p> - -<ul> - <li>{@link android.content.Context#getCacheDir -getCacheDir()} 和 {@link android.content.ContextWrapper#getCodeCacheDir getCodeCacheDir()} -方法所參考目錄中的檔案。 - </li> - - <li>位於外部儲存空間的檔案,除非這些檔案位於 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} -方法所參考的目錄中。 - </li> - - <li>{@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()} 方法所參考目錄中的檔案。 - - </li> -</ul> - -<h2 id="configuring">設定資料備份</h2> - -<p> - 除了上一節所列自動排除的檔案之外,其餘由 M 預覽版裝置上安裝的任何應用程式所建立的資料一律都會備份。 -您可以使用應用程式宣示說明中的設定,進一步限制和設定要備份應用程式的哪些資料。 - -</p> - -<h3 id="include-exclude">納入或排除資料</h3> - -<p> - 根據您的應用程式需要的資料和您儲存資料的方式,您可能需要設定特定的規則來納入或排除某些檔案或目錄。 -自動備份服務支援使用 XML 設定檔和應用程式宣示說明來設定這些備份規則。 - -在應用程式宣示說明中,您可以依照下列範例指定備份配置設定檔: - -</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.my.appexample"> - <uses-sdk android:minSdkVersion="MNC"/> - <uses-sdk android:targetSdkVersion="MNC"/> - <app ... -<strong> android:fullBackupContent="@xml/mybackupscheme"></strong> - </app> - ... -</manifest> -</pre> - -<p> - 在此範例程式碼中,<code>android:fullBackupContent</code> 屬性指定應用程式開發專案的 <code>res/xml/</code> 目錄中,名為 -<code>mybackupscheme.xml</code> 的 XML 檔案。 -此設定檔包含要備份哪些檔案的規則。 -下列範例程式碼顯示排除特定檔案不要進行備份的設定檔: - -</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<full-backup-content> - <exclude domain="database" path="device_info.db"/> -</full-backup-content> -</pre> - -<p> - 此範例備份設定只排除特定資料庫檔案不要進行備份。 - 其他所有檔案都會備份。 -</p> - -<h4>備份設定語法</h4> - -<p> - 您可以使用備份服務設定指定要將哪些檔案納入或排除備份。 -資料備份設定 XML 檔案的語法如下所示: -</p> - -<pre> -<full-backup-content> - <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" /> - <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" /> -</full-backup-content> -</pre> - -<p> - 您可以使用下列元素和屬性指定要將哪些檔案納入備份或從備份排除: - -</p> - -<ul> - <li> - <code><include></code>。如果您想要指定備份一組資源,請使用此元素,而不要讓系統預設備份應用程式的所有資料。 -當您指定 -<code><include></code> 標籤時,系統只會備份此元素「指定的資源」<em></em>。 - - </li> - - <li> - <code><exclude></code>。如果您想要指定一組資源不要進行備份,請使用此元素。 -系統會備份應用程式中除了此元素指定之資源以外的其他所有資料。 - - </li> - - <li> - <code>domain.</code> 您想要納入或排除備份的資源類型。此屬性可以指定的有效值包括: - - </li> - - <li style="list-style: none"> - <ul> - <li> - <code>root</code>。指定資源位於應用程式的根目錄。 - </li> - - <li> - <code>file</code>。位於 -{@link android.content.Context#getFilesDir getFilesDir()} 方法傳回的目錄中的資源。 - </li> - - <li> - <code>database</code>。{@link android.content.Context#getDatabasePath getDatabasePath()} 方法或使用 -{@link android.database.sqlite.SQLiteOpenHelper} 類別傳回的資料庫。 - - </li> - - <li> - <code>sharedpref</code>。{@link android.content.Context#getSharedPreferences getSharedPreferences()} -方法傳回的 {@link android.content.SharedPreferences} 物件。 - - </li> - - <li> - <code>external</code>。指定資源位於外部儲存空間,並且位於 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} 方法傳回的目錄中的檔案。 - - </li> - - <li> - <code>path</code>。您想要納入或排除備份的資源檔案路徑。 - - </li> - </ul> - </li> -</ul> - - -<h3 id="prohibit">禁止資料備份</h3> - -<p> - 您可以在應用程式的宣示說明元素中將 -<code>android:allowBackup</code> 屬性設定為 <code>false</code>,選擇不要對任何應用程式資料進行自動備份。 -此設定如下列範例程式碼所示: -</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.my.appexample"> - <uses-sdk android:minSdkVersion="MNC"/> - <uses-sdk android:targetSdkVersion="MNC"/> - <app ... -<strong> android:allowBackup="false"></strong> - </app> - ... -</manifest> -</pre> - - -<h2 id="testing">測試備份設定</h2> - -<p> - 如果您建立了備份設定,就要進行測試,以確保您的應用程式能正確儲存和還原資料。 - -</p> - - -<h4>啟用備份記錄</h4> - -<p> - 如要協助判斷備份功能如何剖析您的 XML 檔案,請在執行測試備份之前先啟用記錄功能: - -</p> - -<pre class="noprettyprint"> -$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE -</pre> - -<h4>測試備份</h4> - -<p>如要手動執行備份,首先您必須呼叫下列命令以初始化備份管理員: - -</p> - -<pre class="noprettyprint"> -$ adb shell bmgr run -</pre> - -<p> - 接著,您要使用下列命令,將 <code><PACKAGE></code> 參數指定為您應用程式的套件名稱,手動備份您的應用程式: - -</p> - -<pre class="noprettyprint"> -$ adb shell bmgr fullbackup <PACKAGE></pre> - - -<h4>測試還原</h4> - -<p> - 如要在應用程式資料備份之後手動啟動還原,請呼叫下列命令,將 <code><PACKAGE></code> 參數指定為您應用程式的套件名稱: - -</p> - -<pre class="noprettyprint"> -$ adb shell bmgr restore <PACKAGE> -</pre> - -<p class="warning"> - <b>警告:</b>這個動作在執行還原作業之前會先停止您的應用程式,然後清除其中的資料。 - -</p> - -<p> - 如果解除安裝又重新安裝應用程式,就會針對應用程式啟動還原程序。應用程式安裝完成之後,就會自動從雲端還原應用程式資料。 - -</p> - - -<h4>疑難排解備份</h4> - -<p> - 如果您遇到問題,可以在 [設定] > [備份]<strong></strong> 中關閉備份再開啟、將裝置重設為出廠值,或是呼叫下列命令,清除備份資料及相關的中繼資料: - - -</p> - -<pre>$ adb shell bmgr wipe <TRANSPORT> <PACKAGE></pre> - -<p> - <code><TRANSPORT></code> 值前面必須加上 <code>com.google.android.gms</code>。 - 如要取得傳輸清單,請呼叫下列命令: -</p> - -<pre>$ adb shell bmgr list transports</pre> - -<h2 id="issues">已知問題</h2> - -<p>以下是自動備份服務的已知問題:</p> - -<ul> - <li><strong>Google 雲端通訊</strong> - 使用「Google 雲端通訊」推送通知的應用程式有一個已知問題,亦即備份由「Google 雲端通訊」註冊程序傳回的註冊 ID,會讓還原後的應用程式無法傳送推送通知。在新裝置安裝 API 之後,需要向 API 查詢新的註冊 ID,因此如果之前備份了舊的註冊 ID,就會發生問題。 - - - - -如要避免發生這個情況,請不要備份包含註冊 ID 的檔案。 - - </li> -</ul> diff --git a/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd b/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd deleted file mode 100644 index 405aea1e53ac..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd +++ /dev/null @@ -1,402 +0,0 @@ -page.title=行為變更 -page.keywords=預覽版,sdk,相容性 -sdk.platform.apiLevel=MNC -@jd:body - -<div id="qv-wrapper"> -<div id="qv"> - -<h2>本文件內容</h2> - -<ol id="toc44" class="hide-nested"> - <li><a href="#behavior-runtime-permissions">執行階段權限</a></li> - <li><a href="#behavior-power">省電最佳化</a> - <ol> - <li><a href="#behavior-doze">休眠</a></li> - <li><a href="#behavior-app-standby">應用程式待命</a></li> - </ol> - </li> - <li><a href="#behavior-adoptable-storage">可採用的儲存裝置</a></li> - <li><a href="#behavior-apache-http-client">移除 Apache HTTP 用戶端</a></li> - <li><a href="#behavior-audiomanager-Changes">AudioManager 變更</a></li> - <li><a href="#behavior-test-selection">文字選取</a></li> - <li><a href="#behavior-keystore">Android 金鑰存放區變更</a></li> - <li><a href="#behavior-network">Wi-Fi 和網路變更</a></li> - <li><a href="#behavior-camera">相機服務變更</a></li> - <li><a href="#behavior-art-runtime">ART 執行階段</a></li> - <li><a href="#behavior-apk-validation">APK 驗證</a></li> - <li><a href="#behavior-afw">Android for Work 變更</a></li> -</ol> - -<h2>API 差異</h2> -<ol> -<li><a href="{@docRoot}preview/download.html">API 級別 22 到 M 預覽版»</a> </li> -</ol> - - -<h2>另請參閱</h2> -<ol> -<li><a href="{@docRoot}preview/api-overview.html">M 開發人員預覽版 API 總覽</a> </li> -</ol> - -</div> -</div> - -<p>除了新特性和功能以外,M 開發人員預覽版還包含各種不同的系統變更和 API 行為變更。 -本文件將強調說明一些您應該知道且在您的應用程式中加以考量的重要變更。 -</p> - -<p>如果您先前曾發行過適用於 Android 的應用程式,請注意,您的應用程式可能會受到平台中的這類變更所影響。 -</p> - -<h2 id="behavior-runtime-permissions">執行階段權限</h1> -<p>這個預覽版引進了新的權限模型,使用者現在可以在執行階段直接管理應用程式權限。 -這個模型為使用者提供了改良的能見度並使其可完全控制權限,同時為應用程式開發人員提供更流暢的安裝和自動更新程序。使用者可以針對安裝的應用程式個別授與或撤銷權限。 - - </p> - -<p>在目標為 M 預覽版的應用程式中,請務必在執行階段檢查並要求權限。 -如要判斷您的應用程式是否已獲授與權限,請呼叫新的 {@code Context.checkSelfPermission()} 方法。 -如要要求權限,請呼叫新的 -{@code Activity.requestPermission()} 方法。即使您的應用程式目標不是 M,還是應該在新的權限模型下測試您的應用程式。 -</p> - -<p>如需在您的應用程式中支援新權限模型的詳細資訊,請參閱<a href="{@docRoot}preview/features/runtime-permissions.html">權限</a>開發人員預覽版頁面。 - -如需評估對您應用程式的影響的祕訣,請參閱<a href="{@docRoot}preview/testing/guide.html#runtime-permissions">測試指南</a>。 -</p> - -<h2 id="behavior-power">省電最佳化</h2> -<p>這個預覽版針對閒置的裝置和應用程式引進了新的省電最佳化功能。</p> - -<h3 id="behavior-doze">休眠</h3> -<p>如果拔除裝置電源並關閉螢幕使其保持靜止狀態一段時間,該裝置即會進入「休眠」<em></em>模式,它會嘗試讓系統保持睡眠狀態。 -在此模式中,裝置會在短期間內定期繼續執行正常操作,因此,會進行應用程式同步處理,而系統可以執行任何待處理的操作。 - -</p> - -<p>處於休眠狀態時,下列限制會套用到您的應用程式:</p> -<ul> -<li>除非您的應用程式接收到高優先順序的 Google 雲端通訊活動訊號 (Tickle),否則會停用網路存取。 -</li> -<li><a href="{@docRoot}reference/android/os/PowerManager.WakeLock.html">喚醒鎖定</a>會被忽略。</li> -<li>使用 {@link android.app.AlarmManager} 類別排定的鬧鐘會被停用,但使用 {@link android.app.AlarmManager#setAlarmClock setAlarmClock()}方法和 {@code AlarmManager.setAndAllowWhileIdle()} 設定的鬧鐘則不會被停用。 - -</li> -<li>WiFi 掃描不會執行。</li> -<li>不容許執行您同步配接器的同步處理和工作以及 {@link android.app.job.JobScheduler}。 -</li> -</ul> -</p> -<p>當裝置離開休眠狀態時,就會執行所有待處理的工作和同步處理。</p> -<p>您可以測試此功能,方法是將執行 M 預覽版的裝置連接到您的開發電腦並呼叫下列命令: - -</p> -<pre class="no-prettyprint"> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> -<p class="note"><strong>注意:</strong>即將發行的 <a href="https://developers.google.com/cloud-messaging/" class="external-link">Google 雲端通訊</a>版本讓您能夠指定高優先順序的訊息。 - - -如果您的應用程式收到高優先順序的 GCM 訊息,即使裝置處於休眠狀態,系統還是會授與它短暫的網路存取權限。 - -</p> - -<p>如需如何在您的應用程式中測試休眠的祕訣,請參閱<a href="{@docRoot}preview/testing/guide.html#doze-standby">測試指南</a>。 - - </p> - -<h3 id="behavior-app-standby">應用程式待命</h3> -<p>使用這個預覽版時,系統可在應用程式處於未使用狀態時,判斷它們是否處於閒置狀態。 -除非系統偵測到以下任一個訊號,否則會在一段時間之後將應用程式視為閒置: -</p> - -<ul> -<li>使用者明確啟動應用程式。</li> -<li>應用程式目前在前景中有一個處理程序 (可能是做為活動或前景服務,也可能正由其他活動或前景服務所使用)。 -</li> -<li>應用程式產生使用者可以在鎖定螢幕或通知匣中看見的通知。 -</li> -<li>使用者透過 [設定]<strong></strong> 明確要求應用程式不需進行最佳化。 -</li> -</ul> - -<p>如果拔除了裝置電源,即會停用被視為閒置之應用程式的網路存取,並擱置它們的同步處理和工作。 -為裝置插上電源時,就允許這些應用程式進行網路存取,且可執行所有已擱置的工作和同步處理。 -如果裝置長時間處於閒置狀態,則允許閒置的應用程式進行網路存取,大約是一天一次。 -</p> - -<p>您可以測試此功能,方法是將執行 M 預覽版的裝置連接到您的開發電腦並呼叫下列命令: - -</p> -<pre class="no-prettyprint"> -$ adb shell dumpsys battery unplug -$ adb shell am set-idle <packageName> true -$ adb shell am set-idle <packageName> false -$ adb shell am get-idle <packageName> -</pre> - -<p class="note"><strong>注意:</strong>即將發行的 <a href="https://developers.google.com/cloud-messaging/" class="external-link">Google 雲端通訊</a> (GCM) 版本讓您能夠指定高優先順序的訊息。 - - -如果您的應用程式收到高優先順序的 GCM 訊息,即使應用程式處於閒置狀態,系統還是會授與它短暫的網路存取權限。 - -</p> - -<p>如需如何在您的應用程式中測試應用程式待命的祕訣,請參閱<a href="{@docRoot}preview/testing/guide.html#doze-standby">測試指南</a>。 - - </p> - -<h2 id="behavior-adoptable-storage">可採用的儲存裝置</h2> -<p> -使用這個預覽版時,使用者可以採用<em></em>像是 SD 卡的外部儲存裝置。採用外部儲存裝置會加密並格式化裝置,使其可如內部儲存空間般運作。 -此功能讓使用者能夠在儲存裝置之間移動應用程式和這些應用程式的私人資料。 -移動應用程式時,系統會採用宣示說明中的 <a href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code android:installLocation}</a> 偏好設定。 - - -</p> - -<p>如果您的應用程式會存取下列 API 或欄位,請注意,在內部和外部儲存裝置之間移動應用程式時,它們傳回的檔案路徑將會動態變更。建置檔案路徑時,強烈建議您一律動態呼叫這些 API。請勿使用硬式編碼的檔案路徑或保留先前建置的完整檔案路徑。 - - -</p> - -<ul> -<li>{@link android.content.Context} 方法: - <ul> - <li>{@link android.content.Context#getFilesDir() getFilesDir()}</li> - <li>{@link android.content.Context#getCacheDir() getCacheDir()}</li> - <li>{@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}</li> - <li>{@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}</li> - <li>{@link android.content.Context#getDir(java.lang.String,int) getDir()}</li> - <li>{@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}</li> - <li>{@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}</li> - <li>{@link android.content.Context#getPackageCodePath() getPackageCodePath()}</li> - <li>{@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}</li> - </ul> -</li> -<li>{@link android.content.pm.ApplicationInfo} 欄位: - <ul> - <li>{@link android.content.pm.ApplicationInfo#dataDir dataDir}</li> - <li>{@link android.content.pm.ApplicationInfo#sourceDir sourceDir}</li> - <li>{@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}</li> - <li>{@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}</li> - <li>{@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}</li> - <li>{@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}</li> - </ul> -</li> -</ul> - -<p>如要在開發人員預覽版中對此功能進行偵錯,您可以執行下列命令來採用 USB 磁碟機 (這個磁碟機是透過 USB On-The-Go (OTG) 纜線連接到 Android 裝置): -</p> - -<pre class="no-prettyprint"> -$ adb shell sm set-force-adoptable true -</pre> - -<h2 id="behavior-apache-http-client">移除 Apache HTTP 用戶端</h2> -<p>這個預覽版已移除對於 Apache HTTP 用戶端的支援。如果您的應用程式正在使用這個用戶端且目標為 Android 2.3 (API 級別 9) 或更高版本,請改為使用 {@link java.net.HttpURLConnection} 類別。 - -這個 API 的效率更高,因為它能透過透明的壓縮和回應快取來降低網路使用量,並將電源耗用量降至最低。 -如要繼續使用 Apache HTTP API,您必須先在 {@code build.gradle} 檔案中宣告下列編譯時期的相依性: - -</p> -<pre> -android { - useLibrary 'org.apache.http.legacy' -} -</pre> -<p>Android 正從 OpenSSL 移至 <a href="https://boringssl.googlesource.com/boringssl/" class="external-link">BoringSSL</a> 程式庫。 - -如果您正在應用程式中使用 Android NDK,請勿連結不屬於 NDK API 一部分的密碼編譯程式庫,例如 {@code libcrypto.so} 和 {@code libssl.so}。 -這些程式庫不是公用 API,而且可能在沒有通知的情況下,在新的版本和裝置上變更或終止支援。此外,您可能會讓自己暴露於安全性弱點中。 - -因此,請改為修改您的原生程式碼,透過 JNI 來呼叫 Java 密碼編譯 API,或以靜態方式連結您選擇的密碼編譯程式庫。 - -</p> - -<h2 id="behavior-audiomanager-Changes">AudioManager 變更</h2> -<p>不再支援透過 {@link android.media.AudioManager} 類別直接設定音量或將特定串流設定為靜音。 -{@link android.media.AudioManager#setStreamSolo(int,boolean) -setStreamSolo()} 方法已過時,您應該改為呼叫 -{@code AudioManager.requestAudioFocus()} 方法。同樣地, -{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()} 方法已過時;請改為呼叫 {@code AudioManager.adjustStreamVolume()} 方法並傳入方向值 {@code ADJUST_MUTE} 或 {@code ADJUST_UNMUTE}。 - -</p> - -<h2 id="behavior-test-selection">文字選取</h2> - -<img src="{@docRoot}preview/images/text-selection.gif" style="float:right; margin:0 0 20px 30px" width="360" height="640" /> - -<p>當使用者在您的應用程式中選取文字時,您現在可以在<a href="http://www.google.com/design/spec/patterns/selection.html#selection-text-selection" class="external-link">浮動工具列</a>中顯示文字選取動作,例如,剪下<em></em>、複製<em></em>及貼上<em></em>。 - -使用者互動實作類似於針對內容關聯動作列所做的實作,如<a href="{@docRoot}guide/topics/ui/menus.html#CABforViews">為個別的檢視啟用內容關聯動作模式</a>中所述。 - - -</p> - -<p>如要實作適用於文字選取的浮動工具列,請在您現有的應用程式中進行下列變更: -</p> -<ol> -<li>在您的 {@link android.view.View} 或 {@link android.app.Activity} 物件中,將 -{@link android.view.ActionMode} 呼叫從 -{@code startActionMode(Callback)} 變更為 {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}。</li> -<li>進行 {@code ActionMode.Callback} 的現有實作,並改為讓它擴充 -{@code ActionMode.Callback2}。</li> -<li>覆寫 {@code Callback2.onGetContentRect()} 方法,以在檢視中提供內容 {@link android.graphics.Rect} 物件 (例如,文字選取矩形區塊) 的座標。 -</li> -<li>如果矩形區塊位置不再有效,而且這是唯一變成無效的元素,請呼叫 {@code ActionMode.invalidateContentRect()} 方法。 -</li> -</ol> - -<p>如果您正在使用 <a href="{@docRoot}tools/support-library/index.html">Android 支援程式庫</a>版本 22.2,請注意,浮動工具列無法向下相容,而且 appcompat 預設會取得 {@link android.view.ActionMode} 物件的完整控制權。 - - -這可防止浮動工具列顯示。如要在 -{@link android.support.v7.app.AppCompatActivity} 中啟用 -{@link android.view.ActionMode} 支援,請呼叫 -{@code android.support.v7.app.AppCompatActivity.getDelegate()},然後在傳回的 -{@link android.support.v7.app.AppCompatDelegate} 物件中呼叫 -{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()},並將輸入參數設定為 {@code false}。 -這個呼叫會將 {@link android.view.ActionMode} 物件的控制權傳回架構中。 -儘管在 M 預覽版之前的裝置中,只支援 {@link android.support.v7.app.ActionBar} 模式,但在執行 M 預覽版的裝置中,允許架構支援 -{@link android.support.v7.app.ActionBar} 或浮動工具列模式。 -</p> - -<h2 id="behavior-keystore">Android 金鑰存放區變更</h2> -<p>使用這個預覽版時,<a href="{@docRoot}training/articles/keystore.html">Android 金鑰存放區供應程式</a>不再支援 DSA。 - -但仍支援 ECDSA。</p> - -<p>在停用或重設安全鎖定螢幕時 (例如,由使用者或裝置管理員執行),將不再刪除其餘不需加密的金鑰。 -在這些事件期間,將會刪除其餘需要加密的金鑰。 -</p> - -<h2 id="behavior-network">Wi-Fi 和網路變更</h2> - -<p>這個預覽版引進了下列對於 Wi-Fi 和網路 API 的行為變更。</p> -<ul> -<li>唯有當您建立了 {@link android.net.wifi.WifiConfiguration} 物件時,您的應用程式現在才能變更這些物件的狀態。 -系統不容許您修改或刪除由使用者或其他應用程式所建立的 -{@link android.net.wifi.WifiConfiguration} 物件。 -</li> -<li> -在以前,如果應用程式使用 -{@link android.net.wifi.WifiManager#enableNetwork(int,boolean) enableNetwork()} 搭配 -{@code disableAllOthers=true} 設定來強制裝置連接到特定的 Wi-Fi 網路,裝置即會中斷與其他網路的連線,例如行動數據。 -在這個預覽版中,裝置不再中斷與這類其他網路的連線。如果您應用程式的 {@code targetSdkVersion} 是 {@code “20”} 或更低版本,即會將它固定到選取的 Wi-Fi 網路。 - -如果您應用程式的 {@code targetSdkVersion} 是 {@code “21”} 或更高版本,請使用多網路 API (例如, -{@link android.net.Network#openConnection(java.net.URL) openConnection()}、 -{@link android.net.Network#bindSocket(java.net.Socket) bindSocket()} 及新的 -{@code ConnectivityManager.bindProcessToNetwork()} 方法),以確保會在選取的網路上傳送它的網路流量。 - -</li> -</ul> - -<h2 id="behavior-camera">相機服務變更</h2> -<p>在這個預覽版中,在相機服務中存取分享資源的模型已經從先前的「先進先服務」存取模型變更為依照優先順序針對處理程序進行處理的存取模型。 - -對於服務行為的變更如下:</p> -<ul> -<li>存取相機子系統資源 (包括開啟和設定相機裝置) 的權限是根據用戶端應用程式處理程序的「優先順序」來授與。 -通常會為具有使用者可看見或前景活動的應用程式處理程序提供較高的優先順序,讓相機資源的取得和使用更可靠。 - -</li> -<li>優先順序較低之應用程式的使用中相機用戶端可能會在優先順序較高的應用程式嘗試使用相機時被系統「撤出」。 -在已過時的 {@link android.hardware.Camera} API 中,這會導致針對被撤出的用戶端呼叫 -{@link android.hardware.Camera.ErrorCallback#onError(int,android.hardware.Camera) onError()}。 - -在 {@link android.hardware.camera2 Camera2} API 中,會導致針對被撤出的用戶端呼叫 -{@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected(android.hardware.camera2.CameraDevice) onDisconnected()} -。</li> -<li>在配備適當相機硬體的裝置上,個別的應用程式處理程序能夠單獨開啟,同時使用不同的相機裝置。 -但是,相機服務現在可以偵測到且不允許多處理程序使用案例,同時存取會對任何已開啟的相機裝置造成顯著的效能或功能降級。 - -此變更可能會導致優先順序較低的用戶端被「撤出」,即使沒有任何其他應用程式正直接嘗試存取同一個相機裝置也一樣。 - - -</li> -<li> -變更目前的使用者會導致先前的使用者帳戶所擁有之應用程式的使用中相機用戶端被撤出。 -相機的存取權受限於目前裝置使用者所擁有的使用者設定檔。例如,這實際上表示「訪客」帳戶在使用者切換到不同帳戶之後,將無法保留使用相機子系統的執行中處理程序。 - - -</li> -</ul> - -<h2 id="behavior-art-runtime">ART 執行階段</h2> -<p>ART 執行階段現在可以正確實作 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法的存取規則。這個變更會修正 Dalvik 在先前版本中以不正確方式檢查存取規則的問題。如果您的應用程式使用 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法且您想要覆寫存取檢查,請搭配已設定為 {@code true} 的輸入參數呼叫 -{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()} 方法。 - - - -如果您的應用程式使用 <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7 appcompat 程式庫</a>或 <a href="{@docRoot}tools/support-library/features.html#v7-recyclerview">v7 recyclerview 程式庫</a>,您就必須更新應用程式來使用這些程式庫的最新版本。 - - -否則,請確定從 XML 參考的所有自訂類別都會更新,如此一來就能存取其類別建構函式。 -</p> - -<p>這個預覽版會更新動態連結器的行為。動態連結器現在瞭解程式庫的 {@code soname} 與其路徑 (<a href="https://code.google.com/p/android/issues/detail?id=6670" class="external-link">公開的 Bug 6670</a>) 之間的差異,而且現在會實作依 {@code soname} 進行搜尋。 - - - -先前可運作但含有錯誤 {@code DT_NEEDED} 項目 (通常是組建電腦之檔案系統上的絕對路徑) 的應用程式可能會在載入時失敗。 -</p> - -<p>{@code dlopen(3) RTLD_LOCAL} 旗標現在會以正確的方式實作。請注意, -{@code RTLD_LOCAL} 是預設值,因此,對 {@code dlopen(3)} 的呼叫 (不會明確使用 -{@code RTLD_LOCAL}) 將會受到影響 (除非您的應用程式明確使用 {@code RTLD_GLOBAL})。使用 -{@code RTLD_LOCAL},由後續呼叫 -{@code dlopen(3)} (相對於 {@code DT_NEEDED} 項目所參考) 所載入的程式庫將無法使用符號。</p> -</p> - -<h2 id="behavior-apk-validation">APK 驗證</h2> -<p>此平台現在會執行較嚴格的 APK 驗證。如果檔案宣告於宣示說明中但未出現在 APK 本身中,則 APK 會被視為毀損。 -如果移除了任何內容,就必須重新簽署 APK。 -</p> - -<h2 id="behavior-afw">Android for Work 變更</h2> -<p>這個預覽版包含下列對於 Android for Work 的行為變更:</p> -<ul> -<li><strong>個人內容中的工作聯絡人。</strong>Google 撥號程式通話記錄現在會在使用者檢視過去的通話記錄時顯示工作聯絡人。將 {@code DevicePolicyManager.setCrossProfileCallerIdDisabled()} 設定為 {@code true},可以在 Google 撥號程式通話記錄中隱藏工作設定檔聯絡人。 - - -只有在您將 {@code DevicePolicyManager.setBluetoothContactSharingDisabled()} 設定為 {@code false} 時,工作聯絡人才會透過藍牙,與個人聯絡人一起顯示於裝置上。 - -預設會設定為 {@code true}。 - -</li> -<li><strong>移除 WiFi 設定:</strong>如果將工作設定檔刪除,則現在會移除由設定檔擁有者所新增的 WiFi 設定 (例如,透過呼叫 -{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration) -addNetwork()} 方法)。 -</li> -<li><strong>鎖定 WiFi 設定:</strong>使用者無法再修改或刪除任何由使用中裝置擁有者所建立的 WiFi 設定。 -只要尚未針對使用者設定 {@link android.os.UserManager} 常數 -{@link android.os.UserManager#DISALLOW_CONFIG_WIFI},該使用者就仍能建立和修改他們自己的 WiFi 設定。 -</li> -<li><strong>透過 Google 帳戶新增來下載工作原則控制器:</strong>在將要求透過工作原則控制器 (WPC) 應用程式進行管理的 Google 帳戶新增到受管理內容以外的裝置時,新增帳戶流程現在會提示使用者安裝適當的 WPC。這個行為也適用於在初始裝置設定精靈中透過 [設定] > [帳戶]<strong></strong> 來新增的帳戶。 - - - -</li> -<li><strong>對於特定 DevicePolicyManager API 行為的變更:</strong>呼叫 {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName,boolean) setCameraDisabled()} -方法,只會對正在呼叫之使用者的相機產生影響;從受管理的設定檔呼叫它則不會對在主要使用者上執行的相機應用程式產生影響。 - -此外,除了裝置擁有者, -{@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,int) setKeyguardDisabledFeatures()} -方法現在還可供設定檔擁有者使用。設定檔擁有者可以設定下列滑動解鎖限制: - -<ul> -<li>{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} 和 -{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_FINGERPRINT},它們會對設定檔上層使用者的滑動解鎖設定產生影響。 -</li> -<li>{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS},這只會影響受管理設定檔中由應用程式所產生的通知。 -</li> -</ul> -</li> -</ul> diff --git a/docs/html-intl/intl/zh-tw/preview/download.jd b/docs/html-intl/intl/zh-tw/preview/download.jd deleted file mode 100644 index 163af2c97aa4..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/download.jd +++ /dev/null @@ -1,360 +0,0 @@ -page.title=下載 -page.image=images/cards/card-download_16-9_2x.png - -@jd:body - -<div style="position:relative; min-height:600px"> - - <div class="wrap" id="tos" style="position:absolute;display:none;width:inherit;"> - - <p class="sdk-terms-intro">在下載和安裝 Android 預覽版 -SDK 的元件之前 ,您必須同意遵守下列條款和條件。</p> - - <h2 class="norule">條款和條件</h2> - - <div class="sdk-terms" onfocus="this.blur()" style="width:678px"> -這是「Android SDK 預覽版授權協議」(以下稱「授權協議」)。 - -1.簡介 - -1.1「Android SDK 預覽版」(在「授權協議」中稱為「預覽版」,包括 (如果有可用的) Android 系統檔案、經過封裝的 API 和預覽版程式庫檔案) 是在「授權協議」之條款的約束下授權給您使用。「授權協議」就您對「預覽版」的使用,構成您與 Google 間具法律約束力之合約。 - -1.2「Android」係指「Android 軟體開放原始碼專案」(http://source.android.com/) 所提供的 Android 裝置軟體堆疊 (不定期更新)。 - -1.3「Google」係指 Google Inc.,是一家在美國德拉瓦州註冊的公司,地址為 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States。 - -2.接受「授權協議」 - -2.1 必須先同意遵守「授權協議」,才能使用此「預覽版」。如果不接受「授權協議」,您就無法使用此「預覽版」。 - -2.2 按一下 [接受] 且/或使用「預覽版」,即表示您同意「授權協議」的條款。 - -2.3 如果您是美國或其他國家/地區 (包括您所居住或使用此「預覽版」的國家/地區) 的法律所禁止接收此「預覽版」的人員,就不得使用此「預覽版」及接受「授權協議」。 - -2.4 如果您將在公司或組織內部使用「預覽版」,您就要代表雇主或其他實體同意受「授權協議」約束,且您代表並保證具備完整法定權限來約束您的雇主或這類實體遵守「授權協議」。如果您不具備必要的權限,就不得代表您的雇主或其他實體接受「授權協議」或使用此「預覽版」。 - -3.Google 的預覽版授權 - -3.1 在「授權協議」之條款的約束下,Google 授權您使用此「預覽版」,此授權為買斷式、不可轉讓、非獨占性、不可轉授權、有限且可撤銷,僅在您公司或組織內部私下或內部使用。此「預覽版」僅供您用於開發在 Android 平台上執行的應用程式。 - -3.2 您同意 Google 或第三方對此「預覽版」擁有一切法定權利及權益,包括存在於此「預覽版」中的任何「智慧財產權」。「智慧財產權」係指專利法、著作權法、商業秘密法、商標法及任何和所有其他專利權下的任何及一切權利。Google 保留一切未明確授予您的權利。 - -3.3 您不得將此「預覽版」用於「授權協議」未明文許可的任何用途。除非適用的第三方授權所需,否則您不得:(a) 對此「預覽版」或其任何部分進行複製 (備份用途除外)、修改、改編、轉散佈、反向組譯、還原工程、解編或製作衍生成品;或是 (b) 將此「預覽版」的任何部分載入至行動電話或個人電腦以外的任何其他硬體裝置、將此「預覽版」的任何部分與其他軟體結合,或散佈包含此「預覽版」之任一部分的任何軟體或裝置。 - -3.4 您同意不會從事任何可能導致或造成 Android 分裂的活動,包括但不限於以任何形式散佈從此「預覽版」衍生的軟體開發套件、參其製作或宣傳。 - -3.5 對開放原始碼軟體授權下所授權之「預覽版」的使用、複製及散佈,完全受該開放原始碼軟體授權的條款管制,而不受「授權協議」管制。您同意遵守從這類開放原始碼軟體授權獲得的所有權利,並且避免採取任何可能終止、中止或侵害這類權利的行為。 - -3.6 您同意 Google 可在不事先通知您的情況下變更其所提供之「預覽版」的形式和本質,且此「預覽版」的未來版本可與在此「預覽版」的先前版本上開發的應用程式不相容。您同意 Google 通常可全權斟酌永久或暫時停止提供此「預覽版」(或此「預覽版」的任何功能) 給您或使用者,毋須事先通知。 - -3.7「授權協議」中的所有條款皆未授予您任何使用 Google 之商業名稱、商標、服務標章、標誌、網域名稱或其他明確品牌特徵的權利。 - -3.8 您同意不會移除、遮蔽或更改可能附加至或內含在此「預覽版」中的任何專利權通知 (包括著作權和商標通知)。 - -4.您對「預覽版」的使用 - -4.1 Google 同意在「授權協議」下,任何條款皆未從您 (或您的授權人) 賦予 Google 對您使用此「預覽版」開發之任何軟體應用程式的任何權利及權益,包括存在於這些應用程式中的任何智慧財產權。 - -4.2 您同意只就 (a)「授權協議」和 (b) 相關管轄權中任何適用法律、規定或是普遍獲得接受之慣例或指導方針 (包括任何有關將資料或軟體輸出或輸入美國或其他相關國家/地區的法律) 所允許的用途使用此「預覽版」及撰寫應用程式。 - -4.3 您同意如果使用此「預覽版」開發應用程式,您將保護使用者的隱私權和法定權利。如果使用者提供您使用者名稱、密碼或是其他登入資訊或個人資訊,您必須告知使用者這類資訊將提供給您的應用程式使用,並且必須為這些使用者提供法定充分的隱私權通知和保護。如果您的應用程式會儲存使用者所提供的個人或敏感資訊,它必須確保這些資訊安全無虞。如果使用者提供 Google 帳戶資訊給您,則只有在每個使用者已授權您存取其 Google 帳戶並僅限用於使用者所授權之用途的情況下,您的應用程式才能使用該資訊來存取使用者的 Google 帳戶。 - -4.4 您同意不會使用此「預覽版」從事任何不當Activity,例如開發或散佈會以未經授權的方式干擾、妨礙、損害或存取 Google 或任何第三方之伺服器、網路或是其他財產或服務的應用程式。 - -4.5 您同意對您透過 Android 裝置和 (或) Android 應用程式建立、傳輸或顯示的任何資料、內容或資源,以及上述行為造成的後果 (包括 Google 可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.6 您同意對違反在此「授權協議」、任何適用之第三方合約或《服務條款》或是任何適用之法律或規定下所必須遵守的義務,以及違反相關義務造成的後果 (包括 Google 或任何第三方可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.7「預覽版」目前正在開發中,因此您的測試與意見反應對開發程序非常重要。使用「預覽版」,您即認同某些功能仍處於開發階段,因此您不應期待「預覽版」擁有穩定版本的完整功能。在官方 Android SDK 發行之後,此「預覽版」不再受到支援時,您同意不使用此「預覽版」公開散佈或隨附任何應用程式。 - -5.您的開發人員認證 - -5.1 您同意負責保密 Google 可能核發給您或您自己選擇的任何開發人員認證,並且對在您開發人員認證名義下開發的所有應用程式負起全責。 - -6.隱私權和資訊 - -6.1 為了持續更新及改進此「預覽版」,Google 可能會從軟體收集某些使用狀況統計數據,包括但不限於軟體的唯一識別碼、相關 IP 位址、版本號碼,以及有關使用此「預覽版」中的哪些工具和 (或) 服務及其使用方式的相關資訊。在收集這類資訊之前,此「預覽版」會先通知您並徵求您的同意。如果您不同意,Google 將不會收集這類資訊。 - -6.2 Google 會彙總並檢查收集到的資料,據以改善此「預覽版」,並且會依據《Google 隱私權政策》(http://www.google.com/policies/privacy/) 加以妥善保存。 - -7.第三方應用程式 - -7.1 如果您使用此「預覽版」來執行第三方開發的應用程式,或是執行會存取第三方所提供之資料、內容或資源的應用程式,您同意 Google 不需對這類應用程式、資料、內容或資源負任何責任。您瞭解您透過第三方應用程式存取的所有資料、內容或資源是由其提供者負起全責,而 Google 對您因使用或存取任何這些第三方應用程式、資料、內容或資源所造成的損失或損害不需負任何責任。 - -7.2 您瞭解第三方應用程式提供給您的資料、內容或資源可能受到提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您不得根據這類資料、內容或資源 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品,除非相關擁有者明確授權您從事上述活動。 - -7.3 您瞭解使用第三方應用程式、資料、內容或資源可能受到您與相關第三方之間的個別條款約束。 - -8.使用 Google API - -8.1 Google API - -8.1.1 如果您使用任何 API 從 Google 擷取資料,您瞭解這些資料可能受到 Google 或資料提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您對任何這類 API 的使用可能受到其他《服務條款》約束。除非相關《服務條款》明文允許,否則您不得根據這類資料 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品。 - -8.1.2 使用任何 API 從 Google 擷取使用者的資料時,您瞭解並同意只有在該使用者明確同意且授權您擷取其資料,而且僅限用於使用者所授權之用途的情況下,您才能擷取資料。 - -9.終止「授權協議」 - -9.1 除非您或 Google 終止「授權協議」(請見下方說明),否則「授權協議」將持續具有效力。 - -9.2 如果想終止「授權協議」,只要停止使用此「預覽版」及任何相關的開發人員憑證即可。 - -9.3 Google 可隨時通知您終止「授權協議」,無論有無原因。 - -9.4「授權協議」在先發生下列任一情況時,將自動終止而不另行通知或採取其他行動: -(A) Google 決定不再提供此「預覽版」或此「預覽版」的特定部分給您所居住或使用此服務之國家/地區的使用者;或 -(B) Google 發行最終版本的 Android SDK。 - -9.5 當「授權條款」終止時,您在「授權協議」所獲得的授權也將會一併終止,您將立即停止「預覽版」的所有使用,而第 10、11、12 和 14 項的條款將無限期持續適用。 - -10.免責聲明 - -10.1 您明確瞭解並同意完全自負使用此「預覽版」的風險,並且此「預覽版」是依「現況」和「可提供性」提供,Google 不負任何擔保責任。 - -10.2 您對使用此「預覽版」及透過此「預覽版」以下載或其他方式取得的任何內容,需自行斟酌和自負風險,而且您對因這類使用而對您的電腦系統或其他裝置所造成的任何損害或資料遺失,需負起全責。不限於前述,您瞭解此「預覽版」不是穩定的版本,可能會包含許多錯誤、瑕疵和安全性弱點而對您的電腦系統或其他裝置造成嚴重的損害,包括完全、不可回復的損失。 - -10.3 Google 進一步明確表示不提供任何形式的瑕疵擔保和條件 (不論明示或默示),包括但不限於適售性、適合特定用途及未侵權的默示擔保和條件。 - -11.責任限制 - -11.1 您明確瞭解並同意在任何歸責理論下,就可能由您引起的任何直接、間接、附隨性、特殊性、衍生性或懲罰性損害賠償 (包括任何資料遺失),不論 Google 或其代表是否已獲告知或應已瞭解發生任何這類損失的可能性,Google、其子公司和關係企業及其授權人不必對您負起任何責任。 - -12.賠償 - -12.1 在法律允許的最大範圍內,您同意為 Google、其子公司及其個別董監事、主管、員工和代理人,就任何和一切索賠、法律行動、訴訟或訴訟程序,以及因下列原因而引起的任何和一切損失、責任、損害賠償、費用及開支 (包括合理的律師費),提供辯護、賠償損失並確保其免於承擔賠償責任:(a) 您使用此「預覽版」;(b) 您使用此「預覽版」開發的應用程式侵害任何人的任何智慧財產權,或是詆毀任何人或違反其公開權或隱私權;以及 (c) 您未遵守「授權協議」。 - -13.對「授權協議」做出的變更 - -13.1 Google 可在散佈此「預覽版」的新版本時修改「授權協議」。做出這類變更後,Google 會在提供此「預覽版」的網站上提供「授權協議」的新版本。 - -14.一般法律條款 - -14.1「授權協議」構成您與 Google 之間的法律協議,用於管制您對此「預覽版」(不包括 Google 依據個別書面協議提供給您的任何服務) 的使用,並完全取代先前您與 Google 之間就此「預覽版」簽署的相關協議。 - -14.2 您同意如果 Google 未行使或執行「授權協議」所含的任何法律權利或救濟 (或在任何適用法律下對 Google 有利的權益),並不代表 Google 正式放棄權利,Google 日後仍可行使這些權利或救濟。 - -14.3 如果經任何法院 (就此事宜依管轄權決定) 裁決「授權協議」中有任何條款無效,則該條款將自「授權協議」中移除,「授權協議」的其餘部分則不受影響。「授權協議」的其餘條款將持續具有效力且可執行。 - -14.4 您瞭解並同意 Google 旗下子公司體系的每位成員都是「授權協議」的第三方受益人,而且這類其他公司有權直接執行和依據「授權協議」中對其授予權益 (或對其有利之權利) 的任何條款。除此之外的任何其他人員或公司皆非「授權協議」的第三方受益人。 - -14.5 出口限制。此「預覽版」受美國出口法規約束。您必須遵守適用於此「預覽版」的所有國內和國際出口法規。這些法律包括對目的地、使用者及最終用途的限制。 - -14.6 未事先取得 Google 事先書面核准的情況下,您不得轉讓或轉移「授權協議」,未經這類核准的任何轉讓將會失效。您不得在未事先取得 Google 書面核准的情況下,委派其「授權協議」涵蓋的責任或義務。 - -14.7「授權協議」以及您與 Google 就「授權協議」構成的關係皆受加州法律管轄,毋須理會其法律牴觸條款。您和 Google 同意服從位於加州聖塔克拉拉 (Santa Clara, California) 郡內法院的專屬管轄權,以解決由「授權協議」產生的任何法律事務。儘管如此,您同意 Google 仍可在任何管轄權中申請禁制令救濟 (或同等類型的緊急法定救濟)。 - </div><!-- sdk terms --> - - - - <div id="sdk-terms-form"> - <p> - <input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" /> - <label id="agreeLabel" for="agree">我已閱讀並同意上述條款及細則</label> - </p> - <p><a href="" class="button disabled" id="downloadForRealz" onclick="return onDownloadForRealz(this);"></a></p> - </div> - - - </div><!-- end TOS --> - - - <div id="landing"> - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#sdk">Android 6.0 SDK</a></li> - <li><a href="#docs">開發人員文件</a></li> - <li><a href="#images">硬體系統映像</a></li> - </ol> - <h2>Legacy downloads</h2> - <ol> - <li><a href="{@docRoot}preview/download_mp1.html">Developer Preview 1</a></li> - <li><a href="{@docRoot}preview/download_mp2.html">Developer Preview 2</a></li> - </ol> - </div> -</div> - - -<p> - Android M 預覽版 SDK 有開發工具、Android 系統檔案以及程式庫檔案,可以幫助測試您的應用程式和下一個平台版本隨附的新 API。 -本文件會說明如何取得可下載的預覽版元件來測試您的應用程式。 - -</p> - - -<h2 id="sdk">Android 6.0 SDK</h2> - -<p> - 預覽版 SDK 可透過 <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK 管理器</a>下載取得。如需有關下載和設定預覽版 SDK 的詳細資訊,請參閱<a href="{@docRoot}preview/setup-sdk.html#downloadSdk">設定預覽版 SDK</a>。 - -</p> - - -<h2 id="docs">開發人員文件</h2> - -<p> - 開發人員文件下載套件提供詳細的 API 參考資料和預覽版的 API 差異報告。 -</p> - -<table> - <tr> - <th scope="col">Description</th> - <th scope="col">Download / Checksums</th> - </tr> - <tr id="docs-dl"> - <td>Android M Preview 3<br>Developer Docs</td> - <td><a href="#top" onclick="onDownload(this)" - >m-preview-3-developer-docs.zip</a><br> - MD5: d99b14b0c06d31c8dfecb25072654ca3<br> - SHA-1: 9cefeeda07676130da606a1796e1c00fffc667c1 - </td> - </tr> -</table> - - -<h2 id="images">硬體系統映像</h2> - -<p> - 這些系統映像可以讓您在實體裝置上安裝預覽版的平台來進行測試。 -使用其中一個映像設定裝置,您就可以安裝並測試您的應用程式,瞭解應用程式在下一個版本的平台上表現如何。 -在裝置上安裝系統映像的過程中,會「移除裝置當中所有的資料」,因此您應該在安裝系統映像之前備份您的資料。<em></em> - - -</p> - -<p class="warning"> - <b>警告:</b>下列 Android 系統映像是預覽版,可能隨時會有變更。使用這些系統映像受到「Android SDK 預覽版授權協議」的約束。 -Android 預覽版系統映像還不是穩定的版本,可能會包含許多錯誤和瑕疵而對您的電腦系統、裝置和資料造成損害。 - -預覽版的 Android 系統映像與出廠作業系統的測試不同,可能會導致您的手機和安裝的服務與應用程式停止運作。 - - -</p> - -<table> - <tr> - <th scope="col">Device</th> - <th scope="col">Download / Checksums</th> - </tr> - <tr id="hammerhead"> - <td>Nexus 5 (GSM/LTE) <br>"hammerhead"</td> - <td><a href="#top" onclick="onDownload(this)" - >hammerhead-MPA44I-preview-2ebbc049.tgz</a><br> - MD5: 91a924fb0c9f8e716e3b4c9954fd0dbb<br> - SHA-1: 2ebbc049b68c4da8baeee3e42bb94d7a965ba4a3 - </td> - </tr> - <tr id="shamu"> - <td>Nexus 6 <br>"shamu"</td> - <td><a href="#top" onclick="onDownload(this)" - >shamu-MPA44I-preview-62b9c486.tgz</a><br> - MD5: ac6e58da86125073d9c395257fd42664<br> - SHA-1: 62b9c486fd7a5020e228d53ca5acd5c1857e48ff - </td> - </tr> - <tr id="volantis"> - <td>Nexus 9 <br>"volantis"</td> - <td><a href="#top" onclick="onDownload(this)" - >volantis-MPA44I-preview-5c30a6e2.tgz</a><br> - MD5: 7f83768757913d3fea945a661020d185<br> - SHA-1: 5c30a6e2acd11a81f4105b12d23ff654f534f699 - </td> - </tr> - - <tr id="fugu"> - <td>Nexus Player <br>"fugu"</td> - <td><a href="#top" onclick="onDownload(this)" - >fugu-MPA44I-preview-2860040a.tgz</a><br> - MD5: 438da8d37da9e341a69cfb16a4001ac5<br> - SHA-1: 2860040a326582f1ff5f702bf9a1ef002717fc98 - </td> - </tr> - -</table> - -<h3 id="install-image">在裝置上安裝映像</h3> - -<p> - 如果要使用裝置映像進行測試,您必須先在相容的裝置上安裝映像。請依照下面的指示安裝系統映像: - -</p> - -<ol> - <li>下載此處列出的其中一個系統映像,然後解壓縮。</li> - <li>備份您要保留的所有裝置資料。</li> - <li>依照 -<a href="https://developers.google.com/android/nexus/images#instructions">developers.google.com/android</a> -的指示,將映像更新到您的裝置。</li> -</ol> - -<p class="note"> - <strong>注意:</strong>在您使用預覽版系統映像更新開發裝置之後,裝置就會透過無線 (OTA) 更新方式自動升級為下一個預覽版版本。 - -</p> - -<h3 id="revertDevice">將裝置還原成出廠規格</h3> - -<p> - 如果您要解除安裝預覽版並將裝置還原成出廠規格,請至 -<a href="http://developers.google.com/android/nexus/images">developers.google.com/android</a> 並下載您要為裝置更新的映像。 -依照該頁面的指示,將映像更新到您的裝置。 - -</p> - - </div><!-- landing --> - -</div><!-- relative wrapper --> - - - -<script> - var urlRoot = "http://storage.googleapis.com/androiddevelopers/shareables/preview/"; - function onDownload(link) { - - $("#downloadForRealz").html("Download " + $(link).text()); - $("#downloadForRealz").attr('href', urlRoot + $(link).text()); - - $("#tos").fadeIn('fast'); - $("#landing").fadeOut('fast'); - - return true; - } - - - function onAgreeChecked() { - /* verify that the TOS is agreed */ - if ($("input#agree").is(":checked")) { - /* reveal the download button */ - $("a#downloadForRealz").removeClass('disabled'); - } else { - $("a#downloadForRealz").addClass('disabled'); - } - } - - function onDownloadForRealz(link) { - if ($("input#agree").is(':checked')) { - /* - $("#tos").fadeOut('fast'); - $("#landing").fadeIn('fast'); - */ - - ga('send', 'event', 'M Preview', 'System Image', $("#downloadForRealz").html()); - - /* - location.hash = ""; - */ - return true; - } else { - return false; - } - } - - $(window).hashchange( function(){ - if (location.hash == "") { - location.reload(); - } - }); - -</script> diff --git a/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd b/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd deleted file mode 100644 index 5be8a0f12c01..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd +++ /dev/null @@ -1,123 +0,0 @@ -page.title=應用程式連結 -page.image=images/cards/card-app-linking_2x.png -page.keywords=應用程式連結, 深層連結, 意圖 -@jd:body - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#web-assoc">宣告網站關聯</a></li> - <li><a href="#verfy-links">要求應用程式連結驗證</a></li> - <li><a href="#user-manage">管理應用程式連結設定</a></li> - </ol> - </div> -</div> - -<p> - Android 意圖系統是一個彈性機制,讓應用程式能夠用來處理內容和要求。 - 有許多應用程式可能會在它們的意圖篩選條件中宣告相符的 URI 模式。當使用者按一下不含預設啟動處理常式的 Web 連結時,平台可能會顯示一個對話方塊,讓使用者能夠從具備已宣告相符意圖篩選條件的應用程式清單中選取。 - - -</p> - -<p> - Android M 開發人員預覽版引進了應用程式連結的支援,這樣就能藉由允許應用程式開發人員將應用程式關聯至他們所擁有的 Web 網域,來改善現有的連結處理。 -當開發人員建立這個關聯時,平台可以自動判斷要用來處理特定 Web 連結的預設應用程式,並略過詢問使用者的程序。 - - -</p> - - -<h2 id="web-assoc">宣告網站關聯</h2> - -<p> - 網站擁有者必須宣告與應用程式的關聯,才能建立應用程式連結。站台擁有者可藉由在網域上的已知位置裝載名為 {@code statements.json} 的 JSON 檔案,來宣告與應用程式的關係: - - -</p> - -<pre>http://<domain>:<optional port>/.well-known/statements.json</pre> - -<p class="note"> - <strong>注意:</strong> - 在 M 開發人員預覽版期間,會透過 http 通訊協定來驗證 JSON 檔案。如果是平台的正式版本,就會透過加密的 https 通訊協定來驗證該檔案。 - -</p> - -<p> - 這個 JSON 檔案會指出應該在這個網域中用來做為 URL 預設處理常式的 Android 應用程式。 -它會根據下列欄位來識別應用程式: -</p> - -<ul> - <li>{@code package_name}:宣告於應用程式宣示說明中的套件名稱。</li> - - <li>{@code sha256_cert_fingerprints}:您應用程式簽署憑證的 SHA256 指紋。 - 您可以使用 Java 金鑰工具,利用下列命令來產生指紋: - <pre>keytool -list -v -keystore my-release-key.keystore</pre> - </li> -</ul> - -<p> - 下列檔案清單會顯示 -{@code statements.json} 檔案的內容與格式範例: -</p> - -<pre> -[{ - "relation": ["delegate_permission/common.handle_all_urls"], - "target": { - "namespace": "android_app", - "package_name": "<strong><package name></strong>", - "sha256_cert_fingerprints": ["<strong>6C:EC:C5:0E:34:AE....EB:0C:9B</strong>"] - } -}] -</pre> - - -<h2 id="verfy-links">要求應用程式連結驗證</h2> - -<p> - 應用程式可以要求平台根據裝載於個別 Web 網域上的 {@code statements.json} 檔案,自動驗證在其意圖篩選條件的資料元素中由主機名稱所定義的任何應用程式連結。 - -如要要求應用程式連結驗證,請將 {@code android:autoVerify} - 屬性新增到宣示說明中每個所需的意圖篩選條件中,如下列宣示說明程式碼片段所示: - -</p> - -<pre> -<activity ...> - <intent-filter <strong>android:autoVerify="true"</strong>> - <action android:name="android.intent.action.VIEW" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.BROWSABLE" /> - <data android:scheme="http" android:host="www.android.com" /> - <data android:scheme="https" android:host="www.android.com" /> - </intent-filter> -</activity> -</pre> - -<p> - 當 {@code android:autoVerify} 屬性出現在應用程式宣示說明時,平台即會在安裝該應用程式時嘗試驗證應用程式連結。 -如果平台無法成功驗證應用程式連結,就無法將該應用程式設定為偏好使用的應用程式來處理 Web 連結。 -當使用者下次開啟其中一個連結時,平台就切換回為該使用者展示一個對話方塊。 - - -</p> - -<p class="note"> - <strong>注意:</strong>在測試期間,如果驗證失敗,可能會產生誤判,但是使用者已經使用系統「設定」應用程式,明確地啟用應用程式來自動開啟支援的連結。在此案例中,不會顯示任何對話方塊,而且連結會直接連至您的應用程式,但這只是基於使用者的設定,而不是因為驗證成功所致。 - - - -</p> - - -<h2 id="user-manage">管理應用程式連結設定</h2> - -<p> - 使用者可以變更應用程式連結設定,讓他們能夠以偏好使用的方式來處理 URL。您可以在系統「設定」應用程式中,於 [設定] > [應用程式] > [應用程式資訊] > [預設開啟]<strong></strong> 下方,檢閱和管理應用程式連結。 - - -</p> diff --git a/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd b/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd deleted file mode 100644 index cf756aa5b358..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd +++ /dev/null @@ -1,794 +0,0 @@ -page.title=權限 -page.tags=previewresources, androidm -page.keywords=權限, 執行階段, 預覽 -page.image={@docRoot}preview/features/images/permissions_check.png -@jd:body - - -<div id="qv-wrapper"> - <div id="qv"> - <h2>快速檢視</h2> - <ul> - <li>如果您的應用程式是以 M 預覽版 SDK 為目標,它會提示使用者在執行階段授與權限,而不是安裝期間。 -</li> - <li>使用者能隨時從應用程式 [設定] 畫面撤銷權限。 -</li> - <li>您的應用程式每次執行時都需要檢查它是否有所需的權限。 -</li> - </ul> - - <h2>本文件內容</h2> - <ol> - <li><a href="#overview">總覽</a></li> - <li><a href="#coding">編寫執行階段權限的程式碼</a></li> - <li><a href="#testing">測試執行階段權限</a></li> - <li><a href="#best-practices">建議做法</a></li> - </ol> - -<!-- - <h2>Related Samples</h2> - <ol> - <li></li> - </ol> ---> - -<!-- - <h2>See also</h2> - <ol> - <li></li> - </ol> ---> - </div> <!-- qv --> -</div> <!-- qv-wrapper --> - - -<p> - M 開發人員預覽版導入新的應用程式權限模型,簡化使用者安裝和升級應用程式的程序。 -如果 M 預覽版上執行的應用程式支援新的權限模型,使用者安裝或升級應用程式時,不需要授與任何權限。應用程式會在需要時才要求權限,而且系統會對使用者顯示要求權限的對話方塊。 - - - - -</p> - -<p> - 如果應用程式支援新的權限模型,它仍能在在執行舊版 Android 的裝置上安裝並執行 (在那些裝置上使用舊的權限模型)。 - - -</p> - -<h2 id="overview"> - 總覽 -</h2> - -<p> - 使用 M 開發人員預覽版,平台導入新的應用程式權限模型。 -以下是這個新模型的主要元件摘要: -</p> - -<ul> - <li> - <strong>宣告權限:</strong>應用程式會在宣示說明中宣告所需的所有權限,如舊版 Android 平台。 - - </li> - - <li> - <strong>權限群組:</strong>權限會根據其功能分為「權限群組」 -<em></em>。例如, -<code>CONTACTS</code> 權限群組包含讀取和寫入使用者聯絡人與設定檔資訊的權限。 - - </li> - - <li> - <p><strong>安裝期間授與的有限權限:</strong>當使用者安裝或更新應用程式時,系統會將應用程式所要求且歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL} 的所有權限授與應用程式。 - - - 例如,會在安裝期間自動授與歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL} 的鬧鐘與網際網路權限。 - - </p> - - <p>系統也會授與應用程式簽名和系統權限,如<a href="#system-apps">系統應用程式和簽名</a>所述。 - -安裝期間「不」<em></em>會提示使用者授與任何權限。 -</p> - </li> - - <li> - <strong>使用者在執行階段授與權限:</strong>應用程式要求權限時,系統會對使用者顯示對話方塊,接著呼叫應用程式的回呼函數,通知它是否已授與權限。 - -如果使用者授與權限,應用程式會獲得在應用程式宣示說明中所宣告之權限功能區域中的所有權限。 - - - </li> - -</ul> - -<p> - 此權限模型改變應用程式要求權限的功能行為。 -以下是您應遵循以調整此模型的開發做法摘要: - -</p> - -<ul> - - <li> - <strong>一律檢查是否具備權限:</strong>當應用程式需要執行任何需要權限的動作時,都應要先檢查是否具備有該權限。 - -若不具備,即要求獲得授與該權限。 - - </li> - - <li> - <strong>適當處理缺少權限的情況:</strong>如果應用程式未獲授與適當的權限,它應要能完全處理失敗。 - - 例如,若只有新增功能需要該權限,應用程式可以將該功能停用。 -如果應用程式務必要具備該權限才能運作,應用程式可以停用其所有功能,並通知使用者務必要授與該權限。 - - - </li> - - <div class="figure" style="width:220px" id="fig-perms-screen"> - <img src="{@docRoot}preview/features/images/app-permissions-screen_2x.png" srcset="{@docRoot}preview/features/images/app-permissions-screen.png 1x, {@docRoot}preview/features/images/app-permissions-screen_2x.png 2x" alt="" width="220"> - <p class="img-caption"> - <strong>圖 1.</strong>應用程式 [設定] 的權限畫面。 - </p> - </div> - - <li> - <strong>權限可以撤銷:</strong>使用者可以隨時撤銷應用程式的權限。 -如果使用者關閉應用程式的權限,並「不」<em></em>會通知應用程式。 -再次強調,您的應用程式在執行任何受限制的動作之前,應該驗證它是否具備所需的權限。 - - </li> -</ul> - -<p class="note"> - <strong>注意:</strong>如果應用程式是以 M 開發人員預覽版為目標,「務必要」 -<em></em>使用新的權限模型。 -</p> - -<p> - 自 M 開發人員預覽版推出起,並非所有 Google 應用程式都完全實作新的權限模型。 -Google 正透過 M 開發人員預覽版逐漸更新這些應用程式,以適當保留權限切換設定。 - - -</p> - -<p class="note"> - <strong>注意:</strong>如果您的應用程式有自己的 API 介面,務必要先確定呼叫端具備存取該資料的必要權限後,再 Proxy 權限。 - - -</p> - -<h3 id="system-apps"> - 系統應用程式和簽名權限 -</h3> - -<p> - 一般來說,當使用者安裝應用程式時,系統只會將 - {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL - PROTECTION_NORMAL} 授與應用程式。不過,在某些情況下,系統會授與應用程式更多權限: - -</p> - -<ul> - <li>如果應用程式屬於系統映像的一部分,會自動獲授與其宣示說明中列出的所有權限。 - - </li> - - <li>如果應用程式要求宣示說明中歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_SIGNATURE PROTECTION_SIGNATURE} 的權限,並和宣告那些權限的應用程式一樣,以相同的憑證簽署應用程式,系統會在安裝時將那些權限授與要求的應用程式。 - - - - </li> -</ul> - -<p> - 在這兩種情況下,使用者仍能隨時撤銷權限,只要前往系統的 [設定]<strong></strong> 畫面,然後選擇 [應用程式] ><strong></strong> - - <i>app_name</i> > [權限]<strong></strong>。應用程式應持續在執行階段檢查是否具備權限,並在必要時予以要求。 - - -</p> - -<h3 id="compatibility"> - 往後和回溯相容性 -</h3> - -<p> - 如果應用程式不是以 M 開發人員預覽版為目標,即使在 M 預覽版裝置上,應用程式也會持續使用舊的權限模型。 -當使用者安裝應用程式時,系統會要求使用者授與應用程式的宣示說明中列出的所有權現。 - - -</p> - -<p class="note"> - <strong>注意:</strong>在執行 M 開發人員預覽版的裝置上,使用者能從應用程式的設定畫面關閉任何應用程式 (包括舊版應用程式) 的權限。 - -如果使用者關閉舊版應用程式的權限,系統會自動停用適當功能。 -當應用程式嘗試執行需要那項權限的操作時,該操作不一定會造成例外狀況。 - -而可能傳回空的資料集,通知發生錯誤,或展示未預期的行為。 -例如,如果您不具備查詢行事曆的權限,方法會傳回空的資料集。 - -</p> - -<p> - 如果您在並非執行 M 預覽版的裝置上使用新的權限模型來安裝應用程式,系統會將它和任何其他應用程式一視同仁:系統會在安裝期間要求使用者授與所有宣告的權限。 - - - -</p> - -<p class="note"> - <strong>注意:</strong>對於預覽版,您必須將 SDK 最低版本設定為 M 預覽版 SDK,才能以預覽版 SDK 編譯。 -這表示在開發人員預覽版期間,您將無法在舊版平台上測試這類應用程式。 - - -</p> - -<h3 id="perms-vs-intents">權限與意圖比較</h3> - -<p> - 在許多情況下,您可以為應用程式在兩種方法當中擇一來執行工作。 -您可以讓應用程式要求權限以自行執行操作。 -或者,您可以讓應用程式使用意圖,讓其他應用程式來執行工作。 - -</p> - -<p> - 例如,假設您的應用程式需要能夠使用裝置相機拍攝相片。 -您的應用程式能要求 -<code>android.permission.CAMERA</code> 權限,讓應用程式直接存取相機。 -接著,應用程式會使用相機 API 來控制相機並拍攝相片。 -這種方法可讓您的應用程式對攝影處理程序有完整控制權,並讓您將相機 UI 納入應用程式。 - - -</p> - -<p> - 不過,如果您不需要這類控制權,您可以只使用 {@link -android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} 意圖來要求影像。 -當您啟動意圖時,會提示使用者選擇相機應用程式 (如果還沒有預設的相機應用程式),然後該應用程式會拍攝相片。 - -相機應用程式會將相片傳回應用程式的 {@link - android.app.Activity#onActivityResult onActivityResult()} 方法。 -</p> - -<p> - 同樣地,如果您需要撥打電話、存取使用者的聯絡人等等,您都可以建立適當的意圖來執行,或是要求權限,然後直接存取適當的物件。 - -每種方法各有利弊。 - -</p> - -<p> - 如果您使用權限: -</p> - -<ul> - <li>當您執行操作時,應用程式對使用者體驗有完整控制權。 -不過,如此多樣化控制使您必須設計適當的 UI,而增加工作複雜度。 - - </li> - - <li>當您初次執行操作時,會提示使用者授與權限 (僅此一次)。 -之後應用程式可以逕行執行該操作,不需要再與使用者有其他互動。 -不過,如果使用者未授與權限 (或稍後予以撤銷),您的應用程式會完全無法執行該操作。 - - - </li> -</ul> - -<p> - 如果您使用意圖: -</p> - -<ul> - <li>您不必為該操作設計 UI。處理意圖的應用程式會提供 UI。不過,這表示您對使用者體驗沒有控制權。 - -使用者可能會和您不曾見過的應用程式互動。 - - </li> - - <li>如果使用者沒有執行該操作的預設應用程式,系統會提示使用者選擇應用程式。如果使用者未指定預設處理常式,每次執行操作時可能都要完成額外的對話方塊。 - - - - </li> -</ul> - -<h2 id="coding">編寫執行階段權限的程式碼</h2> - -<p> - 如果您的應用程式是以新的 M 開發人員預覽版為目標,請務必使用新的權限模型。 -這表示除了在宣示說明中宣告所需的權限之外,您也必須在執行階段檢查是否具備權限,並在您不具備時要求權限。 - - - -</p> - -<h3 id="enabling"> - 啟用新的權限模型 -</h3> - -<p> - 如要啟用新的 M 開發人員預覽版權限模型,可將應用程式的 -<code>targetSdkVersion</code> 屬性設定為 <code>"MNC"</code>,並將 -<code>compileSdkVersion</code> 設定為 <code>"android-MNC"</code>。這樣做可啟用所有新權限功能。 - -</p> - -<p> - 對於預覽版,您必須將 <code>minSdkVersion</code> 設定為 -<code>"MNC"</code>,才能以預覽版 SDK 編譯。 -</p> - -<h3 id="m-only-perm"> - 指定只限 M 預覽版使用的權限 -</h3> - -<p> - 您可以在宣示說明中使用新的 <code><uses-permission-sdk-m></code> 元素,指出只有在 M 預覽版上才需要某權限。 -如果您以這種方式宣告權限,每當在舊版裝置上安裝應用程式時,系統都不會提示使用者或將權限授與應用程式。藉由使用 <code><uses-permission-sdk-m></code> 元素,當使用者安裝更新時,您不需要強制他們授與權限,就可以將新的權限新增至更新的應用程式版本。 - - - - - - -</p> - -<p> - 如果應用程式在使用 M 開發人員預覽版的裝置上執行, -<code><uses-permission-sdk-m></code> 的運作方式會和 -<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><uses-permission></a></code> 相同。 - 當他們安裝應用程式時,系統不會提示使用者授與任何權限,而應用程式會在需要時才要求權限。 - -</p> - -<h3 id="prompting"> - 提示授與權限 -</h3> - -<p> - 如果您的應用程式使用新的 M 開發人員預覽版權限模型,在執行 M 預覽版的裝置上初次啟動應用程式時,不會要求使用者授與所有權限。 - -您的應用程式會在需要時才要求權限。 -應用程式要求權限時,系統會對使用者顯示對話方塊。 - -</p> - -<p> - 如果您的應用程式在 SDK 22 以下版本的裝置上執行,應用程式會使用舊的權限模型。 -當使用者安裝應用程式時,會提示他們授與應用程式在其宣示說明中要求的所有權限,但標示為 <code><uses-permission-sdk-m></code> 的那些權限除外。 - - -</p> - -<h4 id="check-platform">檢查應用程式執行所在的平台</h4> - -<p> - 只有執行 M 開發人員預覽版的裝置上才支援此權限模型。 -呼叫這些方法之前,應用程式應該檢查 {@link android.os.Build.VERSION#CODENAME - Build.VERSION.CODENAME} 的值,驗證它執行所在的平台。 - -如果裝置是執行 M 開發人員預覽版, -{@link android.os.Build.VERSION#CODENAME CODENAME} 是 <code>"MNC"</code>。 -</p> - -<h4 id="check-for-permission">檢查應用程式是否具備所需權限</h4> - -<p>當使用者嘗試執行需要權限的操作時,應用程式會檢查它目前是否具備可執行此操作的權限。 -如要這麼做,應用程式可呼叫 - -<code>Context.checkSelfPermission(<i>permission_name</i>)</code>。由於使用者可以隨時撤銷應用程式的權限,即使應用程式知道使用者已授與該權限,還是應該執行此檢查。 - - -例如,如果使用者想要使用應用程式拍攝相片,應用程式會呼叫 <code>Context.checkSelfPermission(Manifest.permission.CAMERA)</code>。 - -</p> - -<p class="table-caption" id="permission-groups"> - <strong>表 1.</strong>權限和權限群組。</p> -<table> - <tr> - <th scope="col">權限群組</th> - <th scope="col">權限</th> - </tr> - - <tr> - <td><code>android.permission-group.CALENDAR</code></td> - <td> - <ul> - <li> - <code>android.permission.READ_CALENDAR</code> - </li> - </ul> - <ul> - <li> - <code>android.permission.WRITE_CALENDAR</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.CAMERA</code></td> - <td> - <ul> - <li> - <code>android.permission.CAMERA</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.CONTACTS</code></td> - <td> - <ul> - <li> - <code>android.permission.READ_CONTACTS</code> - </li> - <li> - <code>android.permission.WRITE_CONTACTS</code> - </li> - <li> - <code>android.permission.READ_PROFILE</code> - </li> - <li> - <code>android.permission.WRITE_PROFILE</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.LOCATION</code></td> - <td> - <ul> - <li> - <code>android.permission.ACCESS_FINE_LOCATION</code> - </li> - <li> - <code>android.permission.ACCESS_COARSE_LOCATION</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.MICROPHONE</code></td> - <td> - <ul> - <li> - <code>android.permission.RECORD_AUDIO</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.PHONE</code></td> - <td> - <ul> - <li> - <code>android.permission.READ_PHONE_STATE</code> - </li> - <li> - <code>android.permission.CALL_PHONE</code> - </li> - <li> - <code>android.permission.READ_CALL_LOG</code> - </li> - <li> - <code>android.permission.WRITE_CALL_LOG</code> - </li> - <li> - <code>com.android.voicemail.permission.ADD_VOICEMAIL</code> - </li> - <li> - <code>android.permission.USE_SIP</code> - </li> - <li> - <code>android.permission.PROCESS_OUTGOING_CALLS</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.SENSORS</code></td> - <td> - <ul> - <li> - <code>android.permission.BODY_SENSORS</code> - </li> - </ul> - <ul> - <li> - <code>android.permission.USE_FINGERPRINT</code> - </li> - </ul> - </td> - </tr> - - <tr> - <td><code>android.permission-group.SMS</code></td> - <td> - <ul> - <li> - <code>android.permission.SEND_SMS</code> - </li> - <li> - <code>android.permission.RECEIVE_SMS</code> - </li> - <li> - <code>android.permission.READ_SMS</code> - </li> - <li> - <code>android.permission.RECEIVE_WAP_PUSH</code> - </li> - <li> - <code>android.permission.RECEIVE_MMS</code> - </li> - <li> - <code>android.permission.READ_CELL_BROADCASTS</code> - </li> - </ul> - </td> - </tr> - -</table> - -<h4 id="request-permissions">必要時要求權限</h4> - -<p>如果應用程式還沒有所需的權限,應用程式會呼叫 -<code>Activity.requestPermissions(String[], int)</code> 方法以要求適當的權限。 -應用程式會傳遞它所需的權限,還有整數「要求代碼」。 - - 這種方法以非同步方式運作:它會立即傳回,並在使用者回應對話方塊後,系統會以該結果呼叫應用程式的回呼方法,傳遞應用程式傳遞給 <code>requestPermissions()</code> 的相同「要求代碼」。 - - -</p> - - <p>下列程式碼會檢查應用程式是否具備讀取使用者聯絡人的權限,並在必要時要求權限。 -</p> - -<pre> -if (checkSelfPermission(Manifest.permission.READ_CONTACTS) - != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, - MY_PERMISSIONS_REQUEST_READ_CONTACTS); - - // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an - // app-defined int constant - - return; -} -</pre> - -<h4 id="handle-response">處理權限要求回應</h4> - -<p> - 當應用程式要求權限時,系統會對使用者呈現對話方塊。 -當使用者回應時,系統會呼叫 <code>Activity.onRequestPermissionsResult(int, String[], int[])</code> 並將使用者回應傳遞給它。 - -您的應用程式需要覆寫該方法。將您傳遞給 <code>requestPermissions()</code> 的相同要求代碼傳遞給回呼。 - -例如,如果應用程式要求 <code>READ_CONTACTS</code> 存取權,可能會有下列回呼方法: - - -</p> - -<pre> -@Override -public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - - // permission was granted, yay! do the - // calendar task you need to do. - - } else { - - // permission denied, boo! Disable the - // functionality that depends on this permission. - } - return; - } - - // other 'switch' lines to check for other - // permissions this app might request - } -} -</pre> - - <p>如果使用者授與權限,系統就會將應用程式宣示說明所列出該功能區域的所有權限給予應用程式。 -如果使用者拒絕要求,您應該執行適當動作。 -例如,您可能會停用依存於此權限的任何選單動作。 - - </li> -</p> - -<p> - 系統要求使用者授與權限時,使用者可選擇告知系統不要再次要求該權限。 -在上述情況下,當應用程式使用 <code>requestPermissions()</code> 要求該權限時,系統會立即拒絕要求。 - -在這種情況下,如果使用者再次明確拒絕您的要求,系統會以相同的方式呼叫您的 <code>onRequestPermissionsResult()</code>。 - -基於這個理由,您的應用程式不能假設已與使用者發生任何直接互動。 - -</p> - -<h2 id="testing">測試執行階段權限</h2> - - -<p> - 如果您的應用程式是以新的 M 開發人員預覽版為目標,您必須測試它是否能適當處理權限。 -您不能假設應用程式在執行時具備任何特定權限。 -應用程式初次啟動時,很可能不具備任何權限,且使用者可隨時撤銷或還原權限。 - - -</p> - -<p> - 您應該測試應用程式,確保它在所有權限情況下都能正常運作。 -使用 M 預覽版 SDK,我們現在提供 <a href="{@docRoot}tools/help/adb.html">Android 偵錯橋 (adb)</a> 命令,不管需要嘗試哪種權限設定都能讓您測試。 - - - -</p> - -<h3> - 新的 adb 命令和選項 -</h3> - -<p> - M 預覽版 SDK 平台工具提供的數個命令可讓您測試應用程式如何處理權限。 - -</p> - -<h4> - 連同權限一併安裝 -</h4> - -<p> - 您可以使用 <a href="{@docRoot}tools/help/adb.html#move"><code>adb - install</code></a> 命令的新 <code>-g</code> 選項,安裝應用程式並授與其宣示說明中列出的所有權限: - -</p> - -<pre class="no-pretty-print"> -$ adb install -g <path_to_apk> -</pre> - -<h4> - 授與和撤銷權限 -</h4> - -<p> - 您可以使用新的 ADB <a href="{@docRoot}tools/help/adb.html#pm">套件管理員 (pm)</a> 命令,對安裝的應用程式授與和撤銷權限。此功能在自動化測試時非常實用。 - - -</p> - -<p> - 如要授與權限,可使用套件管理員的 <code>grant</code> 命令: -</p> - -<pre class="no-pretty-print"> -$ adb pm grant <package_name> <permission_name> -</pre> - -<p> - 例如,如要將可錄製音訊的權限授與 com.example.myapp 套件,請使用此命令: - -</p> - -<pre class="no-pretty-print"> -$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO -</pre> - -<p> - 如要撤銷權限,可使用套件管理員的 <code>revoke</code> 命令: -</p> - -<pre class="no-pretty-print"> -$ adb pm revoke <package_name> <permission_name> -</pre> - -<h2 id="best-practices">最佳做法</h2> - -<p> - 新的權限模型讓使用者有更順暢的體驗,並能輕鬆安裝應用程式且對應用程式執行的工作感到自在。 - -建議使用下列最佳做法以充分利用新的模型。 - -</p> - - -<h3 id="bp-what-you-need">只要求您所需的權限</h3> - -<p> - 每次要求權限時,您都是在強迫使用者做出決定。 - 如果使用者拒絕要求,就會減少您應用程式的功能。 - 您應該儘可能減少提出這些要求的次數。 -</p> - -<p> - 例如,您的應用程式可經常使用<a href="{@docRoot}guide/components/intents-filters.html">意圖</a>來取得所需功能,而不是要求權限。 - -如果您的應用程式需要使用手機的相機拍攝相片,應用程式可以使用 {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE - MediaStore.ACTION_IMAGE_CAPTURE} 意圖。 -當您的應用程式執行意圖時,系統會提示使用者選擇已安裝的相機應用程式來拍攝相片。 - - -</p> - -<h3 id="bp-dont-overwhelm"> - 別讓使用者無法承受 -</h3> - -<p> - 如果您讓使用者一次面對太多權限要求,可能會讓使用者無法承受而結束您的應用程式。您應該改為在需要時才要求權限。 - - -</p> - -<p> - 在某些情況下,您的應用程式可能必須具備一或多個權限。在那種情況下,在應用程式啟動時立即要求所有權限是合理的舉措。 - -例如,如果您建立攝影應用程式,該應用程式會需要存取裝置相機。 -使用者初次啟動應用程式時,看到要求使用相機的權限不會被嚇到。 - -但如果相同的應用程式具有與使用者聯絡人分享相片的功能,您可能「不」<em></em>應該在初次啟動時要求該權限。 - -可以等到使用者嘗試使用「分享」功能時,再要求該權限。 - -</p> - -<p> - 如果您的應用程式提供教學課程,在教學課程結束時要求應用程式的基本權限是合理的舉措。 - -</p> - -<h3 id="bp-explain"> - 說明需要權限的原因 -</h3> - -<p> - 當您呼叫 -<code>requestPermissions()</code> 時,系統顯示的權限對話方塊會說明您的應用程式想要的權限,但不會說明原因。 -在某些情況下,使用者可能會感到不解。 - 在呼叫 <code>requestPermissions()</code> 前對使用者說明應用程式想要權限的原因是不錯的想法。 - -</p> - -<p> - 例如,攝影應用程式可能想要使用定位服務,以便將相片加上地理標籤。 -一般使用者可能不明白相片可以包含定位資訊,而不明白為何攝影應用程式會想要知道位置。 - -在這種情況下,在呼叫 <code>requestPermissions()</code>「之前」<em></em>,將此功能的相關資訊告訴使用者會是不錯的想法。 - - -</p> - -<p> - 您可以將這些要求與應用程式教學課程結合來完成此作業。教學課程可依序顯示應用程式的各項功能,並可以同時說明需要哪些權限。 - -例如,攝影應用程式的教學課程可以示範「與聯絡人分享相片」功能,接著告訴使用者需要提供權限,應用程式才能看到使用者的聯絡人。 - - -接著,應用程式可以呼叫 <code>requestPermissions()</code>,要求使用者提供該存取權。 -當然,並非每位使用者都會依照教學課程執行動作,所以您仍需要在應用程式的正常操作期間檢查和要求權限。 - - -</p> diff --git a/docs/html-intl/intl/zh-tw/preview/index.jd b/docs/html-intl/intl/zh-tw/preview/index.jd deleted file mode 100644 index 3aaf3820b3df..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/index.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Android M 開發人員預覽版 -page.tags="preview", -meta.tags="preview, M preview", androidm -fullpage=true -section.landing=true -header.hide=1 -footer.hide=1 -@jd:body - -<section class="dac-expand dac-hero dac-light" > - <div class="wrap"> - <div class="cols dac-hero-content"> - <div class="col-9of16 col-push-7of16 dac-hero-figure"> - <img class="dac-hero-image" src="{@docRoot}images/home/devices-hero_620px_2x.png" srcset="{@docRoot}images/home/devices-hero_620px.png 1x, - {@docRoot}images/home/devices-hero_620px_2x.png 2x"> - </div> - <div class="col-7of16 col-pull-9of16"> - <h1 class="dac-hero-title">Android M 開發人員預覽版</h1> - <p class="dac-hero-description"> - 準備使用新版 Android。在 Nexus 5、6、9 和 Player 上測試您的應用程式。 -瞭解新功能 — <strong>執行階段權限</strong>、<strong>休眠</strong>、<strong>應用程式待命</strong>等省電功能、新的<strong>輔助技術</strong>,以及其他功能。 - - - </p> - - <a class="dac-hero-cta" href="{@docRoot}preview/overview.html"> - <span class="dac-sprite dac-auto-chevron"></span> - 開始使用! -</a><br> - <a class="dac-hero-cta" href="{@docRoot}preview/support.html"> - <span class="dac-sprite dac-auto-chevron"></span> - Developer Preview 3 (final SDK)</a> - </div> - </div> - <div class="dac-section dac-small"> - <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/resources" - data-cardSizes="6x2" - data-maxResults="6"></div> - </div> - </div> -</section> - -<section class="dac-section dac-gray"><div class="wrap"> - <h1 class="dac-section-title">資源</h1> - <div class="dac-section-subtitle"> - 以下重要資訊可幫助您的應用程式準備好使用 Android M。 - </div> - - <div class="resource-widget resource-flow-layout col-16" - data-query="collection:preview/landing/more" - data-cardSizes="6x6" - data-maxResults="16"></div> - - <ul class="dac-section-links"> - <li class="dac-section-link"> - <a href="https://code.google.com/p/android-developer-preview/"> - <span class="dac-sprite dac-auto-chevron"></span> - 回報問題 -</a> - </li> - <li class="dac-section-link"><a href="http://g.co/dev/AndroidMDevPreview"> - <span class="dac-sprite dac-auto-chevron"></span> - 參加 G+ 社群 -</a> - </li> - </ul> - </div> -</section> - diff --git a/docs/html-intl/intl/zh-tw/preview/license.jd b/docs/html-intl/intl/zh-tw/preview/license.jd deleted file mode 100644 index c4acb91a450c..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/license.jd +++ /dev/null @@ -1,143 +0,0 @@ -page.title=授權協議 - -@jd:body - -<p> -如果想要開始使用 Android SDK 預覽版,您必須同意遵守下列條款和條件。如下方所述,請注意這是 Android SDK 預覽版,可能隨時會有變更,請您自負使用風險。 -Android SDK 預覽版還不是穩定的版本,可能會包含許多錯誤和瑕疵而對您的電腦系統、裝置和資料造成嚴重的損害。 -</p> - -<p> -這是「Android SDK 預覽版授權協議」(以下稱「授權協議」)。 -</p> -<div class="sdk-terms" style="height:auto;border:0;padding:0;width:700px"> -1.簡介 - -1.1「Android SDK 預覽版」(在「授權協議」中稱為「預覽版」,包括 (如果有可用的) Android 系統檔案、經過封裝的 API 和預覽版程式庫檔案) 是在「授權協議」之條款的約束下授權給您使用。「授權協議」就您對「預覽版」的使用,構成您與 Google 間具法律約束力之合約。 - -1.2「Android」係指「Android 軟體開放原始碼專案」(http://source.android.com/) 所提供的 Android 裝置軟體堆疊 (不定期更新)。 - -1.3「Google」係指 Google Inc.,是一家在美國德拉瓦州註冊的公司,地址為 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States。 - -2.接受「授權協議」 - -2.1 必須先同意遵守「授權協議」,才能使用此「預覽版」。如果不接受「授權協議」,您就無法使用此「預覽版」。 - -2.2 按一下 [接受] 且/或使用「預覽版」,即表示您同意「授權協議」的條款。 - -2.3 如果您是美國或其他國家/地區 (包括您所居住或使用此「預覽版」的國家/地區) 的法律所禁止接收此「預覽版」的人員,就不得使用此「預覽版」及接受「授權協議」。 - -2.4 如果您將在公司或組織內部使用「預覽版」,您就要代表雇主或其他實體同意受「授權協議」約束,且您代表並保證具備完整法定權限來約束您的雇主或這類實體遵守「授權協議」。如果您不具備必要的權限,就不得代表您的雇主或其他實體接受「授權協議」或使用此「預覽版」。 - -3.Google 的預覽版授權 - -3.1 在「授權協議」之條款的約束下,Google 授權您使用此「預覽版」,此授權為買斷式、不可轉讓、非獨占性、不可轉授權、有限且可撤銷,僅在您公司或組織內部私下或內部使用。此「預覽版」僅供您用於開發在 Android 平台上執行的應用程式。 - -3.2 您同意 Google 或第三方對此「預覽版」擁有一切法定權利及權益,包括存在於此「預覽版」中的任何「智慧財產權」。「智慧財產權」係指專利法、著作權法、商業秘密法、商標法及任何和所有其他專利權下的任何及一切權利。Google 保留一切未明確授予您的權利。 - -3.3 您不得將此「預覽版」用於「授權協議」未明文許可的任何用途。除非適用的第三方授權所需,否則您不得:(a) 對此「預覽版」或其任何部分進行複製 (備份用途除外)、修改、改編、轉散佈、反向組譯、還原工程、解編或製作衍生成品;或是 (b) 將此「預覽版」的任何部分載入至行動電話或個人電腦以外的任何其他硬體裝置、將此「預覽版」的任何部分與其他軟體結合,或散佈包含此「預覽版」之任一部分的任何軟體或裝置。 - -3.4 您同意不會從事任何可能導致或造成 Android 分裂的活動,包括但不限於以任何形式散佈從此「預覽版」衍生的軟體開發套件、參其製作或宣傳。 - -3.5 對開放原始碼軟體授權下所授權之「預覽版」的使用、複製及散佈,完全受該開放原始碼軟體授權的條款管制,而不受「授權協議」管制。您同意遵守從這類開放原始碼軟體授權獲得的所有權利,並且避免採取任何可能終止、中止或侵害這類權利的行為。 - -3.6 您同意 Google 可在不事先通知您的情況下變更其所提供之「預覽版」的形式和本質,且此「預覽版」的未來版本可與在此「預覽版」的先前版本上開發的應用程式不相容。您同意 Google 通常可全權斟酌永久或暫時停止提供此「預覽版」(或此「預覽版」的任何功能) 給您或使用者,毋須事先通知。 - -3.7「授權協議」中的所有條款皆未授予您任何使用 Google 之商業名稱、商標、服務標章、標誌、網域名稱或其他明確品牌特徵的權利。 - -3.8 您同意不會移除、遮蔽或更改可能附加至或內含在此「預覽版」中的任何專利權通知 (包括著作權和商標通知)。 - -4.您對「預覽版」的使用 - -4.1 Google 同意在「授權協議」下,任何條款皆未從您 (或您的授權人) 賦予 Google 對您使用此「預覽版」開發之任何軟體應用程式的任何權利及權益,包括存在於這些應用程式中的任何智慧財產權。 - -4.2 您同意只就 (a)「授權協議」和 (b) 相關管轄權中任何適用法律、規定或是普遍獲得接受之慣例或指導方針 (包括任何有關將資料或軟體輸出或輸入美國或其他相關國家/地區的法律) 所允許的用途使用此「預覽版」及撰寫應用程式。 - -4.3 您同意如果使用此「預覽版」開發應用程式,您將保護使用者的隱私權和法定權利。如果使用者提供您使用者名稱、密碼或是其他登入資訊或個人資訊,您必須告知使用者這類資訊將提供給您的應用程式使用,並且必須為這些使用者提供法定充分的隱私權通知和保護。如果您的應用程式會儲存使用者所提供的個人或敏感資訊,它必須確保這些資訊安全無虞。如果使用者提供 Google 帳戶資訊給您,則只有在每個使用者已授權您存取其 Google 帳戶並僅限用於使用者所授權之用途的情況下,您的應用程式才能使用該資訊來存取使用者的 Google 帳戶。 - -4.4 您同意不會使用此「預覽版」從事任何不當活動,例如開發或散佈會以未經授權的方式干擾、妨礙、損害或存取 Google 或任何第三方之伺服器、網路或是其他財產或服務的應用程式。 - -4.5 您同意對您透過 Android 裝置和 (或) Android 應用程式建立、傳輸或顯示的任何資料、內容或資源,以及上述行為造成的後果 (包括 Google 可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.6 您同意對違反在此「授權協議」、任何適用之第三方合約或《服務條款》或是任何適用之法律或規定下所必須遵守的義務,以及違反相關義務造成的後果 (包括 Google 或任何第三方可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.7「預覽版」目前正在開發中,因此您的測試與意見反應對開發程序非常重要。使用「預覽版」,您即認同某些功能仍處於開發階段,因此您不應期待「預覽版」擁有穩定版本的完整功能。在官方 Android SDK 發行之後,此「預覽版」不再受到支援時,您同意不使用此「預覽版」公開散佈或隨附任何應用程式。 - -5.您的開發人員認證 - -5.1 您同意負責保密 Google 可能核發給您或您自己選擇的任何開發人員認證,並且對在您開發人員認證名義下開發的所有應用程式負起全責。 - -6.隱私權和資訊 - -6.1 為了持續更新及改進此「預覽版」,Google 可能會從軟體收集某些使用狀況統計數據,包括但不限於軟體的唯一識別碼、相關 IP 位址、版本號碼,以及有關使用此「預覽版」中的哪些工具和 (或) 服務及其使用方式的相關資訊。在收集這類資訊之前,此「預覽版」會先通知您並徵求您的同意。如果您不同意,Google 將不會收集這類資訊。 - -6.2 Google 會彙總並檢查收集到的資料,據以改善此「預覽版」,並且會依據《Google 隱私權政策》(http://www.google.com/policies/privacy/) 加以妥善保存。 - -7.第三方應用程式 - -7.1 如果您使用此「預覽版」來執行第三方開發的應用程式,或是執行會存取第三方所提供之資料、內容或資源的應用程式,您同意 Google 不需對這類應用程式、資料、內容或資源負任何責任。您瞭解您透過第三方應用程式存取的所有資料、內容或資源是由其提供者負起全責,而 Google 對您因使用或存取任何這些第三方應用程式、資料、內容或資源所造成的損失或損害不需負任何責任。 - -7.2 您瞭解第三方應用程式提供給您的資料、內容或資源可能受到提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您不得根據這類資料、內容或資源 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品,除非相關擁有者明確授權您從事上述活動。 - -7.3 您瞭解使用第三方應用程式、資料、內容或資源可能受到您與相關第三方之間的個別條款約束。 - -8.使用 Google API - -8.1 Google API - -8.1.1 如果您使用任何 API 從 Google 擷取資料,您瞭解這些資料可能受到 Google 或資料提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您對任何這類 API 的使用可能受到其他《服務條款》約束。除非相關《服務條款》明文允許,否則您不得根據這類資料 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品。 - -8.1.2 使用任何 API 從 Google 擷取使用者的資料時,您瞭解並同意只有在該使用者明確同意且授權您擷取其資料,而且僅限用於使用者所授權之用途的情況下,您才能擷取資料。 - -9.終止「授權協議」 - -9.1 除非您或 Google 終止「授權協議」(請見下方說明),否則「授權協議」將持續具有效力。 - -9.2 如果想終止「授權協議」,只要停止使用此「預覽版」及任何相關的開發人員憑證即可。 - -9.3 Google 可隨時通知您終止「授權協議」,無論有無原因。 - -9.4「授權協議」在先發生下列任一情況時,將自動終止而不另行通知或採取其他行動: -(A) Google 決定不再提供此「預覽版」或此「預覽版」的特定部分給您所居住或使用此服務之國家/地區的使用者;或 -(B) Google 發行最終版本的 Android SDK。 - -9.5 當「授權條款」終止時,您在「授權協議」所獲得的授權也將會一併終止,您將立即停止「預覽版」的所有使用,而第 10、11、12 和 14 項的條款將無限期持續適用。 - -10.免責聲明 - -10.1 您明確瞭解並同意完全自負使用此「預覽版」的風險,並且此「預覽版」是依「現況」和「可提供性」提供,Google 不負任何擔保責任。 - -10.2 您對使用此「預覽版」及透過此「預覽版」以下載或其他方式取得的任何內容,需自行斟酌和自負風險,而且您對因這類使用而對您的電腦系統或其他裝置所造成的任何損害或資料遺失,需負起全責。不限於前述,您瞭解此「預覽版」不是穩定的版本,可能會包含許多錯誤、瑕疵和安全性弱點而對您的電腦系統或其他裝置造成嚴重的損害,包括完全、不可回復的損失。 - -10.3 Google 進一步明確表示不提供任何形式的瑕疵擔保和條件 (不論明示或默示),包括但不限於適售性、適合特定用途及未侵權的默示擔保和條件。 - -11.責任限制 - -11.1 您明確瞭解並同意在任何歸責理論下,就可能由您引起的任何直接、間接、附隨性、特殊性、衍生性或懲罰性損害賠償 (包括任何資料遺失),不論 Google 或其代表是否已獲告知或應已瞭解發生任何這類損失的可能性,Google、其子公司和關係企業及其授權人不必對您負起任何責任。 - -12.賠償 - -12.1 在法律允許的最大範圍內,您同意為 Google、其子公司及其個別董監事、主管、員工和代理人,就任何和一切索賠、法律行動、訴訟或訴訟程序,以及因下列原因而引起的任何和一切損失、責任、損害賠償、費用及開支 (包括合理的律師費),提供辯護、賠償損失並確保其免於承擔賠償責任:(a) 您使用此「預覽版」;(b) 您使用此「預覽版」開發的應用程式侵害任何人的任何智慧財產權,或是詆毀任何人或違反其公開權或隱私權;以及 (c) 您未遵守「授權協議」。 - -13.對「授權協議」做出的變更 - -13.1 Google 可在散佈此「預覽版」的新版本時修改「授權協議」。做出這類變更後,Google 會在提供此「預覽版」的網站上提供「授權協議」的新版本。 - -14.一般法律條款 - -14.1「授權協議」構成您與 Google 之間的法律協議,用於管制您對此「預覽版」(不包括 Google 依據個別書面協議提供給您的任何服務) 的使用,並完全取代先前您與 Google 之間就此「預覽版」簽署的相關協議。 - -14.2 您同意如果 Google 未行使或執行「授權協議」所含的任何法律權利或救濟 (或在任何適用法律下對 Google 有利的權益),並不代表 Google 正式放棄權利,Google 日後仍可行使這些權利或救濟。 - -14.3 如果經任何法院 (就此事宜依管轄權決定) 裁決「授權協議」中有任何條款無效,則該條款將自「授權協議」中移除,「授權協議」的其餘部分則不受影響。「授權協議」的其餘條款將持續具有效力且可執行。 - -14.4 您瞭解並同意 Google 旗下子公司體系的每位成員都是「授權協議」的第三方受益人,而且這類其他公司有權直接執行和依據「授權協議」中對其授予權益 (或對其有利之權利) 的任何條款。除此之外的任何其他人員或公司皆非「授權協議」的第三方受益人。 - -14.5 出口限制。此「預覽版」受美國出口法規約束。您必須遵守適用於此「預覽版」的所有國內和國際出口法規。這些法律包括對目的地、使用者及最終用途的限制。 - -14.6 未事先取得 Google 事先書面核准的情況下,您不得轉讓或轉移「授權協議」,未經這類核准的任何轉讓將會失效。您不得在未事先取得 Google 書面核准的情況下,委派其「授權協議」涵蓋的責任或義務。 - -14.7「授權協議」以及您與 Google 就「授權協議」構成的關係皆受加州法律管轄,毋須理會其法律牴觸條款。您和 Google 同意服從位於加州聖塔克拉拉 (Santa Clara, California) 郡內法院的專屬管轄權,以解決由「授權協議」產生的任何法律事務。儘管如此,您同意 Google 仍可在任何管轄權中申請禁制令救濟 (或同等類型的緊急法定救濟)。 - - -</div>
\ No newline at end of file diff --git a/docs/html-intl/intl/zh-tw/preview/overview.jd b/docs/html-intl/intl/zh-tw/preview/overview.jd deleted file mode 100644 index 9693eec670f1..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/overview.jd +++ /dev/null @@ -1,389 +0,0 @@ -page.title=程式總覽 -page.metaDescription=歡迎使用 Android M 開發人員預覽版,本程式提供為新版 Android 測試和最佳化您應用程式所需的一切。 -page.image=images/cards/card-preview_16-9_2x.png -page.tags="preview", "developer", "android" - -@jd:body - -<div class="cols" style= -"background-color:#ffebc3; padding: 5px 0;margin-bottom:1em; text-align:center;"> -<h3> - Developer Preview 2 is now available - </h3> - - <ul class="dac-section-links"> - <li class="dac-section-link"> - <a href="{@docRoot}preview/support.html#preview2-notes"> - <span class="dac-sprite dac-auto-chevron"></span> - Read the Notes</a> - </li> - - <li class="dac-section-link"> - <a href="{@docRoot}preview/support.html#preview2-get"> - <span class="dac-sprite dac-auto-chevron"></span> - Get the Update</a> - </li> - - <li class="dac-section-link"> - <a href="https://code.google.com/p/android-developer-preview/"> - <span class="dac-sprite dac-auto-chevron"></span> - Report Issues</a> - </li> - </ul> -</div> - -<p> - 歡迎使用「Android M 開發人員預覽版」<strong></strong>,本程式提供為新版 Android 測試和最佳化您應用程式所需的一切。 - -免費使用,您只要下載 M 開發人員預覽版工具,就能立即開始使用。 - -</p> - -<div style="background-color:#eceff1;padding:1em;"> -<div class="wrap"> - <div class="cols"> - <div class="col-4of12"> - <h5> - 硬體與模擬器系統映像 - </h5> - - <p> - 在 Nexus 5、6、9 和 Player (適用於電視) ,以及模擬器上執行和測試您的應用程式。 - - </p> - </div> - - <div class="col-4of12"> - <h5> - 最新的平台程式碼 - </h5> - - <p> - 我們將在預覽版期間提供多次更新,讓您能夠針對最新的平台變更進行測試。 - - </p> - </div> - - <div class="col-4of12"> - <h5> - 透過 OTA 傳遞更新 - </h5> - - <p> - 在您將裝置刷新為初始預覽版之後,就能以無線方式取得更新。 - - </p> - </div> - </div> - - <div class="cols"> - - - <div class="col-4of12"> - <h5> - 新行為和功能 - </h5> - - <p> - 儘早開始開發以支援新的平台行為,例如新的執行階段權限模型和省電功能。 - - </p> - </div> - - <div class="col-4of12"> - <h5> - 開發人員限時優先回報問題 - </h5> - - <p> - 在前幾個星期內,我們將讓開發人員優先回報問題,因此請盡快測試並提供意見反應。 - - </p> - </div> - - <div class="col-4of12"> - <h5> - 意見反應與支援 - </h5> - - <p> - 使用<a href="https://code.google.com/p/android-developer-preview/">問題追蹤器</a>回報問題並提供意見反應。 - 與 <a href="http://g.co/dev/AndroidMDevPreview">M 開發人員社群</a>中的其他開發人員聯絡。 - - </p> - </div> - </div> -</div> -</div> - -<!-- -<p> - With the M Developer Preview, you'll get an early start on testing your apps, - with enough time to make adjustments before the public platform release later - in the year. We'll provide several updates to the Preview tools in the weeks - ahead, so you can keep in sync with the latest changes as the platform moves - toward launch. -</p> -<img src="{@docRoot}preview/images/m-preview-timeline.png" alt= -"Preview program timeline" id="timeline"> -<p> - You can help us improve the platform by <a href= - "https://code.google.com/p/android-developer-preview/">reporting issues</a> - through our feedback channels. This is especially - critical in the first month of the preview, when we’ll be giving priority to - developer-reported issues and feedback. -</p> --> - - -<h2 id="timeline"> - 時間軸和更新 -</h2> -<img src="{@docRoot}preview/images/m-preview-timeline-crop.png" alt="Preview program timeline" id="timeline"> -<p> - M 開發人員預覽版可從 5 月 28 日開始執行,直到我們將在 2015 年第 3 季正式發行之前發行的最終版 Android M SDK。 - - -</p> - -<p> - 我們將在主要的開發里程碑為您的測試裝置提供更新。 - 里程碑暫定如下 -</p> - -<ul> - <li> - <strong>預覽版 1</strong> (初始預覽版,五月下旬)、 - </li> - - <li> - <strong>預覽版 2</strong> (六月下旬/七月上旬) 及 - </li> - - <li> - <strong>預覽版 3</strong> (接近最終版,七月下旬) - </li> -</ul> - -<p> - 這些更新最終會成為「最終版 SDK」<strong></strong> (稍後於第 3 季),為新版 Android 提供正式的 API,以及最終的系統行為和功能。 - - -</p> - -<p> - 當您在 Android M 上測試和開發時,強烈建議您在預覽版更新發行時立即更新,讓「您的開發環境保持在最新狀態」<strong></strong>。 - - 為了讓程序更簡單,我們將對更新為預覽版建置的裝置<strong>以無線 (OTA) 方式提供更新</strong>,還會提供您能手動下載和更新的系統映像。 - - -</p> -<p class="note"> - <strong>注意:</strong>最終版 SDK 與系統映像無法以 OTA 方式提供,將必須<strong>以手動方式更新</strong>至您的測試裝置。</strong> - - -</p> - -<p> - 我們將透過 <a href="http://android-developers.blogspot.com/">Android 開發人員部落格</a>,還有本網站與 <a href="http://g.co/dev/AndroidMDevPreview">Android M 開發人員社群</a>來通知您有可用的預覽版更新。 - - -</p> - -<h2 id="preview_tools"> - 預覽版新增功能 -</h2> - -<p> - M 開發人員預覽版包含您針對各種不同螢幕大小、網路技術、 CPU/GPU 晶片組及硬體架構,測試現有應用程式所需的一切。 - - -</p> - -<h4> - SDK 工具 -</h4> - -<p> - 您可以透過 <a href="{@docRoot}sdk/installing/adding-packages.html">Android Studio</a> 中的「SDK 管理器」下載以下元件: -</p> - -<ul> - <li>M 開發人員預覽版 <strong>SDK 工具</strong> - </li> - - <li>M 開發人員預覽版<strong>模擬器系統映像</strong> (32 位元和 64 位元) - - </li> - - <li>M 開發人員預覽版<strong>模擬器系統映像 (適用於 Android TV)</strong> (32 位元) - - </li> -</ul> - -<h4> - 硬體系統映像 -</h4> - -<p> - 您可以從<a href="download.html">下載頁面</a>下載適用於 Nexus 裝置的以下硬體系統映像: - -</p> - -<ul> - <li> - <strong>Nexus 5</strong> (GSM/LTE)“hammerhead”裝置系統映像 - </li> - - <li> - <strong>Nexus 6</strong>“shamu”裝置系統映像 - </li> - - <li> - <strong>Nexus 9</strong> (Wi-Fi)“volantis”裝置系統映像 - </li> - - <li> - <strong>Nexus Player</strong> (Android TV)“fugu”裝置系統映像 - </li> -</ul> - -<h4> - 文件和範例程式碼 -</h4> - -<p> - 這些文件資源可協助您瞭解預覽版: -</p> - -<ul> - <li> - <a href="setup-sdk.html">設定 SDK</a> 涵蓋開始使用的逐步指示。 - - </li> - - <li> - <a href="{@docRoot}preview/testing/guide.html">測試指南</a>與<a href="behavior-changes.html">行為變更</a>指出要測試的主要區域。 - </li> - - <li>新 API 的文件,包括 <a href="api-overview.html">API 總覽</a>、可下載的 <a href="{@docRoot}preview/download.html#docs">API 參考資料</a>以及主要功能 (例如<a href="{@docRoot}preview/features/runtime-permissions.html">權限</a>、<a href="{@docRoot}preview/backup/index.html">應用程式備份</a>及其他功能) 的詳細開發人員指南。 - - - - - </li> - - <li> - 示範如何支援權限和其他新功能的<a href="{@docRoot}preview/samples.html">範例程式碼</a>。 - - </li> - - <li> - 適用於目前 M 開發人員預覽版的<a href="{@docRoot}preview/support.html#release-notes">版本資訊</a>,包括變更資訊與差異報告。 - - </li> -</ul> - -<h4> - 支援資源 -</h4> - -<p> - 在 M 開發人員預覽版上測試和開發時,請使用以下支援資源: - -</p> - -<ul> - <li><a href="https://code.google.com/p/android-developer-preview/">M 開發人員預覽版問題追蹤器</a>是您的<strong>主要意見反應</strong>管道。 - -您可以透過問題追蹤器來回報錯誤、效能問題及一般意見反應。 -您也可以檢查<a href="https://code.google.com/p/android-developer-preview/wiki/KnownIssues">已知問題</a> -和尋找因應方式步驟。 - </li> - - <li><a href="http://g.co/dev/AndroidMDevPreview">Android M 開發人員社群</a>是您能<strong>與其他 Android M 開發人員聯絡</strong>的 Google+ 社群。您可以分享有關 Android M 的觀察或想法,或尋找解答。 - - - - </li> -</ul> - - -<h2 id="preview_apis_and_publishing"> - 目標設定、預覽版 API 及發行 -</h2> - -<p> - Android M 開發人員預覽版是開發專用的版本,而且<strong>沒有標準的 API 層級</strong>。 -如果您想要選擇退出相容性行為以測試您的應用程式 (強烈建議),您可以將應用程式的 <code><a href= - "/guide/topics/manifest/uses-sdk-element.html">targetSdkVersion</a></code> 設定為 <code>“MNC”</code>,就能以 M 開發人員預覽版為目標。 - - - -</p> - -<p> - Android M 開發人員預覽版提供<strong>預覽版 API</strong> — 在最終版 SDK (目前規劃在 2015 年第三季) 發行之前的都不是正式 API。 - -這表示您可以預期 API 會隨時間而有些許變更,特別是程式一開始的幾週。<strong></strong> - -我們會將 Android M 開發人員預覽版每次更新的變更摘要提供給您。 - -</p> - -<p class="note"> - 請注意,雖然預覽版 API 可能會改變,但例如執行階段權限和省電功能等基本系統行為,均已穩定且能夠立即測試。 - - -</p> - -<p> - 在發行方面,Google Play 會<strong>禁止您發行以 M 開發人員預覽版為目標的應用程式</strong>。 -當 Android M 最終版 SDK 推出時,您將能夠以正式 Android M API 層級為目標,並將您的應用程式發行至 Google Play。 - -同時,您可以透過電子郵件或直接從您的網站下載,來對測試者散佈以 Android M 為目標的應用程式。 - - -</p> - -<h2 id="get_started"> - 如何開始 -</h2> - -<p> - 如要開始測試您的應用程式: -</p> - -<ol> - <li>檢閱 <a href="{@docRoot}preview/api-overview.html">API 總覽</a>與<a href="{@docRoot}preview/behavior-changes.html">行為變更</a>,以瞭解新增功能,還有它會如何影響您的應用程式。 - -特別是瞭解新的<a href="{@docRoot}preview/features/runtime-permissions.html">執行階段權限</a>模型、省電功能以及自動化備份。 - - - </li> - - <li>依照<a href="{@docRoot}preview/setup-sdk.html">設定預覽版 SDK</a> 的指示來設定您的環境,並設定測試裝置。 - - - </li> - - <li>依照<a href="https://developers.google.com/android/nexus/images">刷新指示</a>,針對 Nexus 5、6、9 及 Player 刷新最新的 M 開發人員預覽版系統映像。 - -在您刷新開發裝置之後,預覽版更新將以無線 (OTA) 更新</a>的方式提供。 - - </li> - - <li>下載 <a href="{@docRoot}preview/download.html#docs">M 預覽版 API 參考資料</a>與 <a href="{@docRoot}preview/samples.html">M 預覽版範例</a>,以深入瞭解新的 API 功能以及如何在您的應用程式中運用。 - - - - </li> - - <li>加入 <a href="http://g.co/dev/AndroidMDevPreview">Android M 開發人員社群</a>以取得最新消息,並與其他使用新平台的開發人員聯絡。 - - - </li> -</ol> - -<p> - 感謝您參與 Android M 開發人員預覽版程式! -</p> diff --git a/docs/html-intl/intl/zh-tw/preview/samples.jd b/docs/html-intl/intl/zh-tw/preview/samples.jd deleted file mode 100644 index 2ef9a60b5129..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/samples.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=範例 -page.image=images/cards/samples-new_2x.png -@jd:body - -<p> - 下列提供 M 開發人員預覽版的程式碼範例。如要下載 Android Studio 的範例,請選取 [File] (檔案) > [Import Samples] (匯入範例) 選單選項。 -<b></b> -</p> - -<p class="note"> - <strong>注意:</strong>這些可下載的專案是專為與 Gradle 和 Android Studio 一起使用而設計。 - -</p> - - -<h3 id="RuntimePermissions">執行階段權限</h3> - -<p> - Android M 改變系統權限的運作方式。改為在執行階段才要求使用者核准權限要求,而不是安裝期間。 -這個範例顯示如何要求這些權限。 - -</p> - -<p><a href="https://github.com/googlesamples/android-RuntimePermissions">在 GitHub 上取得</a></p> - -<h3 id="ConfirmCredentials">確認認證</h3> - -<p> - 這個範例示範如何在您的應用程式中使用裝置認證做為驗證方法。 -</p> - -<p><a href="https://github.com/googlesamples/android-ConfirmCredential">在 GitHub 上取得</a> -</p> - -<h3 id="FingerprintDialog">指紋對話方塊</h3> - -<p> - 這個範例示範如何在您的應用程式中辨識註冊的指紋以驗證使用者。 - -</p> - -<p><a href="https://github.com/googlesamples/android-FingerprintDialog">在 GitHub 上取得</a></p> - -<h3 id="AutomaticBackup">針對應用程式進行自動備份</h3> - -<p> - Android M 導入自動備份應用程式設定的功能。這個範例示範如何將篩選規則新增至應用程式以管理設定備份。 - -</p> - -<p><a href="https://github.com/googlesamples/android-AutoBackupForApps">在 GitHub 上取得</a></p> - -<h3 id="CameraRaw">相機 2 Raw</h3> - -<p> - 示範如何使用 <code>Camera2</code> API,以擷取 RAW 相機緩衝區並另存為 <code>DNG</code> 檔案。 - -</p> - -<p><a href="https://github.com/googlesamples/android-Camera2Raw">在 GitHub 上取得</a></p> - -<h3 id="ActiveNotification">使用中通知</h3> - -<p> - 這個範例示範 -<a href="{@docRoot}reference/android/app/NotificationManager.html"><code>NotificationManager</code></a> - 如何將您應用程式目前顯示的通知數目告訴您。 -</p> - -<p><a href="https://github.com/googlesamples/android-ActiveNotifications">在 GitHub 上取得</a></p> diff --git a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd deleted file mode 100644 index 1769f74dcfd7..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd +++ /dev/null @@ -1,207 +0,0 @@ -page.title=設定預覽版 SDK -page.image=images/cards/card-set-up_16-9_2x.png - -@jd:body - - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#get-as13">取得 Android Studio 1.3</a></li> - <li><a href="#get-sdk">取得預覽版 SDK</a></li> - <li><a href="#create-update">建立或更新專案</a></li> - <li><a href="#setup-test">設定以進行測試</a></li> - </ol> - </div> -</div> - -<p>M 開發人員預覽版 SDK 可以從 Android SDK Manager 取得。本文件假設您熟悉 Android 應用程式開發工作,例如使用 Android SDK Manager 和建立專案。 - -如果您是 Android 的新手,請先參閱<a href="{@docRoot}training/basics/firstapp/index.html">建置您的第一個應用程式</a>訓練課程。 -</a> -</p> - -<h2 id="get-as13">取得 Android Studio 1.3</h2> - -<p>開發人員預覽版最適合與處於預覽版狀態的 Android Studio 1.3 一起使用。 -強烈建議您安裝 Android Studio 1.3 預覽版與開發人員預覽版一起使用。 -</p> - -<p class="caution"><strong>注意:</strong>Android Studio 1.3 的測試預覽版仍在持續開發中。 -如果您使用主要開發電腦來測試開發人員預覽版,可以建立第二個 Android Studio 安裝以用於測試。 - -</p> - -<p>如要安裝 Android Studio 1.3 預覽版:</p> - -<ol> - <li>下載並啟動 <a href="{@docRoot}tools/studio/index.html">Android Studio</a>。 - - </li> - - <li>開啟 [Settings] (設定) 視窗 (在 Windows 上,您可以選擇 [檔案] > [設定] 來執行此動作)。<strong> -</strong><strong></strong>選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Updates] (更新) 面板。<strong></strong> - - - - <p class="aside">在 OSX 上,您可以在 Android Studio 的「Preferences」(偏好設定) 視窗中,找到 [Appearance & Behavior] (外觀和行為) 面板。<strong></strong><strong></strong> - -</p> - </li> - - <li> 在「Updates」(更新) 面板上,選擇以下選項:[Automatically check updates for: (自動檢查以下的可用更新:) -<strong></strong><strong>Canary Channel] (測試版管道)。</strong> - </li> - - <li>在「Updates」(更新) 面板上,選取 [Check Now] (立即檢查) 以檢查是否有可用的最新測試版建置。<strong></strong><strong></strong> -版本請在系統提示您時,下載並安裝該版本。 - - </li> -</ol> - -<h2 id="get-sdk">取得預覽版 SDK</h2> - -<p>如要將預覽版 SDK 元件新增至您的開發環境:</p> - -<ol> - <li>啟動 Android Studio 1.3 預覽版。 - </li> - - <li>開啟 [Settings] (設定) 視窗 (在 Windows 上,您可以選擇 [檔案] > [設定] 來執行此動作)。<strong></strong><strong></strong> -選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Updates] (更新) 面板。<strong></strong> - - - - <p class="aside">在 OSX 上,您可以在 Android Studio 的「Preferences」(偏好設定) 視窗中,找到 [Appearance & Behavior] (外觀和行為) 面板。<strong></strong><strong></strong> - -</p> - </li> - - <li>在「Updates」(更新) 面板上,選擇以下選項:[Automatically check updates for: (自動檢查以下的可用更新:) -<strong></strong><strong></strong>Canary Channel] (測試版管道) 以及 [Automatically check updates for Android SDK: (自動檢查 Android SDK 的可用更新:)<strong> -Preview Channel] (預覽版管道)。</strong> - </li> - - <li>啟動 <strong>Android Studio Manager</strong>。(使用 Android Studio 1.3,SDK Manager 會與 Android Studio 整合,而不是獨立的應用程式。) - - - </li> - - <li>在「Platforms」(平台) 區段下,選取 [Android MNC Preview] (Android MNC 預覽版)。<strong></strong><strong></strong> - - </li> - - <li>在「Tools」(工具) 區段中,選擇最新的 Android SDK 工具、平台工具以及建置工具。<strong></strong><strong></strong><strong></strong><strong></strong> - - - </li> - - <li>按一下 [Install packages] (安裝套件) 並接受所有套件的授權合約。<strong></strong> - - </li> - - <li>開啟 [Settings] (設定) 視窗,然後選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Android SDK] 面板,來驗證已安裝 M 開發人員預覽版。<strong></strong><strong></strong> - -</li> - - <li>在「Android SDK」面板上,選擇 [SDK Platforms] (SDK 平台) 索引標籤。<strong></strong><strong></strong> -「Android MNC Preview」(Android MNC 預覽版) 應該會列在「Installed」(已安裝)。<strong></strong><em></em> -此外,開啟 [SDK Tools] (SDK 工具) 索引標籤以確認已經安裝最新的工具。<strong></strong> - - - </li> -</ol> -<p>完成這些步驟之後,就能在您的開發環境中使用預覽版元件。 - </p> - - -<h2 id="create-update">建立或更新專案</h2> - -<p> - 為了使用預覽版 API,您必須建立或更新開發專案,才能使用預覽版元件。 - -</p> - - -<h3 id="create">建立新專案</h3> - -<p> - 建議您使用 Android Studio 搭配預覽版來建立專案。依照<a href="{@docRoot}sdk/installing/create-project.html">建立專案</a>中所述的步驟執行,直到您到達專案精靈中的「大小」畫面。 - -<em></em>接著,執行下列步驟以建立為預覽版設定的專案。 - -</p> - -<ul> - <li>核取 [Phone and Tablet] (手機與平板電腦)。<strong></strong></li> - <li>選取 [MNC:<strong></strong>Android M (Preview)] (Android M (預覽版)),它位於 [Minimum SDK] (SDK 最低版本)。<strong></strong> -</li> -</ul> - - -<h3 id="update">更新現有的專案</h3> - -<p> - 對於現有專案,您必須修改專案設定以啟用預覽版 API。在您的開發環境中,開啟模組的 <code>build.gradle</code> 檔案,然後將這些值設定如下: - - -</p> - -<ul> - <li>將 <code>compileSdkVersion</code> 設定為 <code>'android-MNC'</code></li> - <li>將 <code>minSdkVersion</code> 設定為 <code>'MNC'</code></li> - <li>將 <code>targetSdkVersion</code> 設定為 <code>'MNC'</code></li> -</ul> - - -<h2 id="setup-test">設定以進行測試</h2> - -<p> - 利用預覽版測試應用程式,需要您將裝置或虛擬裝置設定為使用平台的預覽版本。 -如果您有相容的裝置,您可以安裝預覽版平台以用於測試。 -否則,您可以設定虛擬裝置以用於測試。 -</p> - -<h3 id="setup-device">設定實體裝置</h3> - -<p> - 如果您有 Nexus 5、Nexus 6、Nexus 9 或 Android 電視,您可以在這些裝置上安裝預覽版系統映像,以測試您的應用程式。您可以使用 Android Virtual Device Manager (Android 虛擬裝置管理員) 工具,從 Android Studio 內設定使用平台預覽版本的虛擬裝置。 - - - -</p> - -<p class="caution"> - <strong>重要說明:</strong>在裝置上安裝預覽版映像,會「移除當中的所有資料」,因此您應該在安裝預覽版映像之前備份任何資料。<em></em> - -</p> - -<h3 id="setupAVD">設定虛擬裝置</h3> - -<p> - 您可以使用 Android Virtual Device Manager (Android 虛擬裝置管理員) 工具,從 Android Studio 內設定使用平台預覽版本的虛擬裝置。 - -</p> - -<p>如要使用 AVD Manager 來建立 AVD:</p> - -<ol> - <li>請在您的開發環境中安裝預覽版 SDK,如<a href="{@docRoot}preview/setup-sdk.html">設定預覽版 SDK</a> 中所述。 - -</li> - <li>依照<a href="{@docRoot}tools/devices/managing-avds.html">使用 AVD Manager 來管理 AVD</a>中的步驟執行。 - -使用下列設定: - <ul> - <li><strong>裝置:</strong>Nexus 5、Nexus 6、Nexus 9 或 Android 電視</li> - <li><strong>目標:</strong> - Android M (預覽版) - API 級別 M</li> - <li><strong>ABI:</strong> x86</li> - </ul> - </li> -</ol> - -<p> - 如需建立虛擬裝置以用於測試的詳細資訊,請參閱<a href="{@docRoot}tools/devices/index.html">管理虛擬裝置</a>。 -</p> diff --git a/docs/html-intl/intl/zh-tw/preview/testing/guide.jd b/docs/html-intl/intl/zh-tw/preview/testing/guide.jd deleted file mode 100644 index 879ec02cd5f5..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/testing/guide.jd +++ /dev/null @@ -1,187 +0,0 @@ -page.title=測試指南 -page.image=images/cards/card-build_16x9_2x.png -page.keywords=previewresources,androidm,testing,permissions - -@jd:body - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#runtime-permissions">測試權限</a></li> - <li><a href="#doze-standby">測試休眠與應用程式待命</a></li> - <li><a href="#ids">自動備份與裝置識別碼</a></li> - </ol> - </div> -</div> - -<p> - Android M 開發人員預覽版能夠讓您有機會確保您的應用程式能搭配下一個版本的平台運作。 -此預覽版包含許多 API 和足以影響應用程式的行為變更,如 <a href="{@docRoot}preview/api-overview.html">API 總覽</a>和<a href="{@docRoot}preview/behavior-changes.html">行為變更</a>中所述。 - -使用預覽版測試應用程式時,您必須著重在許多特定的系統變更,以確保使用者都能擁有良好的體驗。 - - -</p> - -<p> - 本指南說明對應用程式測試預覽功能的內容與方法。您應優先測試這些特定的預覽功能,因為這些功能可能會對您的應用程式行為有非常大的影響: - - -</p> - -<ul> - <li><a href="#runtime-permissions">權限</a> - </li> - <li><a href="#doze-standby">休眠與應用程式待命</a> - </li> - <li><a href="#ids">自動備份與裝置識別碼</a></li> -</ul> - -<p> - 如需有關如何設定使用預覽版系統映像的裝置或虛擬裝置以進行測試的詳細資訊,請參閱<a href="{@docRoot}preview/setup-sdk.html">設定預覽版 SDK</a>。 - -</p> - - -<h2 id="runtime-permissions">測試權限</h2> - -<p> - 新的<a href="{@docRoot}preview/features/runtime-permissions.html">權限</a>模型改變使用者對您的應用程式分配權限的方式。 -以前在安裝過程中會授與所有權限,現在您的應用程式則必須在執行階段向使用者要求個別的權限。 - -對於使用者而言,這個行為能夠對每個應用程式的Activity提供更細膩的控制,而且也能更瞭解應用程式為何要要求特定權限。 -使用者可以隨時個別對應用程式授與或撤銷權限。 -預覽版的這個功能最有可能影響到您應用程式的行為,而且可能會導致應用程式某些功能無法正常運作,或者這些功能會以降級的狀態運作。 - - -</p> - -<p class="caution"> - 這個變更會影響在新平台上執行的所有應用程式,甚至影響並非針對新平台版本開發的應用程式。 -平台會對舊版應用程式提供有限的相容性行為,但是您最好還是立即開始規劃將應用程式移轉到新的權限模型,以便能在官方平台上市時發佈更新的應用程式版本。 - - -</p> - - -<h3 id="permission-test-tips">測試祕訣</h3> - -<p> - 使用下列測試祕訣,可以幫助您使用新的權限行為,規劃和執行應用程式測試。 - -</p> - -<ul> - <li>辨別應用程式目前的權限與相關的程式碼路徑。</li> - <li>針對所有權限保護的服務與資料測試使用者流程。</li> - <li>測試各種組合的授與/撤銷權限。</li> - <li>從命令列使用 {@code adb} 工具管理權限: - <ul> - <li>依群組列出權限與狀態: - <pre>adb shell pm list permissions -d -g</pre> - </li> - <li>使用下列語法授與或撤銷一或多個權限:<br> - <pre>adb shell pm [grant|revoke] <permission.name> ...</pre> - </li> - </ul> - </li> - <li>分析使用權限的應用程式服務。</li> -</ul> - -<h3 id="permission-test-strategy">測試策略</h3> - -<p> - 權限變更會影響應用程式的結構和設計,同時也會影響使用者的體驗以及您提供給使用者的流程。 -您應該評估應用程式目前的權限作法,然後開始規劃您要提供的新流程。 -官方版本的平台會提供相容性行為,但是您最好規劃更新您的應用程式,而不要依賴這些行為。 - - -</p> - -<p> - 辨別您的應用程式實際需要且使用到的權限,然後找出使用受權限保護之服務的各種程式碼路徑。 -您可以在新平台上測試,並且進行程式碼分析,來達到上述目的。 -測試時,您應該變更應用程式的 {@code targetSdkVersion} 為預覽版本,著重在加入執行階段權限。 -如需詳細資訊,請參閱<a href="{@docRoot}preview/setup-sdk.html#">設定預覽版 SDK</a>。 - -</p> - -<p> - 測試各種撤銷/加入權限的組合,著重在依據這些權限的使用者流程。 -如果相依性不明顯或邏輯不清楚時,您可以考慮重新分解或劃分流程以消除相依性,或是更清楚為何需要某個權限。 - - -</p> - -<p> - 如需有關執行階段權限行為、測試和建議做法的詳細資訊,請參閱 <a href="{@docRoot}preview/features/runtime-permissions.html">權限</a>開發人員預覽版頁面。 - - -</p> - - -<h2 id="doze-standby">測試休眠與應用程式待命</h2> - -<p> - 當裝置處於閒置狀態,或者當您的應用程式不在使用中時,「休眠」與「應用程式待命」的省電功能會限制應用程式可以執行的背景處理量。 -系統可能加諸在應用程式的限制,包括網路受到限制或沒有網路、背景工作暫停、通知暫停、喚醒要求被略過,以及警示被略過。 - -如要確保您的應用程式能正確運作,而且這些省電功能也達到最好的效益,您應該透過模擬這些低電量狀態,測試您的應用程式。 - - -</p> - -<h4 id="doze">搭配休眠測試應用程式</h4> - -<p>對您的應用程式測試休眠:</p> - -<ol> -<li>設定 M 預覽版系統映像的硬體裝置或虛擬裝置。</li> -<li>將裝置連線到您的開發電腦並安裝您的應用程式。</li> -<li>執行您的應用程式並讓應用程式保持使用中。</li> -<li>執行下列命令,模擬裝置進入「休眠」模式: - -<pre> -$ adb shell dumpsys battery unplug -$ adb shell dumpsys deviceidle step -$ adb shell dumpsys deviceidle -h -</pre> - - </li> - <li>觀察當裝置重新啟動時的應用程式行為。當裝置結束「休眠」時,確定應用程式順利回復。 -</li> -</ol> - - -<h4 id="standby">搭配應用程式待命測試應用程式</h4> - -<p>對您的應用程式測試應用程式待命模式:</p> - -<ol> - <li>設定 M 預覽版系統映像的硬體裝置或虛擬裝置。</li> - <li>將裝置連線到您的開發電腦並安裝您的應用程式。</li> - <li>執行您的應用程式並讓應用程式保持使用中。</li> - <li>執行下列命令,模擬應用程式進入待命模式: - -<pre> -$ adb shell am broadcast -a android.os.action.DISCHARGING -$ adb shell am set-idle <packageName> true -</pre> - - </li> - <li>使用下列命令,模擬喚醒應用程式: - <pre>$ adb shell am set-idle <packageName> false</pre> - </li> - <li>觀察當應用程式被喚醒時的行為。確定應用程式順利從待命模式回復。 -您尤其要檢查應用程式通知與背景工作是否有如預期般繼續運作 -。</li> -</ol> - -<h2 id="ids">針對應用程式進行自動備份與裝置特定識別碼</h2> - -<p>如果您的應用程式在內部儲存空間存有任何裝置特定識別碼,例如 Google -雲端通訊註冊 ID,請確認按照建議做法排除這些儲存位置不要進行自動備份,如<a href="{@docRoot}preview/backup/index.html">針對應用程式進行自動備份</a>中所述。 - - - </p> diff --git a/docs/html-intl/intl/zh-tw/preview/testing/performance.jd b/docs/html-intl/intl/zh-tw/preview/testing/performance.jd deleted file mode 100644 index 5437e9da6bfa..000000000000 --- a/docs/html-intl/intl/zh-tw/preview/testing/performance.jd +++ /dev/null @@ -1,656 +0,0 @@ -page.title=測試顯示效能 -page.image=images/cards/card-test-performance_2x.png -page.keywords=效能, fps, 工具 - -@jd:body - - -<div id="qv-wrapper"> - <div id="qv"> - <h2>本文件內容</h2> - <ol> - <li><a href="#measure">測量 UI 效能</a> - <ul> - <li><a href="#aggregate">彙總畫面統計資料</a></li> - <li><a href="#timing-info">精確畫面計時資訊</a></li> - <li><a href="#timing-dump">簡易畫面計時傾印</a></li> - <li><a href="#collection-window">控制統計資料收集時間</a></li> - <li><a href="#diagnose">診斷效能回復</a></li> - <li><a href="#resources">其他資源</a></li> - </ul> - </li> - <li><a href="#automate">自動化 UI 效能測試</a> - <ul> - <li><a href="#ui-tests">設定 UI 測試</a></li> - <li><a href="#automated-tests">設定自動化 UI 測試</a></li> - <li><a href="#triage">分類和修正觀察到的問題</a></li> - </ul> - </li> - </ol> - </div> -</div> - - -<p> - 使用者介面效能測試可確保您的應用程式不只符合功能需求,與使用者與應用程式的互動也無比順暢,執行時每秒一致有 60 個畫面 (<a href="https://www.youtube.com/watch?v=CaMTIgxCSqU&index=25&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">為什麼 60fps?</a>),任何畫面都不會遺漏或延遲,或稱為「閃避」<em></em>現象。 - - -本文件說明可用以測量 UI 效能的工具,以及呈現可將 UI 效能測量與測試做法整合的方法。 - - -</p> - - -<h2 id="measure">測量 UI 效能</h2> - -<p> - 為改善效能,首先您需要測量系統效能的能力,接著在管道的各部分發生問題時加以診斷和辨識。 - - -</p> - -<p> - <em><a href="https://source.android.com/devices/tech/debug/dumpsys.html">dumpsys</a></em> 是一種 Android 工具,可在裝置上執行和傾印有關系統服務狀態的有趣資訊。 - -將 <em>gfxinfo</em> 命令傳送至 dumpsys,會將錄製階段所發生與動畫的畫面相關的效能資訊以 logcat 提供輸出。 - - -</p> - -<pre> -> adb shell dumpsys gfxinfo <PACKAGE_NAME> -</pre> - -<p> - 此命令會產生多種不同的畫面計時資料。 -</p> - -<h3 id="aggregate">彙總畫面統計資料</h3> - -<p> - 使用 M 預覽版,命令會在程序的生命週期全程收集畫面資料,並將彙總的分析列印到 logcat。 -例如: -</p> - -<pre class="noprettyprint"> -Stats since: 752958278148ns -Total frames rendered: 82189 -Janky frames: 35335 (42.99%) -90th percentile: 34ms -95th percentile: 42ms -99th percentile: 69ms -Number Missed Vsync: 4706 -Number High input latency: 142 -Number Slow UI thread: 17270 -Number Slow bitmap uploads: 1542 -Number Slow draw: 23342 -</pre> - -<p> - 這些高階統計資料是以高階方式轉換應用程式的轉譯效能,還有其在許多畫面的穩定性。 - -</p> - - -<h3 id="timing-info">精確畫面計時資訊</h3> - -<p> - M 預覽版隨附新的命令 gfxinfo,而 <em>framestats</em> 可從最近的畫面提供相當詳細的畫面計時資訊,讓您可以追蹤並更準確進行除錯。 - - -</p> - -<pre> ->adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats -</pre> - -<p> - 此命令會從應用程式所產生至少 120 個畫面當中,加上奈秒時間戳記印出畫面計時資訊。以下範例是 adb dumpsys gfxinfo <PACKAGE_NAME> framestats 的原始輸出: - - -</p> - -<pre class="noprettyprint"> -0,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954, -0,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120, -0,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527, -0,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683, -</pre> - -<p> - 這裡的每一行輸出都代表應用程式產生的一個畫面。每行都有固定的資料欄編號,描述畫面產生管道的各階段所花費的時間。 -下一節會詳細說明此格式,包括各資料欄代表的意義。 - -</p> - - -<h4 id="fs-data-format">Framestats 資料格式</h4> - -<p> - 由於資料區塊是以 CSV 格式輸出,所以可以直接將它貼到選擇的試算表工具,或使用指令碼來收集和剖析。 -下表說明輸出資料欄的格式。 -所有時間戳記都以奈秒為單位。 -</p> - -<ul> - <li>FLAGS - <ul> - <li>FLAGS 資料欄的資料列均為 0,從 FRAME_COMPLETED 資料欄減去 INTENDED_VSYNC 資料欄可計算得出總畫面時間。 - - </li> - - <li>如果這個值非零,應該略過該資料列,因為從正常效能來判斷,畫面已是極端值,版面配置與繪製預期都要花費 16ms 以上的時間。 - -以下是發生這種情況的幾個原因: - <ul> - <li>視窗版面配置改變 (例如應用程式的第一個畫面或經過旋轉) - - </li> - - <li>也可能是略過畫面,在這種情況下,某些值會含有記憶體回收的時間戳記。 -例如,如果畫面速度超過 60fps,或如果螢幕上空無一物,最後卻有所改變,就可能會略過畫面,這不一定是應用程式發生問題的徵兆。 - - - </li> - </ul> - </li> - </ul> - </li> - - <li>INTENDED_VSYNC - <ul> - <li>預期的畫面起始點。如果這個值和VSYNC 不同,表示 UI 執行緒中發生的工作使它無法即時回應 vsync 訊號。 - - - </li> - </ul> - </li> - - <li>VSYNC - <ul> - <li>用於所有 vsync 接聽器和繪製畫面的時間值 (Choreographer 畫面回呼、動畫、View.getDrawingTime() 等等) - - </li> - - <li>如要深入瞭解有關 VSYNC 和它如何影響您的應用程式,請參閱<a href="https://www.youtube.com/watch?v=1iaHxmfZGGc&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&index=23">瞭解 VSYNC</a> 影片。 - - - </li> - </ul> - </li> - - <li>OLDEST_INPUT_EVENT - <ul> - <li>輸入佇列中最舊輸入事件的時間戳記,或為 Long.MAX_VALUE,若沒有該畫面的輸入事件的話。 - - </li> - - <li>這個值主要用於平台工作,對應用程式開發人員用處不大。 - - </li> - </ul> - </li> - - <li>NEWEST_INPUT_EVENT - <ul> - <li>輸入佇列中最新輸入事件的時間戳記,或為 0,若沒有該畫面的輸入事件的話。 - - </li> - - <li>這個值主要用於平台工作,對應用程式開發人員用處不大。 - - </li> - - <li>不過,透過查看 (FRAME_COMPLETED - NEWEST_INPUT_EVENT),可以大略知道應用程式還會延遲多少時間。 - - </li> - </ul> - </li> - - <li>HANDLE_INPUT_START - <ul> - <li>將輸入事件分配給應用程式時的時間戳記。 - </li> - - <li>查看這個值與 ANIMATION_START 之間的時間,即可測量出應用程式花費在處理輸入事件的時間。 - - </li> - - <li>如果這個數字很高 (>2ms),這表示應用程式花費在處理輸入事件(例如 View.onTouchEvent()) 的時間過長,指出需要將這項工作最佳化,或卸載交由其他執行緒處理。 - -請注意,還有一些情況本就預期且可接受這個數字較大,例如會啟動新活動或類似工作的點擊事件。 - - - </li> - </ul> - </li> - - <li>ANIMATION_START - <ul> - <li>向 Choreographer 註冊的動畫執行時的時間戳記。 - </li> - - <li>查看這個值與 PERFORM_TRANVERSALS_START 之間的時間,即可判斷它花費多久的時間評估所有執行中的動畫器 (常見的有 ObjectAnimator、ViewPropertyAnimator 及 Transitions)。 - - - </li> - - <li>如果這個數字很高 (>2ms),可查看您的應用程式是否撰寫任何自訂動畫器,或 ObjectAnimators 正進行動畫處理的資料欄,並確定它們都適用於動畫。 - - - </li> - - <li>如要深入瞭解 Choreographer,請參閱<a href="https://developers.google.com/events/io/sessions/325418001">順滑流暢與否</a>影片。 - - </li> - </ul> - </li> - - <li>PERFORM_TRAVERSALS_START - <ul> - <li>如果您從這個值中減去 DRAW_START,就可以得知版面配置和測量階段花費多久的時間完成。(請注意,在捲動或動畫期間,您會希望這個值趨近於零。) - - - </li> - - <li>如要深入瞭解轉譯管道的測量與版面配置階段,請參閱<a href="https://www.youtube.com/watch?v=we6poP0kw6E&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&index=27">無效判定、版面配置及效能</a>影片。 - - - </li> - </ul> - </li> - - <li>DRAW_START - <ul> - <li>performTraversals 的繪製階段開始的時間。這是判定無效的任何檢視顯示清單的記錄起始點。 - - </li> - - <li>這個值與 SYNC_START 之間的時間,就是對樹狀結構中所有無效判定檢視呼叫 View.draw() 所花費的時間。 - - </li> - - <li>如需繪製模型的詳細資訊,請參閱<a href="{@docRoot}guide/topics/graphics/hardware-accel.html#hardware-model">硬體加速</a>或<a href="https://www.youtube.com/watch?v=we6poP0kw6E&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu&index=27">無效判定、版面配置及效能</a>影片。 - - - </li> - </ul> - </li> - - <li>SYNC_START - <ul> - <li>繪製的同步階段開始的時間。 - </li> - - <li>如果這個值與 ISSUE_DRAW_COMMANDS_START 之間的時間相當長 (約為 >0.4ms ),通常代表已繪製許多必須上傳至 GPU 的新點陣圖。 - - - </li> - - <li>如要深入瞭解同步階段,請參閱<a href="https://www.youtube.com/watch?v=VzYkVL1n4M8&index=24&list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu">設定檔 GPU 轉譯</a>影片。 - - </li> - </ul> - </li> - - <li>ISSUE_DRAW_COMMANDS_START - <ul> - <li>硬體轉譯器開始對 GPU 發出繪製命令的時間。 - </li> - - <li>這個值與 FRAME_COMPLETED 之間的時間大約就是應用程式產生的 GPU 工作量。 -太過度繪製或無效轉譯效果之類的問題都會顯示於此。 - - </li> - </ul> - </li> - - <li>SWAP_BUFFERS - <ul> - <li>相對於無趣的平台工作以外,呼叫 eglSwapBuffers 的時間。 - - </li> - </ul> - </li> - - <li>FRAME_COMPLETED - <ul> - <li>全部完成!花費在處理這個畫面的總時間,計算方法是 FRAME_COMPLETED - INTENDED_VSYNC。 - - </li> - </ul> - </li> - -</ul> - -<p> - 您能以不同的方式使用這項資料。顯示不同延遲貯體中畫面時間分布的長條圖就是一種簡單但實用的方式,請見下圖。 - -本圖可一目瞭然地告訴我們,大部分畫面都低於 16ms 的上限 (紅色除外),但只有幾個畫面明顯超過上限。 - -我們可以查看此長條圖一段時間的變化,觀察出現的大規模位移或產生新的極端值。 -您也能根據資料中的許多時間戳記將輸入延遲、花費在版面配置的時間或其他類似的有趣度量指標繪成圖表。 - - -</p> - -<img src="{@docRoot}preview/images/perf-test-framestats.png"> - - -<h3 id="timing-dump">簡易畫面計時傾印</h3> - -<p> - 如果 [開發人員選項] 中的 [設定檔 GPU 轉譯]<strong></strong> 設定為 [In adb shell dumpsys gfxinfo]<strong></strong> -,<code>adb shell dumpsys gfxinfo</code> 命令會印出最近 120 個畫面的計時資訊,以定位鍵分隔值分成數個不同類別。 - -這項資料非常適合用來指出可能是繪製管道的哪個部分太慢。 - -</p> - -<p> - 類似於上述的 <a href="#fs-data-format">framestats</a>,可以直接將它貼到選擇的試算表工具,或使用指令碼來收集和剖析。 - -下圖顯示許多由應用程式產生的畫面花費時間的分類細項。 - -</p> - -<img src="{@docRoot}preview/images/perf-test-frame-latency.png"> - -<p> - 執行 gfxinfo、複製輸出、將輸出貼入試算表應用程式,然後將資料繪製成堆疊長條圖的結果。 - -</p> - -<p> - 每個直條都代表動畫的一個畫面,其高度代表計算該畫面所花費的毫秒數。 -長條的每個色塊都代表轉譯管道的不同階段,好讓您看出應用程式的哪部分可能產生瓶頸。 - -如需瞭解繪製管道以及如何最佳化的詳細資訊,請參閱硬體加速或<a href="https://www.youtube.com/watch?v=we6poP0kw6E&index=27&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">無效判定、版面配置及效能</a>影片。 - - -</p> - - -<h3 id="collection-window">控制統計資料收集時間</h3> - -<p> - framestats 與簡單的畫面計時都會收集極短時間內的資料 – 轉譯約需兩秒。 -為了精確控制這段時間,例如只限特定動畫的資料,您可以重設所有計數器,然後彙總收集的統計資料。 - - -</p> - -<pre> ->adb shell dumpsys gfxinfo <PACKAGE_NAME> reset -</pre> - -<p> - 這也能和傾印命令本身結合使用,定期收集和重設,持續擷取兩秒時間內的畫面。 - - -</p> - - -<h3 id="diagnose">診斷效能回復</h3> - -<p> - 識別回復是追蹤問題和維護應用程式健康情況的第一步。 -不過,dumpsys 只能識別有問題存在與相關的嚴重性。 -您仍需要診斷造成效能問題的特定原因,以及找出適當的修正方式。 -因此,強烈建議您使用 <a href="{@docRoot}tools/help/systrace.html">systrace</a> 工具。 - -</p> - - -<h3 id="resources">其他資源</h3> - -<p> - 如需 Android 的轉譯管道如何運作、常見問題以及如何修正的詳細資訊,下列的一些資訊可能會很實用: - - -</p> - -<ul> - <li>轉譯效能 101 - </li> - <li>為什麼 60fps? - </li> - <li>Android UI 和 GPU - </li> - <li>無效判定、版面配置及效能 - </li> - <li>利用 Systrace 分析 UI 效能 - </li> -</ul> - - -<h2 id="automate">自動化 UI 效能測試</h2> - -<p> - UI 效能測試的方法之一就是讓測試人員對目標應用程式執行一組使用者操作,並以肉眼查看,或花費很長一段時間使用工具導向的方法,尋找閃避現象。 - -但這種靠人工的方式充滿危險,人類對畫面率變化的感知能力因人而異,而且這種方法也很費時、繁瑣且容易出錯。 - - -</p> - -<p> - 較有效率的方法是從自動化的 UI 測試中記錄和分析重要效能度量指標。 -Android M 開發人員預覽版包含新的記錄功能,能夠輕鬆判斷應用程式動畫中閃避現象的數量與嚴重程度,還能用來建置嚴謹的程序,判斷目前的效能並追蹤未來的效能目標。 - - - -</p> - -<p> - 本文會逐步說明建議用來使用資料以自動化效能測試的方法。 - -</p> - -<p> - 這種方法大多分成兩個主要動作。首先,識別您要測試的項目,以及測試的方法。其次是設定和維護自動化測試環境。 - - -</p> - - -<h3 id="ui-tests">設定 UI 測試</h3> - -<p> - 在您開始進行自動化測試之前,務必要決定幾個高階決策,才能適當瞭解您的測試空間與可能會有的需求。 - -</p> - -<h4> - 識別要測試的主要動畫 / 流程 -</h4> - -<p> - 請記住,流暢的動畫有所中斷時,就是使用者最容易看見效能低落的時候。 -因此,識別要測試哪種類型的 UI 動作時,最好著重在使用者最常看見或對他們的體驗最重要的主要動畫。 - -例如,以下是一些可能有利於識別的常見情況: -</p> - -<ul> - <li>捲動主要的 ListView 或 RecyclerView - </li> - - <li>非同步等待週期內的動畫 - </li> - - <li>當中會載入 / 操縱點陣圖的任何動畫 - </li> - - <li>包含 Alpha 透明混色的動畫 - </li> - - <li>使用畫布繪製的自訂檢視 - </li> -</ul> - -<p> - 和您團隊的工程人員、設計師及產品經理合作,優先考慮將這些主要產品動畫放入測試涵蓋範圍內。 - -</p> - -<h4> - 定義未來目標並予以追蹤 -</h4> - -<p> - 從高階觀點來看,重要的是識別特定的效能目標,並著重在撰寫測試及收集相關資料。 -例如: -</p> - -<ul> - <li>您是否只想初次開始追蹤 UI 效能以深入瞭解? - </li> - - <li>您是否想要避免可能在未來導入的回復? - </li> - - <li>今日有 90% 的順暢畫面並想要在本季達到 98%? - </li> - - <li>今日有 98% 的順暢畫面且不想要回復? - </li> - - <li>您的目標是改善低階裝置上的效能嗎? - </li> -</ul> - -<p> - 在上述的這些情況中,您會想要有歷史追蹤功能,來顯示不同應用程式版本間的效能。 - -</p> - -<h4> - 識別測試要用的裝置 -</h4> - -<p> - 應用程式效能會因其執行所在裝置而異。有些裝置包含的記憶體較少、GPU 較不強大或 CPU 晶片速度較慢。 -這表示可在某組硬體上執行良好的動畫,在其他組合上不一定能執行良好,更糟的是可能會在管道的不同部分產生瓶頸。 - -使用者所見可能會不同,為將這點列入考量,請挑選涵蓋當前高階裝置、低階裝置、平板電腦等的一系列裝置執行測試。 - -尋找 CPU 效能、RAM、畫面密度、大小等方面的變化。 -高階裝置上通過的測試,在低階裝置上可能會失敗。 - -</p> - -<h4> - UI 測試的基本架構 -</h4> - -<p> - 工具套件 (例如 <a href="{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a> 和 <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a>) 是為協助將使用者在您的應用程式四處移動的動作自動化而建置。 - -這些都是模擬使用者與裝置互動的簡單架構。 -如要使用這些架構,您要有效地建立會逐一執行一組使用者動作的獨特指令碼,然後在裝置上自行播放。 - - -</p> - -<p> - 連同 <code>dumpsys gfxinfo</code>,再結合這些自動化測試,您可快速建立可重現系統,讓您執行測試並測量該特定情況下的效能資訊。 - - -</p> - - -<h3 id="automated-tests">設定自動化 UI 測試</h3> - -<p> - 在您能夠執行 UI 測試,還有可從單一測試收集資料的管道後,下一個重要步驟是利用可多次執行該項測試的架構,然後彙總產生的效能資料,以供您的開發團隊進一步分析。 - - - -</p> - -<h4> - 測試自動化的架構 -</h4> - -<p> - 直接在目標裝置/模擬器上執行的 UI 測試架構 (例如 <a href="{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a>) 毫無價值。 -因為效能收集資訊是由主控機器透過 ADB 傳送命令驅動 <em>dumpsys gfxinfo</em> 來完成。 -<a href="{@docRoot}tools/help/monkeyrunner_concepts.html">MonkeyRunner</a> 架構是為了協助橋接這些個別實體開發。在主控機器上執行的指令碼處理系統可對一組連接的裝置發出命令,也能接收來自這些裝置的資料。 - - - -</p> - -<p> - 建置一組指令碼以適當自動化 UI 效能測試,至少應能利用 monkeyRunner 來完成下列工作: - -</p> - -<ul> - <li>對目標裝置或模擬器載入和啟動所需的 APK。 - </li> - - <li>啟動並允許執行 UI Automator UI 測試 - </li> - - <li>透過 dumpsys gfxinfo 收集效能資訊。<em></em><em></em> - </li> - - <li>彙總資訊並以對開發人員有用的方式顯示。 - </li> -</ul> - - -<h3 id="triage">分類和修正觀察到的問題</h3> - -<p> - 在辨識出問題模式與回復之後,下一個步驟是辨識和套用修正。 -如果您的自動化測試架構會為畫面保留精確的計時分類細項,可幫助您詳細審察目前可疑的程式碼/版面配置變化 (在回復的情況下),或在您切換為靠人工探究時縮小要分析的系統部分。 - - -如需靠人工探究時,<a href="{@docRoot}tools/help/systrace.html">systrace</a> 是開始進行的好地方,顯示轉譯管道各階段、系統中每個執行緒與核心,還有您所定義任何自訂事件標記的精確計時資訊。 - - -</p> - -<h4> - 適當分析暫時的計時 -</h4> - -<p> - 請務必注意,從轉譯效能中取得和測量計時的困難度。 -這些數字不具決定性且通常受系統狀態、可用記憶體數目、溫度調節,還有上次太陽閃焰何時衝擊您所在地區影響。 - -重點是您執行相同的測試兩次,而每次得到的數字都有些微不同,數字很接近但不會完全相同。 - - -</p> - -<p> - 以這種方式適當收集和分析資料,表示執行相同的測試多次,並累積結果取平均值或中間值。(為了簡單起見,我們稱之為「批次」) 這可讓您粗略計算測試的效能,而不需要精確的計時。 - - - -</p> - -<p> - 在變更的程式碼之間使用批次,可看出那些變更對效能的影響。 -如果前次變更批次的平均畫面率大於後來變更批次,您通常會有那項特定變更的整體 win wrt 效能。 - - -</p> - -<p> - 這表示您執行的任何自動化 UI 測試都應將此概念列入考量,同時考量可能會在測試期間發生的任何異常情況。 -例如,您的應用程式效能若因為某些裝置問題而突然下降 (並非由您的應用程式引起),您可能會想要重新執行批次,以讓取得的計時較不混亂。 - - - -</p> - -<p> - 應該執行多少次測試才能獲得有意義的測量結果呢?最少應執行 10 次,若執行更多次 (像是 50 或 100 次) 可以產生更準確的結果 (當然您現在是以時間換取準確度)。 - - -</p> diff --git a/docs/html-intl/intl/zh-tw/sdk/index.jd b/docs/html-intl/intl/zh-tw/sdk/index.jd new file mode 100644 index 000000000000..ba11c46cd791 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/sdk/index.jd @@ -0,0 +1,429 @@ +page.title=下載 Android Studio 和 SDK 工具 +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=下載官方 Android IDE 和開發人員工具以建置適用於 Android 手機、平板電腦、穿戴式裝置、電視等裝置的應用程式。 + +@jd:body + +<style type="text/css"> + .offline {display:none;} + h2.feature { + padding-top:30px; + margin-top:0; + clear:both; + } + .feature-blurb { + margin:0px; font-size:16px; font-weight:300; + padding-top:40px; + } + + .landing-button .small { + font-size: 12px; + font-weight: normal; + line-height: 12px; + display: block; + } + + h1.studio-logo { + width:226px; + height:78px; + display:block; + padding:0; + white-space: nowrap; + text-indent: -10000px; + font-size:0px; + background: url({@docRoot}images/tools/studio-logo.png); + background-image: -webkit-image-set(url({@docRoot}images/tools/studio-logo.png) 1x, url({@docRoot}images/tools/studio-logo_2x.png) 2x); + background-size: 226px 78px; + } + +</style> + + + + + +<div style="position:relative;"> + + +<div class="wrap" id="tos" style="display:none;width:inherit;height:650px"> +<div class="col-13" style="margin:0;"> </div><!-- provides top margin for content --> + +<h1 id="tos-header" style="margin-top:0">下載</h1> + +<p class="sdk-terms-intro">在安裝 Android Studio 或單獨的 SDK 工具之前,您必須同意遵守下列條款及細則。 +</p> + +<div class="sdk-terms" onfocus="this.blur()"> +<h2 class="norule">條款及細則</h2> +此為「Android 軟體開發套件授權協議」 + +<h3>1. Introduction</h3> +1.1 The Android Software Development Kit (referred to in the License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK. + +1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time. + +1.3 A "compatible implementation" means any Android device that (i) complies with the Android Compatibility Definition document, which can be found at the Android compatibility website (http://source.android.com/compatibility) and which may be updated from time to time; and (ii) successfully passes the Android Compatibility Test Suite (CTS). + +1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. + + +<h3>2. Accepting this License Agreement</h3> +2.1 In order to use the SDK, you must first agree to the License Agreement. You may not use the SDK if you do not accept the License Agreement. + +2.2 By clicking to accept, you hereby agree to the terms of the License Agreement. + +2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries, including the country in which you are resident or from which you use the SDK. + +2.4 If you are agreeing to be bound by the License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity. + + +<h3>3. SDK License from Google</h3> +3.1 Subject to the terms of the License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable, non-exclusive, and non-sublicensable license to use the SDK solely to develop applications for compatible implementations of Android. + +3.2 You may not use this SDK to develop applications for other platforms (including non-compatible implementations of Android) or to develop another SDK. You are of course free to develop applications for other platforms, including non-compatible implementations of Android, provided that this SDK is not used for that purpose. + +3.3 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you. + +3.4 You may not use the SDK for any purpose not expressly permitted by the License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK. + +3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement. + +3.6 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you. + +3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. + +3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK. + + +<h3>4. Use of the SDK by You</h3> +4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications. + +4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) the License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). + +4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so. + +4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier. + +4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. + +4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach. + + +<h3>5. Your Developer Credentials</h3> +5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials. + + +<h3>6. Privacy and Information</h3> +6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected. + +6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy. + + +<h3>7. Third Party Applications</h3> +7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources. + +7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. + +7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, the License Agreement does not affect your legal relationship with these third parties. + + +<h3>8. Using Android APIs</h3> +8.1 Google Data APIs + +8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service. + +8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so. + + +<h3>9. Terminating this License Agreement</h3> +9.1 The License Agreement will continue to apply until terminated by either you or Google as set out below. + +9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials. + +9.3 Google may at any time, terminate the License Agreement with you if: +(A) you have breached any provision of the License Agreement; or +(B) Google is required to do so by law; or +(C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or +(D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable. + +9.4 When the License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst the License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely. + + +<h3>10. DISCLAIMER OF WARRANTIES</h3> +10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE. + +10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. + +10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + + +<h3>11. LIMITATION OF LIABILITY</h3> +11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING. + + +<h3>12. Indemnification</h3> +12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with the License Agreement. + + +<h3>13. Changes to the License Agreement</h3> +13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available. + + +<h3>14. General Legal Terms</h3> +14.1 The License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK. + +14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. + +14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable. + +14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement. + +14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE. + +14.6 The rights granted in the License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under the License Agreement without the prior written approval of the other party. + +14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. + +<em>November 20, 2015</em> +</div> + + + + + +<div id="next-steps" style="display:none;position:absolute;width:inherit"> + <p>只要再進行幾個步驟,即可建置適用於 Android 的應用程式!</p> + <p>系統即將把您重新導向至 +<a id="next-link" href="{@docRoot}sdk/installing/index.html">安裝 Android SDK</a> 網頁。</p> + +</div><!-- end next-steps --> + + + +<div id="sdk-terms-form"> +<p> +<input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" /> +<label id="agreeLabel" for="agree">我已閱讀並同意上述條款及細則</label> +</p> +<p><a href="" class="button disabled" id="downloadForRealz" onclick="return onDownloadForRealz(this);"></a></p> +</div> + + +</div><!-- end TOS --> + + + + + + +<div id="landing"> + +<div class="col-13"> </div><!-- provides top margin for content --> + +<img src="{@docRoot}images/tools/studio-hero.png" srcset="{@docRoot}images/tools/studio-hero_2x.png 2x, {@docRoot}images/tools/studio-hero.png 1x" width="760" height="400"/> + +<div style="color: #fff; width:226px; height:0; overflow:visible; position:absolute; top:40px; left:25px"> + +<h1 class="studio-logo" style="margin:0 0 35px !important">Android Studio</h1> + +<p style="font-size: 16px; color:#bbb; position: absolute;left: 297px; top: 5px; display: block; +width: 400px;text-align: center;">官方 Android IDE</p> + +<ul style="font-size:12px;line-height:19px;"> +<li>Android Studio IDE</li> +<li>Android SDK 工具</li> +<li>Android 6.0 (Marshmallow) 平台</li> +<li>含 Google API 的 Android 6.0 模擬器系統映像檔</li> +</ul> + +<a class="online landing-button green download-bundle-button" +href="#Other" >Download Android Studio<br/><span class='small'></span></a> + +<!-- this appears when viewing the offline docs --> +<p class="offline"> +想要取得 Android Studio 或獨立 SDK 工具,請造訪 <a href="http://developer.android.com/sdk/index.html">developer.android.com/sdk/</a> +</p> +</div> + +<ul> + <li><a href="#Requirements">系統需求</a></li> + <li><a href="#Other">其他下載選項</a></li> + <li><a href="{@docRoot}sdk/installing/migrate.html">遷移至 Android Studio</a></li> + <li><a href="https://docs.google.com/a/google.com/forms/d/1mjsyfzv3HAnDY-_Kfj-3QJKdpuksyMFs9e73CRwmT6Q/viewform" target="_blank">參與問卷調查</a></li> +</ul> + + + + +<h2 class="feature norule" >智慧型程式碼編輯器</h2> + +<div class="col-9"> + <img src="{@docRoot}images/tools/studio-hero-code.png" srcset="{@docRoot}images/tools/studio-hero-code_2x.png 2x, {@docRoot}images/tools/studio-hero-code.png 1x" width="520" /> +</div><!-- end col-9 (left column) --> + +<div class="col-4 feature-blurb"> + <p>Android Studio 的核心是一個智慧型程式碼編輯器,能夠進行進階的程式碼自動完成、重構及程式碼分析作業。 +</p> + <p>這項功能強大的程式碼編輯器可協助您成為更具生產力的 Android 應用程式開發人員。</p> +</div> + + + + + +<h2 class="feature norule">程式碼範本與 GitHub 整合</h2> + +<div class="col-9"> + <img src="{@docRoot}images/tools/studio-hero-import.png" srcset="{@docRoot}images/tools/studio-hero-import_2x.png 2x, {@docRoot}images/tools/studio-hero-import.png 1x" width="520" /> +</div><!-- end col-9 (left column) --> + +<div class="col-4 feature-blurb"> + <p>全新的專案精靈可協助您及早發起新專案。</p> + + <p>您可以使用模式 (例如導覽匣和資料檢視巡覽區) 範本程式碼,甚至是從 GitHub 匯入 Google 程式碼來發起專案。 +</p> +</div> + + + + +<h2 class="feature norule">多螢幕應用程式開發</h2> + +<div class="col-9"> + <img src="{@docRoot}images/tools/studio-hero-screens.png" srcset="{@docRoot}images/tools/studio-hero-screens_2x.png 2x, {@docRoot}images/tools/studio-hero-screens.png 1x" width="520" /> +</div><!-- end col-9 (left column) --> + +<div class="col-4 feature-blurb"> + <p>建置適用於 Android 手機、平板電腦、Android Wear、Android TV、Android Auto 和 Google Glass 的應用程式。 +</p> + <p>透過 Android Studio 中新的「Android 專案檢視」和模組支援功能,管理應用程式專案和相關資源變得更加容易。 + +</div> + + + + +<h2 class="feature norule">適用於所有形狀和大小的虛擬裝置</h2> + +<div class="col-9"> + <img src="{@docRoot}images/tools/studio-hero-avds.png" srcset="{@docRoot}images/tools/studio-hero-avds_2x.png 2x, {@docRoot}images/tools/studio-hero-avds.png 1x" width="520" /> +</div><!-- end col-9 (left column) --> + +<div class="col-4 feature-blurb"> + <p>Android Studio 隨附預先設定、經過最佳化的模擬器映像檔。</p> + <p>經過更新及簡化的 Virtual Device Manager (虛擬裝置管理員) 可為常見的 Android 裝置提供預先定義的裝置設定檔。 +</p> +</div> + + + + +<h2 class="feature norule"> +Android 版本已隨 Gradle 更新</h2> + +<div class="col-9"> + <img src="{@docRoot}images/tools/studio-hero-gradle.png" srcset="{@docRoot}images/tools/studio-hero-gradle_2x.png 2x, {@docRoot}images/tools/studio-hero-gradle.png 1x" width="520" /> +</div><!-- end col-9 (left column) --> + +<div class="col-4 feature-blurb"> + <p>可使用同一項專案為您的 Android 應用程式建立多個具有不同功能的 APK。</p> + <p>可使用 Maven 管理應用程式依附功能。</p> + <p>可使用 Android Studio 或命令列建置 APK。</p> +</div> + + + + +<h2 class="feature norule">瞭解 Android Studio</h2> +<div style="background:#424242;padding:30px; color:#fff;margin:0 0 15px;"> + +<a class="online landing-button green download-bundle-button" style="margin:0 0 40px 60px;float:right" href="">Download</a> + + <ul> + <li>以 IntelliJ IDEA Community Edition (JetBrains 所推出的常用 Java IDE) 為基礎。</li> + <li>具有彈性的 Gradle 型建置系統。</li> + <li>可產生建置變體和多個 APK。</li> + <li>可為 Google 各項服務和各種裝置類型提供額外的範本支援。</li> + <li>具有支援主題編輯功能的版面配置編輯器。</li> + <li>具有可取得效能、可用性、版本相容性資料及偵測其他問題的 Lint 工具。</li> + <li>具有 ProGuard 和應用程式簽署功能。</li> + <li>內建 Google Cloud Platform 支援,可讓您輕鬆整合 Google Cloud Messaging 與 App Engine。 +</li> + </ul> + +<p style="margin:0"> +如要進一步瞭解 Android Studio 提供的功能,請參閱《<a href="{@docRoot}tools/studio/index.html">Android Studio 基本概念</a>》指南。 +</p> +</div> + + +<p>如果您搭配 ADT 使用 Eclipse,請注意,Android Studio 現已成為 Android 的官方 IDE,因此請遷移至 Android Studio 以接收 IDE 的所有更新資訊。 + +如果想瞭解如何遷移專案,請參閱<a href="{@docRoot}sdk/installing/migrate.html">遷移至 Android Studio</a>。 + +</p> + + + + + + + +<h2 id="Requirements">系統需求</h2> + +<h3>Windows</h3> + +<ul> +<li>Microsoft® Windows® 8/7/Vista/2003 (32 或 64 位元)</li> +<li>2 GB 以上的 RAM (建議準備 4 GB 的 RAM)</li> +<li>400 MB 的硬碟空間</li> +<li>至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)</li> +<li>1280 x 800 以上的螢幕解析度</li> +<li>Java Development Kit (JDK) 7 </li> +<li>提升模擬器效能的選用配件:支援 Intel® VT-x、Intel® EM64T (Intel® 64) 及 Execute Disable (XD) Bit 功能的 Intel® 處理器 +</li> +</ul> + + +<h3>Mac OS X</h3> + +<ul> +<li>Mac® OS X® 10.8.5 以上版本;最高可支援 10.9 (Mavericks)</li> +<li>2 GB 以上的 RAM (建議準備 4 GB 的 RAM)</li> +<li>400 MB 的硬碟空間</li> +<li>至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)</li> +<li>1280 x 800 以上的螢幕解析度</li> +<li>Java Runtime Environment (JRE) 6</li> +<li>Java Development Kit (JDK) 7</li> +<li>提升模擬器效能的選用配件:支援 Intel® VT-x、Intel® EM64T (Intel® 64) 及 Execute Disable (XD) Bit 功能的 Intel® 處理器 +</li> +</ul> + +<p>如果您使用 Mac OS,可搭配 Java Runtime Environment (JRE) 6 執行 Android Studio 以獲得最佳字型顯示效果。 +此外,您還可以設定專案使用 Java Development Kit (JDK) 6 或 JDK 7。</p> + + + +<h3>Linux</h3> + +<ul> +<li>GNOME 或 KDE 桌面</li> +<li>GNU C 程式庫 (glibc) 2.15 以上版本</li> +<li>2 GB 以上的 RAM (建議準備 4 GB 的 RAM)</li> +<li>400 MB 的硬碟空間</li> +<li>至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)</li> +<li>1280 x 800 以上的螢幕解析度</li> +<li>Oracle® Java Development Kit (JDK) 7 </li> +</ul> +<p>已在 Ubuntu® 14.04 Trusty Tahr (能夠執行 32 位元應用程式的 64 位元發行版本) 上經過測試。 +</p> + + + + +<h2 id="Other" style="clear:left">其他下載選項</h2> + +<!-- alternative SDK options follows --> diff --git a/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd b/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd new file mode 100644 index 000000000000..563df51e22d3 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=新增 SDK 封裝 + +page.tags=sdk manager + +@jd:body + +<style> +ol.large { + margin-left:0; +} +ol.large > li { + list-style-position: inside; + list-style-type:none; + margin:30px 0 0 0; + padding:30px 20px; + background:#eee; +} +ol.large > li:nth-child(odd) { +} +ol.large > li:before { + display:inline; + left:-40px; + float:left; + width:20px; + font-size:20px; + line-height:20px; +} +ol.large > li > h2 { + font-size:20px; + line-height:20px; + padding:0 0 0 20px; + margin:0 0 20px 0; + display:inline; + font-weight:normal; +} +ol.large > li:nth-child(1):before { + content:"1. "; +} +ol.large > li:nth-child(2):before { + content:"2. "; +} +ol.large > li:nth-child(3):before { + content:"3. "; +} +ol.large > li:nth-child(4):before { + content:"4. "; +} +ol.large > li:nth-child(5):before { + content:"5. "; +} +ol.large > li:nth-child(6):before { + content:"6. "; +} +</style> + + +<p> +Android SDK 預設不會包括開發所需的所有內容。SDK 將工具、平台及其他元件劃分為不同的封裝。您可以使用 + <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a> 視需要下載。因此,您要先將封裝加入 Android SDK 後,才可以開始進行。 + + +</p> + +<p>如要新增封裝,請透過下列其中一種方式啟動 Android SDK Manager:</p> +<ul> + <li>在 Android Studio 中,按一下工具列的 [<strong>SDK Manager</strong>] +<img src="{@docRoot}images/tools/sdk-manager-studio.png" style="vertical-align:bottom;margin:0;height:17px" />。</li> + <li>如果您不是使用 Android Studio: + <ul> + <li>Windows:按兩下位於 Android SDK 目錄之根目錄的 <code>SDK Manager.exe</code> 檔案。 +</li> + <li>Mac/Linux:開啟終端機並瀏覽到安裝 Android SDK 的 <code>tools/</code> 目錄,然後執行 <code>android sdk</code>。 +</li> + </ul> + </li> +</ul> + +<p>第一次開啟 SDK Manager 時,預設會選取多個封裝。 +保留預設選取的封裝,但務必按照以下步驟確認您已經取得開發所需的所有內容: +</p> + + +<ol class="large"> +<li> + <h2 id="GetTools" class="norule">取得最新的 SDK 工具</h2> + +<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" /> + + <p>設定 Android SDK 時,您一定要下載最新的工具和平台: +</p> + <ol> + <li>開啟 Tools 目錄並選取: + <ul> + <li><strong>Android SDK 工具</strong></li> + <li><strong>Android SDK 平台工具</strong></li> + <li><strong>Android SDK 建置工具</strong> (最新版本)</li> + </ul> + </li> + <li>開啟第一個 Android X.X 資料夾 (最新版本) 並選取: + <ul> + <li><strong>SDK 平台</strong></li> + <li>模擬器的系統映像,例如 <br> + <strong>ARM EABI v7a 系統映像</strong></li> + </ul> + </li> + </ol> +</li> + +<li> + <h2 id="GetSupportLib" class="norule">取得額外 API 的支援程式庫</h2> + + <div class="sidebox"> + <p>以下各項需要支援程式庫:</p> + <ul> + <li><a href="{@docRoot}wear/index.html">Android Wear</a></li> + <li><a href="{@docRoot}tv/index.html">Android 電視</a></li> + <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li> + </ul> + + <p>支援程式庫也提供常用的 API:</p> + <ul> + <li><a href="{@docRoot}reference/android/support/v4/widget/DrawerLayout.html">導覽匣</a> +</li> + <li><a href="{@docRoot}reference/android/support/v4/view/ViewPager.html">滑動檢視</a></li> + <li><a href="{@docRoot}reference/android/support/v7/app/ActionBar.html">向下相容的動作列</a> +</li> + </ul> + </div> + + <p><a href="{@docRoot}tools/support-library/features.html">Android 支援程式庫</a>提供的 API 延伸集合,可以與大部分的 Android 版本相容。 +</p> + + <p>開啟 <strong>Extras</strong> 目錄並選取:</p> + <ul> + <li><strong>Android 支援存放庫</strong></li> + <li><strong>Android 支援程式庫</strong></li> + </ul> + + <p> </p> + <p> </p> + +</li> + + +<li> + <h2 id="GetGoogle" class="norule">從 Google Play 服務取得更多 API</h2> + + <div class="sidebox"> + + <p>Google Play 服務 API 為 Android 應用程式提供多種功能與服務,例如: +</p> + <ul> + <li><a href="{@docRoot}google/play-services/plus.html">使用者驗證</a></li> + <li><a href="{@docRoot}google/play-services/maps.html">Google 地圖</a></li> + <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li> + <li><a href="{@docRoot}google/play-services/games.html">遊戲關卡和排行榜</a> +</li> + <li><a href="{@docRoot}google/play-services/index.html">更多功能與服務</a></li> + </ul> + </div> + + <p>如要使用 Google API 進行開發,您需要 Google Play 服務封裝:</p> + <p>開啟 <strong>Extras</strong> 目錄並選取:</p> + <ul> + <li><strong>Google 存放庫</strong></li> + <li><strong>Google Play 服務</strong></li> + </ul> + + <p class="note"><strong>注意:</strong>並非所有 Android 裝置都提供 Google Play 服務,但內含 Google Play 市集的所有裝置都可以使用 Google Play 服務。 +如要在 Android 模擬器中使用這些 API,您必須從 SDK Manager 最新的 Android X.X 目錄中安裝 <strong>Google API</strong> 系統映像。 + +</p> +</li> + + +<li> + <h2 id="Install" class="norule">安裝封裝</h2> + <p>您選好需要的所有封裝後,就可以繼續安裝:</p> + <ol> + <li>按一下 [<strong>安裝 X 封裝</strong>]。</li> + <li>在下一個視窗中,按兩下左側的每個封裝名稱,以接受每項的授權合約。 +</li> + <li>按一下 [<strong>安裝</strong>]。</li> + </ol> + <p>SDK Manager 視窗的下方會顯示下載進度。 + <strong>請勿結束 SDK Manager</strong>,下載會因而取消。</p> +</li> + +<li> + <h2 id="Build" class="norule">開始建置!</h2> + +<p>Android SDK 現在已內含上述封裝,您可以開始建置 Android 應用程式了。 +若有新的工具和其他 API,只要啟動 SDK Manager 即可將新的封裝下載到 SDK。 +</p> + +<p>以下是繼續進行的選項:</p> + +<div class="cols" style="padding:10px 0"> +<div class="col-4"> +<h3>開始使用</h3> +<p>如果您是 Android 開發新手,可參考<strong><a href="{@docRoot}training/basics/firstapp/index.html">建置第一個應用程式</a></strong>指南瞭解 Android 應用程式的基本概念。 +</p> + +</div> +<div class="col-4 box"> +<h3>建置穿戴式應用程式</h3> +<p>如果您準備好開始建置 Android 穿戴式裝置的應用程式,請參閱<strong><a href="{@docRoot}wear/preview/start.html">建置 Android Wear 的應用程式</a></strong>指南。 +</p> + +</div> +<div class="col-4 box"> +<h3>使用 Google API</h3> +<p>如要開始使用 Google API (例如「地圖」或 Play Game 服務),請參閱<strong><a href="{@docRoot}google/play-services/setup.html">設定 Google Play 服務</a></strong>指南。 + + +</p> + +</div> +</div><!-- end cols --> + + +</li> + +</ol> + + diff --git a/docs/html-intl/intl/zh-tw/training/material/animations.jd b/docs/html-intl/intl/zh-tw/training/material/animations.jd new file mode 100644 index 000000000000..ba285751bcdf --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=定義自訂動畫 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#Touch">自訂輕觸回饋</a></li> + <li><a href="#Reveal">使用顯示效果</a></li> + <li><a href="#Transitions">自訂操作行為轉換</a></li> + <li><a href="#ViewState">動畫顯示視圖狀態變更</a></li> + <li><a href="#AnimVector">動畫顯示矢量可繪</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + + +<p>材料設計中的動畫讓使用者在進行操作動作之後獲得回饋,並且在使用者與您的應用程式互動時提供視覺上的連續性。 +材料設計風格針對按鈕和操作行為轉換,提供某些預設的動畫,而 Android 5.0 (API 級別 21) 以上版本則可讓您自訂這些動畫並建立新的動畫: + +</p> + +<ul> +<li>輕觸回饋</li> +<li>循環顯示</li> +<li>操作行為轉換</li> +<li>曲線動作</li> +<li>視圖狀態變更</li> +</ul> + + +<h2 id="Touch">自訂輕觸回饋</h2> + +<p>當使用者與 UI 元素互動時,材料設計中的輕觸回饋在接觸點提供即時的視覺化確認。 +按鈕的預設輕觸回饋動畫使用新的 {@link android.graphics.drawable.RippleDrawable} 類別,透過漣漪效果在不同狀態之間進行轉換。 + +</p> + +<p>大多數情況下,您應該在視圖 XML 中將視圖背景指定為下列項目以套用此功能: +</p> + +<ul> +<li>對有邊界的漣漪指定為 <code>?android:attr/selectableItemBackground</code>。</li> +<li>對延伸出視圖的漣漪指定為 <code>?android:attr/selectableItemBackgroundBorderless</code>。 +這會繪製在具有非空背景的視圖之最接近父項上,並以其作為邊界。 +</li> +</ul> + +<p class="note"><strong>注意:</strong><code>selectableItemBackgroundBorderless</code> 是 API 級別 21 推出的新屬性。 +</p> + + +<p>或者,您也可以使用 <code>ripple</code> 元素將 {@link android.graphics.drawable.RippleDrawable} 定義為 XML 資源。 +</p> + +<p>您可以對 {@link android.graphics.drawable.RippleDrawable} 物件指定色彩。如要變更預設的輕觸回饋色彩,請使用設計風格的 <code>android:colorControlHighlight</code> 屬性。 + +</p> + +<p>如需詳細資訊,請參閱 {@link +android.graphics.drawable.RippleDrawable} 類別的 API 參考資料。</p> + + +<h2 id="Reveal">使用顯示效果</h2> + +<p>當您顯示或隱藏一組 UI 元素時,顯示動畫可提供使用者視覺上的連續性。 +{@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()}方法可讓您以動畫顯示裁剪的圓形,以顯示或隱藏視圖。 +</p> + +<p>使用下列效果顯示之前看不見的視圖:</p> + +<pre> +// previously invisible view +View myView = findViewById(R.id.my_view); + +// get the center for the clipping circle +int cx = (myView.getLeft() + myView.getRight()) / 2; +int cy = (myView.getTop() + myView.getBottom()) / 2; + +// get the final radius for the clipping circle +int finalRadius = Math.max(myView.getWidth(), myView.getHeight()); + +// create the animator for this view (the start radius is zero) +Animator anim = + ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius); + +// make the view visible and start the animation +myView.setVisibility(View.VISIBLE); +anim.start(); +</pre> + +<p>使用下列效果隱藏之前看見的視圖:</p> + +<pre> +// previously visible view +final View myView = findViewById(R.id.my_view); + +// get the center for the clipping circle +int cx = (myView.getLeft() + myView.getRight()) / 2; +int cy = (myView.getTop() + myView.getBottom()) / 2; + +// get the initial radius for the clipping circle +int initialRadius = myView.getWidth(); + +// create the animation (the final radius is zero) +Animator anim = + ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0); + +// make the view invisible when the animation is done +anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + myView.setVisibility(View.INVISIBLE); + } +}); + +// start the animation +anim.start(); +</pre> + + +<h2 id="Transitions">自訂操作行為轉換</h2> + +<!-- shared transition video --> +<div style="width:290px;margin-left:35px;float:right"> + <div class="framed-nexus5-port-span-5"> + <video class="play-on-hover" autoplay=""> + <source src="{@docRoot}design/material/videos/ContactsAnim.mp4"> + <source src="{@docRoot}design/material/videos/ContactsAnim.webm"> + <source src="{@docRoot}design/material/videos/ContactsAnim.ogv"> + </video> + </div> + <div style="font-size:10pt;margin-left:20px;margin-bottom:30px"> + <p class="img-caption" style="margin-top:3px;margin-bottom:10px"><strong>圖 1</strong> - 使用分享元素的轉換。 +</p> + 如要重播影片,請按一下裝置螢幕<em></em> + </div> +</div> + +<p>材料設計應用程式中的操作行為轉換透過常見元素之間的動作和轉換,在不同的狀態之間提供視覺上的連接效果。 +您可針對進入和離開轉換及不同操作行為之間分享元素的轉換指定自訂動畫。 +</p> + +<ul> +<li><strong>進入</strong>轉換決定操作行為中的視圖如何進入場景。例如,在爆炸<em></em>進入轉換中,視圖會從外面進入場景,接著飛入螢幕的中央。 + +</li> + +<li><strong>離開</strong>轉換決定操作行為中的視圖如何離開場景。例如,在爆炸<em></em>離開轉換中,視圖會從中央離開場景。 + +</li> + +<li><strong>分享元素</strong>轉換決定在兩個操作行為轉換之間分享的視圖如何在這些操作行為之間轉換。 +例如,若兩個操作行為在不同的位置和大小具有相同的影像,changeImageTransform<em></em> 分享元素轉換會在這些操作行為之間流暢地解譯影像以及調整影像的大小。 + +</li> +</ul> + +<p>Android 5.0 (API 級別 21) 支援下列進入和離開轉換:</p> + +<ul> +<li>爆炸<em></em> - 視圖從場景的中央移入和移出。</li> +<li>滑動<em></em> - 視圖從場景的其中一邊移入和移出。</li> +<li>淡化<em></em> - 改變視圖的透明度,以將視圖加入場景,或從場景中移除。</li> +</ul> + +<p>所有延伸 {@link android.transition.Visibility} 類別的轉換都可作為進入或離開轉換。 +如需詳細資訊,請參閱 {@link android.transition.Transition} 類別的 API 參考資料。 +</p> + +<p>Android 5.0 (API 級別 21) 也支援下列分享元素轉換:</p> + +<ul> +<li>changeBounds<em></em> - 動畫顯示目標視圖版面配置邊界的變更。</li> +<li>changeClipBounds<em></em> - 動畫顯示目標視圖裁剪邊界的變更。</li> +<li>changeTransform<em></em> - 動畫顯示目標視圖比例和旋轉方向的變更。</li> +<li><em>changeImageTransform</em> - 動畫顯示目標影像大小和比例的變更。</li> +</ul> + +<p>當您在應用程式中啟用操作行為轉換時,進入和離開操作行為之間會啟動預設交錯淡化轉換。 +</p> + +<img src="{@docRoot}training/material/images/SceneTransition.png" alt="" width="600" height="405" style="margin-top:20px" /> +<p class="img-caption"> + <strong>圖 2</strong> - 使用一個分享元素的場景轉換。 +</p> + +<h3>指定自訂轉換</h3> + +<p>首先,當您定義一個從材料設計風格繼承而來的樣式時,請使用 <code>android:windowContentTransitions</code> 屬性啟用視窗內容轉換。 +您也可在樣式定義中指定進入、離開和分享元素轉換: +</p> + +<pre> +<style name="BaseAppTheme" parent="android:Theme.Material"> + <!-- enable window content transitions --> + <item name="android:windowContentTransitions">true</item> + + <!-- specify enter and exit transitions --> + <item name="android:windowEnterTransition">@transition/explode</item> + <item name="android:windowExitTransition">@transition/explode</item> + + <!-- specify shared element transitions --> + <item name="android:windowSharedElementEnterTransition"> + @transition/change_image_transform</item> + <item name="android:windowSharedElementExitTransition"> + @transition/change_image_transform</item> +</style> +</pre> + +<p>此範例中的 <code>change_image_transform</code> 轉換定義如下:</p> + +<pre> +<!-- res/transition/change_image_transform.xml --> +<!-- (see also Shared Transitions below) --> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> + <changeImageTransform/> +</transitionSet> +</pre> + +<p><code>changeImageTransform</code> 元素對應至 {@link android.transition.ChangeImageTransform} 類別。 +如需詳細資訊,請參閱 {@link android.transition.Transition} 的 API 參考資料。 +</p> + +<p>如要在程式碼中改為啟用視窗內容轉換,請呼叫 {@link android.view.Window#requestFeature Window.requestFeature()}方法: +</p> + +<pre> +// inside your activity (if you did not enable transitions in your theme) +getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + +// set an exit transition +getWindow().setExitTransition(new Explode()); +</pre> + +<p>如要在程式碼中指定轉換,請以 {@link +android.transition.Transition} 物件呼叫下列方法:</p> + +<ul> + <li>{@link android.view.Window#setEnterTransition Window.setEnterTransition()}</li> + <li>{@link android.view.Window#setExitTransition Window.setExitTransition()}</li> + <li>{@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()}</li> + <li>{@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}</li> +</ul> + +<p>{@link android.view.Window#setExitTransition setExitTransition()} 和 {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} 方法定義了呼叫操作行為的離開轉換。 +{@link android.view.Window#setEnterTransition +setEnterTransition()} 和 {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} 方法定義被呼叫操作行為的進入轉換。</p> + +<p>如要取得轉換的完整效果,您必須同時對呼叫與被呼叫操作行為啟用視窗內容轉換。 +否則,呼叫操作行為會啟動離開轉換,然後您只會看到視窗轉換 (類似調整大小或淡化)。 +</p> + +<p>如要立即啟動進入轉換,請對被呼叫的操作行為使用 {@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} 方法。 + +這樣您就會有更戲劇性的進入轉換。</p> + +<h3>使用轉換啟動操作行為</h3> + +<p>若您啟用轉換並對操作行為設定離開轉換,當您啟動另一個操作行為時,就會啟動轉換,如下所示: +</p> + +<pre> +startActivity(intent, + ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); +</pre> + +<p>若您已針對第二個操作行為設定進入轉換,當操作行為啟動時,也會啟動轉換。 +若要在啟動另一個操作行為時停用轉換,請提供 <code>null</code> 選項組合。 +</p> + +<h3>使用一個分享元素啟動操作行為</h3> + +<p>在兩個擁有分享元素的操作行為之間製作螢幕轉換動畫:</p> + +<ol> +<li>在設計風格中啟用視窗內容轉換。</li> +<li>在樣式中指定分享元素轉換。</li> +<li>將您的轉換定義為 XML 資源。</li> +<li>使用 <code>android:transitionName</code> 屬性,對兩個版面配置中的分享元素指定通用名稱。 +</li> +<li>使用 {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()} 方法。</li> +</ol> + +<pre> +// get the element that receives the click event +final View imgContainerView = findViewById(R.id.img_container); + +// get the common element for the transition in this activity +final View androidRobotView = findViewById(R.id.image_small); + +// define a click listener +imgContainerView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(this, Activity2.class); + // create the transition animation - the images in the layouts + // of both activities are defined with android:transitionName="robot" + ActivityOptions options = ActivityOptions + .makeSceneTransitionAnimation(this, androidRobotView, "robot"); + // start the new activity + startActivity(intent, options.toBundle()); + } +}); +</pre> + +<p>對於您在程式碼中所產生的分享動態視圖,請使用 {@link android.view.View#setTransitionName View.setTransitionName()} 方法,在兩個操作行為中指定通用元素名稱。 + +</p> + +<p>如要在完成第二個操作行為時倒轉場景轉換動畫,請呼叫 {@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} 方法而不是 {@link android.app.Activity#finish Activity.finish()}。 + +</p> + +<h3>使用多個分享元素啟動操作行為</h3> + +<p>如要在兩個具有多個分享元素的操作行為之間製作場景轉換動畫,請使用 <code>android:transitionName</code> 屬性在兩個版面配置中定義分享元素 (或在兩個操作行為中使用 {@link android.view.View#setTransitionName View.setTransitionName()} 方法),然後建立 {@link android.app.ActivityOptions} 物件,如下所示: + + +</p> + +<pre> +ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, + Pair.create(view1, "agreedName1"), + Pair.create(view2, "agreedName2")); +</pre> + + +<h2 id="CurvedMotion">使用曲線動作</h2> + +<p>材料設計中的動畫依據使用時間插值法和空間移動模式的曲線而定。 +在 Android 5.0 (API 級別 21) 及以上版本中,您可為動畫定義自訂時間曲線和曲線動作模式。 +</p> + +<p>{@link android.view.animation.PathInterpolator} 類別是根據貝茲曲線 (Bézier curve) 或 {@link android.graphics.Path} 物件的一種新插值器。 +這個插值器在一個 1x1 的方格中指定動作曲線,在 (0,0) 和 (1,1) 有兩個錨定點,以及使用建構函式引數指定的控制點。 + +您也可以將路徑插值器定義為 XML 資源:</p> + +<pre> +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + android:controlX1="0.4" + android:controlY1="0" + android:controlX2="1" + android:controlY2="1"/> +</pre> + +<p>系統在材料設計規格中提供三種基本曲線的 XML 資源: +</p> + +<ul> + <li><code>@interpolator/fast_out_linear_in.xml</code></li> + <li><code>@interpolator/fast_out_slow_in.xml</code></li> + <li><code>@interpolator/linear_out_slow_in.xml</code></li> +</ul> + +<p>您可將 {@link android.view.animation.PathInterpolator} 物件傳送至 {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()} 方法。</p> + +<p>{@link android.animation.ObjectAnimator} 類別有新的建構函式,可讓您一次使用兩個以上的屬性沿著路徑以動畫顯示座標。 +例如,下列動畫器使用 {@link android.graphics.Path} 物件,以動畫顯示視圖的 X 和 Y 屬性: +</p> + +<pre> +ObjectAnimator mAnimator; +mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path); +... +mAnimator.start(); +</pre> + + +<h2 id="ViewState">動畫顯示視圖狀態變更</h2> + +<p>{@link android.animation.StateListAnimator} 類別可讓您定義視圖狀態變更時所執行的動畫器。 +下列範例示範如何將 {@link +android.animation.StateListAnimator} 定義為 XML 資源:</p> + +<pre> +<!-- animate the translationZ property of a view when pressed --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <set> + <objectAnimator android:propertyName="translationZ" + android:duration="@android:integer/config_shortAnimTime" + android:valueTo="2dp" + android:valueType="floatType"/> + <!-- you could have other objectAnimator elements + here for "x" and "y", or other properties --> + </set> + </item> + <item android:state_enabled="true" + android:state_pressed="false" + android:state_focused="true"> + <set> + <objectAnimator android:propertyName="translationZ" + android:duration="100" + android:valueTo="0" + android:valueType="floatType"/> + </set> + </item> +</selector> +</pre> + +<p>如要將自訂的視圖狀態動畫連接到視圖,請依照此範例在 XML 資源檔案中使用 <code>selector</code> 元素定義動畫器,並使用 <code>android:stateListAnimator</code> 屬性將其指派到您的視圖。 + +如要在程式碼中為視圖指派狀態清單動畫器,請使用 {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()} 方法,然後使用 {@link android.view.View#setStateListAnimator View.setStateListAnimator()} 方法將動畫器指派到您的視圖。 + +</p> + +<p>當您的設計風格延伸材料設計風格時,按鈕預設會有 Z 動畫。如果要避免在按鈕中出現這類行為,請將 <code>android:stateListAnimator</code> 屬性設定為 <code>@null</code>。 + +</p> + +<p>{@link android.graphics.drawable.AnimatedStateListDrawable} 類別可讓您在相關視圖的狀態變更之間,建立顯示動畫的可繪項目。 +Android 5.0 中的某些系統小工具預設會使用這些動畫。 +下列範例示範如何將 {@link android.graphics.drawable.AnimatedStateListDrawable} 定義為 XML 資源: +</p> + +<pre> +<!-- res/drawable/myanimstatedrawable.xml --> +<animated-selector + xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- provide a different drawable for each state--> + <item android:id="@+id/pressed" android:drawable="@drawable/drawableP" + android:state_pressed="true"/> + <item android:id="@+id/focused" android:drawable="@drawable/drawableF" + android:state_focused="true"/> + <item android:id="@id/default" + android:drawable="@drawable/drawableD"/> + + <!-- specify a transition --> + <transition android:fromId="@+id/default" android:toId="@+id/pressed"> + <animation-list> + <item android:duration="15" android:drawable="@drawable/dt1"/> + <item android:duration="15" android:drawable="@drawable/dt2"/> + ... + </animation-list> + </transition> + ... +</animated-selector> +</pre> + + +<h2 id="AnimVector">動畫顯示矢量可繪</h2> + +<p><a href="{@docRoot}training/material/drawables.html#VectorDrawables">矢量可繪</a>可以調整大小,但又不會喪失定義。 +{@link android.graphics.drawable.AnimatedVectorDrawable} 類別可讓您以動畫顯示矢量可繪的屬性。 +</p> + +<p>您通常會在下列三個 XML 檔案中定義能可動矢量可繪:</p> + +<ul> +<li>在 <code>res/drawable/</code> 中包含 <code><vector></code> 元素的矢量可繪 +</li> +<li>在 <code>res/drawable/</code> 中包含 <code><animated-vector></code> 元素的可動矢量可繪 +</li> +<li>在 <code>res/anim/</code> 中包含 <code><objectAnimator></code> 元素的一或多個物件動畫器 +</li> +</ul> + +<p>可動的矢量可繪能以動畫顯示 <code><group></code> 和 <code><path></code> 元素的屬性。 +<code><group></code> 元素定義一組路徑或子群組,而 <code><path></code> 元素則定義要繪製的路徑。 +</p> + +<p>當您定義要可動的矢量可繪時,請使用 <code>android:name</code> 屬性為群組和路徑指派唯一的名稱,以便從您的動畫器定義參考這些群組和路徑。 + +例如:</p> + +<pre> +<!-- res/drawable/vectordrawable.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="64dp" + android:width="64dp" + android:viewportHeight="600" + android:viewportWidth="600"> + <group + <strong>android:name="rotationGroup"</strong> + android:pivotX="300.0" + android:pivotY="300.0" + android:rotation="45.0" > + <path + <strong>android:name="v"</strong> + android:fillColor="#000000" + android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> + </group> +</vector> +</pre> + +<p>可動的矢量可繪定義是依照名稱來參考矢量可繪中的群組和路徑: +</p> + +<pre> +<!-- res/drawable/animvectordrawable.xml --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/vectordrawable" > + <target + android:name="rotationGroup" + android:animation="@anim/rotation" /> + <target + android:name="v" + android:animation="@anim/path_morph" /> +</animated-vector> +</pre> + +<p>動畫定義代表 {@link android.animation.ObjectAnimator} 或 {@link +android.animation.AnimatorSet} 物件。此範例中的第一個動畫器會 360 度旋轉目標群組: +</p> + +<pre> +<!-- res/anim/rotation.xml --> +<objectAnimator + android:duration="6000" + android:propertyName="rotation" + android:valueFrom="0" + android:valueTo="360" /> +</pre> + +<p>此範例中的第二個動畫器會將矢量可繪的路徑從一種形狀變成另外一種形狀。 +兩個路徑必須相容才能變形:意即兩個路徑必須有相同數量的命令,而每個命令必須有相同數量的參數。 +</p> + +<pre> +<!-- res/anim/path_morph.xml --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:duration="3000" + android:propertyName="pathData" + android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z" + android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z" + android:valueType="pathType" /> +</set> +</pre> + +<p>如需詳細資訊,請參閱 {@link +android.graphics.drawable.AnimatedVectorDrawable} 的 API 參考資料。</p> diff --git a/docs/html-intl/intl/zh-tw/training/material/compatibility.jd b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd new file mode 100644 index 000000000000..767788bf75be --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=維持相容性 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#Theme">定義替代樣式</a></li> + <li><a href="#Layouts">提供替代版面配置</a></li> + <li><a href="#SupportLib">使用支援程式庫</a></li> + <li><a href="#CheckVersion">檢查系統版本</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + + +<p>某些材料設計功能如材料設計風格和自訂操作行為轉換等,只能在 Android 5.0 (API 級別 21) 及以上版本中使用。 +然而,您還是可以將應用程式設計為在支援材料設計的裝置上執行時才使用這些功能,並仍然與執行舊版 Android 的裝置相容。 + +</p> + + +<h2 id="Theme">定義替代樣式</h2> + +<p>您可以設定應用程式在支援材料設計風格的裝置上執行時才予以使用,在執行舊版 Android 的裝置上執行時則轉換成舊版的設計風格: +</p> + +<ol> +<li>在 <code>res/values/styles.xml</code> 中定義繼承自舊版設計風格 (例如 Holo) 的設計風格。 +</li> +<li>在 <code>res/values-v21/styles.xml</code> 中定義與材料設計風格相同名稱的設計風格。 +</li> +<li>在宣示說明檔案中將此設計風格設定為應用程式的設計風格。</li> +</ol> + +<p class="note"><strong>注意</strong>:若您的應用程式使用材料設計風格,但卻未以此方式提供替代設計風格,您的應用程式將無法在 Android 5.0 之前的版本上執行。 + + +</p> + + +<h2 id="Layouts">提供替代版面配置</h2> + +<p>若您根據材料設計指南所設計的版面配置並未使用任何 Android 5.0 (API 級別 21) 中推出的全新 XML 屬性,則這些版面配置就可以在舊版 Android 上運作。 + +或者,您也可以提供替代的版面配置。您也可提供替代的版面配置以自訂應用程式在舊版 Android 上如何顯示。 +</p> + +<p>在 <code>res/layout-v21/</code> 內建立 Android 5.0 (API 級別 21) 的版面配置檔案,並在 <code>res/layout/</code> 內建立舊版 Android 的替代版面配置檔案。例如,<code>res/layout/my_activity.xml</code> 是 <code>res/layout-v21/my_activity.xml</code> 的替代版面配置。 + + +</p> + +<p>為了避免程式碼重複,請在 <code>res/values/</code> 內定義您的樣式,在 <code>res/values-v21/</code> 中針對新的 API 修改樣式,再使用樣式繼承,在 <code>res/values/</code> 中定義基礎樣式,然後從 <code>res/values-v21/</code> 中的樣式繼承。 + +</p> + + +<h2 id="SupportLib">使用支援程式庫</h2> + +<p><a href="{@docRoot}tools/support-library/features.html#v7">v7 支援程式庫</a> r21 以及更新版本包含下列材料設計功能: +</p> + +<ul> +<li>當您套用其中一個 <code>Theme.AppCompat</code> 設計風格時,某些系統小工具的<a href="{@docRoot}training/material/theme.html">材料設計樣式</a>。 +</li> +<li>在 <code>Theme.AppCompat</code> 設計風格中的<a href="{@docRoot}training/material/theme.html#ColorPalette">色板設計風格屬性</a>。 +</li> +<li><a href="{@docRoot}training/material/lists-cards.html#RecyclerView">顯示資料集合</a>的 {@link android.support.v7.widget.RecyclerView} 小工具。 +</li> +<li><a href="{@docRoot}training/material/lists-cards.html#CardView">建立卡片</a>的 {@link android.support.v7.widget.CardView} 小工具。</li> +<li><a href="{@docRoot}training/material/drawables.html#ColorExtract">從影像提取顯著顏色</a>的 {@link android.support.v7.graphics.Palette} 類別。 +</li> +</ul> + +<h3>系統小工具</h3> + +<p><code>Theme.AppCompat</code> 設計風格提供下列小工具的材料設計樣式:</p> + +<ul> + <li>{@link android.widget.EditText}</li> + <li>{@link android.widget.Spinner}</li> + <li>{@link android.widget.CheckBox}</li> + <li>{@link android.widget.RadioButton}</li> + <li>{@link android.support.v7.widget.SwitchCompat}</li> + <li>{@link android.widget.CheckedTextView}</li> +</ul> + +<h3>色板</h3> + +<p>如果要取得材料設計樣式並使用 Android v7 支援程式庫自訂色板,請套用其中一個 <code>Theme.AppCompat</code> 設計風格: +</p> + +<pre> +<!-- extend one of the Theme.AppCompat themes --> +<style name="Theme.MyTheme" parent="Theme.AppCompat.Light"> + <!-- customize the color palette --> + <item name="colorPrimary">@color/material_blue_500</item> + <item name="colorPrimaryDark">@color/material_blue_700</item> + <item name="colorAccent">@color/material_green_A200</item> +</style> +</pre> + +<h3>清單和卡片</h3> + +<p>在舊版 Android 中,透過 Android v7 支援程式庫可以使用 {@link android.support.v7.widget.RecyclerView} 和 {@link +android.support.v7.widget.CardView} 小工具,但有下列限制: +</p> +<ul> +<li>{@link android.support.v7.widget.CardView} 只能使用額外的填補方式,有計畫地實作陰影。 +</li> +<li>{@link android.support.v7.widget.CardView} 不會裁剪與圓形邊角重疊的下方視圖。 +</li> +</ul> + + +<h3>相依性</h3> + +<p>如果要在 Android 5.0 (API 級別 21) 以前的版本中使用這些功能,請在您的專案中包含 Android v7 支援程式庫做為 <a href="{@docRoot}/sdk/installing/studio-build.html#dependencies">Gradle 相依性</a>: +</p> + +<pre> +dependencies { + compile 'com.android.support:appcompat-v7:21.0.+' + compile 'com.android.support:cardview-v7:21.0.+' + compile 'com.android.support:recyclerview-v7:21.0.+' +} +</pre> + + +<h2 id="CheckVersion">檢查系統版本</h2> + +<p>下列功能只能在 Android 5.0 (API 級別 21) 以及更新版本中使用:</p> + +<ul> +<li>操作行為轉換</li> +<li>輕觸回饋</li> +<li>顯示動畫</li> +<li>路徑型動畫</li> +<li>矢量可繪</li> +<li>繪製著色</li> +</ul> + +<p>如要維持與舊版 Android 的相容性,在呼叫這些功能的 API 之前,請先在執行期間檢查系統 {@link +android.os.Build.VERSION#SDK_INT version}: +</p> + +<pre> +// Check if we're running on Android 5.0 or higher +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // Call some material design APIs here +} else { + // Implement this feature without material design +} +</pre> + +<p class="note"><strong>注意:</strong>如果要指定應用程式支援的 Android 版本,請在您的宣示說明檔案中使用 <code>android:minSdkVersion</code> 和 <code>android:targetSdkVersion</code> 屬性。 + +如果要在 Android 5.0 中使用材料設計功能,請將 <code>android:targetSdkVersion</code> 屬性設定為 <code>21</code>。 +如需詳細資訊,請參閱 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk> API 指南</a>。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/training/material/drawables.jd b/docs/html-intl/intl/zh-tw/training/material/drawables.jd new file mode 100644 index 000000000000..ae807c0b1806 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=使用可繪項目 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#DrawableTint">為可繪項目資源著色</a></li> + <li><a href="#ColorExtract">從影像提取顯著顏色</a></li> + <li><a href="#VectorDrawables">建立矢量可繪</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + +<p>下列可繪項目功能可以幫助您在應用程式中運用材料設計:</p> + +<ul> +<li>繪製著色</li> +<li>顯著顏色提取</li> +<li>矢量可繪</li> +</ul> + +<p>本課程示範如何在應用程式中使用這些功能。</p> + + +<h2 id="DrawableTint">為可繪項目資源著色</h2> + +<p>若您使用 Android 5.0 (API 級別 21) 或以上版本,您可對點陣圖和定義為 Alpha 遮罩的九宮格影像著色。 +您可使用顏色資源或解析為顏色資源的設計風格屬性 (如 <code>?android:attr/colorPrimary</code> 為圖片著色。 +這些資產您通常只會建立一次,然後自動著色以符合您的設計風格。 +</p> + +<p>您可使用 {@code setTint()} 方法對 {@link android.graphics.drawable.BitmapDrawable} 或 {@link +android.graphics.drawable.NinePatchDrawable} 物件套用著色。您也可以使用 <code>android:tint</code> 和 <code>android:tintMode</code> 屬性,設定版面配置中的著色顏色和模式。 + +</p> + + +<h2 id="ColorExtract">從影像提取顯著顏色</h2> + +<p>Android 支援程式庫 r21 和更新版本包含 {@link +android.support.v7.graphics.Palette} 類別,可讓您從影像提取出顯著的顏色。此類別會提取下列顯著顏色: +</p> + +<ul> +<li>鮮明</li> +<li>鮮明 (深色)</li> +<li>鮮明 (淺色)</li> +<li>柔和</li> +<li>柔和 (深色)</li> +<li>柔和 (淺色)</li> +</ul> + +<p>如果要提取這些顏色,請將 {@link android.graphics.Bitmap} 物件傳遞到載入影像之背景執行緒中的 {@link android.support.v7.graphics.Palette#generate Palette.generate()} 靜態方法。 + +如果您無法使用該執行緒,請改為呼叫 {@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} 方法並提供接聽程式。 + +</p> + +<p>您可使用 <code>Palette</code> 類別中的 getter 方法 (如 <code>Palette.getVibrantColor</code> 從影像擷取顯著顏色。 +</p> + +<p>如果要在專案中使用 {@link android.support.v7.graphics.Palette} 類別,請在應用程式的模組中加入下列 <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle 相依性</a>: + +</p> + +<pre> +dependencies { + ... + compile 'com.android.support:palette-v7:21.0.0' +} +</pre> + +<p>如需詳細資訊,請參閱 {@link android.support.v7.graphics.Palette} 類別的 API 參考資料。 +</p> + + +<h2 id="VectorDrawables">建立矢量可繪</h2> + +<!-- video box --> +<a class="notice-developers-video" href="https://www.youtube.com/watch?v=wlFVIIstKmA" style="margin-top:18px"> +<div> + <h3>影片</h3> + <p>Android Vector Graphics</p> +</div> +</a> + +<p>在 Android 5.0 (API 級別 21) 或以上版本中,您可定義矢量可繪,這種圖形可以調整大小但不會遺失定義。 +一個矢量影像只需要一個資產檔案,但點陣圖影像的每一種螢幕密度都需要一個資產檔案。 +如果要建立矢量影像,您必須在 <code><vector></code> XML 元素內定義圖形的詳細資料。 +</p> + +<p>下列範例定義一個具有心形圖案的矢量影像:</p> + +<pre> +<!-- res/drawable/heart.xml --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + <!-- intrinsic size of the drawable --> + android:height="256dp" + android:width="256dp" + <!-- size of the virtual canvas --> + android:viewportWidth="32" + android:viewportHeight="32"> + + <!-- draw a path --> + <path android:fillColor="#8fff" + android:pathData="M20.5,9.5 + c-1.955,0,-3.83,1.268,-4.5,3 + c-0.67,-1.732,-2.547,-3,-4.5,-3 + C8.957,9.5,7,11.432,7,14 + c0,3.53,3.793,6.257,9,11.5 + c5.207,-5.242,9,-7.97,9,-11.5 + C25,11.432,23.043,9.5,20.5,9.5z" /> +</vector> +</pre> + +<p>在 Android 中,{@link android.graphics.drawable.VectorDrawable} 物件代表矢量影像。 +如需有關 <code>pathData</code> 語法的詳細資訊,請參閱 <a href="http://www.w3.org/TR/SVG11/paths.html#PathData">SVG 路徑參考資料</a>。如需有關繪製矢量可繪屬性的詳細資訊,請參閱<a href="{@docRoot}training/material/animations.html#AnimVector">動畫顯示矢量可繪</a>。 + +</p> diff --git a/docs/html-intl/intl/zh-tw/training/material/get-started.jd b/docs/html-intl/intl/zh-tw/training/material/get-started.jd new file mode 100644 index 000000000000..dfa0074e17ce --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=開始使用 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#ApplyTheme">套用材料設計風格</a></li> + <li><a href="#Layouts">設計版面配置</a></li> + <li><a href="#Depth">指定視圖中的高度</a></li> + <li><a href="#ListsCards">建立清單和卡片</a></li> + <li><a href="#Animations">自訂動畫</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + + +<p>如果要使用材料設計建立應用程式:</p> + +<ol> + <li style="margin-bottom:10px"> + 請閱讀<a href="http://www.google.com/design/spec">材料設計規格</a>。</li> + <li style="margin-bottom:10px"> + 對應用程式套用材料<strong>設計風格</strong>。</li> + <li style="margin-bottom:10px"> + 依照材料設計指南建立<strong>版面配置</strong>。</li> + <li style="margin-bottom:10px"> + 指定視圖的<strong>高度</strong>來投射出陰影。</li> + <li style="margin-bottom:10px"> + 使用系統<strong>小工具</strong>製作清單和卡片。</li> + <li style="margin-bottom:10px"> + 自訂應用程式中的<strong>動畫</strong>。</li> +</ol> + +<h3>維持向下相容性</h3> + +<p>您可在應用程式中加入多種材料設計功能,同時維持與 Android 5.0 之前版本的相容性。 +如需詳細資訊,請參閱<a href="{@docRoot}training/material/compatibility.html">維持相容性</a>。 +</p> + +<h3>更新應用程式以納入材料設計功能</h3> + +<p>如要更新現有應用程式以納入材料設計功能,請依照下列材料設計指南更新版面配置。 +同時確保加入深度、輕觸回饋和動畫。 +</p> + +<h3>使用材料設計建立新應用程式</h3> + +<p>如果您要使用材料設計功能建立新應用程式,<a href="http://www.google.com/design/spec">材料設計指南</a>可以提供一致性的設計架構。 +請依照下列指南,使用 Android 架構中的新功能來設計和開發應用程式。 +</p> + + +<h2 id="ApplyTheme">套用材料設計風格</h2> + +<p>如要在應用程式中套用材料設計風格,請指定一個繼承自 <code>android:Theme.Material</code> 的樣式: +</p> + +<pre> +<!-- res/values/styles.xml --> +<resources> + <!-- your theme inherits from the material theme --> + <style name="AppTheme" parent="android:Theme.Material"> + <!-- theme customizations --> + </style> +</resources> +</pre> + +<p>材料設計風格提供已更新的系統小工具,可讓您設定系統小工具的色板和預設動畫,作為輕觸回饋與操作行為轉換。 +如需詳細資訊,請參閱<a href="{@docRoot}training/material/theme.html">使用材料設計風格</a>。 +</p> + + +<h2 id="Layouts">設計版面配置</h2> + +<p>除了套用和自訂材料設計風格之外,您的版面配置也必須遵守<a href="http://www.google.com/design/spec">材料設計指南</a>。 +當您設計版面配置時,請注意下列事項: +</p> + +<ul> +<li>基準線網格</li> +<li>邊線</li> +<li>間距</li> +<li>輕觸目標大小</li> +<li>版面配置結構</li> +</ul> + + +<h2 id="Depth">指定視圖中的高度</h2> + +<p>檢視可以投射出陰影,因此檢視的高度值會決定陰影的大小及其繪製順序。 +如要設定視圖的高度,請在版面配置中使用 <code>android:elevation</code> 屬性: +</p> + +<pre> +<TextView + android:id="@+id/my_textview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/next" + android:background="@color/white" + android:elevation="5dp" /> +</pre> + +<p>您可以使用新的 <code>translationZ</code> 屬性建立動畫,來反映視圖高度的短暫變化。 +高度變化可以用來<a href="{@docRoot}training/material/animations.html#ViewState">回應輕觸手勢</a>。 + +</p> + +<p>如需詳細資訊,請參閱<a href="{@docRoot}training/material/shadows-clipping.html">定義陰影和裁剪檢視</a>。 +</p> + + +<h2 id="ListsCards">建立清單和卡片</h2> + +<p>{@link android.support.v7.widget.RecyclerView} 是一個更容易插入的 {@link +android.widget.ListView} 版本,可支援不同的版面配置類型並提供更好的效能。{@link android.support.v7.widget.CardView} 可讓您在不同的應用程式之間以一致的外觀顯示卡片內部的資訊。 + +下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.CardView}: +</p> + +<pre> +<android.support.v7.widget.CardView + android:id="@+id/card_view" + android:layout_width="200dp" + android:layout_height="200dp" + card_view:cardCornerRadius="3dp"> + ... +</android.support.v7.widget.CardView> +</pre> + +<p>如需詳細資訊,請參閱<a href="{@docRoot}training/material/lists-cards.html">建立清單和卡片</a>。 +</p> + + +<h2 id="Animations">自訂動畫</h2> + +<p>Android 5.0 (API 級別 21) 包含許多新的 API 以便在應用程式中建立自訂動畫。例如,您可以啟用操作行為轉換,並在操作行為內部定義離開轉換: + +</p> + +<pre> +public class MyActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // enable transitions + getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); + setContentView(R.layout.activity_my); + } + + public void onSomeButtonClicked(View view) { + getWindow().setExitTransition(new Explode()); + Intent intent = new Intent(this, MyOtherActivity.class); + startActivity(intent, + ActivityOptions + .makeSceneTransitionAnimation(this).toBundle()); + } +} +</pre> + +<p>當您從這個操作行為開始另一個操作行為時,就會啟動離開轉換。</p> + +<p>如需深入了解新的動畫 API,請參閱<a href="{@docRoot}training/material/animations.html">定義自訂動畫</a>。</p> diff --git a/docs/html-intl/intl/zh-tw/training/material/index.jd b/docs/html-intl/intl/zh-tw/training/material/index.jd new file mode 100644 index 000000000000..5599d5936973 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=開發人員材料設計 +page.type=設計 +page.image=images/cards/material_2x.png +page.metaDescription=學習如何套用材料設計至您的應用程式。 + + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2>相依性和先決條件</h2> + <ul> + <li>Android 5.0 (API 級別 21)</li> + </ul> +</div> +</div> + +<p>材料設計是一份廣泛綜合性的指南,引導您跨平台、跨裝置進行視覺、動態和互動的設計。 +如要在 Android 應用程式中使用材料設計,請依照<a href="http://www.google.com/design/spec/material-design/introduction.html">材料設計規格中的指示</a>,使用 Android 5.0 (API 級別 21) 中的新元件和新功能。 + + + +</p> + +<p>本課程示範如何使用下列元素建立材料設計應用程式:</p> + +<ul> +<li>材料設計風格</li> +<li>卡片和清單的小工具</li> +<li>自訂陰影和裁剪視圖</li> +<li>矢量可繪</li> +<li>自訂動畫</li> +</ul> + +<p>本課程也示範當您在應用程式中使用材料設計功能時,如何與 Android 5.0 (API 級別 21) 之前的版本維持相容性。 +</p> + +<h2>課程</h2> + +<dl> + <dt><a href="{@docRoot}training/material/get-started.html">開始使用</a></dt> + <dd>了解如何更新應用程式以納入材料設計功能。</dd> + + <dt><a href="{@docRoot}training/material/theme.html">使用材料設計風格</a></dt> + <dd>了解如何對應用程式套用材料設計樣式。</dd> + + <dt><a href="{@docRoot}training/material/lists-cards.html">建立清單和卡片</a></dt> + <dd>了解如何使用系統小工具,建立外觀和操作方式一致的清單和卡片。</dd> + + <dt><a href="{@docRoot}training/material/shadows-clipping.html">定義陰影和裁剪視圖</a></dt> + <dd>了解如何對視圖設定高度建立自訂陰影,以及如何裁剪視圖。</dd> + + <dt><a href="{@docRoot}training/material/drawables.html">使用可繪項目</a></dt> + <dd>了解如何建立矢量可繪項目,以及如何對可繪資源著色。</dd> + + <dt><a href="{@docRoot}training/material/animations.html">定義自訂動畫</a></dt> + <dd>了解如何使用分享元素對視圖建立自訂動畫和操作行為轉換。</dd> + + <dt><a href="{@docRoot}training/material/compatibility.html">維持相容性</a></dt> + <dd>了解如何與 Android 5.0 之前的平台版本維持相容性。</dd> +</dl> diff --git a/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd b/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd new file mode 100644 index 000000000000..71adffbd4c82 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=建立清單和卡片 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#RecyclerView">建立清單</a></li> + <li><a href="#CardView">建立卡片</a></li> + <li><a href="#Dependencies">新增相依性</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + + +<p>如要在應用程式中使用材料設計樣式建立複雜的清單和卡片,可以使用 {@link android.support.v7.widget.RecyclerView} 和 {@link android.support.v7.widget.CardView} 小工具。 + +</p> + + +<h2 id="RecyclerView">建立清單</h2> + +<p>{@link android.support.v7.widget.RecyclerView} 小工具是比較進階和彈性的 {@link android.widget.ListView} 版本。 +這個小工具是一個用來顯示大型資料集的容器,只要維護少數幾個視圖,就可極有效率地捲動資料集。 +當您的資料集元素在執行階段會根據使用者操作動作或網路事件而變更,請使用 {@link android.support.v7.widget.RecyclerView} 小工具。 + +</p> + +<p>{@link android.support.v7.widget.RecyclerView} 類別會提供下列項目,簡化大型資料集的顯示和處理方式: +</p> + +<ul> + <li>版面配置管理員,用來將項目定位</li> + <li>常見項目操作 (例如移除或新增項目) 的預設動畫</li> +</ul> + +<p>您也可針對 {@link +android.support.v7.widget.RecyclerView} 小工具定義自訂的版面配置管理員。</p> + +<img src="{@docRoot}training/material/images/RecyclerView.png" alt="" width="550" height="106" /> +<p class="img-caption"> +<strong>圖 1.</strong><code>RecyclerView</code> 小工具。 +</p> + +<p>如果要使用 {@link android.support.v7.widget.RecyclerView} 小工具,您必須指定配接器和版面配置管理員。 +如要建立配接器,請延伸 {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter} 類別。實作的詳細情形根據您的資料集和檢視類型而定。 +如需詳細資訊,請參閱下面的<a href="#RVExamples">範例</a>。 +</p> + +<div style="float:right"> +<img src="{@docRoot}design/material/images/list_mail.png" alt="" width="250" height="426" /> +<p class="img-caption" style="margin-left:8px"> +<strong>圖 2</strong> - 含有 <code>RecyclerView</code> 的清單。 +</p> +</div> + +<p><strong>版面配置管理員</strong>會將 {@link +android.support.v7.widget.RecyclerView} 內的項目視圖定位,並判斷何時要重複使用使用者不會再看到的項目視圖。 +如要重複使用 (或回收<em></em>) 某個視圖,版面配置管理員會要求配接器以資料集中不同的元素,取代視圖的內容。 +使用此方式回收視圖可避免建立不必要的視圖或執行耗費資源的 {@link android.app.Activity#findViewById findViewById()} 查詢,以增進效能。 + +</p> + +<p>{@link android.support.v7.widget.RecyclerView} 提供下列內建的版面配置管理員:</p> + +<ul> +<li>{@link android.support.v7.widget.LinearLayoutManager} 在垂直或水平捲動清單中顯示項目。 +</li> +<li>{@link android.support.v7.widget.GridLayoutManager} 會在網格中顯示項目。</li> +<li>{@link android.support.v7.widget.StaggeredGridLayoutManager} 會在交錯網格中顯示項目。</li> +</ul> + +<p>如要建立自訂版面配置管理員,請延伸 {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager} 類別。</p> + +<h3>動畫</h3> + +<p>在 {@link +android.support.v7.widget.RecyclerView} 中,預設已啟用新增和移除項目的動畫。如果要自訂這些動畫,請延伸 {@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} 類別並使用 {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} 方法。 + + +</p> + +<h3 id="RVExamples">範例</h3> + +<p>下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.RecyclerView}: +</p> + +<pre> +<!-- A RecyclerView with some commonly used attributes --> +<android.support.v7.widget.RecyclerView + android:id="@+id/my_recycler_view" + android:scrollbars="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</pre> + +<p>一旦您在版面配置中加入 {@link android.support.v7.widget.RecyclerView} 小工具之後,先取得該物件的控制代碼、將控制代碼連線到版面配置管理員,再連接配接器以便顯示資料: + +</p> + +<pre> +public class MyActivity extends Activity { + private RecyclerView mRecyclerView; + private RecyclerView.Adapter mAdapter; + private RecyclerView.LayoutManager mLayoutManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.my_activity); + mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); + + // use this setting to improve performance if you know that changes + // in content do not change the layout size of the RecyclerView + mRecyclerView.setHasFixedSize(true); + + // use a linear layout manager + mLayoutManager = new LinearLayoutManager(this); + mRecyclerView.setLayoutManager(mLayoutManager); + + // specify an adapter (see also next example) + mAdapter = new MyAdapter(myDataset); + mRecyclerView.setAdapter(mAdapter); + } + ... +} +</pre> + +<p>配接器可存取資料集中的項目、建立項目的視圖,並在原來的項目無法再顯示時,使用新的資料項目取代某些視圖的內容。 + +下列程式碼範例簡單實作一個由使用 {@link android.widget.TextView} 小工具顯示的字串陣列所組成的資料集: +</p> + +<pre> +public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { + private String[] mDataset; + + // Provide a reference to the views for each data item + // Complex data items may need more than one view per item, and + // you provide access to all the views for a data item in a view holder + public static class ViewHolder extends RecyclerView.ViewHolder { + // each data item is just a string in this case + public TextView mTextView; + public ViewHolder(TextView v) { + super(v); + mTextView = v; + } + } + + // Provide a suitable constructor (depends on the kind of dataset) + public MyAdapter(String[] myDataset) { + mDataset = myDataset; + } + + // Create new views (invoked by the layout manager) + @Override + public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, + int viewType) { + // create a new view + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.my_text_view, parent, false); + // set the view's size, margins, paddings and layout parameters + ... + ViewHolder vh = new ViewHolder(v); + return vh; + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + // - get element from your dataset at this position + // - replace the contents of the view with that element + holder.mTextView.setText(mDataset[position]); + + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return mDataset.length; + } +} +</pre> + + +<div style="float:right;margin-top:15px;margin-left:30px"> +<img src="{@docRoot}design/material/images/card_travel.png" alt="" width="225" height="383"> +<p class="img-caption" style="margin-left:12px"> +<strong>圖 3.</strong>卡片範例。 +</p> +</div> + +<h2 id="CardView">建立卡片</h2> + +<p>{@link android.support.v7.widget.CardView} 會延伸{@link android.widget.FrameLayout} 類別,讓您跨平台以一致的外觀顯示卡片內部的資訊。{@link +android.support.v7.widget.CardView} 小工具可以有陰影和圓形邊角。 +</p> + +<p>如要建立有陰影的卡片,請使用 <code>card_view:cardElevation</code> 屬性。在 Android 5.0 (API 級別 21) 以及以上版本,{@link android.support.v7.widget.CardView} 使用實際高度和動態陰影,但在較舊的版本上只能有計畫地實作陰影。如需詳細資訊,請參閱<a href="{@docRoot}training/material/compatibility.html">維持相容性</a>。 + + + +</p> + +<p>使用下列屬性可自訂 {@link android.support.v7.widget.CardView} 小工具的外觀: +</p> + +<ul> + <li>如果要在版面配置中設定圓角的半徑,請使用 <code>card_view:cardCornerRadius</code> 屬性。 + </li> + <li>如果要在程式碼中設定圓角的半徑,請使用 <code>CardView.setRadius</code> 方法。</li> + <li>如果要設定卡片的背景顏色,請使用 <code>card_view:cardBackgroundColor</code> 屬性。 + </li> +</ul> + +<p>下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.CardView}小工具: + </p> + +<pre> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + xmlns:card_view="http://schemas.android.com/apk/res-auto" + ... > + <!-- A CardView that contains a TextView --> + <android.support.v7.widget.CardView + xmlns:card_view="http://schemas.android.com/apk/res-auto" + android:id="@+id/card_view" + android:layout_gravity="center" + android:layout_width="200dp" + android:layout_height="200dp" + card_view:cardCornerRadius="4dp"> + + <TextView + android:id="@+id/info_text" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + </android.support.v7.widget.CardView> +</LinearLayout> +</pre> + +<p>如需詳細資訊,請參閱 {@link android.support.v7.widget.CardView} 的 API 參考資料。</p> + + +<h2 id="Dependencies">新增相依性</h2> + +<p>{@link android.support.v7.widget.RecyclerView} 和 {@link android.support.v7.widget.CardView} 小工具都屬於 <a href="{@docRoot}tools/support-library/features.html#v7">v7 支援程式庫</a>。 + +如要在專案中使用這些小工具,請在應用程式的模組中加入下列 <a href="{@docRoot}sdk/installing/studio-build.html#dependencies">Gradle 相依性</a>: + +</p> + +<pre> +dependencies { + ... + compile 'com.android.support:cardview-v7:21.0.+' + compile 'com.android.support:recyclerview-v7:21.0.+' +} +</pre> diff --git a/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd b/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd new file mode 100644 index 000000000000..d8c996d0255b --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=定義陰影和裁剪視圖 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#Elevation">指定視圖的高度</a></li> + <li><a href="#Shadows">自訂視圖陰影和外框</a></li> + <li><a href="#Clip">裁剪視圖</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + +<p>材料設計為 UI 元素引入高度的概念。高度有助使用者了解每個元素的相對重要性並將焦點放在手邊的工作上。 +</p> + +<p>視圖的高度以 Z 屬性表示,決定其陰影的視覺外觀:視圖的 Z 值越高,投射範圍越大,陰影也就越柔和。 +Z 值較高的視圖會遮住 Z 值較低的視圖。然而,視圖的 Z 值不會影響視圖的大小。 +</p> + +<p>陰影是由較高的上層視圖繪製,因此也和標準視圖裁剪一樣,預設由上層視圖裁剪陰影。 +</p> + +<p>當小工具在執行某些操作動作而暫時升高到視圖平面的上方時,高度對於建立動畫也非常實用。 +</p> + +<p>如需有關材料設計高度的詳細資訊,請參閱 <a href="http://www.google.com/design/spec/what-is-material/objects-in-3d-space.html">3D 空間中的物件</a>。 + +</p> + + +<h2 id="Elevation">指定視圖的高度</h2> + +<p>視圖的 Z 值有兩個部分: + +<ul> +<li>高度:靜態部分。</li> +<li>解譯:用在動畫的動態部分。</li> +</ul> + +<p><code>Z = elevation + translationZ</code></p> + +<img src="{@docRoot}training/material/images/shadows-depth.png" width="580" height="261" alt="" /> +<p class="img-caption"><strong>圖 1</strong> - 不同視圖高度的陰影。</p> + +<p>如要在版面配置定義中設定視圖的高度,請使用 <code>android:elevation</code> 屬性。 +如果要在操作行為的程式碼中設定檢視的高度,請使用 {@link android.view.View#setElevation View.setElevation()} 方法。 +</p> + +<p>如要在操作行為的程式碼中設定檢視的高度,請使用 {@link android.view.View#setTranslationZ +View.setTranslationZ()} 方法。</p> + +<p>新的 {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} 和 {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} 方法可以很輕易地讓視圖高度變得栩栩如生。 +如需詳細資訊,請參閱 {@link android.view.ViewPropertyAnimator}的 API 參考資料和<a href="{@docRoot}guide/topics/graphics/prop-animation.html">屬性動畫開發人員指南</a>。 + +</p> + +<p>您也可以使用 {@link android.animation.StateListAnimator} 以宣告的方式指定這些動畫。 +當狀態變更觸發動畫時, 例如使用者按下按鈕,這會特別實用。 +如需詳細資訊,請參閱<a href="{@docRoot}training/material/animations.html#ViewState">動畫顯示視圖狀態變更</a>。 +</p> + +<p>Z 值的測量單位為 dp (密度獨立像素)。</p> + + +<h2 id="Shadows">自訂視圖陰影和外框</h2> + +<p>檢視背景可繪項目的邊界決定了視圖陰影的預設形狀。<strong>外框</strong>代表圖形物件的外部形狀,並可定義輕觸回饋的漣漪區域。 + +</p> + +<p>我們來看看這個使用背景可繪項目定義的視圖:</p> + +<pre> +<TextView + android:id="@+id/myview" + ... + android:elevation="2dp" + android:background="@drawable/myrect" /> +</pre> + +<p>背景可繪項目定義為有圓形邊角的長方形:</p> + +<pre> +<!-- res/drawable/myrect.xml --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#42000000" /> + <corners android:radius="5dp" /> +</shape> +</pre> + +<p>因為背景可繪項目定義了視圖的外框,所以視圖投射出有圓形邊角的陰影。 +如果提供自訂的外框,則會覆寫視圖陰影的預設形狀。</p> + +<p>在程式碼中定義視圖的自訂外框:<p> + +<ol> +<li>延伸 {@link android.view.ViewOutlineProvider} 類別。</li> +<li>覆寫 {@link android.view.ViewOutlineProvider#getOutline getOutline()} 方法。</li> +<li>使用 {@link +android.view.View#setOutlineProvider View.setOutlineProvider()} 方法對您的視圖指定新的外框提供者。</li> +</ol> + +<p>您可以使用 {@link android.graphics.Outline} 類別中的方法,建立橢圓形外框和有圓形邊角的長方形外框。 +視圖的預設外框提供者會從視圖的背景取得外框。 +如果要避免視圖投射出陰影,請將視圖的外框提供者設定為 <code>null</code>。 +</p> + + +<h2 id="Clip">裁剪視圖</h2> + +<p>裁剪視圖可以讓您輕鬆地變更視圖的形狀。您可以裁剪視圖,讓視圖與其他設計元素有一致的外觀,或變更視圖的形狀來回應使用者的輸入。您可以使用 {@link android.view.View#setClipToOutline +View.setClipToOutline()} 方法或 <code>android:clipToOutline</code> 屬性,將視圖裁剪為其外框區域。 + +根據 {@link android.graphics.Outline#canClip Outline.canClip()} 方法的定義,只有長方形、圓形和圓角長方形的外框才支援裁剪。 + +</p> + +<p>如要將視圖裁剪為可繪項目的形狀,請將可繪項目設定為視圖的背景 (如上所示),然後呼叫 {@link android.view.View#setClipToOutline View.setClipToOutline()} 方法。 + +</p> + +<p>裁剪視圖是一種耗費資源的操作,所以請不要將您用來裁剪視圖的形狀做成動畫。 +如果想要達到這種效果,請使用<a href="{@docRoot}training/material/animations.html#Reveal">顯示效果</a>動畫。</p> diff --git a/docs/html-intl/intl/zh-tw/training/material/theme.jd b/docs/html-intl/intl/zh-tw/training/material/theme.jd new file mode 100644 index 000000000000..61cd8bdcfda5 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=使用材料設計風格 + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>本課程示範</h2> +<ol> + <li><a href="#ColorPalette">自訂色板</a></li> + <li><a href="#StatusBar">自訂狀態列</a></li> + <li><a href="#Inheritance">設計風格個別視圖</a></li> +</ol> +<h2>您也應該閱讀</h2> +<ul> + <li><a href="http://www.google.com/design/spec">材料設計規格</a></li> + <li><a href="{@docRoot}design/material/index.html">Android 上的材料設計</a></li> +</ul> +</div> +</div> + + +<p>新的材料設計風格提供:</p> + +<ul> + <li>可設定其色板的系統小工具</li> + <li>系統小工具的輕觸回饋動畫</li> + <li>操作行為轉換動畫</li> +</ul> + +<p>您可根據品牌特性,使用您控制的色板以自訂材料設計風格的外觀。 +您可以使用風格主題屬性,為行為欄和狀態列著色,如<a href="#fig3">圖 3</a> 所示。 +</p> + +<p>系統小工具經過全新設計,有輕觸回饋動畫。您可以為應用程式自訂色板、輕觸回饋動畫,以及操作行為轉換。 +</p> + +<p>材料設計風格定義為:</p> + +<ul> + <li><code>@android:style/Theme.Material</code> (深色版本)</li> + <li><code>@android:style/Theme.Material.Light</code> (淺色版本)</li> + <li><code>@android:style/Theme.Material.Light.DarkActionBar</code></li> +</ul> + +<p>如需您可以使用的材料樣式清單,請參閱 {@link android.R.style R.style} 的 API 參考資料。 +</p> + +<!-- two columns, dark/light material theme example --> +<div style="width:700px;margin-top:25px;margin-bottom:10px"> +<div style="float:left;width:250px;margin-left:40px;margin-right:60px;"> + <img src="{@docRoot}design/material/images/MaterialDark.png" width="500" height="238"> + <div style="width:170px;margin:0 auto"> + <p style="margin-top:8px;font-size:12px"><strong>圖 1</strong>.深色材料設計風格</p> + </div> +</div> +<div style="float:left;width:250px;margin-right:0px;"> + <img src="{@docRoot}design/material/images/MaterialLight.png" width="500" height="238"> + <div style="width:170px;margin:0 auto"> + <p style="margin-top:8px;font-size:12px"><strong>圖 2</strong>.淺色材料設計風格</p> + </div> +</div> +<br style="clear:left"> +</div> + +<p class="note"> +<strong>注意:</strong>材料設計風格只能在 Android 5.0 (API 級別 21) 及以上版本中使用。 +<a href="{@docRoot}tools/support-library/features.html#v7">v7 支援程式庫</a>針對某些小工具提供使用材料設計樣式的設計風格,並支援自訂色板。 + +如需詳細資訊,請參閱<a href="{@docRoot}training/material/compatibility.html">維持相容性</a>。 + +</p> + + +<h2 id="ColorPalette">自訂色板</h2> + +<p style="margin-bottom:30px">如要自訂設計風格的基礎顏色來搭配您的品牌,在繼承材料設計風格時,請使用設計風格屬性定義您的自訂顏色: +</p> + +<pre> +<resources> + <!-- inherit from the material theme --> + <style name="AppTheme" parent="android:Theme.Material"> + <!-- Main theme colors --> + <!-- your app branding color for the app bar --> + <item name="android:colorPrimary">@color/primary</item> + <!-- darker variant for the status bar and contextual app bars --> + <item name="android:colorPrimaryDark">@color/primary_dark</item> + <!-- theme UI controls like checkboxes and text fields --> + <item name="android:colorAccent">@color/accent</item> + </style> +</resources> +</pre> + +<div style="float:right;margin-left:25px;margin-top:20px;margin-bottom:10px" id="fig3"> +<img src="{@docRoot}training/material/images/ThemeColors.png" width="250" height="445" /> +<p class="img-caption" style="margin-bottom:0px"> +<strong>圖 3.</strong>自訂材料設計風格。</p> +</div> + + +<h2 id="StatusBar">自訂狀態列</h2> + +<p>材料設計風格可讓您輕鬆自訂狀態列,因此,您可指定符合品牌的顏色,並提供足夠的對比來顯示白色的狀態圖示。 +如要設定狀態列的自訂顏色,請在延伸材料設計風格時使用 <code>android:statusBarColor</code> 屬性。 + +<code>android:statusBarColor</code> 預設會繼承 <code>android:colorPrimaryDark</code> 的值。 +</p> + +<p>您也可以自行繪製狀態列。例如,若您想在相片上面顯示透明的狀態列,卻想搭配一點深色的漸層以確保白色的狀態圖示清晰可見。 + +請將 <code>android:statusBarColor</code> 屬性設定為 <code>@android:color/transparent</code>,並且視需要調整視窗標幟。 +您也可以使用 {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} 方法顯示動畫或進行淡化。 + +</p> + +<p class="note"> +<strong>注意:</strong>狀態列應該與主要工具列有清楚區分的輪廓,除非您想在這些列後面顯示全版的豐富圖像或媒體內容,或使用漸層以確保圖示清晰可見。 + + +</p> + +<p>當您自訂瀏覽列和狀態列時,請讓兩者都變成透明,或者只修改狀態列。 +在其他所有狀況下,瀏覽列都必須保持黑色。</p> + + +<h2 id="Inheritance">設計風格個別視圖</h3> + +<p>XML 版面配置定義中的元素可以指定 <code>android:theme</code> 屬性,參考設計風格資源。 +此屬性會修改元素和所有子元素的設計風格,對於改變介面特定部份的設計風格色板非常實用。 + +</p> |