Kotlin入門(21)活動頁面的跳轉處理
Activity的活動頁面跳轉是App最常用的功能之一,在前幾章的demo源碼中便多次見到了,常常是點擊界面上的某個按鈕,然後跳轉到與之對應的下一個頁面。對於App開發者來說,該功能的實現非常普通,使用Java編碼不過以下兩行代碼而已:
Intent intent = new Intent(MainActivity.this, LinearLayoutActivity.class); startActivity(intent);
上面代碼的關鍵之處在於Intent的構造函數,其中第一個參數指定了頁面跳轉動作的來源,即MainActivity這個源頁面,MainActivity.this通常簡寫為this;構造Intent的第二個參數則表示頁面跳轉動作的目的地,即LinearLayoutActivity這個目標頁面。倘若把這兩行Java代碼轉換為Kotlin代碼(復制這兩行然後粘貼到kt文件中,Android Studio就會自動完成轉換),則可看到活動跳轉的Kotlin代碼如下所示:
val intent = Intent(this@MainActivity, LinearLayoutActivity::class.java) startActivity(intent)
對比之下,這裏的Kotlin代碼與Java代碼主要有兩點不同之處:
1、在類內部指代自身的this關鍵字,Java的完整寫法是“類名.this”,而Kotlin的完整寫法是“this@類名”,當然二者均可簡寫為“this”;
2、獲取某個類的class對象,Java的寫法是“類名.class”,而Kotlin的寫法是“類名::class.java”,一看便知帶有濃濃的Java風味;
看起來,Kotlin代碼與Java代碼半斤八兩,未有明顯的簡化,令人產生小小的失望。但細心的讀者也許已經註意到了,本書附錄源碼裏的活動跳轉,並非上述的Kotlin正宗寫法,而是下面這種簡化版的寫法:
startActivity<LinearLayoutActivity>()
究其原因,乃是Anko庫利用Kotlin的擴展函數,給Context類新增了名為startActivity的新方法。故而使用簡化版的寫法之前,必須先導入Anko庫的指定文件,即在kt文件頭部添加下面一行導入語句:
import org.jetbrains.anko.startActivity
活動頁面跳轉的時候,往往還要攜帶一些請求參數,如果使用Java編碼,可以很輕松地調用Intent對象的putExtra方法,通過“putExtra(參數名, 參數值)”的方式傳遞消息,就像下面代碼那樣:
Intent intent = new Intent(this, ActSecondActivity.class); intent.putExtra("request_time", DateUtil.getNowTime()); intent.putExtra("request_content", et_request.getText().toString()); startActivity(intent);
如果使用Anko的簡化寫法,其實也很容易,只要在startActivity後面的括號中依次填上每個參數字段的字段名和字段值,具體的Kotlin跳轉代碼如下所示:
//第一種寫法,參數名和參數值使用關鍵字to隔開 startActivity<ActSecondActivity>( "request_time" to DateUtil.nowTime, "request_content" to et_request.text.toString())
註意到上面的寫法使用關鍵字to隔開參數名和參數值,感覺不夠美觀,而且容易使人迷惑,to後面究竟要跟著字段名還是字段值呢?所以Anko庫提供了另一種符合習慣的寫法,也就是利用Pair類把參數名和參數值進行配對,Pair的第一個參數為字段名,第二個參數為字段值。據此改寫後的Kotlin跳轉代碼如下所示:
//第二種寫法,利用Pair把參數名和參數值進行配對 startActivity<ActSecondActivity>( Pair("request_time", DateUtil.nowTime), Pair("request_content", et_request.text.toString()))
不管哪種寫法,在下一個活動中解析請求參數的方式都一樣,都得先獲取Bundle對象,然後分別根據字段名稱獲取對應的字段值。具體的請求參數解析代碼如下所示:
class ActSecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_act_second) val bundle = intent.extras val request_time = bundle.getString("request_time") val request_content = bundle.getString("request_content") tv_response.text = "收到請求消息:\n請求時間為${request_time}\n請求內容為${request_content}" } }
下面通過測試界面觀察一下消息數據發送之前和發送之後的效果,如下面左圖所示,這時第一個頁面準備跳轉到第二個頁面;如下面右圖所示,這是跳轉後的第二個頁面,界面上展示了第一個頁面傳遞過來的參數信息。
Activity之間傳遞的參數類型,除了整型、浮點數、字符串等基本數據類型,還允許傳遞序列化結構如Parcelable對象。這個Parcelable對象可不是簡單的實體類,而是實現了Parcelable接口的實體類,實現接口意味著該類必須重寫接口定義的所有方法,不管你願不願意都得老老實實地照貓畫虎。譬如前面的活動跳轉傳遞了兩個字段數據,如果把這兩個字段放到Parcelable對象中,僅僅包含兩個字段的Parcelable類對應的Java代碼也如下面這般冗長:
public class MessageInfo implements Parcelable { public String content; public String send_time; // 寫數據 @Override public void writeToParcel(Parcel out, int flags) { out.writeString(content); out.writeString(send_time); } // 例行公事實現createFromParcel和newArray public static final Parcelable.Creator<MessageInfo> CREATOR = new Parcelable.Creator<MessageInfo>() { // 讀數據 public MessageInfo createFromParcel(Parcel in) { MessageInfo info = new MessageInfo(); info.content = in.readString(); info.send_time = in.readString(); return info; } public MessageInfo[] newArray(int size) { return new MessageInfo[size]; } }; @Override public int describeContents() { return 0; } }
看看這架勢,如此簡單的自定義Parcelable類,就得重寫包括writeToParcel、createFromParcel、newArray、describeContents在內的四個方法,可謂是興師動眾。由此可見這裏又是Java的一個痛點,正適合Kotlin施展拳腳、好好改進。在第五章的類和對象中,介紹了Kotlin對數據類的寫法,在類名前面關鍵字data,Kotlin即可自動提供get/set、equals、copy、toString等諸多方法。那麽序列化對象的改造也相當簡單,僅需在類名之前增加一行註解“@Parcelize”就好了,整個類的Kotlin代碼只有下面寥寥幾行:
@Parcelize data class MessageInfo(val content: String, val send_time: String) : Parcelable { }
不過若想正常編譯,還需修改模塊的編譯文件build.gradle,在文件末尾添加下面幾行,表示增加安卓插件的編譯支持:
//@Parcelize標記需要設置experimental = true androidExtensions { experimental = true }
編譯文件修改完畢,現在能在Kotlin中使用序列化對象的註解了。雖然自定義的MessageInfo類內部沒有任何一行代碼,但是它除了具備數據類的所有方法,也自動實現了Parcelable接口的幾個方法。接下來就可以利用該類傳輸活動跳轉的序列化數據了,下面是改寫後的Kotlin跳轉代碼:
val request = MessageInfo(et_request.text.toString(), DateUtil.nowTime) startActivity<ParcelableSecondActivity>("message" to request)
跳轉後的下一個頁面,調用getParcelable即可正常獲得原始的序列化數據,具體的數據解析代碼如下所示:
class ParcelableSecondActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_parcelable_second) val request = intent.extras.getParcelable<MessageInfo>("message") tv_response.text = "收到打包好的請求消息:\n請求時間為${request.send_time}\n請求內容為${request.content}" } }
同樣通過測試界面觀察序列化對象的打包和解包效果,如下面左圖所示,這時第一個頁面準備跳轉到第二個頁面;如下面右圖所示,這是跳轉後的第二個頁面,界面上展示了第一個頁面傳遞過來的序列化數據。
Kotlin入門(21)活動頁面的跳轉處理