极简Kotlin-For-Android(一)

安装 Kotlin 插件

Android Studio 3.+ 已经有了 Kotlin 插件,如果是更早的版本,点击 Android Studio | File | Settings | Plugins,搜索 Kotlin ,安装,重启 Android Studio .

创建工程

点击 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关键字.
2.它有一个唯一默认的构造器,大部分情况下只需使用这个默认构造器即可.
3.构造函数的函数体,写在init块{}中

class Person(name:String ,age : Int) {
    init {
    }
}

类继承

1.上帝类是Any(类似于java中的Object)
2.所有类默认是不可继承的(final),我们只能继承声明open或abstract的类

open class Animal(name: String)
class Person(name:String ,age : Int) : Animal(name) 

函数(java中的方法)

1.使用fun关键字
2.如果没有指定返回值,默认返回Unit(java中的void),当然也可以指定返回任何类型

fun add(age1:Int ,age2:Int) : Int{
    return age1+age2
}

Tips: 分号不是必须的,结尾不使用分号会节约很多时间,养成这个好习惯吧!

3.如果返回结果可以使用表达式表达出来,直接使用等号:

fun add2(age1: Int , age2: Int) : Int = age1+age2

构造方法和函数参数
1.kotlin中参数是先写名称,后写类型....(有点不适应)
2.可以给参数一个指定默认值,使其变得可选,例如下面toast函数第二个参数给了默认值,调用时候可以不传第二个值(java中重载方法的替换?)

fun toast(msg : String , length : Int = Toast.LENGTH_LONG){
    Toast.makeText(this,msg,length).show();
}

toast("打印吐司鸭")
toast("打印吐司鸭",Toast.LENGTH_SHORT)

Tips:
String模板内插*:

val name = "susan"
println("name : $name")
输出结果: name : susan

编写你的第一个类

我们在MainActivity的布局文件中加入RecyclerView,然后设置好LayoutManager:

val recycler = findViewById(R.id.recycler) as RecyclerView
recycler.layoutManager = LinearLayoutManager(this)

如上代码,LayoutManager会通过属性设置,而不是通过set方法.
对象实例化也去掉了new关键字,构造函数仍然会被调用.
接着设置Adapter:

class ForecastListAdapter(val items : List<String>) : RecyclerView.Adapter<ForecastListAdapter.ViewHolder>(){
    //绑定数据
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.textView.text = items[position]
    }
    //返回list count
    override fun getItemCount(): Int {
        return items.size
    }
    //创建viewHolder
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(TextView(parent.context))
    }
    class ViewHolder(val textView : TextView) : RecyclerView.ViewHolder(textView)
}

回到MainActivity,现在我们将少量数据放入recyclerview中:

    //定义天气数组数组
    val weaList= listOf<String>(
            "北京 -0 -  微风",
            "北京 -10 - 微风",
            "北京 -13 - 微风",
            "北京 -20 - 微风",
            "北京 -5 -  微风"
            )
recycler.adapter = ForecastListAdapter(weaList)

关于List的创建:
可以通过一个函数listOf创建一个常亮list,它接收任何类型的vararg(可变长度参数).还有很多其他函数:setOf,arrayListOf hashSetOf, etc...

运行你的项目吧:

image

接下来,必须要学习一些基本类型,变量,属性等才能继续.

变量和属性
在kotlin中,一切都是对象~

基本类型
integer float boolean等类型依然存在,不过是以对象存在.它们的工作方式与java十分相似,需要注意以下几点:

数字类型不会自动转型.例如不能给Double分配int型值

    val i: Int = 7
    val d: Double = i.toDouble()

char不能直接作为一个数字来处理,需要转换成数字

    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 可以像数组那样访问,并且被迭代

val s = "test"
val t = s[2]//一个字符's'
//迭代
val o = "test"
for (b in o){
    print(t)
}

变量
变量可简单定义为 : val(不可变)和var(可变).但是不可变在kotlin是一个很重要的概念.

一个不可变对象意味着它在实例化之后就不能再去改变它的状态了。如果你需要一个这个对象修改之后的版本,那就会再创建一个新的对象。这个让编程更加具有健壮性和预估性。

在Java中,大部分的对象是可变的,那就意味着任何可以访问它这个对象的代码都可以去修改它,从而影响整个程序的其它地方。不可变对象也可以说是线程安全的,因为它们无法去改变,也不需要去定义访问控制,因为所有线程访问到的对象都是同一个

一个重要的概念是:尽可能地使用val。除了个别情况(特别是在Android中,有很多类我们是不会去直接调用构造函数的),大多数时候是可以的。

如果我们需要使用更多的范型类型,则需要指定:

val a: Any = 23
val c: Context = activity

属性
没有任何指定,属性会默认使用getter和setter.

class Dog {
    val color : Int = 0
}

当然也可以自定义set,get.

var color : Int = 3
    get() = field.toBigDecimal().intValueExact()
    set(value) {
        field = 3 + value
    }

Anko来了

  • 主要目的是用来替换之前XMl的方式来使用代码生成UI布局
  • Anko还包含了许多有帮助的函数和属性来避免写很多代码
  • 了解Anko的实现方式对学习kotlin有很大帮组

开始使用Anko
刚使用findviewbyid的可以用fins替换

    val recycler : RecyclerView  = find(R.id.recycler) 

Anko还有一些别的实用功能:
实例化Intent,Activity之间的跳转,Fragment的创建,数据库的访问,Alert的创建......。

扩展函数

扩展函数数是指在一个类上增加一种新的行为,甚至我们没有这个类代码的访问权限。这是一个在缺少有用函数的类上扩展的方法。

在Java中,通常会实现很多带有static方法的工具类。Kotlin中扩展函数的一个优势是我们不需要在调用方法的时候把整个对象当作参数传入。扩展函数表现得就像是属于这个类的一样,而且我们可以使用this关键字和调用所有public方法。

举个例子,我们可以创建一个toast函数,这个函数不需要传入任何context,它可以被任何Context或者它的子类调用,比如Activity或者Service:

fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {    
    Toast.makeText(this, message, duration).show()
}

这个方法可以在Activity内部直接调用:

toast("Hello world!")
toast("Hello world!", Toast.LENGTH_LONG)

扩展函数并不是真正地修改了原来的类,它是以静态导入的方式来实现的。扩展函数可以被声明在任何文件中,因此有个通用的实践是把一系列有关的函数放在一个新建的文件里。

执行一个请求
如果只是执行一个简单的api请求,我们可以不用任何非三方库实现

class Request(val url : String) {
    fun run(){
        val jsonStr = URL(url).readText()
        Log.d("result",jsonStr)
    }
}

众所周知,在子线程中是不允许进行网络请求的,java的AsyncTask是非常丑陋的...diss一波...

Anko提供了非常简单的DSL来处理异步任务,它满足大部分的需求。它提供了一个基本的async函数用于在其它线程执行代码,也可以选择通过调用uiThread的方式回到主线程。在子线程中执行请求如下这么简单:

val url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22"
        doAsync() {
            Request(url).run()
            uiThread { toast("Request") }
        }

此时运行项目,查看log,可以发现一个简单请求已经实现了!
接着我们要把他使用json解析,转换成实体类.

数据类
数据类是一种非常强大的类,它可以让你避免创建Java中的用于保存状态但又操作非常简单的POJO的模版代码。它们通常只提供了用于访问它们属性的简单的getter和setter。定义一个新的数据类非常简单:

data class Forecast(val date : Date , val temp : Float ,
                    val details : String) 

转换json到数据类

关于伴生对象Companion objects :
Kotlin允许我们去定义一些行为与静态对象一样的对象。尽管这些对象可以用众所周知的模式来实现,比如容易实现的单例模式。我们需要一个类里面有一些静态的属性、常量或者函数,我们可以使用companion object。这个对象被这个类的所有对象所共享,就像Java中的静态属性或者方法。

修改一下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)
    }
}

数据类:

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
我们都是做梦的梦想家

推荐阅读更多精彩内容