-
kotlin-android-extensions 一个强大的插件,替代 findViewById
-
apply plugin: 'kotlin-android-extensions'
-
给 View 声明 id
-
使用
import kotlinx.android.synthetic.main.activity_main.* // 一个 id 是 hello 的 TextView hello.text = "Hi!" // adapter、自定义View 都可以使用 mView.hello = "Hi"
-
-
函数默认参数,完美替代了 Java 的重载函数
fun toast(message:String,length:Int = Toast.LENGTH_SHORT) { Toast.makeText(this,message,length).show() } // 调用 toast("Kotlin") toast("Kotlin",Toast.LENGTH_LONG)
-
String 可以像数组那样访问和迭代
val s = "Example" val c = s[2] // 这是一个字符'a' // 迭代String val s = "Example" for(c in s){ print(c) }
-
Kotlin 中访问属性会访问默认的 setter、getter
// 使用自己的 getter、setter public classs Person { var name: String = "" // 注意:这里我们使用 field 访问 name // 如果直接使用 name 会调用 getter,最后爆栈 // field 只能在属性访问器内访问 get() = field.toUpperCase() set(value){ field = "Name: $value" } }
-
Anko
Anko 是一个由 JetBrains 开发的一个用于快速开发 Android 的 Kotlin 库,目前包括以下几部分:
- Anko Commons: a lightweight library full of helpers for intents, dialogs, logging and so on;
- Anko Layouts: a fast and type-safe way to write dynamic Android layouts;
- Anko SQLite: a query DSL and parser collection for Android SQLite;
- Anko Coroutines: utilities based on the kotlinx.coroutines library.
-
Kotlin 的网络请求
val json = URL(url).readText() // Kotlin 给 URL 实现了一个扩展函数 readText // 创建 URL、使用 HttpURLConnection 请求网络、将 InputStream 转换为 String // 想想 Java 的代码,蠢哭了 doAsync { // 子线程 val json = URL(url).readText() uiThread { // UI 线程 toast("After request") } } // 注意:doAsync、uiThread、toast 都是 Anko 封装的
-
一切 Kotlin 函数都会返回一个值,如果没有指定返回 Unit。
-
DTO (服务器获取的数据) -> Mapper -> VO (视图显示的数据)
-
with
data class Person(var name:String,var age:Int) { fun eat() { MLog.i("Person is eating") } } val p = Person("lxm",24) with(p) { // with 的作用域内可以直接使用 Persion 的属性和方法 MLog.i("name = $name, age = $age") // name = lxm, age = 24 eat() // Person is eating // 可以直接使用 this 代替 p }
-
操作符重载
- 一些操作符会和一些函数一一对应,我们重载这个函数,可以改变其对应操作的符的作用
- 重载操作符使用关键字 operator
- 操作符和函数对应表
举一个例子:
// [] 和 get 对应 // list[i] 实际上是调用 list.get(i) // 我们重载 get(i) 这个方法,就可以使用 list[i] 按照我们自己的需求起作用 data class ForecastList(val city: String, val country: String, val dailyForecast:List<Forecast>) { // 重载 get 方法,可以直接使用 ForecastList[i] 了 operator fun get(position:Int):Forecast = dailyForecast[position] fun size():Int = dailyForecast.size }
再来一个例子:
// invoke 对应 () // 重载操作符,itemClick(forecast) 就调用了 invoke 这个方法 interface OnItemClickListener { operator fun invoke(forecast: Forecast) }
-
扩展函数中的操作符重载
// 为 ViewGroup 扩展一个方法,重载 get operator fun ViewGroup.get(position: Int): View = getChildAt(position) // 使用 [] 代替 get val container: ViewGroup = find(R.id.container) val view = container[2]
-
扩展属性
// 给 View 添加一个扩展属性 // ctx 返回 context val View.ctx:Context get() = context // 写一个 ViewExtensions.kt 完全不用 UiUtils 了
-
创建一个匿名内部类
// 写一个接口 interface OnItemClickListener { fun onClick(forecast: Forecast) } // 创建这个接口的匿名内部类 object : OnItemClickListener { override fun onClick(forecast: Forecast) { toast(forecast.date) }
-
Lambdas 的演化
// 1. 定义一个函数式接口(只有一个方法的接口) public interface OnClickListener { void onClick(View v); } // 2. Java 正常使用 view.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show(); } }) // 3. Java 使用 Lambdas view.setOnClickListener(v -> { Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show(); }) // 4. Kotlin 正常使用 view.setOnClickListener(object : OnClickListener { override fun onClick(v: View) { toast("Click") } }) // 5. 在 Kotlin 中,函数式接口可以直接定义成一个 lambda // 函数名 listener 参数 View 返回值 Unit // lambda 表达式通过参数的形式被定义在箭头的左边(用圆括号包裹),然后在箭头的右边返回结果值。 fun setOnClickListener(listener: (View) -> Unit) // 6. 使用 Lambdas view.setOnClickListener({ view -> toast("Click")}) // 整个 lambda 表达式需要放在 {} 中 // 在这个例子中,我们接收一个View,然后返回一个Unit(没有东西) // 7. 左边参数没有用到,可以省略 view.setOnClickListener({ toast("Click") }) // 8. 如果 Lambdas 在是最后一个参数,可以移到 () 外面 view.setOnClickListener() { toast("Click") } // 9. 如果只有 Lambdas 一个参数,可以省略 () view.setOnClickListener { toast("Click") }
-
举一个🌰来看下 lambda 演化,给 RecyclerView 添加 Item 点击事件
// 1. 在 Adapter 中声明一个 OnItemClickListener 回调接口 lateinit var itemClickListener: OnItemClickListener interface OnItemClickListener { fun onItemClick(bean: WeatherBean) } fun setOnItemClickListener(itemClickListener: OnItemClickListener) { this.itemClickListener = itemClickListener; } // 2. 给 itemView 设置点击事件 itemView.setOnClickListener(View.OnClickListener { itemClickListener.onItemClick(bean) }) // 3. 使用 adapter.setOnItemClickListener(object :OnItemClickListener { override fun onItemClick(bean: WeatherBean) { toast(bean.date) } }) // 4. 修改 2 为 lambda 形式 itemView.setOnClickListener { onItemClickListener.onItemClick(bean) } // 5. 在 OnItemClickListener 中使用重载的 invoke 代替 onItemClick interface OnItemClickListener { operator fun invoke(bean: WeatherBean) } // 6. 再次修改 2 itemView.setOnClickListener { onItemClickListener(bean) } // 7. 删除 OnItemClickListener 接口,将 itemClickListener 直接声明为 lambda 形式 lateinit var itemClickListener: (WeatherBean) -> Unit // 我们可以直接将一个函数式接口声明成 lambda 形式 // WeatherBean 是我们函数式接口的参数、Unit 是函数式接口的返回值 // 这个函数式接口的方法为 fun invoke(bean : WeatherBean) : Unit // 调用直接 itemClickListener.invoke(bean) 可以简化为 itemClickListener(bean) // 8. 使用 adapter.setOnItemClickListener { toast(it.date) }
-
完整的 Adapter
class WeatherAdapter(val datas: CityBean) : RecyclerView.Adapter<WeatherAdapter.ViewHolder>() { // onItemClickListener 是一个 lambda // 这个 lambda 的作用是 [操作一个 WeatherBean,返回 Unit] // 调用方法 onItemClickListener(weatherBean) lateinit var itemClickListener: (WeatherBean) -> Unit override fun getItemCount() = datas.size() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.ctx) .inflate(R.layout.item_forecast, parent, false) return ViewHolder(view, itemClickListener) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bindForecast(datas[position]) } class ViewHolder(itemView: View, val onItemClickListener: (WeatherBean) -> Unit) : RecyclerView.ViewHolder(itemView) { fun bindForecast(weather: WeatherBean) { with(weather) { Picasso.with(itemView.ctx).load(iconUrl).into(itemView.icon) itemView.date.text = date itemView.description.text = description itemView.maxTemperature.text = high.toString() itemView.minTemperature.text = low.toString() itemView.setOnClickListener { onItemClickListener.invoke(this) } } } } fun setOnItemClickListener(itemClickListener: (WeatherBean) -> Unit) { this.itemClickListener = itemClickListener } } // 在 Activity 中使用 val adapter = WeatherAdapter(result) rvWeather.adapter = adapter adapter.setOnItemClickListener { toast(it.date) }
-
Lamdbas 的另一种使用方法
// 参数声明一个 lamdba,函数名:code,参数:无,返回值:Unit fun supportsLollipop(code: () -> Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { code() } } // 使用 supportsLollipop { toast("supportsLollipop") }
-
可见性修饰符
-
private:如果定义在了文件中,只对被定义的文件可见;如果被定义在了类、接口中,只对这个类、接口可见。
-
protected:只能用来修饰类或接口的成员 ,可以被自己和子类可见。
-
internal:moudle 内可见。
-
public:默认的修饰符,随处可见。
-
委托属性
class Example { // 使用 by 给我们的属性指定一个委托对象 // 当我们使用属性的 get 或者 set 的时候,委托对象的 getValue 和 setValue 就会被调用 var p: String by Delegate() }
-
标准委托(还未用到,先占个坑)
-
map
// 构造函数传一个 map,map[_id] 会赋值给 _id,map[city] 会赋值给 city class CityForecast(val map: MutableMap<String, Any?>, val dailyForecast: List<DayForecast>) { var _id: Long by map // 以 _id 为 key 和 map 映射 var city: String by map var country: String by map // "id","city","country" 会作为 key 将只值保存到 HashMap() 中 constructor(id: Long, city: String, country: String, dailyForecast: List<DayForecast>) : this(HashMap(), dailyForecast) { this._id = id this.city = city this.country = country } }
-
-
自定义委托属性
// 继承 ReadWriteProperty // T 委托属性的类型 // thisRef 委托属性所在类的引用 // property 属性的元数据 class NotNullSingleValueVar<T> : ReadWriteProperty<Any?, T> { // 默认为 null private var value: T? = null override fun getValue(thisRef: Any?, property: KProperty<*>): T { // value 为 null 抛出异常 return value ?: throw IllegalStateException("${property.name} " + "not initialized") } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { // 只有 value 是 null 的时候,才能 setValue // setValue 只能正确执行一次,value 只能被赋值一次 this.value = if (this.value == null) value else throw IllegalStateException("${property.name} already initialized") } } // 使用 NotNullSingleValueVar class App : Application() { companion object { // thisRef var instance: App by NotNullSingleValueVar() // App 是 T } override fun onCreate() { super.onCreate() instance = this // 只能赋值一次,再次赋值会抛出异常 } }
-
使用 Kotlin 简化 SqlLite 操作
compile "org.jetbrains.anko:anko-sqlite:$anko_version"
-
定义表:
object CityForecastTable { val NAME = "CityForecast" val ID = "_id" val CITY = "city" val COUNTRY = "country" }
-
实现 SqliteOpenHelper
// 继承 ManagedSQLiteOpenHelper class ForecastDbHelper() : ManagedSQLiteOpenHelper( App.instance, // context ForecastDbHelper.DB_NAME, // 数据库名称 null, // 游标工厂 ForecastDbHelper.DB_VERSION // 数据库版本 ) { override fun onCreate(db: SQLiteDatabase) { // 创建表 db.createTable(CityForecastTable.NAME, // 表名 true, // true 创建之前检查这个表是否存在 // CityForecastTable.ID:列名 // INTEGER:SqlType 类型,用来描述列的数据类型 // 其他的 SqlType 还有 NULL、REAL、TEXT、BLOB // PRIMARY_KEY:SqlTypeModifier 类型,用来描述列的特性 // 其他的 NOT_NULL、AUTOINCREMENT、UNIQUE // SqlType 和 SqlTypeModifier 可以用 + 链接 (操作符重载) Pair(CityForecastTable.ID, INTEGER + PRIMARY_KEY), // 列 Pair(CityForecastTable.CITY, TEXT), Pair(CityForecastTable.COUNTRY, TEXT) db.createTable(DayForecastTable.NAME, true, // Pair 的重载函数 DayForecastTable.ID to INTEGER + PRIMARY_KEY + AUTOINCREMENT, DayForecastTable.DATE to INTEGER, DayForecastTable.DESCRIPTION to TEXT, DayForecastTable.HIGH to INTEGER, DayForecastTable.LOW to INTEGER, DayForecastTable.ICON_URL to TEXT, DayForecastTable.CITY_ID to INTEGER) } // 数据库升级时调用 override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // 删除表,然后重建 // 注意:这样处理数据库升级是不正确的 ! db.dropTable(CityForecastTable.NAME, true) db.dropTable(DayForecastTable.NAME, true) onCreate(db) } // static 定义一些常量 companion object { val DB_NAME = "forecast.db" val DB_VERSION = 1 // DbHelper 单例,懒加载,线程安全 val instance: DbHelper by lazy { DbHelper() } } }
-
一些集合
-
Iterable:所有集合的父类,我们可以遍历的集合都是实现了这个接口
-
MutableIterable:一个支持遍历的同时可以执行删除的 Iterable
-
Collection:这个类相是一个范性集合,我们通过函数访问可以返回集合的 size、是否为空、是否包含一个或者一些 item,这个集合的所有方法提供查询
-
MutableCollection:一个支持增加和删除 item 的 Collection,它提供了额外的函数,比如
add
、remove
、clear
等等 -
List:可能是最流行的集合,有序
-
MutableList:一个支持增加和删除 item 的 List
-
Set:一个无序并不支持重复 item 的集合
-
MutableSet:一个支持增加和删除 item 的 Set
-
Map:一个 key-value 对的 collection,key 在 map 中是唯一的
-
MutableMap:一个支持增加和删除 item 的 map
-
总数操作符
val list = listOf(1, 2, 3, 4, 5, 6) // any 集合中只要有一个元素符合条件返回 true,都不符合返回 false println(list.any { it % 2 == 0 }) // true println(list.any { it > 10 }) // false // all,集合中所有的元素都符合条件返回 true,否则返回 false println(list.all { it % 2 == 0 }) // false println(list.all { it < 10 }) // true // count,计算集合中符合条件的元素个数 println(list.count { it % 2 == 0 }) // 3 // fold,给定初始值,按照公式计算集合中每个元素的计算一遍 // total 初始值 4,4 + 1 + 2 + 3 + 4 + 5 + 6 = 25 println(list.fold(4) { init, next -> init + next }) // foldRight,与fold一样,但是顺序是从最后一项到第一项 // 2 * 6 * 5 * 4 * 3 * 2 * 1 = 1440 println(list.foldRight(2) { init, next -> init * next }) // forEach,遍历所有的元素,并给出指定操作 list.forEach { print("$it 、") // 1 、2 、3 、4 、5 、6 、 } println() // max,返回最大的一项 println(list.max()) // 6 // maxBy,根据给定的函数返回最大的一项 println(list.maxBy { -it }) // 1, 最大的一项是 -1 // min,返回最小的一项 println(list.min()) // 1 // minBy,根据给定的函数返回最小的一项 println(list.minBy { -it }) // 6, 最小的一项是 -6 // none,没有任何元素符合条件返回 true,否则返回 false println(list.none { it % 7 == 0 }) // true // reduce,与 fold 一样,初始值为永远为 0 println(list.reduce { init, next -> init + next }) // 21 // reduceRight,与 foldRight 一样,初始值为永远为 0 println(list.reduceRight { init, next -> init * next }) // 720 // sumBy,每个元素经过函数计算后,累加求和 println(list.sumBy { it % 2 }) // 3
-
过滤操作符
val list = listOf(1, 2, 3, 4, 5, 6) // dorp,去掉前 n 个元素 println(list.drop(4)) // [5, 6] // dropWhile,从第一个元素开始,去掉截止到第一个不符合条件的元素 println(list.dropWhile { it < 4 }) // [4, 5, 6] // dropLastWhile,和 dropWhile 一样,不过是从最后一个元素开始 println(list.dropLastWhile { it > 4 }) // [1, 2, 3, 4] // filter,保留所有符合条件的元素,去掉其他元素 println(list.filter { it % 2 == 0 }) // [2, 4, 6] // filterNot,去掉所有符合条件的元素 println(list.filterNot { it % 2 == 0 }) // [1, 3, 5] // filterNotNull, 去掉所有 null val listWithNull = listOf(1, null, 3, null, 5, 6) println(listWithNull.filterNotNull()) // [1, 3, 5, 6] // slice,保留 index 区间内的元素 // 保留 [1,2,3] index println(list.slice(1..3)) // [2, 3, 4] // list 也是一个区间,保留 [1,2,4] index println(list.slice(listOf(1, 3, 4))) // [2, 4, 5] // take,保留从第一个元素开始的 n 个元素 println(list.take(2)) // [1, 2] // takeLast,保留从最后一个开始的 n 个元素 println(list.takeLast(2)) // [5, 6] // takeWhile,从第一个元素开始,保留截止到第一个不符合条件的元素 println(list.takeWhile { it < 3 }) // [1, 2] // 当然,还有 takeLastWhile , take 和 drop 刚好相反,一个保留一个去掉
-
映射操作符
val list = listOf(1, 2, 3, 4, 5, 6) // flatMap,遍历所有元素,为每一个元素创建一个集合,合并集合 println(list.flatMap { listOf(it, it + 1) }) // [1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7] // groupBy,根据函数给 list 分组,最后保存到 map 中 val map = list.groupBy { if (it % 2 == 0) "even" else "odd" } println(map) // {odd=[1, 3, 5], even=[2, 4, 6]} println(map["even"]) // [2, 4, 6] println(map["odd"]) // [1, 3, 5] // map,将集合中每个元素进行变换组成新的集合 println(list.map { it * 2 }) // [2, 4, 6, 8, 10, 12] // mapIndexed,比 map 多了一个 index 参数,可以使用 index 参与变换 println(list.mapIndexed { index, it -> index * it }) // [0, 2, 6, 12, 20, 30]
-
元素操作符
fun main(args: Array<String>) { val list = listOf(1, 2, 3, 4, 5, 6) // contains,集合是否包含一个元素 println(list.contains(2)) // true // elementAt,取出 index 为 n 的元素,内部是调用的 get(index) // 如果 index 越界,抛出 IndexOutOfBoundsException println(list.elementAt(1)) // 2 // elementAtOrElse,和 elementAt 相同,数组越界返回默认值 // 这里 it 就是 7 println(list.elementAtOrElse(7, { 2 * it })) // 14 // elementAtOrNull,和 elementAt 相同,数组越界返回 null println(list.elementAtOrNull(10)) // null // first,返回符合条件的第一个元素,不存在抛出 NoSuchElementException println(list.first { it % 2 == 0 }) // 2 // firstOrNull,返回符合条件的第一个函数,没有返回 null println(list.firstOrNull { it % 7 == 0 }) // null // indexOf,返回指定元素的第一个 index,不存在返回 -1 println(list.indexOf(4)) // 3 println(list.indexOf(7)) // -1 // lastIndexOf,返回指定元素的最后一个 index,不存在返回 -1 println(list.lastIndexOf(3)) // 2 // indexOfFirst,返回第一个符合条件元素的 index,不存返回 -1 println(list.indexOfFirst { it % 2 == 0 }) // 1 // indexOfLast,返回最后一个符合条件元素的 index,不存返回 -1 println(list.indexOfLast { it % 2 == 0 }) // 5 // last,返回最后一个符合条件的元素,不存在抛出 NoSuchElementException println(list.last { it % 2 == 0 }) // 6 // lastOrNull,返回最后一个符合条件的元素,没有返回 null println(list.lastOrNull { it % 7 == 0 }) // null // single,返回符合条件的唯一元素 // 不存在抛出 NoSuchElementException,多个符合抛出 IllegalArgumentException println(list.single{it % 5 == 0}) // 5 // singleOrNull,返回符合条件的唯一元素,不存在或多个返回 null println(list.singleOrNull{it % 2 == 0}) // null println(list.singleOrNull{it % 7 == 0}) // null }
-
生产操作符
fun main(args: Array<String>) { val list = listOf(1, 2, 3, 4, 5, 6) val listRepeated = listOf(2, 2, 3, 4, 5, 5, 6) // merge 没找到这个操作符 // partition,根据一个函数将集合分成两个,返回一个 Pair // Pair first 是函数返回 true 的元素组成的集合 // Pair second 是函数返回 false 的元素组成的集合 val pair = list.partition { it % 2 == 0 } println(pair) println(pair.first) // [2, 4, 6] println(pair.second) // [1, 3, 5] // plus,可以用 + 代替,合并两个集合 println(list.plus(listOf(7, 8))) // [1, 2, 3, 4, 5, 6, 7, 8] println(list + listOf(7, 8, 9)) // [1, 2, 3, 4, 5, 6, 7, 8, 9] // zip,返回由 pair组成的 List,每个 pair由两个集合中相同 index的元素组成。 // 这个返回的List的大小由最小的那个集合决定。 println(list.zip(listOf(7, 8))) // [(1, 7), (2, 8)] // unzip,将一个 List<Pair> 转为 Pair(List,List) // Pair 的 first 是 List<Pair> 中每个 Pair 的 first 组成的 List // Pair 的 second 是 List<Pair> 中每个 Pair 的 second 组成的 List val listPair = listOf(Pair(5, 7), Pair(6, 8)) // [(5, 7), (6, 8)] println(listPair.unzip()) // ([5, 6], [7, 8]) }
-
排序操作符
fun main(args: Array<String>) { val list = listOf(3, 2, 7, 5) // reversed,反转 println(list.reversed()) // [5, 7, 2, 3] // sorted,排序(升序) println(list.sorted()) // [2, 3, 5, 7] // sortedBy 根据函数结果排序(升序) println(list.sortedBy { it % 3 }) // [3, 7, 2, 5] // sortedDescending,排序(降序) println(list.sortedDescending()) // [7, 5, 3, 2] // sortedBy 根据函数结果排序(降序) println(list.sortedByDescending { it % 3 }) // [2, 5, 7, 3] }
-
从数据库读取和保存数据
// 先占坑
-
null 安全
-
黄金准则:如果变量是可以是 null,编译器强制我们去用某种方式去处理。
val a: Int? = null a.toString() // 不能编译 vala:Int? = null if(a != null) { // if 代码块中,a 的类型从 Int? 智能转换为 Int a.toString() } a?.toString() // a 不为 null 才调用 toString 方法 val a:Int? = null val myString = a?.toString() ?: "null" // a 不为 null 返回 toString,null 返回 "null" a!!.toString() // 断言 a != null,null 直接抛出异常
-
记住,如果你使用了
!!
,可能是因为你确信对象不可能为null,如果是这样,请定义为非null。
-
-
使用 DataSource 来实现获取数据的逻辑
// 先占坑
-
if 表达式
// if 表达式总会返回一个值 val z = if (condition) x else y
-
when 表达式
// 使用 else 覆盖其他分支 when (x){ 1 -> print("x == 1") 2 -> print("x == 2") else -> { print("I'm a block") print("x is neither 1 nor 2") } } // 使用 , 并列返回,when 可以返回一个直接使用的值 val result = when (x) { 0, 1 -> "binary" else -> "error" } // 可以检查参数类型,可以自动转型 when(view) { is TextView -> view.setText("I'm a TextView") is EditText -> toast("EditText value: ${view.getText()}") is ViewGroup -> toast("Number of children: ${view.getChildCount()} ") else -> view.visibility = View.GONE } // 可以匹配区间 val cost = when(x) { in 1..10 -> "cheap" in 10..100 -> "regular" in 100..1000 -> "expensive" in specialValues -> "special value!" else -> "not rated" } // 可以不使用参数,完全代替 if else valres = when { x in 1..10 -> "cheap" s.contains("hello") -> "it's a welcome!" v is ViewGroup -> "child count: ${v.getChildCount()}" else -> "" }
-
Ranges
// x >=1 && x<= 10 if (i in 0..10) println(i) // 遍历 for (i in 0..10) println(i) // 使用 downTo 反向遍历 for(i in 10 downTo 0) println(i) // 相当与 i += 2 for (i in 1..4 step 2) println(i) // 遍历 [1,4) for (i in 0 until 4) println(i) // Ranges 配和 map 使用获取一个 List<View> val views = (0..viewGroup.childCount - 1).map { viewGroup.getChildAt(it) }
-
try catch 表达式
val x = try { doSomething() "try" } catch(e : Exception) { "catch" } // 返回 try 或者 catch 代码块中最后一个表达式
-
打开一个 Activity
startActivity<WeatherDetailActivity>( WeatherDetailActivity.CITY_ID to id, WeatherDetailActivity.CITY_DATA to it.date) // 给 Context 添加一个扩展方法,接受一个可变参数 Pair<String,Any> 用来给 Intent 传递数据 // inline 函数,泛型声明 <reified T: Activity> 这样可以直接通过 T::class.java 获取泛型的 class inline fun <reified T: Activity> Context.startActivity(vararg params: Pair<String, Any>) { AnkoInternals.internalStartActivity(this, T::class.java, params) } // 更喜欢下面这种写法 WeatherDetailActivity.start(this@WeatherListActivity, id, it.date)
-
去掉 ActionBar 和 Title
parent="Theme.AppCompat.Light.NoActionBar" // 实际上 <style name="Theme.AppCompat.Light.NoActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style>
-
android:clipToPadding="false" // RecyclerView 属性,false 表示控件可以在 padding 中绘制
-
Toolbar 主题
// Toolbar 暗色主题,文字是白色 app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" // 溢出菜单白色主题,文字是黑色 app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
-
使用接口管理 Toolbar
interface ToolbarManager { val toolbar: Toolbar var toolbarTitle: String get() = toolbar.title.toString() set(value) { toolbar.title = value } fun initToolbar() { toolbar.inflateMenu(R.menu.menu_main) toolbar.setOnMenuItemClickListener { when (it.itemId) { R.id.action_settings -> App.instance.toast("Settings") else -> App.instance.toast("Unknown option") } true } } fun enableHomeAsUp(up: () -> Unit) { toolbar.navigationIcon = createUpDrawable() toolbar.setNavigationOnClickListener { up() } } private fun createUpDrawable() = with(DrawerArrowDrawable(toolbar.ctx)) { progress = 1f this } fun attachToScroll(recyclerView: RecyclerView) { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { MLog.i(dy) if (dy > 0) toolbar.slideExit() else toolbar.slideEnter() } }) } fun View.slideExit() { if (translationY == 0f) animate().translationY(-height.toFloat()) } fun View.slideEnter() { if (translationY < 0f) animate().translationY(0f) } } // 1. 在 xml 布局中添加 toolbar // 2. 在 Activity/Fragment 中初始化 toolbar // 3. 实现 ToolbarManager 接口 // 4. 直接调用 ToolbarManager 方法
-
apply 用法
val textView = TextView(context).apply { text = "Hello" hint = "Hint" textColor = android.R.color.white }