您好,登錄后才能下訂單哦!
本篇內容介紹了“Scala+Eclipse+Android手機開發的方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
眾所周知Android平臺上可以跑Java,因此運行在JVM之上的Scala也可以跑。在本文中,我們將創建一個在 Android 設備上運行的移動應用程序。您將需要安裝 Android SDK;本文使用 V1.5 SDK。應用程序代碼將用 Scala 編程語言編寫。如果您從來沒用過 Scala,那么沒有關系,因為本文將解釋 Scala 代碼。但是,即使您不熟悉 Scala,建議您至少熟悉 Java 語言。本文使用 Scala V2.7.5 進行開發。對于 Android 和 Scala 都提供了很好的 Eclipse 插件。本文使用 Eclipse V3.4.2 和 Android Development Tools(ADT) V0.9.1 以及 Scala IDE 插件 V2.7.5。
設置
編寫 Android 應用程序聽起來像是一個復雜的命題。Android 應用程序在它們自己的虛擬機中運行:Dalvik 虛擬機。但是,Android 應用程序的構建路徑是開放的。下面表明了我們將使用的基本策略。
圖 1. Android 上 Scala 的構建路徑
其思想是,我們首先將所有 Scala 代碼編譯成 Java 類文件。這是 Scala 編譯器的工作,所以這方面沒什么太復雜的事情。接下來,獲取 Java 類文件,使用 Android dex 編譯器將類文件編譯成 Android 設備上的 Dalvik VM 使用的格式。這就是所謂的 dexing,也是 Android 應用程序的常規編譯路徑。通常,要經歷從 .java 文件到 .class 文件再到 .dex 文件的過程。在本文,惟一不同的是我們從 .scala 文件開始。***,.dex 文件和其他應用程序資源被壓縮成一個 APK 文件,該文件可安裝到 Android 設備上。
那么,如何讓這一切發生?我們將使用 Eclipse 做大部分工作。但是,此外還有一個較復雜的步驟:要讓代碼運行,還需要來自標準 Scala 庫中的代碼。在典型的 Scala 安裝中,這是 /lib/scala-library.jar 中一個單獨的 JAR。但是,這個 JAR 包括一些不受 Android 支持的代碼。有些代碼需要稍作調整,有些代碼則必須移除。scala-library.jar 的定制構建是運行得***的,至少目前是這樣。請參閱 參考資料,了解這里使用的定制構建。我們將把這個 JAR 稱作 Android 庫 JAR。
有了這個 JAR,剩下的事情就很容易了。只需使用 Eclipse 的 ADT 插件創建一個 Android 項目。然后將一個 Scala 特性(nature)添加到項目中。用前面談到的 Android 庫替代標準的 Scala 庫。***,將輸出目錄添加到類路徑中。現在,可以開始了。現在,我們有了基本的設置,接下來看看我們將使用 Scala 創建的 Android 應用程序。
UnitsConverter
現在,我們知道如何利用 Scala 代碼,將它轉換成將在 Android 設備上運行的二進制格式,接下來可以使用 Scala 創建一個移動應用程序。我們將創建的應用程序是一個簡單的單位轉換應用程序。通過這個應用程序可以方便地在英制單位與公制單位之間來回轉換。這是一個非常簡單的應用程序,但是我們將看到,即使是最簡單的應用程序也可以從使用 Scala 中獲益。我們首先看看 UnitsConverter 的布局元素。
創建布局
您也許對編寫手機上運行的 Scala 感到興奮,但是并非所有的移動開發編程都應該用 Scala 或 Java 語言完成。Android SDK 提供了一種很好的方式,使用基于 XML 的布局系統將用戶界面代碼與應用程序邏輯分離。我們來看看本文中的應用程序的主要布局文件,如清單 1 所示。
清單 1. Converter 應用程序的主要布局
< ?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center_horizontal" android:padding="10px" > < TextView android:id="@+id/prompt_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/prompt_metric"/> < EditText android:id="@+id/amount" android:layout_below="@id/prompt_label" android:layout_width="fill_parent" android:layout_height="wrap_content"/> < TextView android:id="@+id/uom_label" android:layout_below="@id/amount" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/uom"/> < Spinner android:id="@+id/uom_value" android:layout_below="@id/uom_label" android:layout_width="wrap_content" android:layout_height="wrap_content"/> < Button android:id="@+id/convert_button" android:layout_below="@id/uom_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/convert_button_label"/> < TextView android:id="@+id/result_value" android:layout_below="@id/convert_button" android:layout_width="fill_parent" android:layout_height="fill_parent"/> < /RelativeLayout> |
以上代碼非常簡潔地創建了該應用程序的主 UI。它的根節點是一個 RelativeLayout 容器元素。Android SDK 中有很多布局選項。RelativeLayout 指示運行時使用相對定位對不同的 UI 小部件進行布局。要使用相對定位,可添加可見元素 — 在這里是一個 TextView 元素。這是用于顯示文本的一個簡單的元素。它被賦予一個 ID prompt_label。接下來的元素,即一個 EditText 元素(一個文本輸入框)將用到它。這個元素有一個 layout_below 屬性,它的值等于 prompt_label ID。換句話說,EditText 應該放在名為 prompt_label 的元素的下方。
布局代碼剩下的部分非常簡單。有一個帶標簽的文本輸入框、一個帶標簽的微調器(一個組合框或下拉框)、一個按鈕和一個用于輸出的文本框。圖 2 顯示正在運行的應用程序的一個截圖,其中標出了不同的元素。
圖 2. Android lLayout — 分解圖
那么,以上視圖中看到的不同文本值來自哪里呢?注意,清單 1 中的一些元素有一個 text 屬性。例如,prompt_label 元素有一個等于 @string/prompt_metric 的 text 屬性。這表明它將使用 Android 應用程序中一個標準的資源文件:strings.xml 文件,如清單 2 所示。
清單 2. strings.xml 資源
< ?xml version="1.0" encoding="utf-8"?> < resources> < string name="prompt_metric">Enter amount (KM, g, L, C)< /string> < string name="prompt_english">Enter amount (miles, lbs, gallons,F)< /string> < string name="uom">Units of Measure< /string> < string name="convert_button_label">Convert< /string> < string name="app_name">Converter< /string> < string name="english_units">English< /string> < string name="metric_units">Metric< /string> < /resources> |
現在可以看到,圖 2 中所有的文本來自何處。微調器有一個下拉框,其中包含可用于度量的單位,那些單位在清單 2 中沒有列出。相反,它們來自另一個文件 arrays.xml,如清單 3 所示。
清單 3. arrays.xml 資源
< ?xml version="1.0" encoding="utf-8"?> < resources> < array name="english_units"> < item>Fahrenheit< /item> < item>Pounds< /item> < item>Ounces< /item> < item>Fluid Ounces< /item> < item>Gallons< /item> < item>Miles< /item> < item>Inches< /item> < /array> < array name="metric_units"> < item>Celsius< /item> < item>Kilograms< /item> < item>Grams< /item> < item>Millileters< /item> < item>Liters< /item> < item>Kilometers< /item> < item>Centimeters< /item> < /array> < /resources> |
現在,我們可以看到將用于微調器的那些值。那么,這些值如何出現在微調器中,應用程序如何在英制單位與公制單位之間切換?要回答這些問題,我們需要看看應用程序代碼本身。
Scala 應用程序代碼
Converter 應用程序的代碼非常簡單 — 不管用什么語言編寫。當然,用 Java 編寫起來非常容易,但是用 Scala 編寫也同樣不復雜。首先我們看看前面見過的 UI 背后的代碼。
視圖背后的代碼
解釋創建 UI 的 Scala 代碼的最簡單方式是先看看代碼,然后走查一遍。對于任何應用程序,都是在應用程序的 AndroidManifest.xml 文件中定義應用程序的默認活動。任何 UI 背后都有一個 Activity 類,默認的 Activity 定義當應用程序初次裝載時執行的 Activity 類。對于像本文這樣簡單的應用程序,有一個 Converter 類,清單 4 中顯示了它的源代碼。
清單 4. Converter 活動類
class Converter extends Activity{ import ConverterHelper._ private[this] var amountValue:EditText = null private[this] var uom:Spinner= null private[this] var convertButton:Button = null private[this] var resultValue:TextView = null override def onCreate(savedInstanceState:Bundle){ super.onCreate(savedInstanceState) setContentView(R.layout.main) uom = findViewById(R.id.uom_value).asInstanceOf[Spinner] this.setUomChoice(ENGLISH) amountValue = findViewById(R.id.amount).asInstanceOf[EditText] convertButton = findViewById(R.id.convert_button).asInstanceOf[Button] resultValue = findViewById(R.id.result_value).asInstanceOf[TextView] convertButton.setOnClickListener( () => { val unit = uom.getSelectedItem.asInstanceOf[String] val amount = parseDouble(amountValue.getText.toString) val result = UnitsConverter.convert(Measurement(unit,amount)) resultValue.setText(result) }) } override def onCreateOptionsMenu(menu:Menu) = { super.onCreateOptionsMenu(menu) menu.add(NONE, 0, 0, R.string.english_units) menu.add(NONE, 1, 1, R.string.metric_units) true } override def onMenuItemSelected(featureId:Int, item:MenuItem) = { super.onMenuItemSelected(featureId, item) setUomChoice(if (item.getItemId == 1) METRIC else ENGLISH) true } private def setUomChoice(unitOfMeasure:UnitsSystem){ if (uom == null){ uom = findViewById(R.id.uom_value).asInstanceOf[Spinner] } val arrayId = unitOfMeasure match { case METRIC => R.array.metric_units case _ => R.array.english_units } val units = new ArrayAdapter[String](this, R.layout.spinner_view, getResources.getStringArray(arrayId)) uom.setAdapter(units) } } |
我們從這個類的頂部開始。它擴展 android.app.Activity。這是一個 Java 類,但是從 Scala 中可以對 Java 類輕松地進行細分。接下來,它有一些實例變量。每個實例變量對應前面定義的一個 UI 元素。注意,每個實例變量還被限定為 private[this]。這演示了 Scala 中特有的一種訪問控制級別,而 Java 語言中不存在這種訪問控制。這些變量不僅是私有的,而且只屬于 Converter 類的特定實例。這種級別的訪問控制對于移動應用程序來說有些大材小用,但是如果您是一名 Scala 開發人員,可以放心地在 Android 應用程序上使用您熟悉的語法。
回到清單 4 中的代碼,注意,我們覆蓋了 onCreate 方法。這是 Activity 類中定義的方法,通常被定制的 Activity 覆蓋。如果用 Java 語言編寫該代碼,那么應該添加一個 @Override 標注。在 Scala 中,override 是一個關鍵詞,用于確保正確性。這樣可以防止誤拼方法名之類的常見錯誤。如果誤拼了方法名,Scala 編譯器將捕捉到方法名并返回一個錯誤。注意,在這個方法上,以及任何其他方法上,不需要聲明返回類型。Scala 編譯器可以輕松推斷出該信息,所以不需要多此一舉。
onCreate 中的大部分代碼類似于 Java 語言編寫的代碼。但是有幾點比較有趣。注意,我們使用 findViewById 方法(在 Activity 子類中定義)獲得不同 UI 元素的句柄。這個方法不是類型安全的,需要進行類型轉換(cast)。在 Scala 中,要進行類型轉換,可使用參數化方法 asInstanceOf[T],其中 T 是要轉換的類型。這種轉換在功能上與 Java 語言中的轉換一樣。不過 Scala 有更好的語法。接下來,注意對 setUomChoice 的調用(稍后我們將詳細談到這個方法)。***,注意上述代碼獲得一個在布局 XML 中創建的按鈕的句柄,并添加一個單擊事件處理程序。
如果用 Java 語言編寫,那么必須傳入 Android 接口 OnClickListener 的一個實現。這個接口只定義一個方法:onClick。實際上,您關心的只是那個方法,但是在 Java 語言中無法直接傳入方法。而在 Scala 中則不同,在 Scala 中可以傳入方法字面量(literal)或閉包。在這里,我們用語法 () => { ... } 表示閉包,其中方法的主體就是花括號中的內容。開始/結束括號表示一個不帶參數的函數。但是,我將這個閉包傳遞到 Button 的一個實例上的 setOnClickListener 方法,Button 是 Android SDK 中定義的一個 Java 類。如何將 Scala 閉包傳遞到 Java API?我們來看看。
Android 上的函數式編程
為了理解如何讓 Android API 使用函數字面量,看看 Converter 類定義的***行。這是一條重要的語句。這是 Scala 的另一個很好的特性。您可以在代碼的任何地方導入包、類等,它們的作用域限于導入它們的文件。在這里,我們導入 ConverterHelper 中的所有東西。清單 5 顯示 ConverterHelper 代碼。
清單 5. ConverterHelper
object ConverterHelper{ import android.view.View.OnClickListener implicit def funcToClicker(f:View => Unit):OnClickListener = new OnClickListener(){ def onClick(v:View)=f.apply(v)} implicit def funcToClicker0(f:() => Unit):OnClickListener = new OnClickListener() { def onClick(v:View)=f.apply} } |
這是一個 Scala 單例(singleton),因為它使用對象聲明,而不是類聲明。單例模式被直接內置在 Scala 中,可以替代 Java 語言中的靜態方法或變量。在這里,這個單例存放一對函數:funcToClicker 和 funcToClicker0。這兩個函數以一個函數作為輸入參數,并返回 OnClickListener 的一個實例,OnClickListener 是 Android SDK 中定義的一個接口。例如,funcToClicker 被定義為以一個函數 f 為參數。這個函數 f 的類型為帶一個 View 類型(Android 中的另一個類)的輸入參數的函數,并返回 Unit,它是 void 在 Scala 中的對等物。然后,它返回 OnClickListener 的一個實現,在這個實現中,該接口的 onClick 方法被實現為將輸入函數 f 應用到 View 參數。另一個函數 funcToClick0 也做同樣的事情,只是以一個不帶輸入參數的函數為參數。
這兩個函數(funcToClicker 和 funcToClicker0)都被定義為隱式函數(implicit)。這是 Scala 的一個方便的特性。它可以讓編譯器隱式地將一種類型轉換成另一種類型。在這里,當編譯器解析 Converter 類的 onCreate 方法時,它遇到一個 setOnClickListener 調用。這個方法需要一個 OnClickListener 實例。但是,編譯器卻發現一個函數。在報錯并出現編譯失敗之前,編譯器將檢查是否存在隱式函數,允許將函數轉換為 OnClickListener。由于確實還有這樣的函數,所以它執行轉換,編譯成功。現在,我們理解了如何使用 Android 中的閉包,接下來更仔細地看看應用程序邏輯 — 特別是,如何執行單位轉換計算。
單位轉換和計算
我們回到清單 4。傳入 onClickListener 的函數收到用戶輸入的度量單位和值。然后,它創建一個 Measurement 實例,并將該實例傳遞到一個 UnitsConverter 對象。清單 6 顯示相應的代碼。
清單 6. Measurement 和 UnitsConverter
case class Measurement(uom:String, amount:Double) object UnitsConverter{ // constants val lbToKg = 0.45359237D val ozToG = 28.3495231 val fOzToMl = 29.5735296 val galToL = 3.78541178 val milesToKm = 1.609344 val inchToCm = 2.54 def convert (measure:Measurement)= measure.uom match { case "Fahrenheit" => (5.0/9.0)*(measure.amount - 32.0) + " C" case "Pounds" => lbToKg*measure.amount + " kg" case "Ounces" => ozToG*measure.amount + " g" case "Fluid Ounces" => fOzToMl*measure.amount + " mL" case "Gallons" => galToL*measure.amount + " L" case "Miles" => milesToKm*measure.amount + " km" case "Inches" => inchToCm*measure.amount + " cm" case "Celsius" => (9.0/5.0*measure.amount + 32.0) + " F" case "Kilograms" => measure.amount/lbToKg + " lbs" case "Grams" => measure.amount/ozToG + " oz" case "Millileters" => measure.amount/fOzToMl + " fl. oz." case "Liters" => measure.amount/galToL + " gallons" case "Kilometers" => measure.amount/milesToKm + " miles" case "Centimeters" => measure.amount/inchToCm + " inches" case _ => "" } } |
Measurement 是一個 case 類。這是 Scala 中的一個方便的特性。用 “case” 修飾一個類會導致這個類生成這樣一個構造函數:這個構造函數需要類的屬性,以及 equals、 hashCode 和 toString 的實現。它對于像 Measurement 這樣的數據結構類非常適合。它還為定義的屬性(在這里就是 uom 和 amount)生成 getter 方法。也可以將那些屬性定義為 vars(可變變量),然后也會生成 setter 方法。僅僅一行 Scala 代碼可以做這么多事情!
接下來,UnitsConverter 也是一個單例模式,因為它是使用 object 關鍵詞定義的。它只有一個 convert 方法。注意,convert 被定義為相當于一條單一語句 — 一條 match 語句。它是一個單一表達式,所以不需要額外的花括號。它使用 Scala 的模式匹配。這是函數式編程語言中常見的一個強大特性。它類似于 Java 語言和很多其他語言中的 switch 語句。但是,我們可以匹配字符串(實際上,還可以有比這高級得多的匹配)。如果字符串匹配,則執行適當的計算,并返回格式化的字符串,以供顯示。***,注意與 _ 匹配的***一個 case。Scala 中的很多地方使用下劃線作為通配符。在這里,它表示匹配任何東西,這類似于 Java 語言中的 default 語句。
現在,我們理解了應用程序中的計算,***來看看剩下的 UI 設置和菜單。
UI 初始化和菜單
回到清單 4。我們說過要看看 setUomChoice。這個方法被定義為帶有一個 UnitsSystem 類型的參數。我們來看看如何定義這個類型。
清單 7. UnitsSystem
sealed case class UnitsSystem() case object ENGLISH extends UnitsSystem case object METRIC extends UnitsSystem |
我們看到,UnitsSystem 是一個密封的 case 類,沒有屬性。看上去它不是很有用。接下來,我們看看兩個 case 對象。還記得嗎,object 表示 Scala 中的一個單例。在這里,有兩個 case 對象,每個 case 對象都擴展 UnitsSystem。這是 Scala 中的一個常見的特色,它可以提供更簡單、更類型安全的枚舉方式。
現在 setUomChoice 的實現更加合理。在獲得微調器的一個句柄后,我們匹配傳入的 UnitsSystem 的類型。這標識了我們在前面見到的 arrays.xml 中的一個數組。這是使用 Android SDK 生成的 R 類表示資源,例如 arrays.xml 文件。一旦知道使用哪個數組,我們就通過創建一個傳入微調器的適配器(在這里是一個 ArrayAdapter),使用那個數組作為微調器的數據源。
***,看看清單 4 中的 onCreateOptionsMenu 和 onMenuItemSelected 方法。這些方法是在 Activity 中定義的,我們將在 Converter 活動中覆蓋這些方法。***個方法創建一個菜單。第二個方法處理用戶從菜單中選擇 English 或 metric 的事件。它再次調用 setUomChoice。這使用戶可以在從英制單位轉換為公制單位與從公制單位轉換為英制單位之間進行切換。
“Scala+Eclipse+Android手機開發的方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。