基于Android Studio的用户行程记录APK开发指南(一):项目基础配置与速通Kotlin

前言

  • 最近博主在unity开发独立游戏,UE5系列的相关长期教程先暂时不更新了~ 请大家多多谅解~
  • 本系列教程我们来看看如何使用Android Studio去开发一个APK用于用户的实时行程记录。
  • 本期我们来项目基础配置与速通Kotlin

安装Android Studio

  • 我们进入Android Studio的官网,选择下载官网链接请添加图片描述

  • 下载后点击exe运行安装,这里我们要选择安卓虚拟设备请添加图片描述

  • 根据提示一路accept和next,这边会进行Android SDK的安装请添加图片描述

  • 安装完成后我们在创建页面找到virtual Device Mannager请添加图片描述

  • 我们可以看到这里以及预先帮我们下载好了一个虚拟设备,点击绿色箭头即可运行,如下图请添加图片描述

新建项目

  • 点击新建项目,选择模板empty activity请添加图片描述

  • 简单进行命名创建即可,注意语言我们选择默认推荐的Koltlin请添加图片描述

  • 打开以后会有如下界面!请添加图片描述

可能的报错
  • 值得一提的是,初次创建项目有可能会出现下述报错请添加图片描述

  • 网上的解决方法很多,一般是网络问题,我们可以重新点击左侧reload进行下载即可(我这边这样就解决了,如果不能解决的可以自行搜寻,网上教程挺多的~)请添加图片描述

运行第一个程序
  • 点击绿色箭头运行默认程序,右侧的虚拟设备输出Hello Android请添加图片描述

Kotlin

介绍
  • Kotlin 是一种在 Java 虚拟机(JVM)上运行的静态类型编程语言,由 JetBrains 开发。Kotlin 旨在与 Java 语言完全兼容,并为其提供一些额外的特性,例如更简洁的语法、空安全、扩展函数、协程等请添加图片描述

  • 以下是一些 Kotlin 的关键特性:

    1. 简洁的语法:Kotlin 语法设计得非常简洁,可以减少代码量,提高开发效率。
    2. 空安全:Kotlin 提供了空安全特性,可以减少空指针异常。
    3. 扩展函数:Kotlin 允许开发者扩展现有类的功能,而不需要继承它们或使用设计模式,如装饰器。
    4. 协程:Kotlin 协程是一种轻量级的并发编程抽象,可以简化异步编程。
    5. 与 Java 的互操作性:Kotlin 与 Java 完全兼容,这意味着你可以无缝地在 Kotlin 和 Java 之间切换。
    6. 数据类:Kotlin 提供了数据类,可以自动为常见的数据类任务(如生成 toString、equals、hashCode 和 copy 方法)提供实现。
    7. 属性委托:Kotlin 支持属性委托,允许你为属性提供自定义的行为。
    8. 类型安全构建器:Kotlin 支持类型安全构建器模式,可以创建复杂的对象层次结构。

Kotlin基础语法

  • 这是攻,这是防,这是苇名弦一郎。我们来快速速通Kotlin的基础语法。
  • Kotlin 文件以.kt为后缀。
基础数据类型
  • 整数类型
    • Byte: 8 位,范围从 -128 到 127。
    • Short: 16 位,范围从 -32,768 到 32,767。
    • Int: 32 位,范围从 -2^31 到 2^31 - 1。
    • Long: 64 位,范围从 -2^63 到 2^63 - 1。
    • 浮点数类型
    • Float: 32 位,单精度,带有 6-7 位有效数字。
    • Double: 64 位,双精度,带有 15-16 位有效数字。
  • 字符类型
    • Char: 16 位的 Unicode 字符。
  • 布尔类型
    • Boolean: 有两个值:truefalse
  • 字符串类型
    • String: 一系列字符的序列。
  • 数组类型
    • IntArray: 存储 Int 类型的数组。
    • DoubleArray: 存储 Double 类型的数组。
    • Array< T>:泛型数组,可以存储任意类型。
  • 常量
var a:Int=10
  • 常量
val x=10 //系统自动推断为Int
  • 字符串模板
    • $ 表示一个变量名或者变量值
    • $varName 表示变量值
    • ${varName.fun()} 表示变量的方法返回值:
var a = 1
val s1 = "a is $a"

函数定义
fun sum(a:Int,b:Int):Int{return a+b}
fun sum(a: Int, b: Int) = a + b
fun printSum(a: Int, b: Int): Unit { 
    print(a + b)
}
  • 可变参数函数:vararg 关键字
fun vars(vararg v:Int){
    for(vt in v){
        print(vt)
    }
}
  • lambda表达式
val sumLambda:(Int,Int)->Int={x,y->x+y}

NULL检查机制
  • 对于声明可为空的参数,在使用时要进行空判断处理
    • 字段后加!!抛出空异常
    • 字段后加?可不做处理返回值为 null 或配合 ?: 做空判断处理
val ages = age!!.toInt() //如果为空抛出空指针异常
val ages1 = age?.toInt() //如果为空返回null
val ages2 = age?.toInt() ?: -1
//返回值可能为空的函数
fun parseInt(str: String): Int? {
    return str.toIntOrNull()
}
  • 类型检测:通过下述函数可以返回传入参数的类型
fun getTypeName(obj: Any): String? 
{ 
	return obj::class.simpleName 
}
  • 区间:.. 的 rangeTo 函数辅以 in 和 !in 形成。
for (i in 1..4) print(i) // 输出“1234”
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
for (i in 1 until 10) {   // i in [1, 10) 排除了 10
     println(i)
}

条件语句
  • if
  • when(相当于switch)
if{}else if(){}else{}
when (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")
    else -> print("none of the above")
}
  • when 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支
when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

循环语句
for(i in 1..10){]}
for (item in collection){print(item)}
while( 布尔表达式 ) { }
do { }while(布尔表达式);
  • 结构化跳转表达式:
    • return。默认从最直接包围它的函数或者匿名函数返回。
    • break。终止最直接包围它的循环。
    • continue。继续下一次最直接包围它的循环。

标签
  • 在Kotlin中,标签用于在存在多个返回点的情况下指定从哪个循环或lambda表达式返回。标签可以是隐式的也可以是显式的。
    • *隐式标签
    val ints = listOf(1, 0, 2, 3, 0, 4, 5)
    fun foo() 
    { 
    	ints.forEach 
    	{ 
    		if (it == 0)return@forEach 
    		print(it) 
    	} 
    }
    
    • *显式标签
    val ints = listOf(1, 0, 2, 3, 0, 4, 5)
    fun foo() {
        ints.forEach lit@ 
        {
            if (it == 0) return@lit
            print(it)
        }
    }
    

  • 类,Kotlin 中没有 new 关键字
class MyClass{}
var myClass=MyClass()
构造函数
  • 值得一提的是主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
//有参构造
class Person constructor(firstName: String, lastName: String) {} 
//无参构造
class Person { } 
//初始化成员变量
class Person(val firstName: String, val lastName: String) {}
//初始化代码段
class Person constructor(firstName: String) {
    init {
        println("FirstName is $firstName")
    }
}
  • 次级构造函数:Kotlin中的次构造函数可以看作是构造函数的重载。
    • 需要加前缀 constructor
    • 当类有主构造函数时,每个次构造函数都必须直接或间接地调用主构造函数
class Person(val name: String, val age: Int) {
    // 主构造函数

    // 次构造函数
    constructor(name: String) : this(name, 0) {
        // 初始化逻辑
    }

    // 另一个次构造函数
    constructor() : this("John Doe") {
        // 初始化逻辑
    }
}

lateinit
  • 在Kotlin中,lateinit关键字用于延迟初始化一个非空的可变属性(var属性),这意味着你可以在构造函数之外的其他地方初始化该属性,而不是在声明属性时就必须初始化它。lateinit关键字只适用于var属性,不能用于val属性,因为val属性必须在声明时初始化或者通过构造函数初始化。
class MyService 
{ 
	lateinit var service: Any // 声明一个lateinit属性 
	fun doSomething() 
	{ 
	// 在这里使用service属性 
	println(service) 
	}
}
getter和setter
  • gettersetter:在Kotlin中,getter和setter是用于属性访问和修改的幕后方法。它们是属性抽象化的关键组成部分,允许你以声明性的方式定义如何获取(读取)和设置(写入)属性的值。Kotlin中的属性可以自动提供getter和setter,也可以自定义它们。
  • 当你声明一个属性时,Kotlin会自动为你生成一个getter和setter。
  • val不允许设置setter函数,因为它是只读的。
var name: String = "John" // 自动生成 getter 和 setter
  • 自定义getter和setter
var score: Int 
	get() = field // 自定义 getter 
	set(value) { 
		if (value >= 0) 
		{ 
			field = value // 自定义 setter
		}else { 
			throw IllegalArgumentException("Score cannot be negative")  
		}
	  }
feild
  • field关键字来引用属性的BackingField。BackingField是编译器为每个属性生成的幕后字段,用于存储属性的值。它只能在访问器(getter 或 setter)内部使用,并且仅在至少使用一个访问器的默认实现,或者自定义访问器通过 field 标识符引用它时才存在。
//name 属性有一个自定义的 setter,它检查传入的值是否为空。如果不为空,则使用 field 标识符将值分配给 backing field。这样,当我们尝试将空字符串分配给 name 属性时,它的值不会更改。
class Person {
    var name: String = "initial value"
        set(value) {
            if (value.isNotEmpty()) {
                field = value
            }
        }
}

抽象类
  • 不用多说这是啥了吧,抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的
abstract class Derived : Base() {
    override abstract fun f()
}
类的修饰符
  • 类的修饰符包括 classModifier 和_accessModifier_:
    • classModifier: 类属性修饰符,标示类本身特性。
      • abstract // 抽象类
      • final // 类不可继承,默认属性
      • enum // 枚举类
      • open // 类可继承,类默认是final的
      • annotation // 注解类
    • accessModifier: 访问权限修饰符
      • private // 仅在同一个文件中可见
      • protected // 同一个文件中或子类可见
      • public // 所有调用的地方都可见
      • internal // 同一个模块中可见
open class Animal 
{ 
	protected open val protectedMember = "Protected in Base"
	open fun makeSound() 
	{ 
	println("Some sound")
	} 
}
嵌套类
  • 在Kotlin中,嵌套类是指定义在另一个类内部的类。嵌套类可以访问外部类的成员,就像它们是自己的成员一样。但是,嵌套类并不持有对外部类的引用,因此它们不是Java中的内部类(inner classes)。
class OuterClass {
    val outerClassValue = "I am from the outer class"

    class NestedClass {
        fun accessOuterClassMember() {
            println(outerClassValue)
        }
    }
}

fun main() {
    val nestedClassInstance = OuterClass.NestedClass()
    nestedClassInstance.accessOuterClassMember()
}
内部类
  • 在Kotlin中,内部类是指定义在另一个类内部的类。与Java中的内部类不同,Kotlin中的内部类默认不是static的,这意味着它们持有对外部类的引用。因此,内部类可以访问外部类的私有成员,并且可以访问外部类的实例状态。
class OuterClass {
    private val outerClassValue = "I am from the outer class"

    inner class InnerClass {
        fun accessOuterClassMember() {
            println(outerClassValue)
        }
    }
}

fun main() {
    val outerClassInstance = OuterClass()
    val innerClassInstance = outerClassInstance.InnerClass()
    innerClassInstance.accessOuterClassMember()
}
  • 区别:
    1. 访问权限
      • 嵌套类默认是静态的,不能访问外部类的私有成员,除非使用inner关键字。
      • 对象表达式和lambda表达式可以访问定义它们的作用域内的任何变量。
    2. 实例化
      • 嵌套类可以通过外部类实例化。
      • 对象表达式和lambda表达式是表达式的一部分,不需要显式实例化。

继承
  • Kotlin 中所有类都继承该 Any 类,它是所有类的超类
  • 使用冒号(:)来指定子类继承自哪个父类
open class ParentClass {
    // ...
}

class ChildClass : ParentClass() {
    // ...
}
构造
  • 如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
open class Person(var name : String, var age : Int){// 基类

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}
  • 如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。
open class Person(var name: String, var age: Int) { // 基类
    // ...
}

// 子类没有主构造函数,必须代理到基类构造函数
class Student : Person {
    var no: String
    var score: Int

    constructor(name: String, age: Int, no: String, score: Int) : super(name, age) {
        this.no = no
        this.score = score
    }

    // 如果子类有多个构造函数,它们都必须直接或间接地代理到基类构造函数
    constructor(name: String, age: Int) : this(name, age, "", 0) {
        // 在这里可以执行更多的初始化操作
    }
}

重写
  • 在Kotlin中,函数重写(Function Overriding)和属性重写(Property Overriding)是面向对象编程中的两个重要概念,它们允许子类继承并修改父类中的成员。
  • 函数重写是指子类提供与父类中具有相同名称和签名的方法的新实现。
open class Base {
    open fun display() {
        println("Display from Base")
    }
}

class Derived : Base() {
    override fun display() {
        println("Display from Derived")
    }
}
  • 属性重写是指子类提供与父类中具有相同名称的属性的新实现。
open class Base {
    open var name: String = "Base"
}

class Derived : Base() {
    override var name: String = "Derived"
}

  • 重写属性的 getter 或 setter
open class Base {
    open var name: String = "Base"
        get() = "Base $field"
}

class Derived : Base() {
    override var name: String = "Derived"
        get() = "Derived $field"
        set(value) {
            field = "Overridden $value"
        }
}


接口
  • 在Kotlin中,接口(Interface)是一种引用类型,它定义了一个完全抽象的类,其中只包含抽象方法和抽象属性。
  • 接口使用interface关键字定义,可以包含抽象方法、默认方法和静态方法。
interface MyInterface {
    fun bar() // 抽象方法
    fun foo() { // 默认方法
        // 默认实现
    }
    companion object { // 静态方法
        fun staticMethod() {
            // 静态方法实现
        }
    }
}
class MyClass : MyInterface {
    override fun bar() {
        // 实现抽象方法
    }
    // foo()方法已经由接口提供了默认实现,因此可以不重写
}
  • 接口可以包含属性,但它们必须是抽象的,并且不能有后端字段。
interface MyInterfaceWithProperty {
    val property: Int // 抽象属性
}
class MyClassWithProperty : MyInterfaceWithProperty {
    override val property: Int = 42 // 提供属性实现
}
  • 值得一提的是,Kotlin 不支持传统意义上的多继承,即一个类直接继承自多个类。
interface A {
    fun display() {
        println("A")
    }
}

interface B {
    fun display() {
        println("B")
    }
}

// 类 C 实现了接口 A 和 B,需要处理 display 方法的冲突
class C : A, B {
    override fun display() {
        super<A>.display() // 调用 A 接口的 display 方法
        super<B>.display() // 调用 B 接口的 display 方法
    }
}

fun main() {
    val c = C()
    c.display() // 输出 "A" 然后 "B"
}


Kotlin 扩展
  • Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。(wc好东西)
扩展函数
  • 扩展函数允许你向一个类添加新的方法,而无需修改该类的源代码。扩展函数的定义使用了特殊的语法:fun 关键字后跟一个接收者类型,然后是一个点,接着是方法名和参数。
fun String.spaceToCamelCase(): String {
    return this.split(" ").joinToString("") { it.capitalize() }
}

fun main() {
    val s = "hello world"
    println(s.spaceToCamelCase()) // 输出 "HelloWorld"
}
扩展属性
  • 与扩展函数类似,扩展属性允许你向一个类添加新的属性。但与扩展函数不同,扩展属性不能有初始化器,因为它们没有后端字段,并且只能被声明为 val
val String.lastChar: Char
    get() = this[length - 1]

fun main() {
    val s = "hello"
    println(s.lastChar) // 输出 "o"
}
扩展的作用域
  • Kotlin 允许你将扩展定义在特定的作用域中,这样它们只在该作用域内可见。这可以通过使用 file 作用域、package 作用域或 object 作用域来实现。
// 文件作用域
fun Any.println() = println(this)

// 包作用域
package com.example.util

fun String.reverse(): String = this.reversed()

// 对象作用域
object Extensions {
    fun String.removeSpaces(): String = this.replace(" ", "")
伴生对象的扩展
  • 可以伴生对象定义扩展函数和属性,就像为其他对象定义扩展一样。这些扩展函数和属性可以在伴生对象的作用域内被调用,就像它们是伴生对象的一部分一样。
class MyClass {
    companion object {
        fun companionMethod() {
            println("Companion method")
        }
    }
}

// 为 MyClass 的伴生对象定义扩展函数
fun MyClass.Companion.companionExtension() {
    println("Companion extension")
}

fun main() {
    MyClass.companionMethod() // 输出 "Companion method"
    MyClass.companionExtension() // 输出 "Companion extension"
}
其他扩展
  • 扩展函数如何访问扩展接收者类型的成员以及定义扩展函数的类的成员:
class A {
    fun display() {
        println("A display")
    }
}

class B {
    fun display() {
        println("B display")
    }
}

class C {
    fun display() {
        println("C display")
    }

    // 扩展函数,扩展A类
    fun A.extendedDisplay() {
        display() // 调用A的display方法
        this@C.display() // 调用C的display方法
    }

    // 扩展函数,扩展B类
    fun B.extendedDisplay() {
        display() // 调用B的display方法
        this@C.display() // 调用C的display方法
    }
}

fun main() {
    val a = A()
    val b = B()
    val c = C()

    c.extendedDisplay(a) // 输出 "A display" 和 "C display"
    c.extendedDisplay(b) // 输出 "B display" 和 "C display"
}


数据类与密封类
数据类
  • 在Kotlin中,数据类(Data Class)是一种特殊的类,它主要用于数据持有,通常包含多个属性,并且编译器会为它自动生成一些常用的方法,比如 toString()equals()hashCode()copy() 等。
  • 要定义一个数据类,你只需要在类定义前加上 data 修饰符。例如:
data class User(val name: String, val age: Int)
  • 在这个例子中,User 是一个数据类,它有两个属性:nameage
  • 数据类的特性
    1. 自动生成的方法
      • toString():生成一个包含所有属性值的字符串表示形式。
      • equals():实现 equals() 方法,使得两个数据类实例可以通过其属性值来判断是否相等。
      • hashCode():生成一个基于所有属性值的哈希码。
      • copy():生成一个拷贝当前对象的新对象,可以修改部分属性。
    2. 解构声明: 数据类实例可以使用解构声明来分解为变量。
    3. 继承: 数据类可以继承其他类,但是它本身不能被继承。
data class User(val name: String, val age: Int)

fun main() {
    val user1 = User("Alice", 30)
    val user2 = User("Bob", 25)
    val user3 = User("Alice", 30)

    println(user1) // 输出 "User(name=Alice, age=30)"
    println(user1.equals(user2)) // 输出 "false"
    println(user1.equals(user3)) // 输出 "true"

    val (name, age) = user1
    println("Name: $name, Age:$age") // 输出 "Name: Alice, Age: 30"

    val copiedUser = user1.copy(age = 31)
    println(copiedUser) // 输出 "User(name=Alice, age=31)"
}

密封类
  • 在Kotlin中,密封类(Sealed Class)是一种用来表示受限的类继承结构的工具。当你想定义一个类,并且只允许这个类有特定的子类时,密封类非常有用。密封类被用来表示当出现多个子类时,它们的数量是有限的,并且可以被明确列举出来。
  • 密封类使用 sealed 修饰符来定义,并且所有直接继承自密封类的子类都必须在同一个文件中声明。
sealed class Expr

data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
  • 密封类的特性
    1. 受限的继承: 密封类的子类必须声明在同一个文件中,这限制了类的继承结构。
    2. when 表达式: 密封类非常适合用于 when 表达式,因为编译器知道所有可能的密封类的子类,所以不需要提供 else 子句。
sealed class Expr

data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

fun main() {
    val expr: Expr = Sum(Const(1.0), Const(2.0))
    println(eval(expr)) // 输出 3.0
}

泛型
  • Kotlin中的泛型允许你编写可重用的代码,它允许你定义一个类、接口或方法,可以工作于任何类型。泛型通过类型参数化来提供这种灵活性,类型参数化意味着你可以在定义类、接口或方法时指定一个或多个类型参数。
泛型类
  • 泛型类使用尖括号(<>)来定义类型参数。例如:
class Box<T>(t: T) {
    var value = t
}
val boxInt = Box<Int>(1)
val boxString = Box<String>("Hello")
泛型函数
  • 泛型函数使用类型参数来定义函数可以操作的数据类型。例如:
fun <T> printArray(arr: Array<T>) {
    for (item in arr) {
        println(item)
    }
}
printArray(arrayOf(1, 2, 3, 4, 5))
printArray(arrayOf("a", "b", "c", "d", "e"))
泛型约束
  • 有时候,你可能希望对类型参数设置一些限制,这可以通过泛型约束来实现。例如,如果你只想允许 Number 类型的参数,你可以这样定义:
fun <T : Number> printNumberInfo(item: T) {
    println("The number is ${item.toInt()}")
}
printNumberInfo(100) // 输出 "The number is 100"
printNumberInfo(3.14) // 输出 "The number is 3"
型变
  • Kotlin提供了两种型变(Variance)的概念:协变(Covariance)和逆变(Contravariance)。这些概念主要用于泛型类和接口的继承。
    • 协变(Covariance):如果你有一个类 Box<out T>,那么你可以将 Box<Int> 赋值给 Box<Number> 类型的变量。这意味着 Box 的类型参数 T 是协变的。
    • 逆变(Contravariance):如果你有一个类 Box<in T>,那么你可以将 Box<Number> 赋值给 Box<Int> 类型的变量。这意味着 Box 的类型参数 T 是逆变的。
class Box<out T>(t: T) // 协变
class Box<in T> // 逆变
  • 型变是Kotlin泛型的一个重要特性,它允许你在不牺牲类型安全的前提下,提供更多的灵活性。
无变(Invariant)
  • 如果你没有指定协变或逆变,那么泛型类型参数是无变的(Invariant)。这意味着你不能将子类型赋值给父类型的泛型类型参数,也不能将父类型赋值给子类型的泛型类型参数。
class Box<T>(t: T) // 无变

fun main() {
    val boxInt: Box<Number> = Box<Int>(1) // 错误:Box<Int> 不是 Box<Number> 的子类型
    // ...
}
星号投影(Star-Projections)
  • 当你不知道泛型类的确切类型参数时,可以使用星号投影(*)来表示。例如,如果你有一个 List 类型的变量,但你不知道它具体是 List<Int> 还是 List<String>,你可以使用 List<*> 来表示。
fun printList(list: List<*>) {
    for (item in list) {
        println(item)
    }
}

枚举类
  • 在Kotlin中,枚举(Enum)是一种特殊的类,用于定义一组固定的常量。枚举常量在运行时是单例的,这意味着每个枚举值都是唯一的,并且可以保证在整个程序中的一致性。
  • 下面是几种常用的用法
enum class Color { RED, GREEN, BLUE }

enum class Color(val r: Int, val g: Int, val b: Int) { RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); fun rgb() = (r * 256 + g) * 256 + b }
  • 迭代和范围
for (color in Color.values()) {
    println(color)
}

println(Color.valueOf("RED"))

if (color in enumValues<Color>()) {
    // ...
}

对象表达式和匿名函数
对象表达式
  • 对象表达式用于创建匿名内部类的实例。当你需要创建一个实现了某个接口或继承了某个类的匿名类实例时,可以使用对象表达式。对象表达式可以访问作用域中的变量,并且可以定义多个方法。
interface MyInterface { fun myMethod() }
val myObject = object : MyInterface {
    override fun myMethod() {
        // 实现方法
    }
}
myObject.myMethod() // 输出 "Hello from object expression!"
匿名函数
  • 匿名函数是一种没有名称的函数,它可以被用作值传递。匿名函数通常用于表示简短的功能或传递函数作为参数。
val myLambda: (Int, Int) -> Int = { x, y -> x + y }

委托
  • 在 Kotlin 中,委托(Delegation)是一种设计模式,它允许你将一个对象的一部分功能委托给另一个对象来实现。这种模式在 Kotlin 中是通过语言特性直接支持的,使得代码更加简洁和易于维护。
委托的基本概念
  • Kotlin 中的委托通常涉及到两个角色:
    1. 委托对象(Delegate):包含你想要委托的功能的对象。
    2. 被委托对象(Delegatee):将部分功能委托给委托对象的对象。
委托的类型
  • Kotlin 中有几种不同类型的委托:
  1. 属性委托(Property Delegation): 属性委托允许你将属性的 getter 和 setter 实现委托给另一个对象。Kotlin 提供了标准委托,如 LazyObservableVetoable,以及自定义委托。
import kotlin.properties.Delegates

class Example {
    var p: String by Delegates.notNull()
}

在这个例子中,p 属性的 getter 和 setter 实现被委托给了 Delegates.notNull()
2. 方法委托(Method Delegation): 方法委托允许你将方法调用委托给另一个对象。Kotlin 中的 by 关键字用于实现方法委托。

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

在这个例子中,Derived 类将 print 方法的实现委托给了 b 对象。
3. 扩展函数委托(Extension Function Delegation): 扩展函数委托允许你将扩展函数的实现委托给另一个对象。

class Example

fun Example.print() {
    println("Printed")
}

val example = Example()
example.print() // 输出 "Printed"

实战

  • 我们来看看MainActivity.kt
package com.example.pathrecorderapp  
  
import android.os.Bundle  
import androidx.activity.ComponentActivity  
import androidx.activity.compose.setContent  
import androidx.activity.enableEdgeToEdge  
import androidx.compose.foundation.layout.fillMaxSize  
import androidx.compose.foundation.layout.padding  
import androidx.compose.material3.Scaffold  
import androidx.compose.material3.Text  
import androidx.compose.runtime.Composable  
import androidx.compose.ui.Modifier  
import androidx.compose.ui.tooling.preview.Preview  
import com.example.pathrecorderapp.ui.theme.PathRecorderAppTheme  
  
class MainActivity : ComponentActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        enableEdgeToEdge()  
        setContent {  
            PathRecorderAppTheme {  
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->  
                    Greeting(  
                        name = "Android",  
                        modifier = Modifier.padding(innerPadding)  
                    )  
                }  
            }        }    }  
}  
  
@Composable  
fun Greeting(name: String, modifier: Modifier = Modifier) {  
    Text(  
  
  
        text = "Hello $name!",  
        modifier = modifier  
    )  
}  
  
@Preview(showBackground = true)  
@Composable  
fun GreetingPreview() {  
    PathRecorderAppTheme {  
        Greeting("Android")  
    }  
}
MainActivity 类

MainActivity 类继承自 ComponentActivity,这是一个 Jetpack Compose 兼容的 Activity 类。在这个类中,onCreate 方法被重写,用于设置应用程序的内容。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge() // 启用边缘到边缘的屏幕显示
        setContent { // 设置应用程序的内容
            PathRecorderAppTheme { // 使用应用程序的主题
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting( // 调用 Greeting Composable 函数
                        name = "Android",
                        modifier = Modifier.padding(innerPadding) // 应用内边距
                    )
                }
            }
        }
    }
}
Greeting Composable 函数
  • Greeting 函数是一个 Composable 函数,用于显示一个问候信息。它接受两个参数:namemodifier
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!", // 显示问候信息
        modifier = modifier // 应用修饰符
    )
}
GreetingPreview Composable 函数

GreetingPreview 函数是一个预览 Composable 函数,用于在 Android Studio 的预览窗口中显示 Greeting 函数的预览。

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    PathRecorderAppTheme { // 使用应用程序的主题
        Greeting("Android") // 调用 Greeting Composable 函数
    }
}

小结

  • 下一节我们将开始书写此应用的具体逻辑,完成用户位置定位的代码设计
  • 如有错误,欢迎指出,感谢大家的支持

参考

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值