Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 中級開發 >> 使用 Android 和 XML 構建動態用戶界面(一)

使用 Android 和 XML 構建動態用戶界面(一)

編輯:中級開發

簡介:  有幾個網站從事一些非盈利服務,提供一些可輕松設置和使用的表單來進行民意測驗和數據收集。本教程介紹一個簡單的架構來為 android 設計類似的應用程序 — 允許非編程員從移動用戶收集數據的動態用戶界面。在本教程中,您將創建一個擁有服務器端和移動端的示例表單引擎。

開始之前

為更好地利用本文,您應該使用 Android SDK 來構造 Android 應用程序。完成本文之後,您將了解如何使用 HTTP(S) 執行從應用程序到 web 服務器的通信,以及如何使用 DOM 解析器解析 XML。在此過程中,您將創建自定義和動態的用戶界面布局、多線程通信、消息處理程序、以及進程對話框。至少,您將了解 androidManifest.XML 和服務器端腳本編寫。

關於本教程

常用縮略詞

  • API:應用程序編程接口
  • DOM:文檔對象模型
  • Html:超文本標記語言
  • HTTP(S):超文本傳輸協議安全
  • IDE:集成開發環境
  • SAX:XML 的簡單 API
  • SDK:軟件開發工具包
  • UI:用戶界面
  • URL:統一資源定位符
  • XML:可擴展標記語言

本教程介紹一個用於在 android 設備上進行移動數據收集的動態表單的架構。我將首先介紹一個高級架構,討論這樣一個應用程序在數據收集的大環境中的應用。快速浏覽這個已完成的項目,包含每個源文件,對本教程將要介紹的內容有一個大概了解。就像 “廚藝展示” 一樣,您使用一些 Java 類從頭開始構建這個應用程序,每個 Java 類都被小心引入並關聯到應用程序的其他方面,其中最顯著是一個數據模型,這個表單引擎就是基於它構造的。最後,您將表單數據保存在服務器上並簡單地查看應用程序的服務器端。

數據收集

我們首先簡要討論數據收集,以及如何在使用 android 移動設備時輕松實現數據收集。

一個 android 數據收集框架

收集數據是計算機時代之前的任務。計算機已經變成一個日常 “訂書釘”,使我們思考、查找和使用信息的方式發生了革命性的變化。市值數百億美元的公司要歸功於他們在存儲、檢索和管理海量信息中的高效。今天使用的數據庫由各種各樣架構的系統供給,包括大型機、客戶服務器、web 應用程序、以及現在的移動應用程序。

物理存貨和周期計數應用程序是移動計算的早期實用應用程序。這些應用程序通常是批量數據收集,其中硬件需要一個擴展插口(docking station)來上傳收集的信息。

從出現到現在,移動應用程序市場已經走過了很長一段路程,無線連通性和設備幾乎遍布各種文化和市場,滲透到日常生活的每個方面。

盡管數據收集方式可能已經變得更具流動性,但數據收集的核心並沒有發生太大變化。用戶必須看到一些問題,並且要有一種簡單的響應方式。本教程演示如何為 android 驅動的移動電話構造一個簡單的數據收集框架,其中要利用一個 XML 支持的動態元數據結構。

應用程序架構

在深入代碼之前,我們先在一個非常高的層面上檢查應用程序設置。

Forms Engine 一瞥

我們首先浏覽一下這個 Forms Engine 應用程序的各個方面。圖 1描繪這個應用程序與提供各種內容的數據錄入表單的一個或多個服務器之間的關系。


圖 1. 應用程序架構
應用程序架構圖表 
 

在 圖 1 中,Form 1 提供 Robotics Competition 的注冊,Form 2 要求用戶提供關於他們的汽車維修習慣的信息。這些表單和 android 應用程序通信使用 HTTP(S)來:

  • 下載表單數據。
  • 向用戶顯示表單數據並(可選)收集特定於設備的數據,比如相機圖像、聲音錄制、GPS 定位、或者羅盤讀數。
  • 收集用戶提供的數據。
  • 將數據提交給適當服務器。

本教程的服務器端實現為一對文件:一個描述表單的 XML 文檔和一個負責記錄表單提交的 PHP 文檔。這個 android 應用程序是一個用 Java 代碼編寫的原生應用程序,使用 android SDK,在 Eclipse 中編碼。

表 2 顯示完整應用程序的源文件。您可以下載包含所有這些源文件的壓縮文件(參見 下載)。本教程將詳細介紹每個源文件。


表 2. 必要的應用程序源文件

文件名 注釋 XMLGui.Java android Activity 的入口點 XMLGuiForm.Java 一個表單的高級數據模型和方法 XMLGuiFormFIEld.Java 表示一個表單域,持有一個表單的每個字段的元數據 XMLGuIEditBox.Java 實現一個文本框類型用戶界面元素 XMLGuiPickOne.Java 實現一個下拉列表類型用戶界面元素 RunForm.Java 使用上述類處理一個表單 main.XML 應用程序用戶界面主頁 androidManifest.XML 此 android 應用程序的部署描述符 xmlgui1.XML 收集 Robotics 比賽注冊信息的樣例表單 XMLgui1-post.PHP 處理表單提交的 PHP 腳本 xmlgui2.XML 汽車維修習慣調查的樣例表單

圖 2 展示本教程末尾完成的應用程序在 Eclipse 中的項目結構。(查看 圖 2 的文本版本。)


圖 2. Eclipse 中的項目
Eclipse 中的項目結構的屏幕截圖 
 

如果您沒有一個正在運行的 Android 開發環境,那麼現在是安裝 Android 工具的絕好時機。要了解如何設置一個 android 開發環境,請參閱 參考資料 中必要工具的鏈接,以及一些關於如何為 Android 開發應用程序的入門文章和教程。熟悉 android 有助於理解本教程。

您已經對架構和應用程序有一個大致了解,現在就開始吧!

項目和數據模型

我們現在已准備好在 Eclipse 中啟動這個 android 項目,創建允許您為這個 Forms Engine 應用程序存儲和管理元數據的數據模型和類。

創建項目

創建 android 應用程序從我們熟悉的地方開始:打開 Eclipse 並選擇 File > New,如 圖 3 所示。


圖 3. 新建一個 android 應用程序
新建一個 Android 應用程序的屏幕截圖 
 

這個步驟將啟動 Eclipse New project 向導。選擇 android Project(針對 android 的專業 Java 環境)。確保向項目提供一個有效的標識符(我使用的是 XMLGUI)。為匹配本教程中介紹的解決方案,在 PropertIEs 下面,指定應用程序名為 XML GUI,包名為 com.msi.ibm。選擇 Create Activity 復選框並將 Activity name 指定為 XMLGui,如 圖 4 所示。


圖 4. 設置一個新項目
設置一個新項目的屏幕截圖 
 

項目創建後,它應該非常類似 圖 5 中的圖像。(查看 圖 5 的文本版本。)


圖 5. New project 向導剛剛完成時的 android 項目
New project 向導剛剛完成時的 Android 項目 
 

現在,項目已創建,確保應用程序干淨地構建並在 android Emulator 中運行是一個最佳實踐。注意,有時應用程序直到您編輯並保存 Java 源文件時才會構建。這導致 android SDK 工具自動在gen 文件夾中生成一些文件。如果 Eclipse 環境中的 Problems 選項卡沒有顯示任何條目,那麼您可以測試這個應用程序。

要測試應用程序,創建一個 Run 配置,如 圖 6 所示。在 android Application 列表中,選擇 XMLGui。確保以下值出現:XMLGui 位於 Name 字段中,XMLGUI 位於 Project 字段中,com.msi.ibm.XMLGui 位於 Launch 字段中。單擊 Run


圖 6. Run 配置設置
Run 配置設置 
 

此項目已在 Android 模擬器中創建、配置並正確啟動,現在可以為 android 創建 XML 驅動的數據收集工具了。


數據模型

這個應用程序的具體細節要求它向一個用戶顯示輸入元素,驗證數據,然後將數據提交到一個指定服務器。如果這個應用程序只針對新記錄設置,那麼它什麼用處也沒有,因為它不支持查詢現有記錄以便編輯或刪除。

要向應用程序提供足夠的關於如何顯示數據錄入表單的指示,需要一個信息集(通常稱為元數據)。元數據是關於數據的數據。簡言之,這個應用程序必須理解幾個數據元素,包括:

  • Form Name — 人們可讀的表單名稱
  • Form IdentifIEr — 這個元數據收集的惟一標識符
  • Submission URL — 收集到的數據的發送地址
  • 一個或多個字段 — 這些字段可能是文本、數字,或者 “從列表中選擇” 類型的字段

幾乎所有種類的問題都映射到這三種類型的用戶界面元素中的一種。例如,可以實現一個復選框作為一個 Yes 或 No 選擇字段。可以實現多重選擇(multi-select)作為多選字段。當然,您可以隨意擴展本教程中展示的代碼。

對於您的應用程序,使用場景如下:您在參加一個活動,其中您可以注冊一個或多個項目。您可以填寫一張注冊表,也可以等到回家後登錄該組織的網站進行注冊。在本例中,您將假定一位用戶將通過在一個 android 設備上下載一個動態表單,以便在現場從他的手機填寫一張簡單表單,提供申請者的姓名、性別和年齡。

清單 1 顯示了 xmlgui1.XML 的內容,該文件表示一個 Robotics 俱樂部活動的注冊表單。


清單 1. xmlgui1.XML

					
<?xml version="1.0" encoding="utf-8"?>
<xmlgui>
<form id="1" name="Robotics Club Registration" 
   submitTo="http://serverurl/XMLgui1-post.PHP" >
<field name="fname" label="First Name" type="text" required="Y" options=""/>
<field name="lname" label="Last Name" type="text" required="Y" options=""/>
<field name="gender" label="Gender" type="choice" required="Y" options="Male|Female"/>
<fIEld name="age" label="Age on 15 Oct. 2010" type="numeric" required="N" options=""/>
</form>
</XMLgui>

 

注意關於這個 XML 文檔的以下幾點:

  • 這個 XML 非常容易解析,這要歸功於元素屬性的廣泛使用。之所以使用這種方法,原因是與多個子元素和標記相比,它使提取數據更容易。以這種方式使用屬性還能保持較小的下載大小,並有助於保持較低的解析時間。
  • submitTo 屬性告知應用程序,數據收集完成後將要發送到的位置。
  • 每個 fIEld 元素同時提供一個字段名和一個標簽的屬性。盡管這些值是相關的,但您希望每個 name 屬性的值都保持惟一,以便接收應用程序能夠正確解析並處理它們。您還應該向用戶提供一個提示性label 值,提示用戶什麼種類的數據將進入一個特定字段。
  • 您可以輕松擴展這個方法以包含每個字段的默認值,一個用於驗證的regex 表達式,或者一個獲取關於特殊字段的更多信息的鏈接。
  • options 字段用作 choice 字段的一個受限列表。

基本了解數據模型後,現在看看負責將這個 XML 數據轉化為一個有用應用程序的代碼。


表示數據

解析數據是一個非常機械的練習,將在本教程後面展示。在檢查解析流程之前,應用程序需要某個位置來在內存中存儲和管理這個元數據。出於這個目的,您擁有兩個 Java 類,一個用於表單,一個用於表示表單字段。我們首先查看 清單 2 中的 XMLGuiForm.Java


清單 2. XMLGuiForm.Java

					
package com.msi.ibm;
import java.util.Vector;
import java.util.ListIterator;
import Java.Net.URLEncoder;

public class XmlGuiForm {

   private String formNumber;
   private String formName;
   private String submitTo;
   public Vector<XmlGuiFormField> fields;


   public XmlGuiForm()
   {
      this.fields = new Vector<XmlGuiFormField>();
      formNumber = "";
      formName = "";
      submitTo = "loopback"; // do nothing but display the results
   }
   // getters & setters
   public String getFormNumber() {
      return formNumber;
   }

   public void setFormNumber(String formNumber) {
      this.formNumber = formNumber;
   }


   public String getFormName() {
      return formName;
   }
   public void setFormName(String formName) {
      this.formName = formName;
   }

   public String getSubmitTo() {
      return submitTo;
   }

   public void setSubmitTo(String submitTo) {
      this.submitTo = submitTo;
   }

   public Vector<XmlGuiFormField> getFields() {
      return fields;
   }

   public void setFields(Vector<XmlGuiFormField> fields) {
      this.fields = fields;
   }

   public String toString()
   {
      StringBuilder sb = new StringBuilder();
      sb.append("XmlGuiForm:\n");
      sb.append("Form Number: " + this.formNumber + "\n");
      sb.append("Form Name: " + this.formName + "\n");
      sb.append("Submit To: " + this.submitTo + "\n");
      if (this.fields == null) return sb.toString();
      ListIterator<XmlGuiFormField> li = this.fields.listIterator();
      while (li.hasNext()) {
         sb.append(li.next().toString());
      }
   
      return sb.toString();
   }

   public String getFormattedResults()
   {
      StringBuilder sb = new StringBuilder();
      sb.append("Results:\n");
      if (this.fields == null) return sb.toString();
      ListIterator<XmlGuiFormField> li = this.fields.listIterator();
      while (li.hasNext()) {
         sb.append(li.next().getFormattedResult() + "\n");
      }

      return sb.toString();
   }

   public String getFormEncodedData()
   {
      try {
      int i = 0;
      StringBuilder sb = new StringBuilder();
      sb.append("Results:\n");
      if (this.fields == null) return sb.toString();
      ListIterator<XmlGuiFormField> li = this.fIElds.listIterator();
      while (li.hasNext()) {
         if (i != 0) sb.append("&");
         XMLGuiFormField thisField = li.next();
         sb.append(thisField.name + "=");
         String encstring = new String();
         URLEncoder.encode((String) thisFIEld.getData(),encstring);
         sb.append(encstring);
      }

      return sb.toString();
      }
      catch (Exception e) {
         return "ErrorEncoding";
      }
   }
}

 

關於 XMLGuiForm 類,有幾個重要項目需要注意:

  1. 這裡有 4 個成員變量:
    • formNumber:這是服務器端表單分發機制的惟一標識符。
    • formName:這是表單標題,向用戶提供上下文和確認。
    • submitTo:這是應用程序提交輸入的數據(經過驗證)的 URL。這個值也可以是一個 loopback。在 loopback 場景中,數據顯示給用戶,而不是提交到服務器。這對測試比較有用。
    • fIElds:這是被模板化以持有表單的字段數據的 Vector 類。清單 3展示了 XMLGuiFormFIEld.Java 的細節。
  2. 一系列針對這些變量的 getters 和 setters
  3. toString() 和 getFormattedResults() 方法負責生成 XMLGuiForm類的可讀摘要。
  4. getFormEncodedData() 方法在准備將數據提交到 submitTo 屬性中指定的 URL 時使用。
  5. 這個代碼不使用嚴格連接的 Java.lang.String 類,而是采用一個StringBuilder 作為構建理想數據字符串的一個更內存高效的方法。
  6. URLEncoder 類用於准備將數據提交到服務器。這使得數據實際上看起來就像一個傳統 Html 表單創建的一樣。
  7. 這個應用程序的一些潛在擴展包括:
    • 元數據本地存儲或緩存使重復任務更快運行。
    • 本地存儲在提交前一段時間內記錄數據。
    • GPS 記錄 — 使用位置數據標記每條記錄。

現在看看 清單 3 中 XMLGuiFormFIEld 類的構造情況。


清單 3. XMLGuiFormFIEld.Java

					
package com.msi.ibm;
// class to handle each individual form
public class XmlGuiFormField {
   String name;
   String label;
   String type;
   boolean required;
   String options;
   Object obj;   // holds the ui implementation
                   // or the EditText for example


   // getters & setters
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getLabel() {
      return label;
   }
   public void setLabel(String label) {
      this.label = label;
   }
   public String getType() {
      return type;
   }
   public void setType(String type) {
      this.type = type;
   }
   public boolean isRequired() {
      return required;
   }
   public void setRequired(boolean required) {
      this.required = required;
   }
   public String getOptions() {
      return options;
   }
   public void setOptions(String options) {
      this.options = options;
   }

   public String toString()
   {
      StringBuilder sb = new StringBuilder();
      sb.append("Field Name: " + this.name + "\n");
      sb.append("Field Label: " + this.label + "\n");
      sb.append("Field Type: " + this.type + "\n");
      sb.append("Required? : " + this.required + "\n");
      sb.append("Options : " + this.options + "\n");
      sb.append("Value : " + (String) this.getData() + "\n");

      return sb.toString();
   }
   public String getFormattedResult()
   {
      return this.name + "= [" + (String) this.getData() + "]";

   }

   public Object getData()
   {
      if (type.equals("text") || type.equals("numeric"))
      {
         if (obj != null) {
            XmlGuiEditBox b = (XmlGuIEditBox) obj;
            return b.getValue();
         }
      }
      if (type.equals("choice")) {
         if (obj != null) {
            XmlGuiPickOne po = (XMLGuiPickOne) obj;
            return po.getValue();
         }
      }

      // You could add logic for other UI elements here
      return null;
   }

}

 

現在更詳細地查看 XMLGuiFormFIEld 類。

  • 有 6 個類級別成員:
    1. name 持有字段名稱 — 這是數據值的字段名稱,類似於 Html 值的表單域名稱或數據庫列名。
    2. label 持有字段說明或顯示給用戶的值。
    3. type 表明要創建的用戶界面字段的風格。
      • text 表明這個字段是用一個 EditText 字段實現的,用於輸入字母數字字符。這是最常用的值。
      • numeric 也是一個 EditText,但它局限於數字值。
      • choice 使這個字段成為一個下拉列表。
    4. required 是一個 Boolean 值,標志字段是否為必要字段。如果字段必要且沒有填充,則當用戶試圖提交表單時,將向用戶顯示一條錯誤消息。
    5. options 是一個字符串值,用於為一個選擇字段傳送一個可用選項列表。這個字段對其他字段可用,可能用作一個 regex 表達式以進行驗證,也可以被覆蓋以指定一個默認值。
    6. obj 表示用戶界面小部件。例如,這個變量持有一個 EditText,用於文本或數字字段。對於一個選擇字段,obj 成員包含一個 spinner小部件。這個方法將在本教程後面進一步解釋。
  • 不出所料,這些變量擁有幾個 getters 和 setters
  • toString() 和 getFormattedResult() 方法都利用 getData() 方法,我們將稍後解釋。
  • 在 XMLGuiFormFIEld 類中,您需要管理多種類型數據,因此代碼需要明確表示數據如何存儲和訪問。getData() 方法檢查這個類型字段並在 obj 字段上執行一個類型設置,以便與存儲的對象正確交互。如果您想向這個框架添加新字段類型,可以擴展 getData() 方法來支持這種新字段類型(參見 清單 3 末尾附近的注釋)。

您現在有一種方法來存儲和管理元數據。現在可以檢查應用程序的運行情況,然後將各部分組合起來。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved