版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
Android編程指南25-37章(共37章)目錄\h第25章XMLDrawable與9-Patches\h25.1XMLdrawable\h25.2statelistdrawable\h25.3layerlist與insetdrawable\h25.4使用9-patch圖像\h第26章HTTP與后臺任務(wù)\h26.1創(chuàng)建PhotoGallery應(yīng)用\h26.2網(wǎng)絡(luò)連接基本\h26.3使用AsyncTask在后臺線程上運行代碼\h26.4線程與主線程\h26.5獲取FlickrXML數(shù)據(jù)\h26.6從AsyncTask回到主線程\h26.7深入學(xué)習(xí):再探AsyncTask\h26.8挑戰(zhàn)練習(xí):分頁\h第27章Looper、Handler與HandlerThread\h27.1設(shè)置GridView以顯示圖片\h27.2批量下載縮略圖\h27.3與主線程通信\h27.4創(chuàng)建并啟動后臺線程\h27.5Message與messageHandler\h27.6深入學(xué)習(xí):AsyncTask與Thread\h27.7挑戰(zhàn)練習(xí):預(yù)加載以及緩存\h第28章搜索\h28.1搜索Flickr網(wǎng)站\h28.2搜索對話框\h28.3在Android3.0以后版本的設(shè)備上使用SearchView\h28.4挑戰(zhàn)練習(xí)\h第29章后臺服務(wù)\h29.1創(chuàng)建IntentService\h29.2服務(wù)的作用\h29.3查找最新返回結(jié)果\h29.4使用AlarmManager延遲運行服務(wù)\h29.5控制定時器\h29.6通知信息\h29.7深入學(xué)習(xí):服務(wù)細(xì)節(jié)內(nèi)容\h第30章broadcastIntent\h30.1隨設(shè)備重啟而重啟的定時器\h30.2過濾前臺通知消息\h30.3receiver與長時運行任務(wù)\h第31章網(wǎng)頁瀏覽\h31.1最后一段Flickr數(shù)據(jù)\h31.2簡單方式:使用隱式intent\h31.3較難方式:使用WebView\h31.4深入學(xué)習(xí):注入JavaScript對象\h第32章定制視圖與觸摸事件\h32.1創(chuàng)建DragAndDraw項目\h32.2創(chuàng)建定制視圖\h32.3處理觸摸事件\h32.4onDraw(...)方法內(nèi)的圖形繪制\h32.5挑戰(zhàn)練習(xí):設(shè)備旋轉(zhuǎn)問題\h第33章跟蹤設(shè)備的地理位置\h33.1啟動RunTracker項目\h33.2地理位置與LocationManager\h33.3接收定位數(shù)據(jù)更新broadcast\h33.4使用定位數(shù)據(jù)刷新UI顯示\h33.5快速定位:最近一次地理位置\h33.6在物理和虛擬設(shè)備上測試地理位置定位\h第34章使用SQLite本地數(shù)據(jù)庫\h34.1在數(shù)據(jù)庫中存儲旅程和地理位置信息\h34.2查詢數(shù)據(jù)庫中的旅程列表\h34.3使用CursorAdapter顯示旅程列表\h34.4創(chuàng)建新旅程\h34.5管理現(xiàn)有旅程\h34.6挑戰(zhàn)練習(xí):識別當(dāng)前跟蹤的旅程\h第35章使用Loader加載異步數(shù)據(jù)\h35.1Loader與LoaderManager\h35.2在RunTracker應(yīng)用中使用Loader\h35.3加載旅程列表\h35.4加載單個旅程\h35.5加載旅程的最近一次地理位置\h第36章使用地圖\h36.1添加MapsAPI給RunTracker應(yīng)用\h36.2在地圖上顯示用戶的地理位置\h36.3顯示旅程路線\h36.4為旅程添加開始和結(jié)束地圖標(biāo)注\h36.5挑戰(zhàn)練習(xí):實時數(shù)據(jù)更新\h第37章編后語\h37.1終極挑戰(zhàn)第25章XMLDrawable與9-Patches上一章,我們使用高級布局技巧,快速搭建了TV遙控器的用戶界面。由于使用的是一成不變的Android式界面風(fēng)格,當(dāng)前用戶界面雖然看上去還行,但似乎給人一種灰蒙蒙且單調(diào)沉悶的感覺。本章,我們將使用兩種新的用戶界面設(shè)計工具,賦予其一種全新的獨特視覺體驗,如圖25-1所示。圖25-1改進后的用戶界面兩種設(shè)計工具均屬于drawable。Android把任何可繪制在屏幕上的圖形圖像都稱為drawable。drawable可以是一種抽象的圖形、一個繼承Drawable類的子類,或者是一張位圖圖像。我們已用過的封裝圖片的BitmapDrawable(詳見第20章)也是一種drawable。本章,我們將會接觸到更多的drawable:statelistdrawable、shapedrawable、layerlistdrawable以及ninepatchdrawable。前三個drawable通常定義在XML布局文件中,因此我們統(tǒng)一將它們歸屬為XMLdrawable類別。25.1XMLdrawable在學(xué)習(xí)使用XMLdrawable之前,先試著使用android:background屬性更改按鈕的背景顏色,如代碼清單25-1所示。代碼清單25-1嘗試更改按鈕的背景顏色(values/styles.xml)<stylename="RemoteButton">
<itemname="android:layout_width">0dp</item>
<itemname="android:layout_height">match_parent</item>
<itemname="android:textColor">#556699</item>
<itemname="android:textSize">20dp</item>
<itemname="android:layout_margin">3dp</item>
<itemname="android:background">#ccd7ee</item>
</style>再次運行應(yīng)用,可看到以下修改后的用戶界面,如圖25-2所示。圖25-2哪里出問題了如圖所示,按鈕的三維視覺效果消失了。點擊任何一個按鈕,會發(fā)現(xiàn)按鈕的狀態(tài)切換也不起作用了。只改變了一個屬性,為什么會帶來如此大的變化?這是因為,和View類不同,Button類沒有被賦予默認(rèn)樣式。默認(rèn)樣式來自于所選主題,并會設(shè)置一個Drawable作為視圖的背景。正是這種背景drawable負(fù)責(zé)著視圖的三維顯示效果以及狀態(tài)的切換。學(xué)完本章,我們會知道如何自己定制可用的drawable。現(xiàn)在開始我們的學(xué)習(xí)。首先是利用ShapeDrawable創(chuàng)建彩色圖形。既然XMLdrawable與特定的像素密度無關(guān),因此無需考慮特定像素密度的目錄,只需將其放入默認(rèn)的drawable目錄即可。在包瀏覽器中,創(chuàng)建一個默認(rèn)的res/drawable目錄。然后在該目錄下,以shape為根元素創(chuàng)建一個名為button_shape_normal.xml的文件,如代碼清單25-2所示。(為何XML文件名包含“normal”字樣?這是因為我們馬上還會創(chuàng)建一個非normal的XML文件。)代碼清單25-2創(chuàng)建一個shapedrawable按鈕(drawable/button_shape_normal.xml)<?xmlversion="1.0"encoding="utf-8"?><shapexmlns:android="/apk/res/android"
android:shape="rectangle">
<cornersandroid:radius="3dp"/>
<gradient
android:angle="90"
android:endColor="#cccccc"
android:startColor="#acacac"/>
</shape>該XML文件定義了一個圓角矩形。corner元素指定了圓角矩形的圓角半徑,而gradient元素則指定了色彩漸變的方向以及起始顏色。也可使用shape創(chuàng)建其他各種圖形,如橢圓、線條以及環(huán)等,并設(shè)置不同的視覺風(fēng)格。請訪問開發(fā)者文檔網(wǎng)頁<\h/guide/topics/resources/drawable-resource.html>,查看更多shape的相關(guān)信息。在styles.xml中,更新按鈕的樣式定義,使用新建的Drawable作為按鈕的背景,如代碼清單25-3所示。代碼清單25-3更新按鈕樣式(values/styles.xml)<stylename="RemoteButton">
<itemname="android:layout_width">0dp</item>
<itemname="android:layout_height">match_parent</item>
<itemname="android:textColor">#556699</item>
<itemname="android:textSize">20dp</item>
<itemname="android:layout_margin">3dp</item>
<itemname="android:background">#ccd7ee</item>
<itemname="android:background">@drawable/button_shape_normal</item>
</style>運行RemoteControl應(yīng)用,比較用戶界面升級前后的異同,如圖25-3所示。圖25-3圓角按鈕25.2statelistdrawable雖然按鈕的顯示效果看上去還不錯,但這些按鈕仍然是靜態(tài)的。實際上,更新前,Button的背景默認(rèn)使用了statelistdrawable。使用statelistdrawable,可根據(jù)關(guān)聯(lián)View的不同狀態(tài)顯示不同的drawable。(第18章中,我們曾用statelistdrawable切換過列表項的背景。)雖然狀態(tài)分很多種,但這里只需關(guān)心按鈕點擊前后的狀態(tài)即可。首先創(chuàng)建點擊狀態(tài)下的按鈕背景。除色彩差別外,其應(yīng)與正常狀態(tài)下的按鈕背景完全相同。在包瀏覽器中,復(fù)制一份button_shape_normal.xml文件,并命名為button_shape_pressed.xml。然后打開該XML文件,將其中定義的角度屬性值增加180度,以改變漸變方向,如代碼清單25-4所示。代碼清單25-4創(chuàng)建按鈕點擊狀態(tài)下應(yīng)顯示的shape(drawable/button_shape_pressed.xml)<?xmlversion="1.0"encoding="utf-8"?>
<shapexmlns:android="/apk/res/android"
android:shape="rectangle">
<cornersandroid:radius="3dp"/>
<gradient
android:angle="90"
android:angle="270"
android:endColor="#cccccc"
android:startColor="#acacac"/>
</shape>接下來,我們需要一個statelistdrawable。statelistdrawable必須包含一個selector根元素,以及用來描述狀態(tài)的一個或多個item。右鍵單擊res/drawable/目錄,以selector為根元素,創(chuàng)建一個名為button_shape.xml的文件,如代碼清單25-5所示。代碼清單25-5創(chuàng)建交互式的按鈕shape(drawable/button_shape.xml)<?xmlversion="1.0"encoding="utf-8"?><selectorxmlns:android="/apk/res/android">
<itemandroid:drawable="@drawable/button_shape_normal"
android:state_pressed="false"/>
<itemandroid:drawable="@drawable/button_shape_pressed"
android:state_pressed="true"/></selector>未點擊狀態(tài)下,按鈕顯示的是深色文字,其與淺色系背景搭配比較合適。由于背景比較灰暗,因此在未點擊狀態(tài)下,按鈕淺色系的背景看上去比較合適。而在點擊狀態(tài)下,使用深色系背景比較合適。與statelistshape的創(chuàng)建類似,我們也可以輕松創(chuàng)建并使用statelistcolor。右鍵單擊res/drawable/目錄,創(chuàng)建另一個名為button_text_color.xml的statelistdrawable,如代碼清單25-6所示。代碼清單25-6狀態(tài)敏感的按鈕文字顏色(drawable/button_text_color.xml)<?xmlversion="1.0"encoding="utf-8"?><selectorxmlns:android="/apk/res/android">
<itemandroid:state_pressed="false"android:color="#ffffff"/>
<itemandroid:state_pressed="true"android:color="#556699"/></selector>現(xiàn)在,在styles.xml中,調(diào)整按鈕樣式使用新建背景drawable和新建文字顏色,如代碼清單25-7所示。代碼清單25-7更新按鈕樣式(values/styles.xml)<stylename="RemoteButton">
<itemname="android:layout_width">0dp</item>
<itemname="android:layout_height">match_parent</item>
<itemname="android:textColor">#556699</item>
<itemname="android:textSize">20dp</item>
<itemname="android:layout_margin">3dp</item>
<itemname="android:background">@drawable/button_shape_normal</item>
<itemname="android:background">@drawable/button_shape</item>
<itemname="android:textColor">@drawable/button_text_color</item>
</style>運行RemoteControl應(yīng)用。查看點擊狀態(tài)下的按鈕背景,如圖25-4所示。圖25-4點擊狀態(tài)下的按鈕25.3layerlist與insetdrawable應(yīng)用初始版本采用的Android老舊樣式按鈕具有陰影顯示效果。不幸的是,shapedrawable沒有可用的陰影屬性。但使用其他兩種類型的XMLdrawable,可自創(chuàng)陰影效果。這兩種XMLdrawable類型分別是:layerlistdrawable和insetdrawable。下面我們來介紹創(chuàng)建陰影效果的方法。首先,使用與當(dāng)前按鈕drawable同樣的shape創(chuàng)建一個陰影。然后,使用layer-list將陰影shape與當(dāng)前按鈕組合起來,再使用inset對按鈕底邊進行適當(dāng)?shù)亩叹嘁莆唬钡侥軌蚩吹疥幱帮@示。在res/drawable/目錄下,以layer-list為根元素,創(chuàng)建一個名為button_shape_shadowed.xml文件,如代碼清單25-8所示。代碼清單25-8默認(rèn)狀態(tài)下的按鈕陰影(drawable/button_shape_shadowed.xml)<?xmlversion="1.0"encoding="utf-8"?><layer-listxmlns:android="/apk/res/android">
<item>
<shapeandroid:shape="rectangle">
<cornersandroid:radius="5dp"/>
<gradient
android:angle="90"
android:centerColor="#303339"
android:centerY="0.05"
android:endColor="#000000"
android:startColor="#00000000"/>
</shape>
</item>
<item>
<inset
android:drawable="@drawable/button_shape"
android:insetBottom="5dp"/>
</item></layer-list>可以看到,layer-list元素包含了多個Drawable,并以從后至前的繪制順序進行排序。列表中第二個drawable是一個insetdrawable,其任務(wù)就是在已創(chuàng)建的drawable底部做5dp單位的移位,并剛好落在移位形成的陰影上。注意,陰影drawable并未使用單獨的文件,而是直接被嵌入了layerlist中。該技巧同樣適用于其他drawable,如前面講到的statelistdrawable??勺孕袥Q定究竟是嵌套drawable還是將其放入單獨的文件使用。以單獨文件的形式使用drawable可減少重復(fù)代碼,簡化各相關(guān)文件,但這也同時會使drawable/目錄充斥著大量文件。不過要記住,應(yīng)總是以簡單且易于理解的方式編寫代碼。在styles.xml中,修改按鈕的樣式定義,指向可顯示陰影的新建drawable,如代碼清單25-9所示。代碼清單25-9按鈕樣式的最終修改(values/styles.xml)<stylename="RemoteButton">
<itemname="android:layout_width">0dp</item>
<itemname="android:layout_height">match_parent</item>
<itemname="android:textSize">20dp</item>
<itemname="android:layout_margin">3dp</item>
<itemname="android:background">@drawable/button_shape_normal</item>
<itemname="android:background">@drawable/button_shape_shadowed</item>
<itemname="android:textColor">@drawable/button_text_color</item>
</style>最后要做的是,為整個主視圖創(chuàng)建一個色彩漸變的drawable,以獲得細(xì)微的光影效果。以shape為根元素,新建一個名為remote_background.xml的drawable文件。然后添加代碼清單25-10所示代碼。(注意,如未指定shape,系統(tǒng)會默認(rèn)使用矩形。)代碼清單25-10用于根視圖的新背景drawable(drawable/remote_background.xml)<?xmlversion="1.0"encoding="utf-8"?><shapexmlns:android="/apk/res/android">
<gradient
android:centerY="0.05"
android:endColor="#dbdbdb"
android:gradientRadius="500"
android:startColor="#f4f4e9"
android:type="radial"/>
</shape>編輯fragment_remote_control.xml文件,引入新建的背景drawable,如代碼清單25-11所示。代碼清單25-11將背景drawable應(yīng)用于布局(layout/fragment_remote_control.xml)<TableLayoutxmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:id="@+id/fragment_remote_control_tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/remote_background"
android:stretchColumns="*">可以看到,在整個用戶界面的調(diào)整過程中,無需對fragment布局文件做出調(diào)整。(最后一次代碼修改除外。當(dāng)然,也可為TableLayout創(chuàng)建新樣式。)這確實給應(yīng)用開發(fā)帶來了方便。添加新按鈕或重新布置視圖需要更新布局文件,但更改用戶界面的顯示效果,使用樣式文件即可。25.4使用9-patch圖像如聘請了專業(yè)設(shè)計師進行UI設(shè)計,他們完成的設(shè)計成果往往無法直接通過XMLdrawable使用。然而,我們?nèi)匀豢赡苄枰诙鄠€地方復(fù)用可渲染資源。對于諸如按鈕背景這樣的可拉伸UI元素,Android提供了一種叫做9-Patch的圖形處理工具。接下來,我們對應(yīng)用界面頂部的兩個TextView進行修復(fù)。保持按鈕布局不變,但使用可拉伸drawable作為背景圖。布局與背景圖比TextView更窄,這樣可以節(jié)約一定的占用空間。第一張window.png為可拉伸背景圖,可為TextView提供雅致的漸變色,并賦予邊緣一定的邊界空間,如圖25-5所示。圖25-5用于頻道顯示窗口的背景圖另一張bar.png背景圖提供了底部輕微陰影以及頂部細(xì)邊。由于原圖太小,這里顯示的是它的放大版本,如圖25-6所示。圖25-6用于頻道輸入?yún)^(qū)的背景圖在隨書代碼文件的25_XMLDrawables/RemoteControl/res/drawable-hdpi目錄下,找到這些圖像。然后,將其復(fù)制到RemoteControl應(yīng)用的/res/drawable-hdpi目錄中。修改fragment_remote_control.xml文件,引入這些圖像作為對應(yīng)視圖的背景圖。同時調(diào)整文字顯示顏色以匹配新的背景圖。將頂部視圖的文字顏色調(diào)整為白色,將底部視圖的文字顏色調(diào)整為默認(rèn)的黑色。最后,為使界面看上去更整潔,設(shè)置底部TextView上的顯示文字為斜體樣式,如代碼清單25-12所示。代碼清單25-12將drawable添加給樣式(layout/fragment_remote_control.xml)<TableLayoutxmlns:android="/apk/res/android"
...>
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:gravity="center"
android:text="0"
android:textColor="#ffffff"
android:textSize="50dp"/>
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="#555555"
android:background="@drawable/bar"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textStyle="italic"
android:textSize="20dp"/>
...
</TableLayout>運行應(yīng)用,查看用戶界面顯示效果,如圖25-7所示。圖25-7破碎的夢可以看到,各圖像經(jīng)均勻的四面拉伸,鋪滿了整個視圖。有時可能會需要這樣的效果,但這里并無必要。顯然,模糊不清的圖像以及底部TextView無法居中顯示數(shù)字并不是我們想要的效果。使用9-patch圖像可解決該問題。9-patch圖像是一種特殊格式的文件,因此Android知道圖像的哪些部分可以拉伸縮放,哪些部分不可以。經(jīng)適當(dāng)處理后,可保證背景圖的邊角與工具創(chuàng)建的圖像保持一致性。為什么要叫做9-patch呢?9-patch可將圖像分成3×3的網(wǎng)格,即由9部分或9patch組成的網(wǎng)格。網(wǎng)格角落的patch不會被縮放,邊緣部分的4個patch只按一個維度縮放,而中間部分則同時按兩個維度縮放,如圖25-8所示。圖25-89-patch的作用9-patch圖像和普通的png圖像基本相同,但以下兩點除外:9-patch圖像文件名是以.9.png結(jié)尾的,圖像邊緣具有一個像素寬度的邊框,用以指定9-patch圖像的中間位置。邊框像素繪制為黑線,以表明中間位置,邊緣部分則用透明色表示??墒褂萌魏螆D形編輯工具來創(chuàng)建一張9-patch圖像,但使用AndroidSDK中自帶的draw9patch工具要更方便些。該工具位于SDK安裝目錄下的tools目錄內(nèi)。工具運行后,可直接拖曳文件到編輯區(qū)或從File菜單打開待編輯文件。在編輯區(qū)打開文件后,在圖像頂部填充黑色像素,并在左邊框標(biāo)記圖像的可伸縮區(qū)域。參照圖25-9所示,為window.png添加像素。圖25-9頻道顯示窗口的9-patch頂部以及左邊框標(biāo)記了圖像的可伸縮區(qū)域。那么底部以及右邊框又要如何處理呢?它們定義了用于9-patch圖像的可選drawable區(qū)域。drawable區(qū)域是內(nèi)容(通常是文字)繪制的地方。如不引用drawable區(qū)域,則默認(rèn)與可拉伸區(qū)域保持一致。這就是所需的效果,因此,可不引用drawable區(qū)域。完成后,將結(jié)果保存到window_patch.9.png中。注意,不要讓9-patch圖像與其他圖像發(fā)生沖突。例如,如同時存在名為window.9.png和window.png的兩張圖像,應(yīng)用編譯會失敗。緊接著,為bar.png圖像編輯一個9-patch。使用drawable區(qū)域以水平垂直方向居中顯示文字,并在邊緣提供4個像素寬度的邊框,如圖25-10所示。圖25-10頻道輸入?yún)^(qū)域的9-patch完成后,將文件保存為bar_patch.9.png。然后,右鍵單擊res/目錄,并點擊Refresh按鈕進行刷新。Eclipse自顧自的運行著,Refresh按鈕可提醒其再次查看文件系統(tǒng)。最后,使用9-patchdrawable調(diào)整兩個TextView,而非使用普通的老舊圖像,如代碼清單25-13所示。代碼清單25-13改用9-patch(layout/fragment_remote_control.xml)<TableLayoutxmlns:android="/apk/res/android"
...>
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:background="@drawable/window_patch"
android:gravity="center"
android:text="0"
android:textSize="50dp"/>
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="@drawable/bar"
android:background="@drawable/bar_patch"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textSize="20dp"/>
...
</TableLayout>運行RemoteControl應(yīng)用。如圖25-11所示,背景圖很漂亮!還記得應(yīng)用最初的簡陋界面嗎?有了9-patch,設(shè)計用戶界面可以說是省時又省力。而且,縱觀各種流行應(yīng)用,有著精美UI的應(yīng)用更容易吸引用戶。圖25-11優(yōu)化后的RemoteControl應(yīng)用界面第26章HTTP與后臺任務(wù)信息時代,互聯(lián)網(wǎng)應(yīng)用占用了用戶的大量時間。餐桌上無人交談,每個人都只顧低頭擺弄手機。一有時間,人們就上網(wǎng)檢查新聞推送、收發(fā)短信息,或是玩著聯(lián)網(wǎng)游戲。為著手學(xué)習(xí)Android網(wǎng)絡(luò)應(yīng)用的開發(fā),我們將創(chuàng)建一個名為PhotoGallery的應(yīng)用。PhotoGallery是圖片共享網(wǎng)站Flickr的客戶端應(yīng)用。它將獲取并展示上傳至Flickr網(wǎng)站的最新公共圖片。應(yīng)用的運行效果如圖26-1所示。圖26-1PhotoGallery應(yīng)用運行效果圖(圖26-1中的圖片是我們自己準(zhǔn)備的圖片,而非Flickr上的公共圖片。Flickr網(wǎng)站上的圖片歸上傳者私人所有,未經(jīng)本人許可,任何人不得使用??稍L問網(wǎng)址\h/pr/ycorp/permissions.aspx,了解更多有關(guān)Flickr上第三方內(nèi)容的使用權(quán)限問題。)接下來的六章我們將學(xué)習(xí)開發(fā)PhotoGallery應(yīng)用。前兩章將介紹有關(guān)網(wǎng)絡(luò)下載、XML文件解析、圖像顯示等基本知識。隨后的幾章里,通過各種特色功能的添加,將介紹有關(guān)搜索、服務(wù)、通知、廣播接收器以及網(wǎng)頁視圖等知識。本章,我們首先學(xué)習(xí)Android高級別的HTTP網(wǎng)絡(luò)編程。當(dāng)前,幾乎所有網(wǎng)絡(luò)服務(wù)的開發(fā)都是以HTTP網(wǎng)絡(luò)協(xié)議為基礎(chǔ)的。至本章結(jié)束時,我們要完成的任務(wù)是:獲取、解析以及顯示Flickr上圖片的文字說明。(第27章會介紹圖片獲取與顯示的相關(guān)內(nèi)容。)圖26-2本章完成的PhotoGallery應(yīng)用效果圖26.1創(chuàng)建PhotoGallery應(yīng)用按照圖26-3所示的配置,創(chuàng)建一個全新的Android應(yīng)用項目。圖26-3創(chuàng)建PhotoGallery應(yīng)用單擊Next按鈕,通過應(yīng)用向?qū)?chuàng)建一個名為PhotoGalleryActivity的空白activity。PhotoGallery應(yīng)用將繼續(xù)沿用前面一直使用的設(shè)計架構(gòu)。PhotoGalleryActivity同樣設(shè)計為SingleFragmentActivity的子類,其視圖為activity_fragment.xml中定義的容器視圖。PhotoGalleryActivity負(fù)責(zé)托管PhotoGalleryFragment實例。稍后我們會創(chuàng)建它。將SingleFragmentActivity.java和activity_fragment.xml從以前的項目復(fù)制到當(dāng)前項目中。在PhotoGalleryActivity.java中,刪除自動產(chǎn)生的模板代碼。然后設(shè)置PhotoGalleryActivity的父類為SingleFragmentActivity并實現(xiàn)它的createFragment()方法。createFragment()方法將返回一個PhotoGalleryFragment類實例。如代碼清單26-1所示。(暫時無需理會代碼的錯誤提示,它會在PhotoGalleryFragment類創(chuàng)建完成后自動消失。)代碼清單26-1activity的調(diào)整(PhotoGalleryActivity.java)publicclassPhotoGalleryActivityextendsActivity{publicclassPhotoGalleryActivityextendsSingleFragmentActivity{
/*Auto-generatedtemplatecode*/
@Override
publicFragmentcreateFragment(){
returnnewPhotoGalleryFragment();
}}PhotoGallery應(yīng)用將在GridView視圖中顯示獲取的內(nèi)容。而GridView由PhotoGalleryFragment的視圖組成。按照繼承關(guān)系,GridView也是一個AdapterView,因此其工作方式與ListView類似。然而,不像ListView,GridView沒有內(nèi)置方便實用的GridFragment。這意味著我們需自己創(chuàng)建布局文件,并在PhotoGalleryFragment類中進行實例化。在本章的后面,我們將在PhotoGalleryFragment類中使用adapter提供圖片說明文字給GridView視圖顯示。為創(chuàng)建fragment布局,重命名layout/activity_photo_gallery.xml為layout/fragment_photo_gallery.xml。然后以圖26-4所示的GridView替換原有布局內(nèi)容。圖26-4GridView視圖(layout/fragment_photo_gallery.xml)這里,我們設(shè)置列的寬度為120dp,并使用numColumns屬性指示GridView創(chuàng)建盡可能多的列,以鋪滿整個屏幕。如果在列的空間分配上出現(xiàn)少于120dp的剩余空間,則stretchMode屬性會要求GridView在全部列間均分這部分剩余空間。最后,創(chuàng)建PhotoGalleryFragment類,設(shè)置其為保留fragment,實例化生成新建布局并引用GridView視圖,如代碼清單26-2所示。代碼清單26-2一些代碼片斷(PhotoGalleryFragment.java)packagecom.bignerdranch.android.photogallery;
...
publicclassPhotoGalleryFragmentextendsFragment{
GridViewmGridView;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){
Viewv=inflater.inflate(R.layout.fragment_photo_gallery,container,false);
mGridView=(GridView)v.findViewById(R.id.gridView);
returnv;
}
}在繼續(xù)學(xué)習(xí)之前,嘗試運行PhotoGallery應(yīng)用。如果一切正常,我們會看到一個空白視圖。26.2網(wǎng)絡(luò)連接基本PhotoGallery應(yīng)用中,我們需要一個處理網(wǎng)絡(luò)連接的專用類。新建一個Java類。應(yīng)用要訪問連接的是Flickr網(wǎng)站,因此命名新建類為FlickrFetchr。FlickrFetchr類一開始只有g(shù)etUrlBytes(String)和getUrl(String)兩個方法。getUrlBytes(String)方法從指定URL獲取原始數(shù)據(jù)并返回一個字節(jié)流數(shù)組。getUrl(String)方法則將getUrlBytes(String)方法返回的結(jié)果轉(zhuǎn)換為String。在FlickrFetchr.java中,為getUrlBytes(String)和getUrl(String)方法添加實現(xiàn)代碼,如代碼清單26-3所示。代碼清單26-3基本網(wǎng)絡(luò)連接代碼(FlickrFetchr.java)packagecom.bignerdranch.android.photogallery;
...
publicclassFlickrFetchr{
byte[]getUrlBytes(StringurlSpec)throwsIOException{
URLurl=newURL(urlSpec);
HttpURLConnectionconnection=(HttpURLConnection)url.openConnection();
try{
ByteArrayOutputStreamout=newByteArrayOutputStream();
InputStreamin=connection.getInputStream();
if(connection.getResponseCode()!=HttpURLConnection.HTTP_OK){
returnnull;
}
intbytesRead=0;
byte[]buffer=newbyte[1024];
while((bytesRead=in.read(buffer))>0){
out.write(buffer,0,bytesRead);
}
out.close();
returnout.toByteArray();
}finally{
connection.disconnect();
}
}
publicStringgetUrl(StringurlSpec)throwsIOException{
returnnewString(getUrlBytes(urlSpec));
}
}在getUrlBytes(String)方法中,根據(jù)傳入的字符串參數(shù),如\h,首先創(chuàng)建一個URL對象。然后調(diào)用openConnection()方法創(chuàng)建一個指向要訪問URL的連接對象。URL.openConnection()方法默認(rèn)返回的是URLConnection對象,但我們要連接的是httpURL,因此需將其強制類型轉(zhuǎn)換為HttpURLConnection對象。隨后,我們得以調(diào)用它的getInputStream()、getResponseCode()等方法。HttpURLConnection對象雖然提供了一個連接,但只有在調(diào)用getInputStream()方法時(如果是POST請求,則調(diào)用getOutputStream()方法),它才會真正連接到指定的URL地址。在此之前我們無法獲得有效的返回代碼。一旦創(chuàng)建了URL并打開了網(wǎng)絡(luò)連接,我們便可循環(huán)調(diào)用read()方法讀取網(wǎng)絡(luò)連接到的數(shù)據(jù),直到取完為止。只要還有數(shù)據(jù)存在,InputStream類便可不斷輸出字節(jié)流數(shù)據(jù)。數(shù)據(jù)全部輸出后,關(guān)閉網(wǎng)絡(luò)連接,并將讀取的數(shù)據(jù)寫入ByteArrayOutputStream字節(jié)數(shù)組中。雖然getUrlBytes(String)方法完成了最重要的數(shù)據(jù)獲取任務(wù),但getUrl(String)才是本章真正需要的方法。它負(fù)責(zé)將getUrlBytes(String)方法獲取的字節(jié)數(shù)據(jù)轉(zhuǎn)換為String。至此,是不是想問,難道不可以在一個方法中完成全部任務(wù)?當(dāng)然可以,但是處理下一章的圖像數(shù)據(jù)下載時,我們需要使用兩個獨立的方法。獲取網(wǎng)絡(luò)使用權(quán)限要連接使用網(wǎng)絡(luò),還需完成一件事:取得使用網(wǎng)絡(luò)的權(quán)限。正如用戶不愿被偷拍照片一樣,他們也不想有人偷偷下載他們的圖片。要取得網(wǎng)絡(luò)使用權(quán)限,參照代碼清單26-4,添加以下代碼到AndroidManifest.xml文件中。代碼清單26-4在配置文件中添加網(wǎng)絡(luò)使用權(quán)限(AndroidManifest.xml)<manifestxmlns:android="/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15"/>
<uses-permissionandroid:name="android.permission.INTERNET"/>
...
</manifest>26.3使用AsyncTask在后臺線程上運行代碼接下來是調(diào)用并測試新添加的網(wǎng)絡(luò)連接代碼。注意,不能直接在PhotoGalleryFragment類中調(diào)用FlickrFetchr.getURL(String)方法,我們應(yīng)創(chuàng)建一個后臺線程,然后在該線程中運行代碼。使用后臺線程最簡便的方式是使用AsyncTask工具類。AsyncTask創(chuàng)建后臺線程后,我們便可在該線程上調(diào)用doInBackground(...)方法運行代碼。在PhotoGalleryFragment.java中,添加一個名為FetchItemsTask的內(nèi)部類。覆蓋AsyncTask.doInBackground(...)方法,從目標(biāo)網(wǎng)站獲取數(shù)據(jù)并記錄下日志,如代碼清單26-5所示。代碼清單26-5實現(xiàn)AsyncTask工具類方法(PhotoGalleryFragment.java)publicclassPhotoGalleryFragmentextendsFragment{
privatestaticfinalStringTAG="PhotoGalleryFragment";
GridViewmGridView;
...
privateclassFetchItemsTaskextendsAsyncTask<Void,Void,Void>{
@Override
protectedVoiddoInBackground(Void...params){
try{
Stringresult=newFlickrFetchr().getUrl("");
Log.i(TAG,"FetchedcontentsofURL:"+result);
}catch(IOExceptionioe){
Log.e(TAG,"FailedtofetchURL:",ioe);
}
returnnull;
}
}
}然后,在PhotoGalleryFragment.onCreate(...)方法中,調(diào)用FetchItemsTask新實例的execute()方法,如代碼清單26-6所示。代碼清單26-6實現(xiàn)AsyncTask工具類方法(PhotoGalleryFragment.java)publicclassPhotoGalleryFragmentextendsFragment{
privatestaticfinalStringTAG="PhotoGalleryFragment";
GridViewmGridView;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);
newFetchItemsTask().execute();
}
...
}調(diào)用execute()方法將啟動AsyncTask,繼而觸發(fā)后臺線程并調(diào)用doInBackground(...)方法。運行PhotoGallery應(yīng)用,查看LogCat窗口,可看到混合了大量Javascript的GoogleHTML主頁源代碼,如圖26-5所示。圖26-5LogCat中的GoogleHTML代碼既然已創(chuàng)建了后臺線程,并成功完成了網(wǎng)絡(luò)連接代碼的測試,接下來,我們來深入學(xué)習(xí)Android線程的知識。26.4線程與主線程網(wǎng)絡(luò)通常無法立即聯(lián)通。網(wǎng)絡(luò)服務(wù)器可能需要1~2秒的時間來響應(yīng)訪問請求,文件下載則耗時更久。鑒于網(wǎng)絡(luò)連接的耗時,自Honeycomb系統(tǒng)版本開始,Android禁止在主線程中發(fā)生任何網(wǎng)絡(luò)連接行為。即使強行為之,Android也會拋出NetworkOnMainThreadException的異常。這是為什么呢?要知道其中原因,首先需了解什么是線程,什么是主線程以及主線程的用途是什么。線程是一個單一的執(zhí)行序列。單個線程中的代碼可得到逐步執(zhí)行。每個Android應(yīng)用的運行都是從主線程開始的。然而,主線程并非如線程般的預(yù)定執(zhí)行序列,如圖26-6所示。相反,它處于一個無限循環(huán)的運行狀態(tài),等待著用戶或系統(tǒng)觸發(fā)事件的發(fā)生。事件觸發(fā)后,主線程便負(fù)責(zé)執(zhí)行代碼,以響應(yīng)這些事件。圖26-6一般線程與主線程想象一下,應(yīng)用就是一家大型鞋店,而閃電俠是唯一的員工。(是不是人人夢寐以求的場景?)要讓客戶滿意,鞋店里有大量工作需要他去處理,如擺放布置商品、為顧客取鞋、用量腳器為顧客量尺寸等等。作為售貨員,即使所有工作都由閃電俠一人完成,他也能夠快速響應(yīng),兼顧每一位顧客的需求。為保證完成任務(wù),閃電俠不能在單一事件上耗時過久。要是一批貨丟了怎么辦?這時,必須得有人打電話四處聯(lián)絡(luò)并設(shè)法找到丟失的貨物才行。假設(shè)讓閃電俠處理這項耗時的任務(wù),他在忙于聯(lián)絡(luò)貨物時,店里的顧客可能會等得有些不耐煩。閃電俠就如同應(yīng)用里的主線程。它運行著所有用于更新UI的代碼,其中包括響應(yīng)activity的啟動、按鈕的點擊等不同UI相關(guān)事件的代碼。(由于響應(yīng)的事件基本都與用戶界面相關(guān),主線程有時也叫做UI線程。)事件處理的循環(huán)使得UI相關(guān)代碼得以順序執(zhí)行。這足以保證任何事件處理都不會發(fā)生沖突,同時代碼也能夠快速響應(yīng)執(zhí)行。目前為止,我們編寫的所有代碼(剛剛使用AsyncTask工具類完成的代碼除外)都是在主線程中執(zhí)行的。主線程之外網(wǎng)絡(luò)連接如同致電經(jīng)營鞋類業(yè)務(wù)的分銷商:相比其他任務(wù),網(wǎng)絡(luò)連接比較耗時。等待響應(yīng)的時候,用戶界面將會毫無反應(yīng),這可能會導(dǎo)致應(yīng)用無響應(yīng)(ANR:ApplicationNotResponding)。如果Android系統(tǒng)監(jiān)控服務(wù)確認(rèn)主線程無法響應(yīng)重要事件,如按下后退鍵等,則應(yīng)用無響應(yīng)會發(fā)生。用戶會看到如圖26-7所示的畫面。圖26-7應(yīng)用無響應(yīng)這也就是為什么自Honeycomb系統(tǒng)開始,Android禁止在主線程中發(fā)生任何網(wǎng)絡(luò)連接行為的原因所在?;氐郊傧氲男曛校虢鉀Q問題,(自然)需要再雇傭一名閃電俠專門負(fù)責(zé)與供銷商之間的聯(lián)絡(luò)工作。Android系統(tǒng)中所做的操作與之差不多,即創(chuàng)建一個后臺線程,然后通過該線程訪問網(wǎng)絡(luò)。怎樣利用后臺線程才最容易呢?答案是:非AsyncTask工具類莫屬。本章的后面,我們會使用AsyncTask類處理一些其他任務(wù)?,F(xiàn)在,我們還是先使用網(wǎng)絡(luò)訪問代碼做點實際工作。26.5獲取FlickrXML數(shù)據(jù)Flickr提供了方便而強大的XMLAPI。可從\h/services/api/文檔頁查看使用細(xì)節(jié)。在常用瀏覽器中打開API文檔網(wǎng)頁,找到RequestFormats列表。我們準(zhǔn)備使用最簡單的REST響應(yīng)格式。查看文檔得知,它的API端點(endpoint)是\h/services/rest/。我們可在此端點上調(diào)用Flickr提供的方法?;氐紸PI文檔主頁,找到APIMethods列表。向下滾動到photos區(qū)域并定位flickr.photos.getRecent方法。點擊查看該方法,可以看到,文檔對該方法的描述為:“返回最近上傳到flickr的公共圖片列表清單?!边@恰好就是PhotoGallery應(yīng)用所需的方法。getRecent方法唯一需要的參數(shù)是一個APIkey。為獲得該參數(shù),返回\h/services/api/文檔主頁,找到并點擊APIkeys鏈接進行申請。申請需使用YahooID進行登錄。登錄成功后,申請一個全新的非商業(yè)用途APIkey。成功提交申請后,可獲得類似4f721bgafa75bf6d2cb9af54f937bb70的APIkey。獲取APIkey后,可直接向Flickr網(wǎng)絡(luò)服務(wù)發(fā)起一個GET請求,即\h/services/rest/?method=flickr.photos.getRecent&api_key=xxx。接下來開始編碼工作。首先,在FlickrFetchr類中添加一些常量,如代碼清單26-7所示。代碼清單26-7添加一些常量(FlickrFetchr.java)publicclassFlickrFetchr{
publicstaticfinalStringTAG="FlickrFetchr";
privatestaticfinalStringENDPOINT="/services/rest/";
privatestaticfinalStringAPI_KEY="yourApiKeyHere";
privatestaticfinalStringMETHOD_GET_RECENT="flickr.photos.getRecent";
privatestaticfinalStringPARAM_EXTRAS="extras";
privatestaticfinalStringEXTRA_SMALL_URL="url_s";這些常量定義了訪問端點、方法名、APIkey以及一個值為url_s的extra參數(shù)。指定url_s值的extra參數(shù),實際是告訴Flickr:如有小尺寸圖片,也請一并將其URL包括在內(nèi)并返回。使用剛才定義的常量,編寫一個方法,構(gòu)建適當(dāng)?shù)恼埱骍RL并獲取所需內(nèi)容,如代碼清單26-8所示。代碼清單26-8添加fetchItems()方法(FlickrFetchr.java)publicclassFlickrFetchr{
...
StringgetUrl(StringurlSpec)throwsIOException{
returnnewString(getUrlBytes(urlSpec));
}
publicvoidfetchItems(){
try{
Stringurl=Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method",METHOD_GET_RECENT)
.appendQueryParameter("api_key",API_KEY)
.appendQueryParameter(PARAM_EXTRAS,EXTRA_SMALL_URL)
.build().toString();
StringxmlString=getUrl(url);
Log.i(TAG,"Receivedxml:"+xmlString);
}catch(IOExceptionioe){
Log.e(TAG,"Failedtofetchitems",ioe);
}
}
}這里我們使用Uri.Builder構(gòu)建完整的FlickrAPI請求URL。便利類Uri.Builder可創(chuàng)建正確轉(zhuǎn)義的參數(shù)化URL。Uri.Builder.appendQueryParameter(String,String)可自動轉(zhuǎn)義查詢字符串。最后,修改PhotoGalleryFragment類中的AsyncTask內(nèi)部類,調(diào)用新的fetchItems()方法,如代碼清單26-9所示。代碼清單26-9調(diào)用fetchItems()方法(PhotoGalleryFragment.java)privateclassFetchItemsTaskextendsAsyncTask<Void,Void,Void>{
@Override
protectedVoiddoInBackground(Void...params){
try{
Stringresult=newFlickrFetchr().getUrl("");
Log.i(TAG,"FetchedcontentsofURL:"+result);
}catch(IOExceptionioe){
Log.e(TAG,"FailedtofetchURL:",ioe);
}
newFlickrFetchr().fetchItems();
returnnull;
}
}運行PhotoGallery應(yīng)用??煽吹絃ogCat窗口中出現(xiàn)的大量的FlickrXML,如圖26-8所示。圖26-8FlickrXML成功獲取FlickrXML返回結(jié)果后,該如何使用它呢?我們可按需處理這些數(shù)據(jù),也即將其存入一個或多個模型對象中。我們待會要為PhotoGallery應(yīng)用創(chuàng)建的模型類名為GalleryItem。圖26-9為PhotoGallery應(yīng)用的對象圖解。圖26-9PhotoGallery應(yīng)用的對象圖解注
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年太原旅游職業(yè)學(xué)院高職單招高職單招英語2016-2024歷年頻考點試題含答案解析
- 2025年大連航運職業(yè)技術(shù)學(xué)院高職單招職業(yè)適應(yīng)性測試近5年??及鎱⒖碱}庫含答案解析
- 2025年四川商務(wù)職業(yè)學(xué)院高職單招職業(yè)技能測試近5年??及鎱⒖碱}庫含答案解析
- 2025年嘉興南洋職業(yè)技術(shù)學(xué)院高職單招職業(yè)技能測試近5年常考版參考題庫含答案解析
- 2025至2031年中國紅木餐吊燈行業(yè)投資前景及策略咨詢研究報告
- 2025至2031年中國淋浴盆水去水連軟管行業(yè)投資前景及策略咨詢研究報告
- 2025至2031年中國懸掛式隧道爐行業(yè)投資前景及策略咨詢研究報告
- 人工智能與城市治理-深度研究
- 智能化客服系統(tǒng)應(yīng)用-深度研究
- 個性化運動方案與體重管理-深度研究
- 2024年全國統(tǒng)一考試高考新課標(biāo)Ⅱ卷數(shù)學(xué)試題(真題+答案)
- 2024山西省文化旅游投資控股集團有限公司招聘筆試參考題庫附帶答案詳解
- 加油站廉潔培訓(xùn)課件
- 2023屆上海市松江區(qū)高三下學(xué)期二模英語試題(含答案)
- 誡子書教案一等獎?wù)]子書教案
- 《民航服務(wù)溝通技巧》教案第16課民航服務(wù)人員平行溝通的技巧
- 深圳市物業(yè)專項維修資金管理系統(tǒng)操作手冊(電子票據(jù))
- 2023年鐵嶺衛(wèi)生職業(yè)學(xué)院高職單招(數(shù)學(xué))試題庫含答案解析
- 起重機械安裝吊裝危險源辨識、風(fēng)險評價表
- 華北理工兒童口腔醫(yī)學(xué)教案06兒童咬合誘導(dǎo)
- 中國建筑項目管理表格
評論
0/150
提交評論