前言
因?yàn)橐恍┰蛎跎枰獙?xiě)一個(gè)Android的小程序盅蝗。剛好因?yàn)樽罱欢螘r(shí)間接觸到了Kotlin卧秘,便想著機(jī)會(huì)難得呢袱,終于可以體會(huì)一下Kotlin的強(qiáng)大了。為什么我之前不去寫(xiě)Kotlin呢翅敌?身為懶癌晚期的我不做解釋羞福。(PS:由于這也是我個(gè)人的第一篇文章,文采什么鬼的也都是沒(méi)有的蚯涮,各位大佬也就湊活著看吧治专。)
Kotlin的安裝
Kotlin的安裝這里就不做過(guò)多的介紹了,度娘一堆堆的,隨便找一篇裝一下應(yīng)該還是很簡(jiǎn)單的遭顶。本篇主要介紹我在這個(gè)程序開(kāi)發(fā)中Kotlin的使用张峰。
正題
安裝好了之后,在Android Studio中創(chuàng)建一個(gè)項(xiàng)目液肌,和平常一樣挟炬。都會(huì)自動(dòng)生成一個(gè)MainActivity類(lèi)以及其他一堆堆的東西鸥滨。不同的是這個(gè)MainActivity類(lèi)是一個(gè)后綴為kt的文件嗦哆,和我們平時(shí)使用的java文件是不一樣的,打開(kāi)后可以看到:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
可以看到婿滓,原本的extends關(guān)鍵字不見(jiàn)了老速,取而代之的是 : ,如果要實(shí)現(xiàn)一個(gè)接口凸主,也不是使用implements橘券,而是在AppCompatActivity()的后面加一個(gè)逗號(hào),然后寫(xiě)上你要實(shí)現(xiàn)的接口名字就OK了卿吐。通過(guò)上面的代碼可以發(fā)現(xiàn)旁舰,Kotlin的編程是不需要;
這種東西的,看上去和實(shí)際開(kāi)發(fā)都是一件很棒的事嗡官。在Kotlin里面箭窜,冒號(hào)幾乎是無(wú)處不見(jiàn),具體作用我們后面慢慢來(lái)講衍腥。
在平時(shí)開(kāi)發(fā)中磺樱,總會(huì)去聲明許多的變量和常量,平常聲明一個(gè)變量:
private String a;
在Kotlin中:
var a:String? = null
可以看到婆咸,是兩個(gè)完全不一樣的方式竹捉,讓我們拆開(kāi)看一下。var
是用來(lái)聲明一個(gè)變量尚骄,而常量則是用val
來(lái)進(jìn)行聲明块差。a
這是變量名就不需要多說(shuō)了,緊跟一個(gè)冒號(hào),然后是一個(gè)變量的類(lèi)型String
后面加一個(gè)?
表示當(dāng)前變量是可空的憨闰,但是做了非空判斷的變量询兴,在使用上不能直接使用,如:
val a1 = a.length
這時(shí)候起趾,編譯就會(huì)報(bào)紅诗舰,在這里需要對(duì)a進(jìn)行非空判斷才能使用它,這樣子就顯得很麻煩训裆,如果一個(gè)項(xiàng)目中如果有十幾二十個(gè)變量眶根,光這個(gè)判斷就能寫(xiě)一堆了。顯然边琉,設(shè)計(jì)師也想到了這個(gè)問(wèn)題属百,在使用這種變量的時(shí)候有兩種方式,
一:val a1 = a?.length
這時(shí)候表示变姨,如果a
這時(shí)候是一個(gè)空的族扰,則返回null
,否則就返回a.length
二:val a1 = a!!.length
這時(shí)候表示定欧,如果a
是一個(gè)空的渔呵,則出現(xiàn)空指針異常,否則就返回a.length
在Android里面砍鸠,我們經(jīng)忱┣猓回去定義一些Fragment、控件等等的東西爷辱。讓我們來(lái)看看在Kotlin中該如何使用的:
XML:
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
var mBtn:Button? = null
mBtn = findViewById(R.id.btn) as Button
mBtn?.text = "按鈕"
上面的這兩行代碼相信作為一個(gè)Android開(kāi)發(fā)录豺,非常的熟悉了。雖然用了Kotlin的寫(xiě)法饭弓,但是理解起來(lái)也很快双饥。可以看到弟断,Kotlin中強(qiáng)轉(zhuǎn)都是通過(guò)as
這個(gè)關(guān)鍵字咏花。通過(guò)代碼發(fā)現(xiàn),我們熟悉的setText()
這個(gè)方法沒(méi)了夫嗓,取而代之的是text
迟螺。Kotlin中,如果一個(gè)屬性同時(shí)擁有get set
舍咖,那么就會(huì)合并成一個(gè)屬性矩父,賦值就如同上面的方式,取值便是調(diào)用btn.text
便可排霉。但是窍株,在Kotlin中,有一個(gè)非常非常方便的方法來(lái)使用一個(gè)控件:
btn.text = "按鈕"
就這一句,就可以和上面的三行代碼呈現(xiàn)相同的效果球订。在Kotlin中后裸,可以直接使用XML中給控件定義的ID值,來(lái)操作這個(gè)控件冒滩。要使用這種方式微驶,需要導(dǎo)入一個(gè)包:
import kotlinx.android.synthetic.main.activity_test.*
當(dāng)你直接只用ID的時(shí)候,Android Studio會(huì)幫你導(dǎo)入這個(gè)包开睡。如此一來(lái)因苹,省略的代碼就很可觀了。
在Kotlin中篇恒,沒(méi)有new
這個(gè)關(guān)鍵字扶檐,所以在聲明一個(gè)類(lèi)就變得更加簡(jiǎn)潔:
var t:Test = Test("參數(shù)1");
接下來(lái)讓我們看一下如何去寫(xiě)一個(gè)方法:
public void show(){
//具體操作
}
而在Kotlin中,長(zhǎng)這個(gè)樣子:
fun show(){
//具體操作
}
Kotlin中聲明一個(gè)方法用fun
關(guān)鍵字胁艰,后面跟著方法命款筑。當(dāng)然如果要定義一個(gè)有返回值的方法怎么辦?萬(wàn)能的冒號(hào)就出現(xiàn)了:
fun show() : String{
return "abc"
}
在Kotlin里面腾么,有一個(gè)特性叫默認(rèn)參數(shù)奈梳,具體就是在你的方法的括號(hào)里面定義參數(shù),并且可以給這個(gè)參數(shù)賦予一個(gè)默認(rèn)值哮翘,具體:
fun show(a: String,b:String = "c") : String{
return a + b
}
這個(gè)方法里面颈嚼,給參數(shù)b
賦予了一個(gè)默認(rèn)值是c
毛秘,如果我們調(diào)用這個(gè)方法的時(shí)候用show("123")
這時(shí)候這個(gè)方法會(huì)給我們返回123c
饭寺,正常使用show("123","abc")
的時(shí)候,便會(huì)返回123abc
的字符串叫挟。身為開(kāi)發(fā)者的你想到什么了沒(méi)有艰匙?沒(méi)錯(cuò),就是重載抹恳。通過(guò)默認(rèn)參數(shù)员凝,原本在java中需要寫(xiě)兩個(gè)方法,在Kotlin中只要一個(gè)方法就夠了奋献。在一個(gè)項(xiàng)目中健霹,這一特性就可以少寫(xiě)很多代碼了。
在Kotlin中瓶蚂,如果想繼承一個(gè)自己寫(xiě)的類(lèi)糖埋,需要在class
關(guān)鍵字前面加上open
,表示當(dāng)前類(lèi)是可以繼承的窃这。而方法也一樣瞳别,如果不加open
關(guān)鍵字,繼承后無(wú)法找到該方法。如果子類(lèi)中使用了父類(lèi)的方法祟敛,也會(huì)在方法加上override
關(guān)鍵字疤坝,相對(duì)應(yīng)的Java中則是@override
。在Kotlin中馆铁,如果不給類(lèi)和方法使用修飾符跑揉,默認(rèn)是public
。其中埠巨,Kotlin中的修飾符沒(méi)有了default
畔裕,而是多了一個(gè)internal
,表示在當(dāng)前模塊(Model)可以使用。其余作用域都與Java中一致乖订。
在Kotlin中扮饶,構(gòu)造方法的使用:
open class Test constructor(a:String) {
var a: String? = null
init {
this.a = a
}
}
Kotlin中,在定義類(lèi)名的后面加上constructor
關(guān)鍵字緊跟一對(duì)括號(hào)乍构,括號(hào)內(nèi)傳入需要的參數(shù)甜无。可以看到在代碼中有一個(gè)init{}
的東西(算代碼塊吧哥遮。算了岂丘,不重要),這里面就是用來(lái)放置構(gòu)造方法的代碼體眠饮。每個(gè)類(lèi)不管有參無(wú)參奥帘,都會(huì)有一個(gè)init{}
的東西用來(lái)給開(kāi)發(fā)者初始化一些東西。
Kotlin中仪召,并沒(méi)有static
這個(gè)關(guān)鍵字寨蹋,那么我們?nèi)绻x一個(gè)靜態(tài)的屬性或者方法怎么辦呢收毫?這時(shí)候窗慎,伴生對(duì)象就上場(chǎng)了攒霹。在開(kāi)發(fā)中奔缠,我是把伴生對(duì)象當(dāng)作一個(gè)用來(lái)定義靜態(tài)屬性或者方法來(lái)使用(具體還有什么作用逛裤,就需要繼續(xù)研究了)闸与。下面看一下用法:
A:
companion object{
val name:String? = "H"
}
B:
Log.d("companion object",Test1.name)
在A類(lèi)中宾袜,我們通過(guò)伴生對(duì)象定義了一個(gè)字符串狰右,然后在B類(lèi)中通過(guò)類(lèi)名調(diào)用了它玖瘸。這就是伴生對(duì)象一個(gè)簡(jiǎn)單的使用秸讹。
在Kotlin中,有一個(gè)特性叫擴(kuò)展函數(shù)雅倒。它可以將我們自定義的一個(gè)方法加到任何一個(gè)地方璃诀。比如,給Activity加一個(gè)更加方便的Toast方法:
fun Activity.toast(msg:String,duration:Int = Toast.LENGTH_SHORT){
Toast.makeText(this,msg,duration)
}
上述代碼屯断,我們給Activity加了一個(gè)toast
方法文虏,需要傳入一個(gè)msg
參數(shù)作為消息主體侣诺,時(shí)間默認(rèn)是Toast.LENGTH_SHORT
,也可以進(jìn)行修改氧秘。這個(gè)方法在任何繼承了Activity
的類(lèi)中都可以進(jìn)行調(diào)用年鸳。具體如何將這個(gè)特性發(fā)揚(yáng)光大,就靠廣大的開(kāi)發(fā)者了丸相。(擴(kuò)展方法要定義在一個(gè)后綴.kt的文件下搔确,我之前定義在類(lèi)里面,并沒(méi)有什么**用)
Kotlin對(duì)For循環(huán)進(jìn)行了改變:
1灭忠、 in關(guān)鍵字膳算,相當(dāng)于foreach:
var m:MutableList<String> = mutableListOf()
for (i in m){
Log(i)
}
MutableList
相當(dāng)于ArrayList
,在Kotlin中也有ArrayList
弛作,不過(guò)它變成了只讀的涕蜂。而mutableListOf
則是用來(lái)初始化這個(gè)集合的。代碼中映琳,定義了一個(gè)i
來(lái)代表m
里面的單個(gè)元素机隙,通過(guò)Log打印在控制臺(tái)(Log是通過(guò)擴(kuò)展函數(shù)實(shí)現(xiàn)的)。在Kotlin中萨西,foreach還有另一種實(shí)現(xiàn)方法:
m.forEach {
Log(it)
}
這兩種方式是等同的有鹿。
2、
for (i in m.indices){
Log("${i}")
}
通過(guò)indices
谎脯,可以循環(huán)拿到m
集合里面元素的下標(biāo)葱跋。這里,我們的Log
打印通過(guò)${代碼塊}
的方式源梭,Kotlin允許通過(guò)這種方式進(jìn)行字符串的拼接娱俺,使代碼更加直觀、美觀咸产。
3矢否、
for ((i,v) in m.withIndex()){
Log("$i and $v")
}
有時(shí)候如果我們既想獲取下標(biāo)也想拿到相對(duì)應(yīng)的值,就可以通過(guò)withIndex
這個(gè)屬性脑溢。
Kotlin用when
來(lái)取代了switch
。
when(edit.text.toString()){
"1" -> Log("a")
"2","4" -> Log("b")
"3" -> Log("c")
else -> {
Log("默認(rèn)")
}
}
在這段代碼中赖欣,我們根據(jù)在edit
這個(gè)輸入框中的值進(jìn)行匹配屑彻,如果輸入1
則打印a
,以此類(lèi)推顶吮。when
的表達(dá)方式社牲,相比較switch
,直觀了許多悴了。
Android中搏恤,經(jīng)常會(huì)用到Intent來(lái)進(jìn)行一些跳轉(zhuǎn)界面或者進(jìn)行一些打開(kāi)照相機(jī)之類(lèi)的操作违寿。那么,我們來(lái)看一下熟空,在Kotlin中怎么操作:
mPicture.setOnClickListener {
var i = intentFor<PictureActivity>(
Pair("1",1)
)
startActivity(i)
}
這個(gè)就是在Kotlin中跳轉(zhuǎn)Activity的語(yǔ)句藤巢。這里通過(guò)intentFor
,在泛型里面將要跳轉(zhuǎn)的類(lèi)名傳入便可息罗,緊跟的括號(hào)里面可以放入想要傳給目標(biāo)類(lèi)的數(shù)據(jù)掂咒,第一次參數(shù)是String
,第二個(gè)數(shù)據(jù)傳的類(lèi)型是Any
(相當(dāng)于Java中的Object
)迈喉。不過(guò)绍刮,intentFor
的寫(xiě)法是Anko
提供的。Anko
封裝了很多實(shí)用的方法挨摸,但是Anko
最大的亮點(diǎn)是用來(lái)寫(xiě)布局孩革,而且非常的簡(jiǎn)潔(當(dāng)然,我并沒(méi)有試過(guò)得运。為什么知道嫉戚?百度唄。)澈圈。有關(guān)Anko
的一些東西彬檀,有生之年我會(huì)寫(xiě)一篇出來(lái)。在這個(gè)項(xiàng)目中有使用到跳轉(zhuǎn)相機(jī)瞬女,看看如何實(shí)現(xiàn)的:
var uri: Uri? = null
path = Environment.getExternalStorageDirectory().absolutePath + "/${System.currentTimeMillis()}.png"
var i: Intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE,null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){
uri = FileProvider.getUriForFile(this,"com.testprojects.fileprovider",File(path))
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}else{
uri = Uri.fromFile(File(path))
}
i.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivityForResult(i, 1)
簡(jiǎn)單的解釋一下這段代碼窍帝,定義一個(gè)Uri
,用于隱式跳轉(zhuǎn)作為參數(shù)诽偷。然后定義一個(gè)路徑字符串坤学,用于存放拍完后照片存儲(chǔ)的位置(我這里是將照片放在手機(jī)內(nèi)存的根目錄,通過(guò)獲取系統(tǒng)時(shí)間作為照片的名字)报慕。然后定義一個(gè)Intent
深浮,定義動(dòng)作。接著是一個(gè)判斷眠冈,用于判斷手機(jī)版本是否是7.0飞苇。在7.0的時(shí)候訪問(wèn)文件需要用到一個(gè)FileProvider
的東西,用于臨時(shí)訪問(wèn)蜗顽,至于這東西怎么用布卡,百度一下就好了,不會(huì)難雇盖。當(dāng)時(shí)剛開(kāi)始寫(xiě)項(xiàng)目的時(shí)候忿等,我手機(jī)是5.1的。根本沒(méi)考慮什么權(quán)限什么鬼的崔挖。結(jié)果贸街,一夜之間庵寞,自動(dòng)升級(jí)到了7.0。第二天一臉懵逼的我因?yàn)闄?quán)限這些的折騰了一早上(題外話題外話)薛匪。
最后捐川,講一下RecyclerView的Adapter:
class PictureAdapter(context: Context, d:MutableList<PictureBean>, val onClick:(Int)->Unit,hide:Boolean) : RecyclerView.Adapter<PictureAdapter.ViewHolder>(){
var c:Context? = null
var data:MutableList<PictureBean>? = null
var h:Boolean? =null
init {
this.c=context
this.data=d
this.h = hide
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
holder!!.abc(position)
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val v:View = LayoutInflater.from(c).inflate(R.layout.picture_view_item,null)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return data!!.size
}
inner class ViewHolder(itemView:View) : RecyclerView.ViewHolder(itemView){
fun abc(i: Int){
if (h!!){
itemView.imgSelect.visibility = View.VISIBLE
}else{
itemView.imgSelect.visibility = View.GONE
}
if (!position.equals(itemView.item_img.getTag(R.id.item_img))){
itemView.item_img.setTag(R.id.item_img,position)
Glide.with(c)
.load(data!!.get(position).path)
.into(itemView.item_img)
}
itemView.setOnClickListener { onClick.invoke(i) }
}
}
}
同樣的,建立一個(gè)類(lèi)繼承RecyclerVIew.Adapter
蛋辈,在構(gòu)造方法里面?zhèn)魅肷舷挛氖羰埃瑪?shù)據(jù),onClick(下面講)冷溶,布爾值(偷懶復(fù)用這個(gè)adapter渐白,控制控件顯示用,不要在意)逞频。在類(lèi)里面聲明變量纯衍,在init{}
里面賦值(慣用套路)。重寫(xiě)onBindViewHolder onCreateViewHolder getItemCount
這三個(gè)方法苗胀。在onBindViewHolder
里面進(jìn)行RecyclerView
的Item
進(jìn)行操作(在這里襟诸,我在這個(gè)方法里面調(diào)用了ViewHolder里的方法,不過(guò)不重要基协,效果都一樣)歌亲,在onCreateViewHolder
里面獲取Item
的布局并返回,在getItemCount
里面返回顯示Item
的個(gè)數(shù)澜驮。然后寫(xiě)一個(gè)內(nèi)部類(lèi)繼承RecyclerView.ViewHolder
陷揪,這里注意,類(lèi)前面要加上inner
才能訪問(wèn)到外面類(lèi)的變量杂穷。在自定義的ViewHolder
里面悍缠,我在里面寫(xiě)了一個(gè)方法,供上面調(diào)用耐量,方法里面也就設(shè)置控件之類(lèi)的飞蚓。但是,有一點(diǎn)特別的:
itemView.setOnClickListener { onClick.invoke(i) }
還記得我們構(gòu)造方法里面的第三個(gè)參數(shù)嗎廊蜒?就是這里面的onClick
趴拧,傳入onBindViewHolder
方法參數(shù)里面的position
,通過(guò)這樣子的調(diào)用就實(shí)現(xiàn)了RecyclerView
的點(diǎn)擊事件劲藐。
咳咳...(就放那個(gè)賭神出場(chǎng)的那個(gè)BGM八堡,沒(méi)錯(cuò)沒(méi)錯(cuò),就那個(gè))聘芜。沒(méi)看錯(cuò),就是這么勉強(qiáng)算是兩句話的代碼就實(shí)現(xiàn)了之前還要寫(xiě)接口缝龄,還要傳來(lái)傳去才能用的點(diǎn)擊事件汰现。當(dāng)然onLongClick
也是一樣的道理挂谍,改一下itemView.setOnClickListener
就可以了。怎么用呢瞎饲?
adapter = PictureAdapter(this, l!!, {
Log("$it")
},true)
在這里口叙,傳入上下文,數(shù)據(jù)嗅战,第三個(gè)就是我們點(diǎn)擊事件要做的事情妄田,it
是我們當(dāng)前點(diǎn)擊的Item
的腳標(biāo),你可以在這里面寫(xiě)驮捍,也可以寫(xiě)一個(gè)方法丟里面疟呐,看個(gè)人了。
最后說(shuō)兩句
因?yàn)閺臎](méi)寫(xiě)過(guò)這種東西东且,上學(xué)時(shí)候作文又寫(xiě)的不好(我也很絕望捌艟摺)。所以可能看的時(shí)候會(huì)怪怪的珊泳,也就湊合著看吧鲁冯。Kotlin還有很多很多沒(méi)發(fā)現(xiàn)的用法,很多的新特性沒(méi)有挖掘出來(lái)色查,這篇文章也只是用來(lái)拋磚引玉薯演。上述內(nèi)容如果有哪里有問(wèn)題的請(qǐng)各位讀者及時(shí)指出,以免誤導(dǎo)了別人秧了,有什么看不懂的也可以問(wèn)我跨扮,知無(wú)不言。
上述項(xiàng)目github地址
代碼可能有點(diǎn)亂示惊,見(jiàn)諒好港。