[譯] 使用 Kotlin 將你的應用程序從 iOS 轉換成 Android

經過本教程,你將親眼看到語言的類似之處,以及經過 iOS 應用程序移植到 Android 上了解將 Swift 轉換爲 Kotlin 是多麼容易。

移動設備是你的平常伴侶,不管你走到哪裏,均可以將它們放入揹包和口袋。技術將不斷適應你的移動設備,使移動開發做爲一我的的業餘愛好或是專業都將愈來愈廣泛。html

一般開發人員會選擇一個開發平臺,最多見的是 Android 或 iOS。這種選擇通常基於我的所能獲得的資源(例如,她或他的我的設備)或當前市場環境。許多開發人員傾向於只爲他們選擇的平臺構建應用程序。Android 和 iOS 工程師從來都使用徹底不一樣的語言和 IDE ,儘管兩個移動平臺之間存在太多類似之處,但在兩個平臺之間的跨平臺開發是使人生畏而且十分罕見的。前端

可是 Android 和 iOS 開發的語言和工具在過去幾年中獲得了極大的改善,主要是 KotlinSwift 的誕生,這兩種語言緩解了跨平臺學習之間的障礙。掌握了語言基礎知識後,你能夠很容易地把代碼能夠從 Swift 轉換爲 Kotlin。java

在本教程中,你將親眼看到這些語言的類似之處,以及將 Swift 轉換爲 Kotlin 是多麼容易。首先打開 Xcode,你將探索使用 Swift 來編寫一個 iOS 應用程序,而後你將在 Android Studio 中使用 Kotlin 從新編寫這個相同的應用程序。android

注意:熟悉 Swift 和 iOS 的基礎知識或 Kotlin 和 Android 的基礎知識有助於更好的完成本教程。你能夠經過 這些教程 瞭解適用於 iOS 的 Swift 或 這些教程瞭解 Android 上的 Kotlin。ios

開始

下載 測試項目 來開始本教程的學習。git

iOS APP 架構

在 Xcode 中打開 iOS-Finished 示例應用程序並在模擬器上運行它,該應用程序將提示你登陸。輸入用戶名和任意至少六個字符長的和密碼來登陸該應用程序,密碼還至少包含一個數字字符。github

Screenshot of login screen

經過用戶名和密碼登入 APP,進入項目主頁面:熊出沒,注意!戳一戳它看看會發生什麼……編程

Screenshot of app with image of bear and Poke button

如今你能夠關注在這個簡單應用程序架構中的兩個 ViewController,每一個交互頁面都有一個。swift

  1. LoginViewController
  2. BearViewController

在項目中找到這些控制器,該應用程序首先加載 LoginViewController,它持有了 Main.storyboard 中定義的 TextFieldButton 的 UI 組件。另外請注意,LoginViewController 包含了兩個用於驗證密碼的輔助函數,以及兩個用於顯示無效密碼錯誤的輔助函數,這些是你將在 Kotlin 中重寫的兩個 Swift 函數。後端

BearViewController 中也持有了 Main.storyboard 中的 UI 組件。一般在 iOS 開發中,每一個 ViewController 都有其單獨的 storyboard 頁面。在本教程中你將專一於 ViewController 邏輯組件而不是 UI。在 BearViewController 中,你保持對一個名爲 tapCount的變量的引用,每次你點擊 pokeButtontapCount 值都會更新,從而觸發熊的不一樣狀態。

Swift 到 Kotlin:基礎部分

如今你已經對該應用程序有了個大致的瞭解,如今能夠經過 playground 進行技術改造,深刻了解一些語言方面的細節。對於 Swift,在 Xcode 裏點擊 File ▸ New ▸ Playground 來建立一個新的 Blank 的 playground,而後就能夠候編寫一些代碼了!

Menu File ▸ New ▸ Playground

Blank Swift playground option

變量和可選項

在 Swift 中有一個稱爲 可選項 的概念。可選值包含一個值或爲 nil。將此代碼粘貼到 Swift playground:

var hello = "world"
hello = "ok"

let hey = "world"
hey = "no"
複製代碼

Swift 使用 varlet 來定義變量,兩個前綴定義了可變性。let 聲明變量以後不可修改,這就是編譯器報錯的緣由,而 var 變量能夠在運行時更改。

Cannot assign value compiler error

在 playground 中爲代碼添加類型聲明,如今它看起來會像這樣:

var hello: String? = "world"
hello = "ok"

let hey: String = "world"
hey = "no"
複製代碼

經過爲這兩個變量增長類型標註,你已經將 hello 設置爲 可爲空 的String,由 String? 中的 ? 表示,而 hey 是一個 非空 的 String。可爲空的變量在 Swift 中稱爲 可選項

爲何這個細節很重要?空值一般會致使應用程序中出現使人討厭的崩潰,尤爲是當你的數據源並非始終在客戶端中進行定義時(例如,若是你但願服務器得到某個值並且它並無返回)。使用 letvar 之類的簡單前綴容許你進行內置的動態檢查以防止程序在值爲空時進行編譯。有關更多信息,請參閱有關 Swift 中函數編程的 相關教程

可是 Android 又會是怎樣呢?可空性一般被認爲是 Java 開發中最大的痛點之一。NPE(或空指針異常)一般是因爲空值處理不當致使程序崩潰的緣由。在 Java 中,你能夠作的最有效的事是使用 @NonNull@Nullable 註解來警告該值是否可爲空。可是這些警告不會阻止你編譯和運行應用程序。幸運的是,Kotlin 拯救了它!有關更多信息,請參閱 Kotlin 的介紹

try.kotlinlang.org 打開 Kotlin playground 並粘貼剛剛在 Swift playground 中編寫的代碼來替換 main 函數的主體:

Kotlin playground with main function body replaced

太棒了對吧?你能夠將代碼從一個 playground 複製到另外一個 playground,即便這兩個 playground 使用不一樣的語言。固然,語法並不徹底相同。Kotlin 使用 val 代替 let,因此如今將該關鍵詞更改成 Kotlin 中聲明不可變變量的方式,以下所示:

fun main(args: Array<String>) {
  var hello: String? = "world"
  hello = "ok"

  val hey: String = "world"
  hey = "no"
}
複製代碼

既然你作出了改變,如今你將 let 轉向 val,而後你就獲得了 Kotlin 代碼!

點擊右上角的 Run,你會看到一個有意義的錯誤:

Kotlin compiler error

這就是你在 Swift playground 看到的一樣的東西。就像 let 同樣你也不能從新給 val 賦值。

操做數組(Map)

數組在 Swift 裏做爲一等公民,操做起來功能很是強大。若是要將整數數組中的全部值加倍,只需調用 map 函數並將其中每一個值乘以 2 便可。將此代碼粘貼到 Swift playground 中。

let xs = [1, 2, 3]
print(xs.map { $0 * 2 })
複製代碼

在 Kotlin裏,你也能夠這樣作!再一次的,將代碼從 Swift playground 複製並粘貼到 Kotlin playground。再進行修改以使其與 Kotlin 語法後,你將獲得如下內容:

val xs = listOf(1, 2, 3)
print(xs.map { it * 2 })
複製代碼

爲了到這一步,你須要:

  1. 像上一個示例中那樣,再次把 let 改成 val
  2. 使用 listOf() 而不是方括號來更改聲明整數數組的方式。
  3. 在 map 函數裏把 $0 改成 it 以引用其中的值。$0 表示 Swift 中閉包的第一個元素,而在 Kotlin 中你要在 lambda 表達式中使用保留關鍵字。

這比手動一次次遍歷數組的每個值再將全部整數乘以 2 要好得多!

獎勵:如今看看你能夠在 Swift 和 Kotlin 中對數組應用哪些其函數把!使整數翻倍就是一個很好的例子。或者可使用 filter 來過濾數組中的特定值,或者 flatMap(另外一個很是強大的內置數組運算符),用於展平嵌套數組。這是 Kotlin 的一個例子,你能夠運行 Kotlin playground 了:

val xs = listOf(listOf(1, 2, 3))
print(xs.flatMap { it.map { it * 2 }})
複製代碼

你能夠繼續使用 Swift 和 Kotlin 的全部優勢,可是你沒時間用 Kotlin 編寫你的 Poke the Bear 應用程序了,你確定不但願像日常同樣丟給你的 Android 用戶一個 iOS 應用程序而讓他們不知所措!

編寫 Android 應用程序

在 Android Studio 3.1.4 或更高版本中打開 Android Starter 應用。你能夠點擊 File ▸ New ▸ Import Project 來導入項目,而後選擇 Kotlin-Starter 項目的根文件夾來打開項目。這個項目比以前完成的 iOS 應用程序更加簡單,但不要懼怕!本教程將指導你構建 Android 版本的應用程序!

實現 LoginActivity

打開 app ▸ java ▸ com.raywenderlich.pokethebear ▸ LoginActivity.kt 文件。這和了 iOS 項目中的 LoginViewController 相似。Android 入門項目有一個與此 activity 相對應的 XML 佈局文件,請打開 app ▸ res ▸ layout ▸ activity_login.xml 以引用你將在此處使用的視圖,即 login_buttonpassword_edit_text

Android login screen

輸入驗證

如今你將從 Swift 項目文件 LoginViewController.swift 複製的第一個名爲 containsNumbers 的函數:

private func containsNumbers(string: String) -> Bool {
  let numbersInString = string.filter { ("0"..."9").contains($0) }
  return !numbersInString.isEmpty
}
複製代碼

再一次地,你可使用你激進的跨平臺複製粘貼方法,複製該函數並將其粘貼到 Android Studio 中 LoginActivity.kt 文件中的 LoginActivity 類中。作了一些更改以後,如下是你如今的 Kotlin 代碼:

private fun containsNumbers(string: String): Boolean {
  val numbersInString = string.filter { ("0".."9").contains(it.toString()) }
  return numbersInString.isNotEmpty()
}
複製代碼
  1. 正如你以前在 playground 上所作的那樣,將 let 更改成 val 以聲明不可變的返回值。
  2. 對於函數聲明,你能夠體會到從 func 中刪除 'c' 得到的一些樂趣!你使用 fun 而不是 func 來聲明 Kotlin 裏的函數。
  3. Kotlin 中函數的返回值用冒號 : 表示而不是 lambda 符號 ->
  4. 此外,在 Kotlin 中,布爾值被稱爲 Boolean 而不是 Bool
  5. 要在 Kotlin 中有聲明一個閉區間 Range,你須要使用兩個點而不是三個,因此 "0"..."9" 要改成 "0".."9"
  6. 就像你在 playground 中使用 map 同樣,你還必須將 $0 轉換爲 it。此外,在 Kotlin 中調用 contain 來比較須要將 it 轉換爲 String。
  7. 最後,你使用 return 語句在 Kotlin 中進行一些清理。你只需使用 Kotlin 裏 String 的函數 isNotEmpty 來檢查是否爲空,而不是用 !

如今,代碼語句從 Swift 更改成了 Kotlin。

從 iOS 項目中的 LoginViewController 複製 passwordIsValid 函數並將其粘貼到 Android 項目的類中:

private func passwordIsValid(passwordInput: String) -> Bool {
  return passwordInput.count >= 6 && self.containsNumbers(string: passwordInput)
}
複製代碼

這還須要進行適當的更改來將代碼從 Swift 轉換爲 Kotlin。你應該獲得這樣的代碼:

private fun passwordIsValid(passwordInput: String): Boolean {
  return passwordInput.length >= 6 && this.containsNumbers(string = passwordInput)
}
複製代碼

其中還有一些細節的差別:

  1. length 而不是 count
  2. this 而不是 self
  3. string = 而不是 string:

請注意,在Kotlin 中不須要 string = 方法,它有助於保持本教程中兩種語言之間的類似性。在其餘實踐裏的標籤是 Kotlin 爲了使 Java 代碼能夠訪問默認函數參數而包含的更多細節。閱讀有關 @JvmOverloads 函數的 更多信息 以瞭解有關默認參數的更多信息!

錯誤展現

showLengthErrorshowInvalidError 函數從 iOS 項目中的 LoginViewController 複製到 Android 項目中 LoginActivity 類裏。

showLengthError 函數肯定用戶輸入的密碼是否包含六個或更多字符,若是不是,則顯示相應的警報消息:

private func showLengthError() {
  let alert = UIAlertController(title: "Error", 
    message: "Password must contain 6 or more characters", 
    preferredStyle: UIAlertControllerStyle.alert)
  alert.addAction(UIAlertAction(title: "Okay", 
    style: UIAlertActionStyle.default, 
    handler: nil))
  self.present(alert, animated: true, completion: nil)
}
複製代碼

showInvalidError 函數用來判斷用戶輸入的密碼是否包含至少一個數字字符,若是不是,則彈出相應的警告消息:

private func showInvalidError() {
  let alert = UIAlertController(title: "Error", 
    message: "Password must contain a number (0-9)", 
    preferredStyle: UIAlertControllerStyle.alert)
  alert.addAction(UIAlertAction(title: "Okay", 
    style: UIAlertActionStyle.default, handler: nil))
  self.present(alert, animated: true, completion: nil)
}
複製代碼

如今,你必須在 Android 應用中將新複製的函數的代碼並從 Swift 轉換爲 Kotlin。你的新 showError 函數須要從新引入 Android 的 API。你如今將使用 AlertDialog.Builder 來實現 UIAlertController 類似的功能。你能夠在 本教程 中查看有關常見設計模式的更多信息,好比 AlertDialog。對話框的標題,消息和肯定按鈕字符串已包含在 strings.xml 中,所以請繼續使用它們!用如下代碼替換 showLengthError

private fun showLengthError() {
  AlertDialog.Builder(this)
    .setTitle(getString(R.string.error))
    .setMessage(getString(R.string.length_error_body))
    .setPositiveButton(getString(R.string.okay), null)
    .show()
}
複製代碼

使用相同的格式建立展現 showInvalidError 的 AlertDialog。用如下內容替換複製的方法:

private fun showInvalidError() {
  AlertDialog.Builder(this)
    .setTitle(getString(R.string.error))
    .setMessage(getString(R.string.invalid_error_body))
    .setPositiveButton(getString(R.string.okay), null)
    .show()
}
複製代碼

處理按鈕點擊事件

如今你已經完成了驗證和錯誤顯示功能,經過實現 loginButtonClicked 函數能夠把它們放在一塊兒。Android 和 iOS 之間須要注意的一個頗有趣的區別是,你的 Android 視圖是在第一個生命週期回調 onCreate() 中顯式建立和設置的,而 iOS 應用中的 Main.storyboard 是在 Swift 中隱式連接的。你能夠在 此處 詳細瞭解本教程中的 Android 生命週期。

這是 iOS 項目中的 loginButtonTapped 函數。

@IBAction func loginButtonTapped(_ sender: Any) {
  let passwordInput = passwordEditText.text ?? ""
  if passwordIsValid(passwordInput: passwordInput) {
    self.performSegue(withIdentifier: "pushBearViewController", sender: self)
  } else if passwordInput.count < 6 {
    self.showLengthError()
  } else if !containsNumbers(string: passwordInput) {
    self.showInvalidError()
  }
}
複製代碼

將 iOS 項目中 loginButtonTapped 函數的主體部分複製並粘貼到 Android 項目中 loginButtonClicked 函數的主體中,並根據你掌握的方法對代碼進行一些小改動,將語法從 Swift 更改成 Kotlin。

val passwordInput = this.password_edit_text.text.toString()
if (passwordIsValid(passwordInput = passwordInput)) {
  startActivity(Intent(this, BearActivity::class.java))
} else if (passwordInput.length < 6) {
  this.showLengthError()
} else if (!containsNumbers(string = passwordInput)) {
  this.showInvalidError()
}
複製代碼

這裏有兩個不一樣之處,分別是從 EditText 中提取字符串的方法以及顯示新 activity 的方法。你可使用語句 this.password_edit_text.text.toString()passwordInput 視圖中獲取文本。而後,調用 startActivity 函數傳入 Intent 以啓動 BearActivity 活動。剩下的都應該很是簡單。

你的 LoginActivity 現已完成。如今 Android Studio 中編譯並運行應用程序,查看你的設備或自帶模擬器中顯示的第一個已實現的活動。輸入用戶名的任何字符串值,並使用有效和無效的密碼組合,以確保你的錯誤對話框顯示達到了預期。

Kotlin login screen

成功登陸後將屏幕上將顯示一隻熊,你如今能夠來實現它了!

實現熊的活動

打開 app ▸ java ▸ com.raywenderlich.pokethebear ▸ BearActivity.kt,你的 BearViewController.swift 文件即將變成 Kotlin 的版本。你將經過實現輔助函數 bearAttackreset 來開始修改此 Activity。你將在 Swift 文件中看到 bearAttack 負責設置 UI 狀態,隱藏 Poke 按鈕五秒鐘,而後重置屏幕:

private func bearAttack() {
  self.bearImageView.image = imageLiteral(resourceName: "bear5")
  self.view.backgroundColor = .red
  self.pokeButton.isHidden = true
  DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5), 
    execute: { self.reset() })
}
複製代碼

從 iOS 項目中複製該函數並將其粘貼到 Android 項目中的 bearAttack 函數體中,而後進行一些小的語法修改讓 Kotlin 中的 bearAttack 函數的主體以下所示:

private fun bearAttack() {
  this.bear_image_view.setImageDrawable(getDrawable(R.drawable.bear5))
  this.bear_container.setBackgroundColor(getColor(R.color.red))
  this.poke_button.visibility = View.INVISIBLE
  this.bear_container.postDelayed({ reset() }, TimeUnit.SECONDS.toMillis(5))
}
複製代碼

你須要作出如下修改:

  1. 調用 setImageDrawable 函數將 bear_image_view 的圖像資源設置爲 bear5.png 可繪製的資源,該資源已包含在 app ▸ res ▸ drawable 目錄下。
  2. 而後調用 setBackgroundColor 函數將 bear_container 視圖的背景設置爲預先定義的顏色 R.color.red
  3. isHidden 屬性更改成 visibility,而不是將按鈕的可見性切換爲 View.INVISIBLE
  4. 也許你對代碼的最不直觀的改變是重寫 DispatchQueue,但不要懼怕!Android的 asyncAfter 是一個簡單的 postDelayed 動做,你在 bear_container 視圖上設置。

幾乎就要完成了!還有另外一個要從 Swift 轉換爲 Kotlin 的功能。複製 Swift reset 函數的主體並將其粘貼到Android項目的 BearActivity 類重置中來重複這個轉換過程:

self.tapCount = 0
self.bearImageView.image = imageLiteral(resourceName: "bear1")
self.view.backgroundColor = .white
self.pokeButton.isHidden = false
複製代碼

而後進行相似的更改:

this.tapCount = 0
this.bear_image_view.setImageDrawable(getDrawable(R.drawable.bear1))
this.bear_container.setBackgroundColor(getColor(R.color.white))
this.poke_button.visibility = View.VISIBLE
複製代碼

最後一步是從 iOS 項目中複製 pokeButtonTapped 函數的主體並將其粘貼到 Android 項目中。因爲 Swift 和 Kotlin 之間的類似性,這個 if/else 語句將也須要對 Android 做出更改,雖然修改很是小。這樣確保了你的 Kotlin 中 pokeButtonClicked 函數主體看起來像這樣:

this.tapCount = this.tapCount + 1
if (this.tapCount == 3) {
  this.bear_image_view.setImageDrawable(getDrawable(R.drawable.bear2))
} else if (this.tapCount == 7) {
  this.bear_image_view.setImageDrawable(getDrawable(R.drawable.bear3))
} else if (this.tapCount == 12) {
  this.bear_image_view.setImageDrawable(getDrawable(R.drawable.bear4))
} else if (this.tapCount == 20) {
  this.bearAttack()
}
複製代碼

額外聲明:這個if/else 階梯語句能夠很容易地用更具表現力的 控制流語句 替換,好比 switch,也就是在 Kotlin 中的 when

若是你想簡化邏輯,請嘗試一下。

如今全部功能都已從 iOS 應用程序移植並從 Swift 轉換爲 Kotlin。在真機或模擬器中編譯並運行應用程序並開始使用,如今你能夠在登陸屏幕後面出現了你的毛茸茸的朋友。

Android screen with bear and poke button

恭喜你,你已將 Swift 轉換爲 Kotlin,將 iOS 應用程序轉換爲全新的 Android 應用程序。你已經經過將 Swift 代碼從 Xcode 中的 iOS 項目移動到 Android Studio 中的 Android 應用程序,將 Swift 轉換爲 Kotlin 來實現跨平臺!沒有多少人和開發人員會說什麼,並且實現它真的很是簡單。

接下來該幹嗎?

使用本教程頂部的 連接 下載已經完成的項目來看看它是如何進行的。

若是你是一名 Swift 開發人員,或者是 Kotlin 新手,請查看 Kotlin 官方文檔 以更深刻地瞭解這些語言。你已經知道如何運行 Kotlin playground 來嘗試用代碼片斷,而且能夠在文檔中編寫可運行的代碼小部件。若是你已是 Kotlin 開發人員,請嘗試在 Swift 中編寫應用程序。

若是你喜歡 Swift 和 Kotlin 的並排比較,請在 本文 中查看更多內容。你可信賴的做者還與 UIConf 的 iOS 同事就 Swift 和 Kotlin 進行了一次快速的討論,你能夠在 這裏 觀看到。

咱們但願你喜歡本教程,瞭解如何把 Swift 編寫的 iOS 應用程序變成用 Kotlin 建立一個全新的 Android 應用程序。咱們也但願你繼續探索這兩種語言和兩種平臺。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索