前言
上一章咱们讲解了 Compose 根底UI 和 Modifier 关键字,本章首要讲解 Compose 分包以及自界说 Composable;
Compose 如何分包
咱们在运用 Button 控件的时分,发现假如咱们想给按钮设置文本的时分,Button 函数并没有直接供给设置 text 的参数,要咱们自己去调用 Text 进行设置;
Column {
Button(onClick = {}) {
Text(text = "我是老A")
}
}
可能到这儿的时分,咱们就会困惑了,Compose 为什么要这么搞呢?咱们能够去源码中一探究竟,咱们能够看到 Button 函数是在androidx.compose.material3 这个包下面
Button 来自 compose.material3 这个组下面的,也便是 Maven 包的 groupId 是 androidx.compose.material3,对应的便是 build.gradle 中的依靠关系
其实Compose 由 androidx
中的 7 个 Maven 组 ID 构成。每个组都包括一套特定用处的功用,并各有专属的版别说明;
Compose 其实一共是分了6层,material 和 material3 是一个,仅仅不同的分支;每个组下面有不同的分包,咱们其实能够看到 ui 下面就有不同的ui、ui-tooling-preview、ui-graphics 等等,Android 团队这么分包,其实是针对 View 体系的一个优化;
View 体系是没有这个分层的,这就导致后期越来越严重的扩展性问题,例如 View 体系中的 ListView,ListView 中有一个对 View 的收回复用机制,这个机制 RecyclerView 是没有办法复用的,也便是它们两个各自维护着一套复用机制,这便是分层不清晰导致的;
所以 Compose 在设计之初就清晰了分层概念,分层之后的各自扩展,就不会受到限制;
compose.compiler 严格来说,它其实并不归于这7层,它供给的并不是库依靠,它代表的是 kotlin 编译插件,转化 @Composable functions 并启用优化功用,它是负责编译进程的,咱们在依靠里边也完全不需求去装备它,只需求在 Compose 的专用装备地方去写上你要的编译插件版别就行,对应的便是这儿:
Compose 剩余的 Group 都是咱们开发 Compose 的时分会用到的,不过它们有依次递进的依靠关系;
最基层是 compose.runtime 它包括了 Compose 编程模型和状况办理的根本构件块,以及 Compose 编译器插件的方针核心运转时,是最底层的概念模型,比方用来保存状况的 State 就在 compose.runtime,还有mutableStateOf、remember
往上一层是 compose.ui 它是用来供给 ui 最根底的功用,比方制作、测量、布局、接触反应等最底层的支持,比方咱们运用的一切控件函数,终究都会调用到一个叫 Layout 的函数,这个函数就在 ui 这层;
再往上一层是 compose.animation,它是用来构建动画的;
在往上一层是 compose.foundation,它供给的是一套相对完整牢靠的 UI 体系,例如 Colum、Row、Image 等都在这一层;
再往上一层便是 comose.material/material3 了,这是一个封装了 一堆 material design 风格控件的包,假如不想运用 MD 风格,能够运用 foundation 层自己拼装一套风格出来;
接下来便是同一个组下面的多个包应该如何引证?例如 compose.ui 下的
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
一般来说,咱们只需求引进和组名相同的包就能够了,由于一般这个包就包括了这个组下其他包的一切依靠,除了测验组的这种,例如 compose.ui:ui 下不会包括 compose.ui:ui-test-xxx 和 compose.ui:ui-tooling 由于 test 和 东西类的一般都不会编译进咱们的 apk 中;
例如 @Preview 就归于 ui-tooling 下的
@Preview
@Composable
fun preview() {
Column {
Button(onClick = {}) {
Text(text = "我是老A")
}
OutlinedButton(onClick = { /*TODO*/ }) {
Text(text = "我是老A")
}
TextButton(onClick = { /*TODO*/ }) {
Text(text = "我是老A")
}
}
}
还有 material3 供给的一些矢量图组件
implementation("androidx.compose.material3:material3-icon-extends")
implementation("androidx.compose.material3:material3-icon-core")
也是需求独自依靠的;
compose.ui:ui 一般包括了 ui 下的一切, compose.material3:material3 一般包括了 material 下的一切;
自界说Composable
用自界说函数的方法来写 Composable,而 Composable 是一种简化的方法,它指的是带有这个 Composable 注解的函数,那么这个注解到底是做什么的呢?咱们来一探究竟
咱们在运用的 Text 函数、Image 函数等其实都带有 Composable 注解,可是这些函数并不是原封不动的被调用的,而是会在编译进程中被动了四肢,给它们增加了一些函数参数,然后在运转的时分,调用的其实是那些被改正的参数更多的版别,比方说它们被参加的其间一个参数便是 Composer 类型的,总之这些 Composable 函数在编译的时分会被 Compose 的编译器插件(Compiler Plugin)修正,增加一些参数,运转的时分也是调用的这些被修正正的函数;
那么,编译器为什么要修正它们呢?
最重要的一点便是:要在代码中增加一些咱们没有写出来的功用,这些功用关于开发者来说不需求,只需求在程序运转的时分能用到就能够了,所以编译的时分增加,即便利了开发者,又不影响程序的运转;
这其实也是一种面向切面(AOP)编程的思想;
那么编译器插件又是怎样认出这些函数的呢?它怎样直到哪些应该被修正呢?
靠的便是 @Composable 注解;只有被加了这个注解的才会进行修正,起到了识别符的作用;咱们能够来看一个小例子:
假如 ui 函数没有增加 @Composable 注解,编译器直接报错了,便是由于这个函数内部调用了被 @Composable 注解的函数,所以咱们能够理解为:一切调用了被 @Composable 注解的函数的函数,也有必要增加上 @Composable 注解;提到这儿的时分,可能会有人有疑问了,setContent 函数增加了 @Composable 注解了吗?假如没有增加,那么它内部怎样能够调用 Compose 函数?假如增加了,那么 MainActivity 为什么不必增加 @Composable 注解?咱们来看看 setContent 的完成:
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit) {
}
咱们发现,setContent 函数并没有被 @Composable 注解符号,它仅仅把一个 @Composable 注解的函数作为了参数,所以 setContent 不需求被其注解;可是终归仍是需求一个被 @Composeable 注解的函数来调用这个参数,那么这个函数是哪个函数呢?它便是 invokeComposable 函数
默许看不了,咱们 Decompile to Java 看下
便是将 composable 强转成了一个 Function2 函数,然后进行调用;
所以自界说 Composable 便是声明的函数被 Composable 注解符号,本质上便是为了便利咱们在开发中能够将咱们的界面元素进行拆分,然后完成不同的功用;一般咱们在自界说 Composable 的时分,直接的只会调用一个 Composable 函数,这样便利咱们关于布局的操控
@Composable
fun ui() {
Column {
Text("老A")
Text("Mars")
}
}
而不是
@Composable
fun ui1() {
Text("老A")
Text("Mars")
}
那么外部在调用 ui1 函数的时分,咱们的布局就不受操控了,假如外部调用的时分 放到了 Column 中,那么就会竖向摆放,假如放到了 Row 中,就会横向摆放,假如放到了 Box 中就会叠加摆放;
而 ui 函数咱们能够自己操控布局的摆放,经过 Column、Row 等函数,而不必受外界调用操控;
自界说 Composable 的运用场景
再说运用场景的时分,咱们能够先想领一个问题,自界说 Composable 在传统 View 中的等价物是什么?自界说View?仍是 xml 文件?仍是 自界说View + xml 文件?
自界说View?
@Composable
fun ui() {
Column {
Text(text = "老A")
Text(text = "Mars")
}
}
这种写法,看起来更像传统的 自界说 LinearLayout
class CustomLinearLayout(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
val name: TextView by lazy { TextView(context) }
val alias: TextView by lazy { TextView(context) }
init {
orientation = VERTICAL
//
name.text = "老A"
alias.text = "Mars"
...
// 省掉部分代码
addView(name)
addView(alias)
}
}
看起来更像是 自界说 View 的等价物;
xml文件?
可是,这种简易布局咱们一般也不会这样去运用,一般都是直接在 xml 中进行了声明
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
这样更直观,快捷,看起来也更像 compose 的写法,一个父控件,两个子控件;
自界说View + xml?
可是假如咱们对 Composable 函数做如下改动运用:
@Composable
fun ui(name: String) {
Column {
Text(text = name)
Text(text = "Mars")
}
}
咱们设置了一个 name 作为参数来传入进来,那么咱们就能够在调用的时分传入不同的值,来表现不同的数据,并且,这个 Composable 函数还能够这么改
@Composable
fun ui(name: String) {
Column {
val realName = remember {
if (name.length > 8) {
"我是laoA"
} else {
"我是马尔斯"
}
}
Text(text = realName)
Text(text = "Mars")
}
}
关于 Compose 能够这么写,可是关于传统的 xml 完成不了,一旦咱们对界面有了定制的需求后,就只能经过自界说 View 来完成了;
所以,看起来自界说 Composable 更像传统 View 的自界说 View + xml 文件!
所以自界说 Composable 的运用场景也就能知道了;
界面声明咱们一般是一个 Activity 对应一个 xml 的文件,那么当咱们运用 Compose 的时分,也能够一个 MainActivity 对应一个 MainLayout 的 Composable 的函数;
当咱们既需求 xml 的简洁有需求自界说view的逻辑处理才能,那么都是能够运用自界说 Composable 的;遇到任务需求对界面有定制需求,就直接运用 Composable 函数处理;
传统自界说 View 还能对布局、制作、接触反应进行定制,这一类的高档自界说 View 在 Compose 中是怎样完成的呢?
其实仍是用的自界说 Composable,当然假如你不自界说 Composable,直接硬写也是能够的,可是就失去了扩展、复用的才能,具体写法上,大部分用的是 Modifier,后边章节会详解自界说 Compose 中的高档自界说 View;
好了,自界说 Composable 就讲到这儿吧~~
下一章预告
MutableState 和 mutableStateOf 详解;
欢迎三连
来都来了,点个重视,点个赞吧,你的支持是我最大的动力~~