drawable 放图片
mipmap 放图标
drawable 我们应该自己创建drawable-hdip、drawable-xhdpi、drawable-xxdpi。当然这只是理想情况,更多的时候美工指挥提供给我们一份图片,这时可以把所有图片放到 drawable-xxhdpi 目录下,因为这是最主流的设备分辨率目录
我们新建的 kotlin 项目,有两个 build.gradle,先看外层的
buildscript {
ext.kotlin_version = "1.3.72"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
首先,两处repositories
的闭包中都声明了google()
和jcenter()
这两行配置,其实它们分别对应了一个代码仓库,google 仓库中包含的主要是 Google 自家 的扩展依赖库,而 jcenter 仓库中包含的大多是些第三方的开源库。声明了这两行配置之后, 我们就可以在项目中轻松引用任何 google 和 jcenter 仓库中的依赖库了
接下来dependencies
中使用 classpath
声明了两个插件:一个 Gradle 插件和一个 Kolin 插件。为什么要声明 Gradle 插件呢? 因为 Gradle 并不是专门为构建Android 项目而开发的。Java、C++等很多种项目也可以使用Gradle来构建,此如果我们想用它来构建 Android 项目,则需要声明 com.android.tools.build:gradle:4.0.1
这个插件。其中,最后面的部分是插件的版本 号,它通常和当前 Android Studio 的版本是对应的,比如我现在使用的是 Android Studio 4.0.1 版 本,那么这里的插件版本号就应该是4.0.1
而另外一个Kotlin 插件则表示当前项目是使用 Kotlin 进行开发的,如果是 Java 版的Android项目,则不需要声明这个插件
通常情况下,你并不需要修改这 个文件中的内容,除非你想添加一些全局的项目构建配置
下面看 app 下的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.xx.kotlinapplication"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
首先第一行应用了一个插件,一般有两种值可选:com. android.application
表示这是一个应用程序模块, com. android.library
表示这是一个库模块。二者最大的区别在于,应用程序模块是可以直接运行的,库模块只能作为代码库依附于别的应用程序模块来运行
接下来的两行应用了kotlin-android
和kotlin-android-extensions
这两个插件。如果你想要使用 Kotlin 来开发Android项目,那么第一个插件就是必须应用的。而第二个插件帮助我们实现了一些非常好用的Kotlin扩展功能
紧接着是一个大的android
闭包,在这个闭包中我们可以配置项目构建的各种属性。其中, compilesdkVersion
用于指定项目的编译版本,这里指定成29表示使用 Android 10.0 系统的 SDK编译。buildToolsversion
用于指定项目构建工具的版本,目前最新的版本就是29.0.3, 如果有更新的版本时,Android Studio会进行提示
android
闭包中又嵌套了一个defaultConfig
闭包,defaultConfig
闭包 中可以对项目的更多细节进行配置。其中,applicationId
是每一个应用的唯一标识符, 绝对不能重复,默认会使用我们在创建项目时指定的包名,如果你想在后面对其进行修改,那么就是在这里修改的。关于 minSdkVersion
、 targetSdkVersion
的含义和区别可查看 minSdkVersion、compileSdkVersion、targetSdkVersion。versionCode
用于指定项目的版本号,versionName
用于指定项目的版本名
testInstrumentationRunner
用于在当前项目中启用JUnit 测 试,你可以为当前项目编写测试用例,以保证功能的正确性和稳定性
buildTypes
闭包 中用于指定生成安装文件的相关配置,通常只会有两个子闭包:一个是debug,一个是release。debug闭包是可以忽略不写的。minifyEnabled
用于指定是否对项目的代码进行混淆。proguardFiles
用于指定混淆时使用的规则文件,这里指定了两个文件:第一个proguard-android-optimize.txt是在
目录下的,里面是所有项目通用的混淆规则;第二个proguard-rules.pro是在当前项目的根目录下的,里面可以编写当前项目特有的混淆规则。需要注意的是,通过Android Studio直接运行项 目生成的都是测试版安装文件
dependencies
闭包可以指定当前项目所有的依赖关系。通常Android Studio项目共有3种依赖 方式:本地依赖、库依赖和远程依赖。本地依赖可以对本地的 jar 包或目录添加依赖关系,库依赖可以对项目中的库模块添加依赖关系,远程依赖则可以对 jcenter 仓库上的开源项目添加依赖关系
dendencie
闭包中,第一行的implementation fileTree
就是个本地 依赖声明,它表示将libs目录下所有后缀为 .jar 的文件都添加到项目的构建路径中
而implementaton
则是远程依赖声明,implementation 'androidx.appcompat:appcompat:1.2.0'
就是一个标准的远程依赖库格式, 其中androidx.appcompat
是域名部分,用于和其他公司的库做区分;appcompat
是工程名部分. 用于和同一个公司中不同的库工程做区分;1.2.0
是版本号,用于和同一个库不同的版本做区分。 加上这句声明后,Gradle在构建项目时会首先检查下本地是否已经有这个库的缓存,如果没有的 话则会自动联网下载,然后再添加到项目的构建路径中
至于库依赖声明这里没有用到,它的基本格式是implementation project
后面加上要依赖的库的名称,比如有一个库 模块的名字叫 helper, 那么添加这个库的依赖关系只需要加入implementation project(':helper)
这句 声明即可
testImplementation
和androidTestImplementation
都是用于声明测试用例库的,这个我们暂时用不到,先忽略
编程语言大致可分为两种:编译型语言和解释型语言。编译型语言的特点是编译器会将我们编写的源代码一次性编译成计算机可识别的二进制文件,然后计算机直接执行,像C和C++都属于编译型语言。解释型语言则完全不一样,它有一个解释器,在程序运行时,解释器会一行行地读取我们编写 的源代码,然后实时地将这些源代码解释成计算机可识别的二进制数据后再执行,因此解释型语言通常效率会差一些,像Python和JavaScript都属于解释型语言
那Java 是属于编译型语言还是解释型语言呢?对于这个问 题,即使是做了很多年Java开发的人也可能会答错。有Java 编程经验的人或许会说,Java 代码
肯定是要先编译再运行的,初学Java的时候都用过javac这个编译命令,因此Java属于编译型语 言。如果这也是你的答案的话,那么恭喜你,答错了!虽然Java代码确实是要先编译再运行的, 但是Java代码编译之后生成的并不是计算机可识别的二进制文件,而是一种特殊的class
文件, 这种class
文件只有Java 虚拟机( Android中叫ART,一种移动优化版的虚拟机)才能识别,而 这个Java虚拟机担当的其实就是解释器的角色,它会在程序运行时将编译后的class
文件解释成计算机可识别的二进制数据后再执行, 因此,准确来讲,Java 属于解释型语言
其实Java虚拟机并不直接和 你编写的java代码打交道,而是和编译之后生成的class
文件打交道。那么如果我开发了一门新的编程语言。然后自己做了个编译器,让它将这门新语言的代码编译成同样规格的class
文件,java 虚拟机能不能识别呢?设错,这其实就是Kolin的工作原理了
kotlin语法简洁、语法更高级,而且它和 java 100%兼容,可以直接调用 java 编写的代码,而且可以无缝使用 java 第三方开源库
第一种办法是下载 IntelliJ IDEA,具体可以看我之前的 Kotlin教程
第二种办法是在线运行kotlin,之后就快点了
第三种办法就是Android Studio,我们可以创建一个安卓项目,然后编写一个 kotlin 的 main() 函数就可以独立运行 kotlin 代码了
我们使用第三种方法,新建一个Kotlin项目,和平时新建 Android 项目一样,不过语言选择 kotlin
然后 MainActivity 同级新建 一个 LearnKotlin 文件
在这个文件编写一个 main 函数,点击左侧绿色按钮即可运行
通常的判空代码
if(a != null){
a.doSomething()
}
可以使用 ?.
操作符进行简化。?.
的作用是对象不为空时正常调用相应方法,为空时什么都不做
a?.doSomething()
再学习一个操作符 ?:
,这个操作符左右两边都接收一个表达式,如果左边表达式不为空就返回左边表达式的结果,否则就返回右边表达式结果,例如之前代码:
val c = if(a != null){
a
}else{
b
}
可简化成
val c = a ?: b
例如我们写一个得到文本长度的方法
fun getTextLength(text:String?):Int{
if(text!=null){
return text.length
}
return 0
}
使用操作符可以简化为
fun getTextLength(text:String?) = text?.length ?: 0
我们写这样一段代码,定义了一个可以为空的全局变量 content,然后在 main() 函数中 content 不为空时调用 printUpperCase()。结果发现 printUpperCase() 认为存在空指针风险,编译不通过
我们可以使用非空断言工具,告诉 Kotlin,我非常确信这里不会为空,不用做空指针检查了
fun printUpperCase(){
val upperCase = content!!.toUpperCase()
println(upperCase)
}
这样写并不好,其实是一个潜在的空指针异常
最后再学习一个辅助工具 let
。这是一个函数,这个函数提供了函数式 API 编程接口,并将原始调用对象作为参数传递到 Lambda 表达式中
obj.let{obj2 ->
//编写具体业务逻辑
}
obj 对象调用了 let 函数,Lambda 表达式中代码被执行,obj 对象会作为参数传递到 Lambda 表达式中。不过为了防止变量重名,这里将参数改成了 obj2,实际上它们是同一个对象。先看一段代码:
fun doStudy(study:Study?){
study?.readBooks()
study?.doHomework()
}
doStudy()
这个方法传入的 study 可以为空,然后在方法内再进行判断:如果 study 不为空执行study.readBooks()
,如果为空不进行任何操作;如果 study 不为空执行 study.doHomework()
,如果为空不进行任何操作
这样写是有点啰嗦的,受制于 ?.
我们判断了两遍 study 是否为空,这时候我们使用 let
进行优化
fun doStudy(study:Study?){
study?.let{
study.readBooks()
study.doHomework()
}
}
对象不为空调用 let
函数。let
会将 study 本身作为参数传到 Lambda 表达式中。当 Lambda 表达式的参数列表只有1个参数时,可直接使用 it
代替,所以简化为:
fun doStudy(study:Study?){
study?.let{
it.readBooks()
it.doHomework()
}
}
最后一点:let
函数可以处理全局变量判空问题,if
不行。全局变量的值随时可能被其他线城修改,即使做了判空处理也无法保证 study 变量空指针风险。这一点上就体现了 let
函数的优势
点击布局中的按钮,用 Toast 弹出 “Hello”
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button:Button = findViewById(R.id.button)
button.setOnClickListener{ Toast.makeText(this,"Hello",Toast.LENGTH_SHORT).show()}
}
}
使用 Kotlin编写的 Android 项目在 build.gradle
文件头部默认引入了一个 kotlin-android-extensions
插件,这个插件会根据布局文件中定义的控件 id 自动生成一个具有相同名称的变量,在Activity 中可以直接使用这个变量,因此我们不用再写 findViewById
了:
在 menu 下增加 main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add_item"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:title="Remove" />
</menu>
在这里插入代码片override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main,menu)
return true
}
我们在使用 menuInflater
时,实际上是调用了父类的 getMenuInflater()
方法。 getMenuInflater()
方法能得到一个 MenuInflater 对象。这是 Kotlin 给我们的语法糖。最后这个方法返回 true,表示允许将这个菜单显示出来,如果返回 false ,创建的菜单将无法显示
重写 onOptionsItemSelected
方法
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
R.id.add_item -> Toast.makeText(this,"You clicked Add",Toast.LENGTH_SHORT).show()
R.id.remove_item -> Toast.makeText(this,"You clicked Remove",Toast.LENGTH_SHORT).show()
}
return true
}
这里的 item.itemId
也用到了刚才的语法糖,背后调用 item 的 getItemId()
方法
打开 SecondActivity
val intent = Intent(this,SecondActivity::class.java)
startActivity(intent)
注意:Kotlin 中 SecondActivity::class.java 的写法就相当于 Java 中 SecondActivity.class 的写法
打开网页
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.baidu.com")
startActivity(intent)