Android全新UI框架之Jetpack Compose入门基础

  • Jetpack Compose是什么

    如果有跨端开发经验的同学,理解和学习compose可能没有那么大的压力。简单地说,compose可以让Android的原生开发也可以使用类似rn的jsx的语法来开发UI界面 。以往,我们开发Android原生页面的时候,通常是在xml中画相关的UI控件,然后在activity中通过findViewById拿到相关的控件对象,接着根据业务需求调用该控件对象的相关API方法。这种方式官方称之为命令式UI 。再加上大前端浪潮的兴起,React、Vue等声明式UI技术的的日益成熟,以往那套继承自View的组件体系已经越发难以维护(目前View.java已超过3万行代码),因此Compose这门技术就应运而生了。

  • Jetpack Compose的优势

    这点感兴趣的同学就到官网看下就行了,反正就是一堆好处。值得注意的是compose最低兼容到API21

  • Compose API设计原则

    由于Compose在编程范式上与传统视图体系有着根本的不同,在开始深入学习Compose之前,有必要对Compose API的设计原则做一个介绍。无论是Compose预置的Composable还是开发者自己定义的Composable,都应该遵守这些原则

    1. 一切皆为函数
      Compose声明式UI的基础是Composable函数Composable函数通过多级嵌套形成结构化的函数调用链,函数调用链经过运行后生成一颗UI视图树 。视图树一旦生成便不可随意改变。视图的刷新依靠Composable函数的反复执行(重组 )来实现。当需要显示的数据发生变化时,Compoable基于新参数再次执行,更新底层的视图树,最终完成视图的刷新。

      Composable函数只能在Composable函数中调用 ,这与挂起函数只能在协程或其他挂起函数中调用类似,都是在编译期保证的。在Compose的世界中,一切组件都是函数,由于没有类的概念,因此不会有任何继承的层次结构,所有组件都是顶层函数,可以在DSL中直接调用。
    2. 组合优于继承
      Composable作为函数相互没有继承关系,这有利于促使开发者使用组合的视角去思考问题。
    3. 单一数据源
      在传统的命令行UI中(以EditText为例),文字的变化来源多处,有可能来自用户的输入,也可能来自代码某处的setText。状态变化可能不止一个来源,即所谓的多数据源。多数据源下的状态变化不容易跟踪,且会使后期难以维护。单一数据源决定了Composable数据流的单向流动,数据(name)总是自上而下(由父组件流向子组件)流动,而事件(onNameChange)总是自下而上(由子组件流向父组件)传递。
  • Compose与View的关系

    在传统视图体系中由View与ViewGroup构成视图树,而Compose中也有同样一颗视图树,它由LayoutNode构成,由Composition负责管理。

    两种树的节点类型不同,但是它们并非全没关系,依然可以共存于一棵树中。就像DOM节点与View也不同,但是可以通过WebView显示在一棵树上,Compose也可以借助这样一个连接点挂载在View树上。使用Android Studio自带的Layout Inspector可以看到这个连接点就是ComposeView,它就是连接View与Compose的桥梁。

    ComposeView有一个唯一子节点AndroidComposeView,它既是一个ViewGroup,也是LayoutNode视图树的持有者,它实现了LayoutNode视图结构与View视图结构的连接。既然AndroidComposeView已经承担了两套体系的连接,那为什么还要多一层ComposeView呢?
    ComposeView继承自AbstractComposeView,而后者有三个子类,分别对应着Activity窗口、Dialog窗口与PopupWindow窗口。Android平台存在所谓Window的概念,我们在很多场景下会有多窗口需求,例如在页面中弹出一个对话窗。AbstractComposeView的子类负责Android平台各类窗口的适配并生成对应的Composition, ComposeView作为其中一个子类负责Activity窗口的适配。总体来说,ComposeView负责对Android平台的Activity窗口的适配,AndroidComposeView负责连接LayoutNode视图系统与View视图系统。如此的职责划分可以实现上层视图适配与下层窗口适配逻辑的解耦。

Composition是视图树的创建者。从Composable函数到视图树的生成经历这个过程:

第一步Composable函数执行后填充SlotTable, SlotTable中记录着Composable执行过程的状态信息;

第二步基于SlotTable生成和更新LayoutNode视图树。Composition负责从Composable执行到视图树生成(更新)的整个过程。

ComposeView接入View视图后,内部的UI工作都在Compose侧闭环处理,来自AndroidComposeView的绘制、测量布局与手势事件分发等都下沉到LayoutNode去完成。ComposeView作为View可以挂载到原有View视图树中的任意位置。因此一个传统视图项目可以通过ComposeView阶段性地接入Compose。一个纯Compose页面就是将ComposeView直接挂载到ContentView上面。

  • 不只是Android UI框架
    Compose还包含一些其他的库,如下图所示:

    Compose从上到下分为四层,每一层都可以被单独使用,在不同维度提供能力支持。可以只使用Compose的Runtime层构建任何基于数据驱动能力的系统或类库。在这样清晰的分层结构下,我们甚至可以隔离那些平台相关代码,自底向上自己来实现跨平台的UI系统。
  • Compose开发环境搭建
    1. 创建一个新的Compose项目
      这个比较简单,使用AndroidStudio创建项目时,选择compose项目,然后无脑下一步即可,如下图所示:

    2. 已有项目引入Compose
      按照下面步骤改造已有项目:

      • 在工程中引入Compose相关依赖:

        kotlin 复制代码
        dependencies {
            implementation platform('androidx.compose:compose-bom:2022.10.00')
            implementation 'androidx.compose.ui:ui'
            implementation 'androidx.compose.material3:material3'
        }
      • 在build.gradle文件的android闭包中添加如何下配置:

        kotlin 复制代码
            buildFeatures {
                compose true
            }
            composeOptions {
                kotlinCompilerExtensionVersion '1.3.2'
            }
            packagingOptions {
                resources {
                    excludes += '/META-INF/{AL2.0,LGPL2.1}'
                }
            }
      • 修改相关的xml布局文件

        kotlin 复制代码
        <?xml version="1.0" encoding="utf-8"?>
        <androidx.compose.ui.platform.ComposeView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/root"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">
        
        </androidx.compose.ui.platform.ComposeView>
      • 在activity中查找该View并使用setContent即可

        kotlin 复制代码
        class MainActivity : AppCompatActivity() {
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                findViewById<ComposeView>(R.id.root).setContent { 
                    Text(text = "Hello Compose")
                }
            }
        }
    3. 在Compose中使用View组件
      不少功能性的传统视图控件在Compose中没有对应的Composable实现,例如SurfaceView、WebView、MapView等。因此在Compose中可能会有使用传统View控件的需求。Compose提供了名为AndroidView 的Composable组件,允许在Composable中插入任意基于继承自View的传统视图控件。

      kotlin 复制代码
      class MainActivity : ComponentActivity() {
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContent {
                  AndroidView(factory = { context ->
                      WebView(context).apply {
                          settings.javaScriptEnabled = true
                          webViewClient = WebViewClient()
                          loadUrl("https://www.baidu.com/")
                      }
                  }, modifier = Modifier.fillMaxSize())
              }
          }
      }
相关推荐
城东米粉儿4 小时前
Android AIDL 笔记
android
城东米粉儿4 小时前
Android 进程间传递大数据 笔记
android
城东米粉儿5 小时前
Android KMP 笔记
android
冬奇Lab6 小时前
WMS核心机制:窗口管理与层级控制深度解析
android·源码阅读
松仔log6 小时前
JetPack——Paging
android·rxjava
城东米粉儿7 小时前
Android Kotlin DSL 笔记
android
城东米粉儿7 小时前
Android Gradle 笔记
android
城东米粉儿7 小时前
Android Monkey 笔记
android
城东米粉儿7 小时前
Android 组件化 笔记
android