Kotlin for Android (二)

Anko

  • Anko Commons:一个轻量级的库,里面包含了intents,对话框,日志等帮助类
  • Anko Layouts:用于编写动态Android布局的快速且类型安全的方法
  • Anko SQLite:查询适用于Android SQLite的DSL和分析器集合
  • Anko Coroutines:基于kotlinx.coroutines库的实用程序
  • 简化获取RecyclerView
    1
    val forecastList: RecyclerView = find(R.id.recyclerView)

扩展函数

扩展函数数是指在一个类上增加一种新的行为,甚至我们没有这个类代码的访问权
限。这是一个在缺少有用函数的类上扩展的方法。在Java中,通常会实现很多带有
static方法的工具类。Kotlin中扩展函数的一个优势是我们不需要在调用方法的时候
把整个对象当作参数传入。扩展函数表现得就像是属于这个类的一样,而且我们可
以使用 this 关键字和调用所有public方法

1
2
3
fun Context.toastF(message: CharSequence,duration: Int=Toast.LENGTH_SHORT){
Toast.makeText(this, "$message", duration).show()
}

  • Anko已经扩展toast函数,提供了CharSequence,(resource id)Int的函数

    1
    2
    toast(R.string.app_name)
    longToast("longToast")
  • 扩展函数并不是真正地修改了原来的类,它是以静态导入的方式来实现的。扩展函数可以被声明在任何文件中,通用的实践是把一系列有关的函数放在一个新建的文件里

    1
    2
    3
    var TextView.text: CharSequence
    get() = getText()
    set(v) = setText(v)

网络请求

  • 网络请求类

    1
    2
    3
    4
    5
    6
    7
    public class Request(var url: String) {
    public fun run() {
    //这个方法不推荐
    val forcastJson = URL(url).readText()
    Log.e(javaClass.simpleName, forcastJson)
    }
    }
  • 执行异步请求,async用于其他线程执行请求,uiThread回到主线程,UIThread 是可以依赖于调用者如果它是被一个Activity 调用的,那么如果 activity.isFinishing() 返回true,则 uiThread 不会执行,在Activity销毁的时候避免程序崩溃

    1
    2
    3
    4
    5
    6
    doAsync {
    val url="http://samples.openweathermap.org/data/2.5/weather?id=2172797&appid=b6907d289e10d714a6e88b30761fae22"
    Request(url).run()

    uiThread { longToast("Request Performed") }
    }
  • 数据类,属性访问,以及其他函数

    • equals(): 它可以比较两个对象的属性来确保他们是相同的
    • hashCode(): 我们可以得到一个hash值,也是从属性中计算出来的
    • copy(): 你可以拷贝一个对象,可以根据你的需要去修改里面的属性
    • 一系列可以映射对象到变量中的函数
      1
      2
      data class Forecast(val date: Date, val temperature: Float, val
      details: String)
  • 复制一个数据类,修改某个属性

    1
    2
    3
    4
    5
    6
    7
    val f1 = Forecast(Date(), 23f, "Sunny")
    Log.e(tag, f1.toString())
    val f2 = f1.copy(temperature = 30f)
    Log.e(tag, f2.toString())

    E/MainActivity: Forecast(date=Fri Mar 23 00:22:42 EDT 2018, temperature=23.0, details=Sunny)
    E/MainActivity: Forecast(date=Fri Mar 23 00:22:42 EDT 2018, temperature=30.0, details=Sunny)
  • 映射对象到变量中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //映射对象到变量中,多声明
    val (date, temp, details) = f1
    Log.e(tag, "date " + date.toString())
    Log.e(tag, "temp " + temp.toString())
    Log.e(tag, "details $details")
    // 上面的多声明会编译成下面的代码
    // val date = f1.component1()
    // val temperature = f1.component2()
    // val details = f1.component3()

    E/MainActivity: date Fri Mar 23 00:33:41 EDT 2018
    E/MainActivity: temp 23.0
    E/MainActivity: details Sunny
  • Map 类含有一些扩展函数的实现,允许它在迭代时使用key和value

    1
    2
    3
    4
    5
    6
    7
    val map = mutableListOf(f1,f2)
    for ((key, value) in map) {
    Log.d("map", "key:$key, value:$value")
    }

    E/map: key:Fri Mar 23 00:41:25 EDT 2018, value:23.0
    E/map: key:Fri Mar 23 00:41:25 EDT 2018, value:30.0
  • 转换josn到数据类,这里有个插件JsonToKotlinClass,转换json to kotlin data class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48

    data class ForecastResult(
    val city: City,
    val cod: String,
    val message: Double,
    val cnt: Int,
    val list: List<Item8>
    )

    data class City(
    val id: Int,
    val name: String,
    val coord: Coord,
    val country: String,
    val population: Int
    )

    data class Coord(
    val lon: Double,
    val lat: Double
    )

    data class Item8(
    val dt: Int,
    val temp: Temp,
    val pressure: Double,
    val humidity: Int,
    val weather: List<Weather>,
    val speed: Double,
    val deg: Int,
    val clouds: Int
    )

    data class Temp(
    val day: Double,
    val min: Double,
    val max: Double,
    val night: Double,
    val eve: Double,
    val morn: Double
    )

    data class Weather(
    val id: Int,
    val main: String,
    val description: String,
    val icon: String
    )
  • Companion objects (伴随对象)Kotlin允许我们去定义一些行为与静态对象一样的对象。尽管这些对象可以用众所周知的模式来实现,比如容易实现的单例模式。我们需要一个类里面有一些静态的属性、常量或者函数,我们可以使用 companion objecvt 。这个对象被这个类的所有对象所共享,就像Java中的静态属性或者方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Request(var 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&zip="
    }

    public fun execute(): ForecastResult {
    //这个方法不推荐
    val forecastJson = URL(COMPLETE_URL + zipCode).readText()
    Log.e(javaClass.simpleName, forecastJson)
    return Gson().fromJson(forecastJson, ForecastResult::class.java)
    }
    }

    val result = Request(94040).execute()
    Log.e(tag, "result " + result.toString())

构建domain层

  • 定义一个commnad,执行一个任务,返回泛型,一起Kotlin函数都有返回值,没有指定默认返回Unit,所以不返回数据可以指定类型会Unit

    1
    2
    3
    public interface Command<T> {
    fun execute(): T
    }
  • command需要去请求天气预报接口然后转换结果为domain类

    1
    2
    3
    4
    5
    6
    7
    public interface Command<T> {
    data class ForecastList(val city: String, val country: String,
    val dailyForecast: List<Forecast>)

    data class Forecast(val date: String, val description: String,
    val high: Int, val low: Int)
    }
  • 数据映射到Domain,创建DataMapper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class ForecastDataMapper {
    fun convertFromDataModel(forecast: ForecastResult): Forecast
    List {
    return ForecastList(forecast.city.name, forecast.city.co
    untry,
    convertForecastListToDomain(forecast.list))
    private fun convertForecastListToDomain(list: List<Forecast>)
    :
    List<ModelForecast> {
    return list.map { convertForecastItemToDomain(it) }
    }
    private fun convertForecastItemToDomain(forecast: Forecast):
    ModelForecast {
    return ModelForecast(convertDate(forecast.dt),
    forecast.weather[0].description, forecast.temp.m
    ax.toInt(),
    forecast.temp.min.toInt())
    }
    private fun convertDate(date: Long): String {
    val df = DateFormat.getDateInstance(DateFormat.MEDIUM, L
    ocale.getDefault())
    return df.format(date * 1000)
    }
    }
  • 当我们使用了两个相同名字的类,我们可以给其中一个指定一个别名,这样我们就不需要写完整的包名了:import com.willkernel.app.kotlindemo.model.Forecast as ModelForecast,从一个forecast list中转换为domain model return list.map { convertForecastItemToDomain(it) }循环这个集合并且返回一个转换后的新的List

    • ForecastMapper

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
       public class ForecastDataMapper {
      fun convertFromDataModel(forecast: ForecastResult): ForecastList {
      return ForecastList(forecast.city.name, forecast.city.country,
      convertForecastListToDomain(forecast.list))
      }

      private fun convertForecastListToDomain(list: List<Forecast>)
      : List<ModelForecast> {
      return list.map { convertForecastItemToDomain(it) }
      }

      private fun convertForecastItemToDomain(forecast: Forecast):
      ModelForecast {
      return ModelForecast(convertDate(forecast.dt),
      forecast.weather[0].description, forecast.temp.max.toInt(),
      forecast.temp.min.toInt())
      }

      private fun convertDate(date: Long): String {
      val df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
      return df.format(date * 1000)
      }
      }
    • 编写网络请求命令

      1
      2
      3
      4
      5
      6
      class RequestForecastCommand(val zipCode: String) : Command<ForecastList> {
      override fun execute(): ForecastList {
      val forecastRequest = ForecastRequest(zipCode)
      return ForecastDataMapper().convertFromDataModel(forecastRequest.execute())
      }
      }
  • 修改Adapter,设置数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class ForecastListAdapter(val items: ForecastList) : RecyclerView.Adapter<ForecastListAdapter.VH>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
    return VH(TextView(parent.context))
    }

    override fun getItemCount(): Int = items.size


    override fun onBindViewHolder(holder: VH, position: Int) {
    with(items.dailyForecast[position]) {
    holder.textView.text = "$date - $description - $high - $low"
    }
    }

    class VH(val textView: TextView) : RecyclerView.ViewHolder(textView)

    }

    doAsync {
    val result = RequestForecastCommand("94043").execute()
    Log.e(tag, "result " + result.toString())
    uiThread { recyclerView.adapter = ForecastListAdapter(result)
    }
    }
  • with 函数

    with是一个非常有用的函数,它包含在Kotlin的标准库中。它接收一个对象
    和一个扩展函数作为它的参数,然后使这个对象扩展这个函数。这表示所
    有我们在括号中编写的代码都是作为对象(第一个参数)的一个扩展函
    数,我们可以就像作为this一样使用所有它的public方法和属性。当我们针
    对同一个对象做很多操作的时候这个非常有利于简化代码

willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!