安裝 Kotlin 插件
Android Studio 3.+ 已經(jīng)有了 Kotlin 插件,如果是更早的版本,點擊 Android Studio | File | Settings | Plugins,搜索 Kotlin ,安裝,重啟 Android Studio .
創(chuàng)建工程
點擊 Android Studio | File | New project : 勾選Incloud Kotlin support.
就會看到下面的類:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
定義類
1.只需使用class關(guān)鍵字.
2.它有一個唯一默認(rèn)的構(gòu)造器,大部分情況下只需使用這個默認(rèn)構(gòu)造器即可.
3.構(gòu)造函數(shù)的函數(shù)體,寫在init塊{}中
class Person(name:String ,age : Int) {
init {
}
}
類繼承
1.上帝類是Any(類似于java中的Object)
2.所有類默認(rèn)是不可繼承的(final),我們只能繼承聲明open或abstract的類
open class Animal(name: String)
class Person(name:String ,age : Int) : Animal(name)
函數(shù)(java中的方法)
1.使用fun關(guān)鍵字
2.如果沒有指定返回值,默認(rèn)返回Unit(java中的void),當(dāng)然也可以指定返回任何類型
fun add(age1:Int ,age2:Int) : Int{
return age1+age2
}
Tips: 分號不是必須的,結(jié)尾不使用分號會節(jié)約很多時間,養(yǎng)成這個好習(xí)慣吧!
3.如果返回結(jié)果可以使用表達式表達出來,直接使用等號:
fun add2(age1: Int , age2: Int) : Int = age1+age2
構(gòu)造方法和函數(shù)參數(shù)
1.kotlin中參數(shù)是先寫名稱,后寫類型....(有點不適應(yīng))
2.可以給參數(shù)一個指定默認(rèn)值,使其變得可選,例如下面toast函數(shù)第二個參數(shù)給了默認(rèn)值,調(diào)用時候可以不傳第二個值(java中重載方法的替換?)
fun toast(msg : String , length : Int = Toast.LENGTH_LONG){
Toast.makeText(this,msg,length).show();
}
toast("打印吐司鴨")
toast("打印吐司鴨",Toast.LENGTH_SHORT)
Tips:
String模板內(nèi)插*:
val name = "susan"
println("name : $name")
輸出結(jié)果: name : susan
編寫你的第一個類
我們在MainActivity的布局文件中加入RecyclerView,然后設(shè)置好LayoutManager:
val recycler = findViewById(R.id.recycler) as RecyclerView
recycler.layoutManager = LinearLayoutManager(this)
如上代碼,LayoutManager會通過屬性設(shè)置,而不是通過set方法.
對象實例化也去掉了new關(guān)鍵字,構(gòu)造函數(shù)仍然會被調(diào)用.
接著設(shè)置Adapter:
class ForecastListAdapter(val items : List<String>) : RecyclerView.Adapter<ForecastListAdapter.ViewHolder>(){
//綁定數(shù)據(jù)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.textView.text = items[position]
}
//返回list count
override fun getItemCount(): Int {
return items.size
}
//創(chuàng)建viewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(TextView(parent.context))
}
class ViewHolder(val textView : TextView) : RecyclerView.ViewHolder(textView)
}
回到MainActivity,現(xiàn)在我們將少量數(shù)據(jù)放入recyclerview中:
//定義天氣數(shù)組數(shù)組
val weaList= listOf<String>(
"北京 -0 - 微風(fēng)",
"北京 -10 - 微風(fēng)",
"北京 -13 - 微風(fēng)",
"北京 -20 - 微風(fēng)",
"北京 -5 - 微風(fēng)"
)
recycler.adapter = ForecastListAdapter(weaList)
關(guān)于List的創(chuàng)建:
可以通過一個函數(shù)listOf創(chuàng)建一個常亮list,它接收任何類型的vararg(可變長度參數(shù)).還有很多其他函數(shù):setOf,arrayListOf hashSetOf, etc...
運行你的項目吧:
接下來,必須要學(xué)習(xí)一些基本類型,變量,屬性等才能繼續(xù).
變量和屬性
在kotlin中,一切都是對象~
基本類型
integer float boolean等類型依然存在,不過是以對象存在.它們的工作方式與java十分相似,需要注意以下幾點:
數(shù)字類型不會自動轉(zhuǎn)型.例如不能給Double分配int型值
val i: Int = 7
val d: Double = i.toDouble()
char不能直接作為一個數(shù)字來處理,需要轉(zhuǎn)換成數(shù)字
val a : Char = 'c'
val b : Int = a.toInt()
位運算 java中使用的 || 或者 && kotlin中使用and or
val willOr = FLAG1 or FLAG2
val willAnd = FLAG1 and FLAG2
字面可以寫明具體的類型,但是不是必須的,編譯器會自動解析類型
val i = 12 //as Int
val l = 3l //as Long
val f = 5f //as Float
val d = 3.5 //as Double
一個String 可以像數(shù)組那樣訪問,并且被迭代
val s = "test"
val t = s[2]//一個字符's'
//迭代
val o = "test"
for (b in o){
print(t)
}
變量
變量可簡單定義為 : val(不可變)和var(可變).但是不可變在kotlin是一個很重要的概念.
一個不可變對象意味著它在實例化之后就不能再去改變它的狀態(tài)了。如果你需要一個這個對象修改之后的版本,那就會再創(chuàng)建一個新的對象。這個讓編程更加具有健壯性和預(yù)估性。
在Java中,大部分的對象是可變的,那就意味著任何可以訪問它這個對象的代碼都可以去修改它,從而影響整個程序的其它地方。不可變對象也可以說是線程安全的,因為它們無法去改變,也不需要去定義訪問控制,因為所有線程訪問到的對象都是同一個
一個重要的概念是:盡可能地使用val。除了個別情況(特別是在Android中,有很多類我們是不會去直接調(diào)用構(gòu)造函數(shù)的),大多數(shù)時候是可以的。
如果我們需要使用更多的范型類型,則需要指定:
val a: Any = 23
val c: Context = activity
屬性
沒有任何指定,屬性會默認(rèn)使用getter和setter.
class Dog {
val color : Int = 0
}
當(dāng)然也可以自定義set,get.
var color : Int = 3
get() = field.toBigDecimal().intValueExact()
set(value) {
field = 3 + value
}
Anko來了
- 主要目的是用來替換之前XMl的方式來使用代碼生成UI布局
- Anko還包含了許多有幫助的函數(shù)和屬性來避免寫很多代碼
- 了解Anko的實現(xiàn)方式對學(xué)習(xí)kotlin有很大幫組
開始使用Anko
剛使用findviewbyid的可以用fins替換
val recycler : RecyclerView = find(R.id.recycler)
Anko還有一些別的實用功能:
實例化Intent,Activity之間的跳轉(zhuǎn),F(xiàn)ragment的創(chuàng)建,數(shù)據(jù)庫的訪問,Alert的創(chuàng)建......。
擴展函數(shù)
擴展函數(shù)數(shù)是指在一個類上增加一種新的行為,甚至我們沒有這個類代碼的訪問權(quán)限。這是一個在缺少有用函數(shù)的類上擴展的方法。
在Java中,通常會實現(xiàn)很多帶有static方法的工具類。Kotlin中擴展函數(shù)的一個優(yōu)勢是我們不需要在調(diào)用方法的時候把整個對象當(dāng)作參數(shù)傳入。擴展函數(shù)表現(xiàn)得就像是屬于這個類的一樣,而且我們可以使用this關(guān)鍵字和調(diào)用所有public方法。
舉個例子,我們可以創(chuàng)建一個toast函數(shù),這個函數(shù)不需要傳入任何context,它可以被任何Context或者它的子類調(diào)用,比如Activity或者Service:
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
這個方法可以在Activity內(nèi)部直接調(diào)用:
toast("Hello world!")
toast("Hello world!", Toast.LENGTH_LONG)
擴展函數(shù)并不是真正地修改了原來的類,它是以靜態(tài)導(dǎo)入的方式來實現(xiàn)的。擴展函數(shù)可以被聲明在任何文件中,因此有個通用的實踐是把一系列有關(guān)的函數(shù)放在一個新建的文件里。
執(zhí)行一個請求
如果只是執(zhí)行一個簡單的api請求,我們可以不用任何非三方庫實現(xiàn)
class Request(val url : String) {
fun run(){
val jsonStr = URL(url).readText()
Log.d("result",jsonStr)
}
}
眾所周知,在子線程中是不允許進行網(wǎng)絡(luò)請求的,java的AsyncTask是非常丑陋的...diss一波...
Anko提供了非常簡單的DSL來處理異步任務(wù),它滿足大部分的需求。它提供了一個基本的async函數(shù)用于在其它線程執(zhí)行代碼,也可以選擇通過調(diào)用uiThread的方式回到主線程。在子線程中執(zhí)行請求如下這么簡單:
val url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22"
doAsync() {
Request(url).run()
uiThread { toast("Request") }
}
此時運行項目,查看log,可以發(fā)現(xiàn)一個簡單請求已經(jīng)實現(xiàn)了!
接著我們要把他使用json解析,轉(zhuǎn)換成實體類.
數(shù)據(jù)類
數(shù)據(jù)類是一種非常強大的類,它可以讓你避免創(chuàng)建Java中的用于保存狀態(tài)但又操作非常簡單的POJO的模版代碼。它們通常只提供了用于訪問它們屬性的簡單的getter和setter。定義一個新的數(shù)據(jù)類非常簡單:
data class Forecast(val date : Date , val temp : Float ,
val details : String)
轉(zhuǎn)換json到數(shù)據(jù)類
關(guān)于伴生對象Companion objects :
Kotlin允許我們?nèi)ザx一些行為與靜態(tài)對象一樣的對象。盡管這些對象可以用眾所周知的模式來實現(xiàn),比如容易實現(xiàn)的單例模式。我們需要一個類里面有一些靜態(tài)的屬性、常量或者函數(shù),我們可以使用companion object。這個對象被這個類的所有對象所共享,就像Java中的靜態(tài)屬性或者方法。
修改一下Request類:
class Request(val zipCode : String) {
companion object {
private val APP_ID = "15646a06818f61f7b8d7823ca833e1ce"
private val URL = "http://api.openweathermap.org/data/2.5/" +"forecast/daily?mode=json&units=metric&cnt=7"
private val COMPLETE_URL = "$URL&APPID=$APP_ID&q="
}
public fun execute() : ForecastResult{
val jsonStr = URL(COMPLETE_URL + zipCode).readText()
return Gson().fromJson(jsonStr,ForecastResult::class.java)
}
@Deprecated("使用execute替換")
public fun run(){
val jsonStr = URL(url).readText()
Log.d("result",jsonStr)
}
}
數(shù)據(jù)類:
data class ForecastResult(val city: ResponseClasses.City,
val cnt: Int,
val cod: String,
val list: List<ResponseClasses.ForeCast>,
val message: Double) {
data class ForeCast(
val clouds: Int,
val deg: Int,
val dt: Int,
val humidity: Int,
val pressure: Double,
val rain: Double,
val speed: Double,
val temp: Temp,
val weather: List<Weather>
)
data class Temp(
val day: Double,
val eve: Double,
val max: Double,
val min: Double,
val morn: Double,
val night: Double
)
data class Weather(
val description: String,
val icon: String,
val id: Int,
val main: String
)
data class City(
val coord: Coord,
val country: String,
val id: Int,
val name: String,
val population: Int
)
data class Coord(
val lat: Double,
val lon: Double
)
}
好累,先寫到這里@_@
分享一首好音樂:
Lo Que Siento - cuco
我們都是做夢的夢想家