https://kotlinlang.org/docs/reference/android-overview.html
学习资料
记录
let, also,apply,run
https://blog.csdn.net/u013064109/article/details/78786646
简单分析下,also和apply一样都是返回自己, let和run一样返回花括号代码块最后一行的返回值
另外一样的2种区别就在于代码块里用it还是this代替变量,而this可以省略不写。
如上图,结果就是 2个hello,以及7和5
简单看下源码
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T, R> T.run(block: T.() -> R): R = block()
看到源码可能感觉和我们的写法不一样。其实原因很简单,kotlin里一个函数可以作为参数的,完事这个函数可以写到括号外边。
比如 T.apply({//函数块}) 可以写成 T.apply(){//函数块}
对于let和run,如果要主动return一个东西,这样写的// return@run 333
在看demo的时候看到这些代码,扩展函数
可以注意到方法名字前有个xxx. 的东西,这个就是声明对哪个类进行扩展
/**
* The `fragment` is added to the container view with id `frameId`. The operation is
* performed by the `fragmentManager`.
*/
fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, @IdRes frameId: Int) {
supportFragmentManager.transact {
replace(frameId, fragment)
}
}
/**
* The `fragment` is added to the container view with tag. The operation is
* performed by the `fragmentManager`.
*/
fun AppCompatActivity.addFragmentToActivity(fragment: Fragment, tag: String) {
supportFragmentManager.transact {
add(fragment, tag)
}
}
fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit) {
setSupportActionBar(findViewById(toolbarId))
supportActionBar?.run {
action()
}
}
/**
* Runs a FragmentTransaction, then calls commit().
*/
private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) {
beginTransaction().apply {
action()
}.commit()
}
代码里这样用的
setupActionBar(R.id.toolbar) {
setHomeAsUpIndicator(R.drawable.ic_menu)
setDisplayHomeAsUpEnabled(true)
}
其实原本应该这样的,不过最后一个参数是一个函数的话,可以直接把括号的实现提到圆括号外面,就成了上边的写法了
setupActionBar(R.id.toolbar, {
setHomeAsUpIndicator(R.drawable.ic_menu)
setDisplayHomeAsUpEnabled(true)
})
when
java 中的switch 在kotlin里的写法,
when(xxx){
is Int->{
}
in 0..10->{
}
1->{
}
2->{
}
3,4->{}
else->{}
}
2个方法互相引用的时候
java代码如下,我是2个runnable 互相调用
private void starte(){
handler.postDelayed(r1,1000);
}
Handler handler=new Handler();
Runnable r1=new Runnable() {
@Override
public void run() {
System.out.println("==========1");
handler.postDelayed(r2,1111);
}
};
Runnable r2=new Runnable() {
@Override
public void run() {
System.out.println("==========2");
handler.postDelayed(r1,1111);
}
};
kotlin代码如下
internal var handler = Handler()
internal var r1: Runnable = Runnable {
println("==========1")
handler.postDelayed(r2, 1111)
}
internal var r2: Runnable = Runnable {
println("==========2")
handler.postDelayed(r1, 1111)
}
private fun starte() {
handler.postDelayed(r1, 1000)
}
看下转化,加了internal,这个是限定同一模块下可以访问,非必需。
关键有的地方 r2: Runnable 这个,2个Runnable必须有一个需要加冒号声明类型,否则就报错。
Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
中文:类型检查遇到了递归问题。最简单的解决方法:显式地指定声明的类型。
可以看到上边都是简化后的写法了。如果这个时候要使用this,就不行了,得写完整的
如下
internal var r2: Runnable = object : Runnable {
override fun run() {
println("==========2")
handler.postDelayed(r1, 1111)
handler.postDelayed(this, 2222)
Runnable { handler.postDelayed(this, 2222) }.run()
}
}
循环的几种写法
下边的代码结果都一样,都是从0到4循环的,从大到小的话可以用 downto关键字
private fun forTest(){
//循环N次,from 0 start
repeat(5){
println("a============$it")
}
for(i in 0 ..4){
println("b==========$i")
}
for(i in 0 until 5){
println("c==========$i")
}
(0..4).forEach {
println("d==============$it")
}
}
set get
//set的简化写法
var tasks: List<Task> = tasks
set(tasks) {
field = tasks
notifyDataSetChanged()
}
//get的简化写法
override var isActive: Boolean = false
get() = isAdded
thread ,简化写法如下,不需要start,内部默认构造方法里start为true的。
thread {
}
问题记录
1~
java 代码 Object obj=new Object(); obj.wait();
kotlin里的Any()对象没有wait()方法,不知道转成kotlin之后咋写。
解决办法,kt里继续new一个Object对象而不是any对象。
https://www.jianshu.com/p/3963e64e7fe7
2~
双冒号::
可以理解为把一个方法当对象处理,如下
class A {
lateinit var b:B
init {
b=B().apply {
this.callbackA=this@A::testA//把testA这个方法传递给B里边的一个变量callbackA了
}
}
fun testA(name: String,age:Int){
}
}
class B{
var callbackA:((String,Int) ->Unit)?=null//这里对应写出参数的类型,返回的类型好匹配,因为是可以为空的,所以括起来加个问号
fun testB(){
callbackA?.invoke("jerry",33)
}
}
上边的可以用来写我们平时用的Callback。
如果是java,我们一般是定义一个interface,完事定义一个interface的变量,然后用的时候实例化一个interface调用
然后看下改版后的写法
如下自定义的类
class ItemTouchListener{
//某个地方调用
singleTapCallback?.invoke(position,rv.getChildViewHolder(child))
//然后定义的回调
var singleTapCallback:((position: Int, viewHolder: RecyclerView.ViewHolder)->Unit)?=null
}
然后是正式使用的地方
rv_temp.addOnItemTouchListener(ItemTouchListener(rv_temp).apply {
singleTapCallback=this@FragmentTempTest::singleTab
//或者这样调用也可以
singleTapCallback={position, viewHolder ->
}
})
//这个就是我们的回调实现方法拉。
fun singleTab(position: Int, viewHolder: RecyclerView.ViewHolder){
}
这里还有一些
https://blog.csdn.net/lmo28021080/article/details/81505211
inline,noline
参考https://blog.csdn.net/Jaden_hool/article/details/78437947
基本数据类型
比较坑的地方,就是不能互相自动转化,java里我们定义个float,传个int也没问题的,在kt里就不行
有点,数字下边可以加下划线,看起来更直观
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
== === 2个等号和3个等号的区别
前者是数值比较,后者是地址比较
== 和 ===
其中这个比较吓人
fun main(args: Array<String>) {
val a: Int = 1000
val b: Int? = a
val c: Int? = a
println(b == c) //true
println(b === c) //false
}
fun main(args: Array<String>) {
val a: Int = 100
val b: Int? = a
val c: Int? = a
println(b == c) //true
println(b === c) //true
}
两段代码除了a的初始值不一样,其他都一样,可结果3个等号的结果不一样。
这个就是java里基本数据类型和对象的问题了,看下源码,128以下的,用的是同一个对象,从缓存数组读出来的,所以对象比较也是相等的,128以上的,就是每次new一个新的了,不是一个对象了。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
运算符
这是完整的位运算列表(只用于 Int 与 Long):
shl(bits) – 有符号左移 (Java 的 <<)
shr(bits) – 有符号右移 (Java 的 >>)
ushr(bits) – 无符号右移 (Java 的 >>>)
and(bits) – 位与
or(bits) – 位或
xor(bits) – 位异或
inv() – 位非
后缀 u 与 U 将字面值标记为无符号
val l: ULong = 1u // ULong,已提供预期类型
val a1 = 42u // UInt:未提供预期类型,常量适于 UInt
"""
原始字符串 使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行以及任何其他字符:
比如下边这个,最终显示结果有4行,第一个换行,最后一行换行的,中间的空白页都保留的
val text = """
for (c in "foo")
print(c)
"""
when
常见的when用法,类似java里的swtich,不过比这个强大很多
when (x) {
parseInt(s) -> print("s encodes x")
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
is String -> x.startsWith("prefix")
else -> print("s does not encode x")
}
when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
还有这种
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
密封类
用sealed 修饰class
sealed
for
for的特殊用法,迭代类型的可以直接拿到key和value
val arr= arrayListOf(2,3,3,33,3)
for((index,value) in arr.withIndex()){
}
标签lable
标示符后边加个@即可
loop@ for (i in 1..100) {
// ……
}
函数
主构造函数和次构造函数,还有init的执行顺序
主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
在实例初始化期间,初始化块按照它们出现在类体中的顺序执行,与属性初始化器交织在一起:
下边4句的结果是按顺序打印的
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints ${name}")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块:
下边init代码先执行的
class Constructors {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor")
}
}
lateinit 和by lazy
lateinit 是给var用的,而且必须给予值,页面销毁的时候如果没有赋值,就挂了。
by lazy是给val用的, 后边跟一个代码块,代码块最后一行就是返回值,并且代码块只会执行一次,也就是第一次调用s这个变量的时候println会打印,再后边调用s这个值,就不会执行这个代码块了,s已经有值了,直接返回即可。
val s:String by lazy {
println("lazy==============")
"xxxx"
}
lateinit var ss:String
覆盖属性
在超类中声明然后在派生类中重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。每个声明的属性可以由具有初始化器的属性或者具有 getter 方法的属性覆盖
open class Foo {
open val x: Int get() { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
你也可以用一个 var 属性覆盖一个 val 属性,但反之则不行.因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
覆盖方法
2个父类有同名的方法,子类咋调用,用尖括号声明父类
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } // 接口成员默认就是“open”的
fun b() { print("b") }
}
class C() : A(), B {
// 编译器要求覆盖 f():
override fun f() {
super<A>.f() // 调用 A.f()
super<B>.f() // 调用 B.f()
}
}
扩展函数
定义也比较简单,想扩展哪个类,就点一下
class D:()
fun D.foo() = "d"
扩展属性
扩展属性不能有初始化器,他们的行为只能由显式提供的 getters/setters 定义。
val <T> List<T>.lastIndex: Int
get() = size - 1
消费者 in, 生产者 out
如下方法里的参数,有个in修饰,如果用out修饰,你在方法体里边无法修改args参数的,in可以修改
override fun invoke(proxy: Any?, method: Method, args: Array<in Any>): Any? {}
可变参数
java里省略号表示可变参数,可以传多个参数,也可以传个数组,可kotlin调用的时候咋办?
//java里代码如下
public native Object invoke(Object obj, Object... args)
//kotlin里我们拿到的是个数组,下边的参数args
override fun invoke(proxy: Any?, method: Method, args: Array<in Any>)
//使用的时候如下,前边加个星号
method.invoke(xxx,*args);