Kotlin
,它是JetBrains
開發的基於JVM的面向對象的語言。2017年的時候被Google
推薦Android
的官方語言,同時Android studio 3.0
正式支持這門語言,在這個編譯器上建立一個Kotlin
項目,很是方便,甚至能夠Java
轉爲Kotlin
。html
View
過程當中,說一下Kotlin
與Java
的異同,其實二者很是類似對Kotlin
語法不是太瞭解的,能夠先去看看它的官方翻譯文檔java
Barchart-Kotlin是我用Kotlin
寫的一個簡易靈活的柱狀圖庫,喜歡的能夠點個star!android
在一個類裏面咱們須要定義一些屬性來保存數據和狀態git
咱們先來看看Java
代碼,在BarChartView
定義了一些屬性github
private SpeedLinearLayoutManger mLayoutManager;
private BarChartAdapter mAdapter;
private ItemOnClickListener mClickListener;
private int mDefaultWidth = 150;
複製代碼
而後咱們再看看Kotlin
是怎麼定義這些屬性的,下面的是Kotlin
代碼後端
private lateinit var mLayoutManager: SpeedLinearLayoutManger
private lateinit var mAdapter: BarChartAdapter
private var mClickListener: ItemOnClickListener? = null
private val mDefaultWidth = 150
複製代碼
你會發現不同的聲明方式,但重要的是var
和val
這兩個關鍵字api
var
表明的是可變的變量,至關於如今Java
聲明變量的方式安全
val
表明的是不可變的變量,初始化後不能再修改,至關於加了final
關鍵字的變量數據結構
並且在Kotlin
中屬性是須要初始化的,沒有值的時候你能夠賦值null
,否則編譯會報錯。加上?
的意思是你不肯定是不是這個類型,或者說是否爲null
。若是以爲實在是不方便你的使用邏輯,你可使用這兩種方式延遲初始化。app
lazy
是指推遲一個變量的初始化時機,只有在使用的時候纔會去實例化它。適用於一個變量直到使用時才須要被初始化。在我這個項目裏面沒有使用by lazy
,它大體的用法是這樣的
val data: Data by lazy {
Data(number,string)
}
複製代碼
lateinit
是指你保證接下來在使用以前或者使用的時候會實例化它,否則你就會crash掉,這不就跟咱們使用Java
屬性的方式同樣麼。。。它適用於一些view
和必須用到數據結構的初始化,我以爲仍是謹慎使用比較好。
Kotlin
能夠說是分了兩個大類型,可空類型和不可空類型,這樣作的緣由是它但願在編譯階段就把空指針這問題顯式的檢測出來,把問題留在了編譯階段,讓程序更加健壯。它經過?
來表達可爲空。
mClickListener?.invoke1(position)
mClickListener?.invoke2(position)
mClickListener?.invoke3(position)
複製代碼
若是mClickListener
爲null
的話,後面的語句是不會執行的。並且Kotlin
提供了更加簡潔的操做符let
mListener?.let {
it.invoke1(position)
it.invoke2(position)
it.invoke3(position)
}
複製代碼
只有在非空的狀況下才執行let
裏面的操做,很是簡潔。
經過簡單瞭解以後,咱們開始寫一個自定義View,咱們須要繼承View
,Java
的實現方式是這樣的
public class BarChart extends View {
public BarChart(Context context) {
super(context);
}
public BarChart(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BarChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
複製代碼
用Kotlin
你能夠實現的更簡潔
class BarChart @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: View(context, attrs, defStyleAttr) {
private val mContext: Context = context
init { }
複製代碼
你能夠在init
代碼塊裏面得到構造函數的傳參,固然你也能夠直接在聲明屬性的時候得到,@JvmOverloads
若是你沒有加上這個註解,它只能重載相匹配的的構造函數,而不是所有。
並且可能你也發現了,你能夠在傳參裏面初始化,這相對於Java
來講,靈活太多
fun shadow(width:Int=100,height:Int = 180){ }
//你能夠這麼使用
shadow()
shadow(140)
shadow(140,200)
複製代碼
通常用咱們創造view
的佈局是xml
,Kotlin
也是支持的,可是它更推薦你使用Anko
的佈局方式。
那是什麼是Anko
呢,Anko
是JetBrains
開發的一個強大的庫,它主要的目的是用來替代之前xml
的方式來使用代碼生成UI佈局的,它包含了不少的很是有幫助的函數和屬性來避免讓你寫不少的模版代碼。
有興趣的你能夠去看看它的源碼與更多使用方式 -- Anko
首先你要在Gradle
裏添加Anko
的引用
// Anko Layouts
compile "org.jetbrains.anko:anko-recyclerview-v7:$anko_version"
compile "org.jetbrains.anko:anko-recyclerview-v7-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-sdk25:$anko_version"
compile "org.jetbrains.anko:anko-appcompat-v7:$anko_version"
// Coroutine listeners for Anko Layouts
compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
複製代碼
而後你能夠在代碼裏寫UI佈局的代碼了,就是用Kotlin
代碼替代xml
private fun createView(attrs: AttributeSet? = null, defStyleAttr: Int = 0) {
var height = dip(150)
attrs?.let {
val typeArray = mContext.obtainStyledAttributes(it, R.styleable.BarChartView, defStyleAttr, 0)
height = typeArray.getDimension(R.styleable.BarChartView_chart_height, dip(150).toFloat()).toInt()
typeArray.recycle()
}
verticalLayout {
lparams(width = matchParent, height = matchParent)
frameLayout {
lparams(width = matchParent, height = wrapContent)
mLineView = view {
backgroundColor = R.color.gray_light
}.lparams(width = matchParent, height = dip(0.5f)) {
gravity = Gravity.BOTTOM
bottomMargin = dip(9)
}
mBarView = recyclerView {
lparams(width = matchParent, height = height)
}
}
mDateView = relativeLayout {
lparams(width = matchParent, height = wrapContent) {
leftPadding = dip(10)
rightPadding = dip(10)
}
mLeftTv = textView {
textSize = 15f
}
mRightTv = textView {
textSize = 15f
}.lparams(width = wrapContent, height = wrapContent) {
alignParentRight()
}
}
}
}
複製代碼
能夠從代碼裏看見verticalLayout
(其實就是LinearLayout
的vertical
模式)包裹了frameLayout
和relativeLayout
,裏面又有各自的子view
,並且你會發現xml
有的屬性這裏也有,調用起來很是的簡潔明瞭,熟練xml
的來寫這個,我以爲上手應該會很快,它的缺點就是沒有預覽效果,以及實現複雜的view結構的時候會比較繁瑣,考驗盲寫的功力了。。。。
擴展函數是Kotlin很是方便實用的一個功能,它可讓咱們隨意的擴展SDK的庫,你若是以爲SDK的api不夠用,這個時候你能夠用擴展函數徹底去自定義。
例如你須要這樣來獲取顏色,每次你都須要一個上下文context
mColor = ContextCompat.getColor(mContext, R.color.primary)
複製代碼
那你能夠經過擴展Context
這個SDK的類來實現更方便的使用
fun Context.color(colorRes: Int) = ContextCompat.getColor(this, colorRes)
fun View.color(colorRes: Int) = context.color(colorRes)
複製代碼
並且爲了更方便在View
裏面使用,又擴展了View
。在第一個方法裏面能夠發現getColor
所須要的this
上下文,就是Context
。一樣,View
裏面的context
是getContext()
所獲得,也就是View
裏面自己具備的公有方法。
這實際上是很驚豔的功能
那咱們能夠想想爲啥能夠這樣作,咱們知道的是Kotlin
和Java
都是在Jvm
上運行的,既然都是編譯成class
字節碼,那咱們是否是能夠經過字節碼來了解一些事情。
經過Android studio 3.0
上的Tools
的工具Show Kotlin Bytecode
,能夠將剛纔的擴展函數的代碼編譯成字節碼
// access flags 0x19
public final static color(Landroid/content/Context;I)I
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 12 L1
ALOAD 0
ILOAD 1
INVOKESTATIC android/support/v4/content/ContextCompat.getColor (Landroid/content/Context;I)I
IRETURN
L2
LOCALVARIABLE $receiver Landroid/content/Context; L0 L2 0
LOCALVARIABLE colorRes I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x19
public final static color(Landroid/view/View;I)I
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 14 L1
ALOAD 0
INVOKEVIRTUAL android/view/View.getContext ()Landroid/content/Context;
DUP
LDC "context"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkExpressionValueIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
ILOAD 1
INVOKESTATIC shadow/barchart/ExtensionsKt.color (Landroid/content/Context;I)I
IRETURN
L2
LOCALVARIABLE $receiver Landroid/view/View; L0 L2 0
LOCALVARIABLE colorRes I L0 L2 1
MAXSTACK = 3
MAXLOCALS = 2
複製代碼
這就是編譯後的字節碼,看不懂是吧,我也看不懂。。可是咱們能夠反編譯啊,生成Java
代碼
public static final int color(@NotNull Context $receiver, int colorRes) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
return ContextCompat.getColor($receiver, colorRes);
}
public static final int color(@NotNull View $receiver, int colorRes) {
Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
Context var10000 = $receiver.getContext();
Intrinsics.checkExpressionValueIsNotNull(var10000, "context");
return color(var10000, colorRes);
}
複製代碼
經過反編譯咱們能夠知道這是個靜態函數,Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
這個函數只起到了判空的做用,真正的代碼是return ContextCompat.getColor($receiver, colorRes);
這個不就是咱們剛剛用的Java
代碼嘛。
重點是$receiver
接收的對象,接收的是Context
實例,這樣的話就能夠調用這個類的全部公有方法和公有屬性,並且它是靜態函數,它能夠經過類直接調用。因此擴展函數的實現只不過是加了一個須要當前對象的靜態方法,調用的時候傳入一個當前對象而已。
咱們剛剛用到了反編譯,由於咱們知道Kotlin
和Java
的生成字節碼是同樣的,那咱們能夠了解一下Kotlin
的編譯過程,它跟Java
的區別是什麼。能夠看一下這篇文章Kotlin編譯過程分析
經過這篇文章你能夠了解到Kotlin
在編譯過程當中,與Java
是大體相同的,只是在最後生成目標代碼的時候作了不少相似於封裝的事情,生成相同的語法結構,Kotlin
將咱們原本在代碼層作的一些封裝工做轉移到了編譯後端階段。那咱們可不能夠在學習Kotlin
的時候去這樣理解,其實Kotlin
是一種封裝了Java
的強大的語法糖,Java
作不到的事情,Kotlin
其實也作不到,例如對象只能訪問公有屬性。
在Kotlin
中你要實現數據類是很是簡單的,並不須要手動加上get/set
方法
data class BarItem(
private val barData: BarData,
var select: Boolean = false) {
fun getData(): Double {
return barData.getData()
}
fun getTag(): String {
return barData.getTag()
}
}
複製代碼
在這個類裏面你會發現,我還聲明瞭兩個方法,我須要的是BarData
裏的數據,但又不只僅只須要這個數據,因此我聲明瞭一個類來封裝它,其實這個至關於裝飾者模式了。Kotlin
有更好的方式實現這個模式
data class BarItem(
private val barData: BarData,
var select: Boolean = false) : BarData by barData
複製代碼
在BarChartView
裏用到一個與switch
語法相似的語句
mSelectPosition = when (mStyle) {
ScrollStyle.DEFAULT -> mDataList.size - 1
ScrollStyle.START -> 0
ScrollStyle.NONE -> -1
ScrollStyle.CUSTOM -> mSelectPosition
else -> { }
}
複製代碼
它是起到了跟switch
同樣的做用,而且更強大,由於它是表達式,因此是有返回值的,在Kotlin
中控制流大都是表達式,都是能夠有返回值的。
Kotlin
是區分可變集合和不可變集合的,它給你提供這兩種選擇。
//不可變
Set<out T>
Map<K, out V>
List<out T>
//可變
MutableSet<T>
MutableMap<K, V>
MutableList<T>
複製代碼
不可變的集合提供只讀屬性,例如size
,get
等,Kotlin
不提供專門的語法結構建立list
或者set
,是用標準庫獲取的,咱們能夠看一下它的源碼是怎樣實現。
/** * Returns an immutable list containing only the specified object [element]. * The returned list is serializable. * @sample samples.collections.Collections.Lists.singletonReadOnlyList */
@JvmVersion
public fun <T> listOf(element: T): List<T> = java.util.Collections.singletonList(element)
/** * Returns an empty new [MutableList]. * @sample samples.collections.Collections.Lists.emptyMutableList */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
複製代碼
從源碼能夠看見這是Java
的java.util.Collections.singletonList
和ArrayList
,這就能夠理解爲啥不可變和可變的了。。。
Kotlin
相對於Java
,更像是封裝了Java
的強大語法糖,使用了更簡潔的語法提升了生產力。