1. 程式人生 > >Kotlin入門(21)活動頁面的跳轉處理

Kotlin入門(21)活動頁面的跳轉處理

分享 文件中 kotlin 消息 插件 自動實現 字符 java reat

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)活動頁面的跳轉處理