1.前言
在上一章节,我们完成了安卓程序开发的环境搭建,从本节开始,我将带着大家从零开始一步一步开发出属于自己的安卓应用程序。
但要注意,我并不会真的完全从零基础开始一步一步给你介绍基本的语法细节,学习本系列教程之前,你至少需要会一门常见的语言,比如C/C++、python、java乃至前端的js。
比如正在开始写本教程的作者本人,截至目前写出这段文字时刻,我对将要使用的kotlin语言甚至都说不上会,只能说有点了解,写过hello world,仅此而已。
至于安卓App的开发,我更是在此前从来没有Android Studio这个开发环境开发过(仅仅使用过tauri框架自带的、将前端转为安卓App的功能)。
但这并不妨碍我写本系列教程,因为事实上各种语言、各种应用,本质上都是互通的,只不过换了个语法、规则,仅此而已。
而我又刚好写过大量的PC端应用程序、网站应用,了解常见的UI布局方式、软件底层运行原理,熟悉很多语言的语法规则及其本质。
所以在我看来,学习语言、框架都不要局限于某一类,当你见识的足够多时,就会逐渐触碰到本质,会发现一切大道均殊途同归,从而当你遇到一个新的框架、语言时,都不会有太多的波澜。
尤其是如今依然来到了大模型时代,如果遇到不会的,问一问deepseek就可以了,基本很快就能上手开发应用程序。
2.项目介绍
首先我们进入安装好的Android Studio程序,新建一个项目:
如果你没有新建过任何项目,那么你的上图界面和我长得并不一样,但依旧是点击“New Project”这个按钮,也就是新建项目。
然后来到下一步,选择手机、空项目,最后点击下一步:
从上面这个界面我们就能看出来,由于安卓系统运行在了各种设备上,所以实际上它并不仅仅是用来开发手机App的,比如第二个wear os
实际上就是开发智能手环App的,第三个 Television
则用来开发智能电视App的,最后一个automotive
则是用来开发智能驾驶App的。
而这里选择的手机类别中的Empty Activity
这个名字也很有讲究,前面Empty
是空的意思,而Activity
则是活动的意思,它为什么不叫空项目、而是叫空活动这样一个奇怪的名字呢?
这个会留到后续适当的时候对其进行介绍,这里就不展开了。
紧接着来到下一个创建项目的页面:
可以看到这里有很多选项,但实际上大部分都直接默认即可,只需要在第一个位置填上我们的App名称,比如我这里填的FirstApp
。
下面简单介绍一下各部分的作用:
- Name:项目名,比如QQ、微信这类手机App,它们的项目名称大概率就是QQ、WeChat之类的,注意项目名最好不要用中文。
- Package name:包名,由于安卓最开始是与Java强绑定在一起的,这实际上属于Java中的语法范畴,这相当于是将我们的APP代码也看做一个代码库,同时我们也能引入别人写的库,而这就是在设置这个代码库的名称,其规则一般是倒序,反应的是目录结构,比如这里是先是顶层的com,然后是例子example,最后是我们的名称firstapp,它会根据我们填入的项目名自动生成,不用管。
- Save Location:保存位置,我这里直接默认了,如果你不想保存在C盘,可以自行更换路径
- Minimum SDK:使用的最小SDK版本,安卓应用程序开发本质上就是调用各种安卓开发库,而这里的SDK指的就是安卓官方的开发库,理论上SDK版本越早,那么开发出来的应用程序支持的设备就越多,比如这里的下一行写的就是该SDK可以支持97.4%的设备,当然你也可以修改这里的版本,但一般选择这里默认的即可,由于不同版本SDK支持的功能并不完全相同,所以推荐你选择我这里所使用的SDK版本。
- Build configuration language:构建我们app的配置文件所用语言,一般也是直接默认选择这里的Kotlin即可。
最后点击完成。
在进入我们新建的项目之后,它会默认执行构建脚本,如果不存在指定版本的gradle构建包,就会自动取拉取。
只有完成了这一步,我们的项目才算真的建立完成。
这里的gradle构建系统,和C++中的cmake、rust中的cargo等项目管理工具类似,就是用来帮助我们添加各种第三方包、配置项目属性、编译构建我们代码成为最终app的。
由于安卓开发中默认用到了大量的文件,因此为了方便使用,android提供了不同的文件展示方式,常用的就是下面两个:
其中“项目”指的就是像文件管理器一样显示目录,选择它后你会发现,整个项目目录非常繁杂,甚至你需要点开四五个文件夹才能找到我们的代码文件,所以实际开发中我们一般不会选择这个。
我们大多数时候选择的都是Android这个选项,它会将我们开发App所需要的资源、代码文件分类整理好,可以让我们快速找到目标文件。
比如上面图中,我们可以快速看到重要的目录与文件:
- manifests:该文件存放清单文件,当安卓系统运行我们程序时,就是通过这个文件知道我们程序所需要哪些权限、限制哪些设备能用等等。
- kotlin+java:我们的代码所在,日后我们的代码也将在这个文件中完成,比如其自动生成的入口文件
MainActivity.kt
就在这个文件夹下。 - res:资源文件,比如各种图片、音乐等等,都将放在这个目录下。
- Gradle Scripts:grade构建脚本目录,用于配置grade这个构建系统的,比如当我们需要添加某个第三方包、修改代理配置等等,都是在这个目录中找到配置文件进行修改。
3.App代码速览
然后我们开始进入App的代码开发环节,让你可以快速上手开发App,如果遇到不理解的,直接询问deepseek即可。
当新建一个空项目之后,进入入口文件,可以看到其默认为我们实现了一个类与两个方法:
其中最前面的MainActivity
继承自ComponentActivity
,它是我们的程序入口,也就是当我们App跑起来之后,第一步就是找到这个类,然后执行它里面的入口函数。
你可能会问为什么是它?我能修改它吗?
这当然是可以的:
如果你想要修改这个入口类的名字,那么你就需要同步修改配置文件中的信息,之所以这里MainActivity
会是入口类,仅仅只是因为配置文件中默认写的是它而已。
但要注意,虽然你可以修改它的名字,但最好不要修改它的继承关系以及类里面的函数代码,除非你真的理解了它的运行逻辑,否则一旦报错你可能就不太好找出愿意。
中间第二个是一个示例组件函数,它的最前面有一个标注:
@Composable
意思是可组合的,使用kotlin开发App时,尤其需要注意这个标注,都有非常重要的含义。
使用kotlin开发App的基础概念就是,任何UI都是一个可被组合的函数,使用这里的Composable
进行标注。
最后第三个函数,它又多了一个标注:
@Preview
这里的意思是预览,可以让我们方便的实时预览展示效果。
比如这里,它的内部代码实际上和前面的入口函数所展示的效果是一模一样的,你可以直接点击它的左边运行它:
它的效果是每当你修改代码后,就会自动重新构建将效果展示在界面上,但可能是bug,在我这里并没有效果,以后有机会再试试吧~
下面来到入口函数:
这里的onCreate是安卓开发的入口函数,从函数签名的override我们可以知道,它实际上是重写的父类函数。
因此该函数第一步就是调用父类的onCreate函数,保证父类中的数据可以被正常的初始化,在kotlin中super指代父类,这里的参数可以看它的名称,就是一个保存App状态的实例:
super.onCreate(savedInstanceState)
至于第二个函数,看起名称是启动边到边功能,不懂,那就问问deepseek、浏览器搜一搜。
大概含义就是启用全面屏功能,如果不调用这个函数,当启动我们的App时,手机屏幕边缘可能会存在白边,也就是没有完全被我们的App占满。
最后第三个函数,setContent,意思就是设置我们App程序的内容,这是我们的重点。
注意它这里的写法,看起来特别奇怪:setContent{ ... }
,这怎么看也不像是在调用一个函数。
这实际上是kotlin中的语法简写,其原本的函数调用代码大致如下:
setContent(null,{ ... })
只不过由于第一个参数有默认值null不需要填,第二个参数为一个尾随的lambda表达式,因此有了这里这种写法(说实话,这种写法我也是第一次见,kotlin对函数调用的简化实在是太狠了)。
setContent(){...}
//由于无参数,可以继续简化
setContent{...}
所以简单来说,这里大括号中的内容,实际上是一个无参lambda表达式作为该函数的最后一个参数。
然后在这个函数里面,调用了一个默认主题FirstAppTheme
:
FirstAppTheme {
...
}
这同样也是为我们自动生成的,一般也不需要管:
可以看到,它的最后一个参数同样是一个尾随lambda表达式,因此它内部的这些内容都是填入这个函数的最后一个参数的。
而它内部的内容就是我们最重要的部分了,因为这就是在写我们App的界面了。
首先是Scaffold
,它是一个基本的界面,你可以把它看作我们App的屏幕,因为它的参数modifier值为Modifier.fillMaxSize(),也就是填充最大尺寸。
注意modifier参数,这是所有基本UI组件都存在的一个参数,你可以将其看做前端里面的CSS,也就是布局参数,通过调用Modifier对象上的各种样式函数设置其各种样式。
它的内部便是我们要填充的各种组件了,比如这里默认填充的就是这个Greeting函数组件。
Scaffold(modifier = Modifier.fillMaxSize()){ innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
这里的innerPadding参数指的是内边距,这是必要的,因为有些手机有类似刘海屏之内的东西,如果不添加这个边距属性,就可能导致你的软件界面被刘海屏给挡住。
4.记事本App实现
有了前面的基础之后,我们就可以来实现一个最简单的记事本程序了,最终的效果如下:
首先入口函数的代码我们就不需要动了,只是简单的修改一下greeting函数即可:
首先将该函数的名字改为了Notepad,并且参数也只接受一个样式的参数。
然后在Notepad函数内部,我声明了四个变量,首先是保存记事本内容的变量note: