前言
2月底的时分,Android 官方发布了Compose的完好课程。了解到许多小伙伴还没开端学习Compose,所以我写了一篇根底文章,让咱们一同轻松上手Compose~
在这篇文章中咱们将初步了解 Jetpack Compose,并学习可组合函数、根本布局和情况以及主题等根底知识。
Jetpack Compose是什么
Jetpack Compose 是用于构建原生 Android 界面的新东西包。它运用更少的代码、强大的东西和直观的 Kotlin API,能够帮助开发者简化并加速 Android 界面开发。
在此之前,咱们怎么完成一个事务功用呢?咱们是在Activity中编写Java/Kotlin的代码,在XML中编写布局代码,这种办法是咱们现已运用了很久的办法,而Jetpack Compose完全抛弃了之前的办法,新发明了一种“运用代码”编写页面的办法,而这种办法,有一个好听的姓名,叫做声明式UI。接着咱们来看,怎么创立一个Compose项目?
初识Compose项目
咱们直接选择Material3的Compose项目模板。
Compose最低支撑的版本是21。创立好项目后,咱们来看默许生成MainActivity的代码。
classMainActivity:ComponentActivity(){
overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContent{
Compose01Theme{
//A surface container using the'background'color from the theme
Surface(modifier=Modifier.fillMaxSize(),
color=MaterialTheme.colorScheme.background){
Greeting("Android")
}
}
}
}
}
@Composable
funGreeting(name:String){
Text(text="Hello$name!")
}
@Preview(showBackground=true)
@Composable
funDefaultPreview(){
Compose01Theme{
Greeting("Android")
}
}
setContent相似setContentView相同为Activity设置布局,这儿的Compose01Theme是依据项目称号生层的主题称号。这一块咱们稍后再来了解。
Greeting函数运用了@Composeable注解称之为组合函数,@Composeable注解注释可告知 Compose 编译器,此函数需求转化为页面显现,而且和协程中suspend函数相同,只能在Composeable注解函数中调用另外一个Composeable注解函数,@Preview注解是方便开发者在不运转的前提下可预览作用。作用如下图所示。
@Composable
funGreeting(name:String){
Text(text="Hello$name!")
}
Greeting函数中的Text组件,便是Compose供给的文本组件,相似XML办法中的TextView组件,代码如下所示:
<TextView
android:id="@+id/tvName"
android:layout_width="200dp"
android:layout_height="200dp"
/>
tvName.setText="Hell$name!"
从上述比照能够看出来,Compose的写法是十分简练的。
现在咱们在Greeting函数中再增加一个Text组件,代码如下:
@Composable
funGreeting(name:String){
Text(text="Hello$name!")
Text(text="First Compose Demo")
}
运转程序,成果如下图所示。
咱们看到文字都堆叠在一同了,咱们知道在XML布局中有LinearLayout、RelativeLayout等布局组件,那么在Compose中有哪些布局呢?
Compose中的根底布局
Compose中的根底布局主要有Column、Row、Box等,接下来咱们来看这些布局怎么运用。
Column
Column布局使得组件笔直摆放,相似LinearLayout 的orientation特点设置为vertical。咱们运用Column布局来处理上面的问题。修正Greeting办法代码如下所示:
@Composable
funGreeting(name:String){
Column(){
Text(text="Hello$name!")
Text(text="First Compose Demo")
}
}
运转程序,成果如下所示:
Row
Row布局使得组件水平摆放,相似LinearLayout 的orientation特点设置为horizontal,将上面两个Text改为水平摆放,修正代码如下所示:
@Composable
funGreeting(name:String){
Row(){
Text(text="Hello$name!")
Text(text="First Compose Demo")
}
}
运转程序,成果如下所示。
Box相当于XML中的FrameLayout,还有ConstraintLayout等布局,这儿就不逐个展示了。感兴趣的咱们可自行了解。
在上面的图中咱们看到,两个Text紧紧的贴在一同了,在XML布局中咱们能够运用padding或许margin来处理这个问题,在Compose中怎么处理呢?以及咱们怎么为文字设置色彩、巨细等款式呢?这就需求运用Compose的Modifier修饰符。
Compose中的Modifier修饰符
运用Compose修饰符能够更改巨细、布局、外观与增加点击事情等。咱们先来处理上面留传的问题。
咱们能够运用padding修饰符来为组件增加内边距。修正代码如下所示:
@Composable
funGreeting(name:String){
Row(){
Text(text="Hello$name!")
Text(text="First Compose Demo",
modifier=Modifier
.padding(10.dp)
.background(Color.Red)
.clickable{
//click
})
}
}
咱们运用padding修饰符为Text增加了10dp的边距,运用background修饰符为Text增加赤色的布景,运用clickable特点为Text增加点击事情。
运转程序,成果如下图所示。
在Compose中是没有相似margin外边距修饰符的。这是由于modifier修饰符的顺序会影响终究成果。怎么理解呢,比方咱们将上面的代码布景与边距的先后顺序调整一下,代码如下所示:
@Composable
funGreeting(name:String){
Row(){
Text(text="Hello$name!")
Text(text="First Compose Demo",
modifier=Modifier
.background(Color.Red)
.padding(10.dp)
.clickable{
//click
})
}
}
再次运转程序,成果如下图所示。
这样先增加布景色,再设置边距就成了内边距的作用,同理,假如调整padding与clickable的修饰符,点击区域也会产生变化,感兴趣的能够自行尝试。
除此之外,modifier还供给了align对其办法等各种修饰符,需求咱们在今后的运用中,慢慢了解。
到现在为止,咱们现已学习了根底布局和修饰符的运用,接下来咱们来依据作用图来“实战一下吧”~
布局小实战
接下来咱们完成这样的一个作用图,文字和按钮左右摆放,并为文字和按钮设置你喜欢的恣意色彩。
OK,实践起来很简略,直接上代码~,如下所示:
@Composable
funMore(){
Row(modifier=Modifier
.background(Color.Red)
.padding(10.dp)){
Text(text="Compose课程已完善,快来学习吧~",
fontSize=16.sp,color=Color.White,modifier=Modifier.weight(1f)
)
Button(onClick={}){
Text(text="检查概况",color=Color.White)
}
}
}
这儿你或许需求关注为Text设置字体巨细色彩的办法以及Button组件,这是咱们之前没有提到过的。运转程序,成果如下图所示。
假如咱们想生成多条数据怎么办?咱们只需求采用Kotlin句子就能够。代码如下所示:
Column(){
for(iin0..10){
more()
}
}
由于组件要笔直摆放,所以咱们在外面套一个Column布局,然后for循环10次,运转程序,成果如下图所示。
现在数据是写死的,无法动态修正数据,More函数并不是一个能够复用的情况。接下来咱们将More办法抽取为可复用的情况,即将相关参数提取出来。修正代码如下所示:
@Composable
funMore(title:String){
Row(modifier=Modifier
.background(Color.Red)
.padding(10.dp)
){
Text(text=title,
fontSize=16.sp,color=Color.White,modifier=Modifier.weight(1f)
)
Button(onClick={}){
Text(text="检查概况",color=Color.White)
}
}
}
然后在调用的地方动态生成数据,代码如下:
Column(){
for(iin0..10){
More("Compose课程第${i+1}课,快来学习吧~")
}
}
再次运转程序,成果如下图所示。
将Compose函数抽取为可复用的组合项,将会是咱们常常运用的。
接着咱们思考一个问题:假如生成20条数据呢?就会发现屏幕显现不下了,在XML中咱们能够嵌套ScrollView或许修正成RecycleView的办法来处理。在Compose中最简略的一种处理办法便是为Column增加可翻滚的特点,代码如下所示:
Column(modifier=Modifier.verticalScroll(rememberScrollState())){
for(iin0..10){
More("Compose课程第${i+1}课,快来学习吧~")
}
}
这种办法尽管能够处理问题,但是当数据量很大的时分功用或许会十分低,Compose中也为咱们供给了推迟列表组件。快来一同学习一下吧~
推迟列表组件
Compose为咱们供给了LazyColumn和LazyRow组件,相当于XML中的RecycleView组件,从姓名中咱们也能够知道一个是笔直翻滚一个是水平翻滚。咱们先来看笔直翻滚列表组件 —— LazyColumn。
修正上面的代码如下所示:
LazyColumn(content={
item{
for(iin0..10){
More("Compose课程第${i+1}课,快来学习吧~")
}
}
})
LazyColumn API 会在其作用域内供给一个 item 元素,并在该元素中编写各项内容,当然在实践项目中咱们或许会把数据包装起来,比方包装成一个list,比方咱们界说一个生成20条数据的办法,代码如下所示:
fungetData():List<String>{
returnList(20){"Compose课程第${it+1}课,快来学习吧~"}
}
这样一来,上面的代码就变成了这样:
LazyColumn(){
items(items=getData()){data->
More(title=data)
}
}
运转程序,成果如下图所示:
咱们也能够为LazyColumn设置其他特点,详细可自行参照LazyColumn的源码。
LazyRow与LazyColumn的运用办法是相同的,仅仅作用是水平翻滚,这儿简略看一下,修正代码如下所示:
LazyRow(){
items(items=getData()){data->
More(title=data)
}
}
运转程序,成果如下图所示。
咱们都知道在RecycleView中还供给了网格布局布局和流布局,在Compose中也别离对应LazyGrid与LazyVerticalStaggeredGrid,感兴趣的咱们可自行了解。
到现在为止咱们现已完成了一个简略的列表完成,但是列表中的“检查概况”功用还没有完成。完成这一功用需求运用Compose中的情况,接下来咱们就一同学习Compose中的情况吧~
Compose中的情况
咱们说Compose是声明式的,与之对应的XML是命令式的,以文本设置值为例,命令式之所以被称之为命令式,是当文本变化的时分咱们都需求手动调用textview.setText = “”,而由于 Compose 是声明式的,所以更新它的仅有办法是经过新参数调用同一可组合项。这些参数是界面情况的表现形式。每当情况更新时,都会产生重组。可组合项也有必要清晰获知新情况,才干相应地进行更新。咱们来经过一个实例看一下。
从计数器功用开端
新建一个Compose函数,咱们来尝试完成一个计数器的功用:点击加号按钮数字增加,代码如下所示:
@Composable
funCounter(){
varnumber=0
Column(){
Text(text="当时数值:$number")
Button(onClick={
number++
}){
Text(text="add")
}
}
}
文本显现变量number,文本和按钮笔直摆放,点击按钮时number加1,运转程序,成果如下图所示:
一切看起来很正常,但是点击“add”咱们会发现,文本中显现的数值并没有改动。这便是咱们上面所说的有必要清晰获取新情况,才干进行相应的更新。
运用mutableStateOf 会创立可观察的 MutableState,源码如下所示:
interfaceMutableState<T>:State<T>{
overridevarvalue:T
}
运用 remember API 将对象存储在内存中。体系会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间返回存储的值。
当 value 产生变化时,体系就会将运用到 value 的所有可组合函数重组。所以这儿咱们将number变量改写,代码如下所示:
@Composable
funCounter(){
varnumberbyremember{
mutableStateOf(0)
}
Column(){
Text(text="当时数值:$number")
Button(onClick={
number++
}){
Text(text="add")
}
}
}
运转程序,成果如下图所示。
能够看到,这样当点击“add”按钮时,文本的数值会不断增加。由于咱们将 number变量声明为State类型使其变为Compose可观察的情况,Compose监测到情况变化触发函数重组,这背后的原理得益于Compose的快照体系,感兴趣的咱们能够自行了解。
Compose 是一个声明性界面结构。它描绘界面在特定情况下的情况,而不是在情况产生变化时移除界面组件或更改其可见性。调用重组并更新界面后,可组合项终究或许会进入或退出组合。
不过Counter函数内部包含有情况的可组合项,或许不利于函数的复用,比方咱们现在还有一个功用,每次点击计数器时数值加10,这个时分咱们就要再copy一个函数改写代码。咱们应该让可组合项尽或许的不保存任何情况。处理这个问题咱们能够运用情况提高。
情况提高
Compose 中的情况提高是一种将情况移至可组合项的调用方以使可组合项无情况的形式。如上面的计数器代码,咱们是在setContent中调用的,代码如下所示:
Compose01Theme{
Surface(modifier=Modifier.padding(vertical=4.dp,horizontal=8.dp),
color=MaterialTheme.colorScheme.background){
Counter()
}
}
}
咱们将情况提高到调用的地方,一般的情况提高是将情况变量替换为两个参数。
-
value: T:要显现的当时值
-
onValueChange: (T) -> Unit:请求更改值的事情,其间 T 是主张的新值
此值表示任何可修正的情况,比方计数器中的number变量,onValueChange仅仅一个办法名,依据上下文随意命名即可。
修正Counter函数代码如下所示:
@Composable
funCounter(number:Int,onClickValue:()->Unit){
Column(){
Text(text="当时数值:$number")
Button(onClick=onClickValue){
Text(text="add")
}
}
}
然后在调用的地方修正如下:
Surface(modifier=Modifier.padding(vertical=4.dp,horizontal=8.dp),
color=MaterialTheme.colorScheme.background){
varnumber by remember{
mutableStateOf(0)
}
Counter(number,onClickValue={
number+=1
})
}
这样就完成了对counter函数的情况提高。假如现在咱们有其他计数需求能够直接这样:
varnumber2byremember{
mutableStateOf(0)
}
Counter(number,onClickValue={
number+=10
})
经过情况提高咱们能够达到组合项复用、解耦等意图。而且情况提高将情况下降,事情上升,也契合官方引荐的UDF单项数据流架构。
当然这儿的number变量或许事情也能够来自ViewModel,这一点将在后面的课程中着重为咱们共享。
了解了Compose的情况和情况提高之后咱们现在回过头来看,怎么完成上面课程列表检查概况的功用。
完成检查概况功用
检查概况功用,这儿咱们设计为卡片打开款式,卡片打开后显现概况,所以咱们需求界说一个变量操控是否打开概况,假如处于打开情况,则显现,而且按钮文字变为“收起”。相信你很快能够编写出下面的代码:
@Composable
funmore(title:String){
varexpandedbyremember{
mutableStateOf(false)
}
Column(){
Row(modifier=Modifier
.background(Color.Red)
.padding(10.dp)
){
Text(text=title,
fontSize=16.sp,color=Color.White,modifier=Modifier.weight(1f)
)
Button(onClick={
expanded=!expanded
}){
Text(text=if(!expanded)"检查概况"else"收起",color=Color.White)
}
}
if(expanded){
Text(text="我是概况哈哈哈哈哈",modifier=Modifier.height(100.dp))
}
}
}
在XML完成这个功用咱们或许是经过躲藏或显现组件,但是在Compose中咱们经过是否将可组合项增加到界面树中来操控。如上代码所示,运用一个高度为100dp的文本组件充当概况。
运转程序,成果如下图所示。
Ok,十分的完美?依然有一些小瑕疵,比方咱们点击检查概况后,旋转屏幕会发现,原本打开的列表收起了。处理这个问题也十分的简略,咱们将remember修正为rememberSaveable即可。这儿就不再演示了。
咱们也能够为检查概况增加动画作用,这一点你能够在学习了动画相关的内容后,自行尝试。
不知道你有没有发现,截图中的顶部和按钮色彩都是褐色的,而且文字也有默许的色彩,这都是Compose中的主题帮咱们设置好的,最后咱们一同简略了解一下吧~
Compose主题
在初识Compose项目中,咱们现已知道,Compose01Theme是体系项目称号生成的,而且体系创立了一个theme文件夹,寄存Theme、Color、Type。Compose01Theme代码如下所示:
@Composable
funCompose01Theme(
darkTheme:Boolean=isSystemInDarkTheme(),
//Dynamic color is available on Android 12+
dynamicColor:Boolean=true,
content:@Composable()->Unit
){
valcolorScheme=when{
dynamicColor&&Build.VERSION.SDK_INT>=Build.VERSION_CODES.S->{
valcontext=LocalContext.current
if(darkTheme)dynamicDarkColorScheme(context)elsedynamicLightColorScheme(context)
}
darkTheme->DarkColorScheme
else->LightColorScheme
}
valview=LocalView.current
if(!view.isInEditMode){
SideEffect{
(view.contextasActivity).window.statusBarColor=colorScheme.primary.toArgb()
ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars=darkTheme
}
}
MaterialTheme(
colorScheme=colorScheme,
typography=Typography,
content=content
)
}
从上述代码能够知道,默许情况下,假如手机体系为12及以上会运用依据darkTheme是否为暗色形式判断运用dynamicLightColorScheme主题或dynamicDarkColorScheme主题,不然直接运用亮色或暗色主题。
文件中界说的DarkColorScheme和LightColorScheme,代码如下所示:
privatevalDarkColorScheme=darkColorScheme(
primary=Purple80,
secondary=PurpleGrey80,
tertiary=Pink80
)
privatevalLightColorScheme=lightColorScheme(
primary=Purple40,
secondary=PurpleGrey40,
tertiary=Pink40
)
dynamicLightColorScheme其实便是二次封装的LightColorScheme,为了便于修正主题,咱们先将dynamicLightColorScheme换成LightColorScheme,代码如下所示:
val colorScheme=when{
dynamicColor&&Build.VERSION.SDK_INT>=Build.VERSION_CODES.S->{
val context=LocalContext.current
if(darkTheme)dynamicDarkColorScheme(context)elseLightColorScheme
}
darkTheme->DarkColorScheme
else->LightColorScheme
}
运转程序,如下图所示。
咱们看到标题栏的色彩和按钮的色彩都产生了改动,现在咱们手动修正标题栏的色彩,从上面的代码中咱们能够看到标题栏的色彩运用的是primary特点值。
所以假如咱们想修正标题栏的色彩为蓝色,咱们只需求修正primary的色彩值即可,在Color文件中界说蓝色值,代码如下所示:
valBLUE=Color(0XFF7CEFA)
然后替换到LightColorScheme中
privatevalLightColorScheme=lightColorScheme(
primary=BLUE,
secondary=PurpleGrey40,
tertiary=Pink40
)
再次运转程序,代码如下所示。
如此咱们就成功修正了标题栏的色彩,当然咱们还能够修正文本默许色彩、文本款式、文本形状等。这儿就不再逐个展示了。
最后
到这儿,本次的共享到这儿就完毕了,经过本次的共享,咱们学到了以下内容:
- Compose的根底布局Column、Row 以及推迟列表LazyColumn组件
- Compose中Modifier根底修饰符的运用
- Compose中的情况与情况提高
- Compose主题
相信,这篇文章满足让你从0入门Compose。不破不立,未来可期~