安卓教程----手把手教你做一個河北空氣品質用戶端

來源:互聯網
上載者:User

        本文將與你一起從零開始,做一個河北省空氣品質自動發布系統的用戶端,文章面向零基礎的、只看過一點安卓教程的同學,對於比較基礎的內容,也會用紅字的連結標出,大家可以點開看詳細的介紹。

        其實做這個,完全是因為老爸的原因,河北的空氣品質太差了,所以他決定天天根據空氣品質來決定散步不散步。總是上這個網站過於複雜,於是我就有了做一個用戶端的想法。下面分幾步介紹關於資訊擷取非同步擷取網路資料資料分析介面設計程式邏輯等內容,下面介紹一個完整的程式是如何做出來的。

        首先需要找到程式的資料來源,找到從網上獲得資料介面的網址。

        其次,要把資料從網上的格式,轉換成我們可以使用的格式。

        接下來進行布局的設計,最後把資料填充到布局裡,整個程式就完成了。

        下面是這個系統的網站,和我做的用戶端:



1、資料擷取        想做這個軟體我們先要有資料來源,資料是河北省環境監測中心給出的,我們現在要找到它的介面。        開啟網址:  http://121.28.49.85:8080/ 我們可以看到這是一個flash做的頁面,而且有明顯的載入過程,說明瀏覽器擷取過資料。我們使用 HttpAnalyze或者 Smsniff來查看瀏覽器發送出去的資料包,當然最方便的是使用Chrome的功能。        開啟Chrome --> F12 --> 選擇NetWork標籤 --> 開啟上面的網路地址,下面會出現很多條請求的資料,我們按 Size排序後找最大的,就是我們需要的資料。如:

發送的請求的地址

得到的回應
        如所示:開啟網頁後瀏覽器發送了若干條資料,其中有一條遠大於其它資料的包,大小為59.75k,我們可以認為這就是資料的來源了,而我們看到它指向了網址 http://121.28.49.85:8080/datas/hour/130000.xml。在回複中,發現編碼是 UTF-8的編碼。 開啟這個網址,我們可以看到如所示的XML資料:

        下面我們就以上面的資料為基礎,做一個用戶端。
2、非同步資訊擷取2.1 建立一個Android項目        開啟一個配置好ADT(Android Developer Tools)的Eclipse(如果沒有配置好點這個 教程),選擇File --> New --> Android Application Project,在Application Name裡給程式起一個名字比如HebeiAir,然後在最小需求SDK為API14(低一點其實也不影響),其它保持預設,確定。        建立好以後我們的程式至少會有下面這些檔案:

2.2 非同步擷取網路資料        在第一章裡,我們找到了擷取資料的網址,在這裡,我們要把這個網址的資料抓下來供我們使用。在src包裡建立一個新的Class,名字定為Util,在裡面定義一個新的靜態方法:HttpGet,這個方法可以類比瀏覽器的訪問,我們輸入參數是網址,這個函數返回得到的網頁原始碼:
public static String HttpGet(String url) throws ClientProtocolException, IOException {//建立一個預設的串連DefaultHttpClient client = new DefaultHttpClient();//建立一個Get方法HttpGet get = new HttpGet(url);//得到網路的回應HttpResponse response = client.execute(get);//獲得的網頁原始碼(xml)String content = null;//如果伺服器響應的是OK的話!if (response.getStatusLine().getStatusCode() == 200) {//以下是把網路資料分段讀取下來的過程InputStream in = response.getEntity().getContent();byte[] data = new byte[1024];int length = 0;ByteArrayOutputStream bout = new ByteArrayOutputStream();while ((length = in.read(data)) != -1) {bout.write(data, 0, length);}//最後把位元組流轉為字串 轉換的編碼為utf-8.content = new String(bout.toByteArray(), "utf-8");}//返回得到的字串 也就是網頁原始碼return content;}

        在上面的Chrome資訊視窗中看到,返回值是utf-8編碼的,所以在這裡我們使用了utf-8編碼,如果這裡我們使用"gbk"或者"gb-2312"就會出現亂碼,每個網站的編碼都不相同,具體情況要具體分析。        
        要注意的是,在我們的程式裡並不能直接使用這個函數,因為Android 4.0中, 主線程不能進行網路操作,所以我們需要開啟一個新的線程。在Android中,有一個成熟的類: AsyncTask(非同步任務),可以完成這項工作(Thread + Handler也是一種方法,由於我們的工作比較簡單,暫時不提及它們)。
        接下來我們嘗試把XML原始碼顯示出來。        在MainActivity類中建立一個類 GetSource,這個類繼承AsyncTask,用來擷取網頁上的資料;得到資料後使用 Logcat(可以理解為Android上的控制台)列印出來:
class GetSource extends AsyncTask<String, Void, String>{//此函數用來處理背景事物@Overrideprotected String doInBackground(String... params) {try {//這裡調用了我們剛才寫的下載函數return Util.HttpGet("http://121.28.49.85:8080/datas/hour/130000.xml");} catch (IOException e) {}return null;}//後台事物完成後,此函數用來更改介面的內容@Overrideprotected void onPostExecute(String result) {//讓Log輸出運行時的記錄Log.i("test",result);}}

        最後在OnCreate函數的最後一行加上下面一句,再 添加網路許可權,然後我們來看看效果吧!
new GetSource().execute();

3、介面設計        我們的目標是把軟體設計成文章開始時的那個樣式,這樣我們就要簡單地修改一下activity_main.xml:        ·在上方放置一個TextView,背景為淺綠色,文字顏色為白色;        ·下方是一個GridView,其中每個格子的顏色根據空氣品質來變化,格子中上方顯示城市名,下方顯示當前的AQI。        ·同時在整個介面上還要顯示一個轉圈的進度條ProgressBar,載入的時候顯示這個進度條        代碼如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity" >        <LinearLayout            android:id="@+id/ll"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:visibility="gone"            android:orientation="vertical" >            <TextView                android:id="@+id/tv_time"                android:layout_width="fill_parent"                android:layout_height="wrap_content"                android:layout_margin="5dp"                android:background="#7a7"                android:padding="5dp"                android:textColor="#eee"                android:textSize="20sp"                android:textStyle="bold" />            <GridView                android:id="@+id/gv"                android:layout_width="match_parent"                android:layout_height="fill_parent"                android:layout_margin="5dp"                android:horizontalSpacing="3dp"                android:verticalSpacing="3dp"                android:numColumns="3" >            </GridView>        </LinearLayout>        <ProgressBar            android:id="@+id/pb1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerHorizontal="true"            android:visibility="visible"            android:layout_centerVertical="true" /></RelativeLayout>

        GridView 是一種網格樣式布局,在上面的代碼裡我設定的每行格子個數為3個,還設定了格子之間的間距。每個格子中的布局需要另一個檔案來控制:gv.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/bg"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:padding="8dp"    android:orientation="vertical" >    <TextView        android:id="@+id/tv_city"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="石家莊"        android:textColor="#4bd"        android:textSize="20sp"        android:textStyle="bold" />    <TextView        android:id="@+id/tv_aqi"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="223"        android:textColor="#4bd"        android:textSize="18sp"        android:textStyle="bold" /></LinearLayout>

4、資料分析4.1 解析XML資料        網上獲得的XML資料需要轉換成我們可以使用的結構化資料,這就使用了 基於DOM的XML解析器。更改 GetSource中的 OnPostExecute中的代碼為:
@Overrideprotected void onPostExecute(String result) {//建立一個解析器DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder;try {builder = factory.newDocumentBuilder();InputStream is = new ByteArrayInputStream(result.getBytes());Document document = builder.parse(is);Element element = document.getDocumentElement();//獲得所有的Citys節點資料NodeList cityList = element.getElementsByTagName("Citys");//獲得mapstitle資料,並分解為兩部分NodeList title = element.getElementsByTagName("MapsTitle");String text1 = title.item(0).getTextContent();String t[] = text1.split("\\(");text1 = t[0];t = t[1].split(",");tv_time.setText(text1+ "\n" +t[0]);Element citys = (Element)cityList.item(0);NodeList city = citys.getChildNodes();for (int i=0;i < city.getLength();i++){//此時的city節點的item上,有的是一個城市的所有資料Node node = city.item(i);if (node.getNodeName().equalsIgnoreCase("city")) { //這是一個有效節點CityData cd = new CityData(node);nodeList.add(node);cdList.add(cd);}}} catch (Exception e) {}

        這樣每個城市的資料就成為了一個Node,解析XML的過程比較複雜,需要不斷地去嘗試。
4.2 應用資料到布局        接下來要編寫一個Adapter來串連布局和代碼。我們假設每一個每一個城市的資料都是一個 Node(節點),GridView的資料存在一個列表裡,格子的個數與列表的長度有關。 (所以如果河北省城市監測點變多了,程式也可以進行變化而自適應)分析資料源的XML可以得到,我們需要的是其中城市,和每個城市中監測點的列表。所以,在這裡建立兩個類: CityData、Pointer,其中,CityData包括一個Pointer的列表。以下是CityData類,Pointer類與這個相似:
public class CityData implements Serializable{/** * 繼承Serializable是為了在兩個不同的介面中進行值的傳遞 */private static final long serialVersionUID = -8473485404751986234L;//城市類包含了一個城市的資料public String name,dataTime,aqi,level,maxPoll,color,intro,tips;public List<Pointer> pointerList;public CityData(Node cityNode) {super();//按標籤挨個取出相應標籤的內容this.name = getByTag(cityNode, "name");this.dataTime = getByTag(cityNode, "datatime");this.aqi = getByTag(cityNode, "aqi");this.level = getByTag(cityNode, "level");this.maxPoll = getByTag(cityNode, "maxpoll");String tmp = getByTag(cityNode, "color");this.color = tmp.replace("0x", "#");this.intro = getByTag(cityNode, "intro");this.tips = getByTag(cityNode, "tips");Element city = (Element)cityNode;NodeList pointers = city.getElementsByTagName("Pointer");//向city的pointer列表中添加監測點pointerList = new ArrayList<Pointer>();for (int i=0;i<pointers.getLength();i++){Node pNode = pointers.item(i);pointerList.add(new Pointer(pNode));}}//從XML的node中取出相應標籤中內容的functionprivate String getByTag(Node node,String tag) {for (int i=0;i<node.getChildNodes().getLength();i++){if (tag.equalsIgnoreCase(node.getChildNodes().item(i).getNodeName()))return node.getChildNodes().item(i).getTextContent();}return null;}}

        負責把資料填充到布局的Adapter:
class gvAdapter extends BaseAdapter{//Adapter的資料來源,根據這個資料來填充網格列表List<CityData> cdList;public gvAdapter(List<CityData> cdList) {super();this.cdList = cdList;}//根據資料的多少返回相應的數值@Overridepublic int getCount() {return cdList.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {//解析之前寫好的每個網格的布局convertView = MainActivity.this.getLayoutInflater().inflate(R.layout.gv, null);//找到布局中的元素,和布局的背景TextView tv_city = (TextView)convertView.findViewById(R.id.tv_city);TextView tv_aqi = (TextView)convertView.findViewById(R.id.tv_aqi);View bg = convertView.findViewById(R.id.bg);//根據資料填充每個格子的內容和背景色tv_city.setText(cdList.get(position).name);tv_aqi.setText(cdList.get(position).aqi);bg.setBackgroundColor(Color.parseColor(cdList.get(position).color));return convertView;}}

        最後在XML解析完成以後,添加一句:gv.setAdapter(new gvAdapter(cdList)); 即把資料填充到了網格中。
5、其它程式邏輯        當點擊每個GridView的item的時候,跳轉到相應的城市詳細資料頁面。        右鍵項目的目錄 New --> Other -->Activity,給新的Activity起名為:CityActivity。依照上面介紹的寫法給新的Activity寫好布局和邏輯。        點擊主介面中的網格,自動跳轉到新的介面:
gv.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {                                                   //從當前的介面跳轉到城市詳細資料介面Intent it = new Intent(MainActivity.this, CityActivity.class);                //對象本身無法使用Intent傳遞,但是我們繼承了Serializable,使傳遞成為了可能it.putExtra("node", cdList.get(position));startActivity(it);}});

        1、在城市詳細資料介面,點擊每個監測點,顯示該監測點的詳細資料。這裡需要用到對話方塊AlertDialog來顯示詳細資料。        2、更改 res --> values -->string.xml 中的內容,個人化我們的程式,如下:
<resources>    <string name="app_name">河北空氣品質</string>    <string name="title_activity_city">城市資料</string></resources>
        這樣,程式的名字就變成了“河北空氣品質”,在進入到詳細資料介面的時候,標題列也變成了“城市資料”        3、找一個圖片,做成png格式,覆蓋res/drawable-hdpi/ic_launcher.png 檔案,這樣就更改了在手機APP列表中的表徵圖了。



        其實做一個APP非常的簡單,只要有想法,上面的工作在一天內就可以完成。本文主要是想帶給大家一個思路,如何發掘身邊的一些內容,來做出自己的APP。        可能光看講解不會太懂,那麼可以到http://download.csdn.net/detail/icyfox_bupt/6908053下載程式的原始碼        轉載請註明來自:http://blog.csdn.net/icyfox_bupt/article/details/18953581

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.