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())
              }
          }
      }
相关推荐
是程序喵呀3 分钟前
MySQL备份
android·mysql·adb
casual_clover4 分钟前
Android 之 List 简述
android·list
锋风Fengfeng1 小时前
安卓15预置第三方apk时签名报错问题解决
android
User_undefined2 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
程序员厉飞雨3 小时前
Android R8 耗时优化
android·java·前端
丘狸尾4 小时前
[cisco 模拟器] ftp服务器配置
android·运维·服务器
van叶~6 小时前
探索未来编程:仓颉语言的优雅设计与无限可能
android·java·数据库·仓颉
生产队队长7 小时前
项目练习:element-ui的valid表单验证功能用法
前端·vue.js·ui
Crossoads10 小时前
【汇编语言】端口 —— 「从端口到时间:一文了解CMOS RAM与汇编指令的交汇」
android·java·汇编·深度学习·网络协议·机器学习·汇编语言
li_liuliu11 小时前
Android4.4 在系统中添加自己的System Service
android