Android Jetpack-Compose相关

一、什么是Compose?

Jetpack Compose 是用于构建原生 Android 界面的新工具包。它使用更少的代码、强大的工具和直观的 Kotlin API,可以帮助您简化并加快 Android 界面开发,打造生动而精彩的应用。它可让您更快速、更轻松地构建 Android 界面。

(注:想要使用Compose编写程序需要先把你的AndroidStudio升级到Android Studio Arctic Fox 或更高版本。)

二、Compose特点

更少的代码

与使用 Android View 系统(按钮、列表或动画)相比,Compose 可使用更少的代码实现更多的功能。

代码体量更小,编写代码只需要采用 Kotlin,而不必拆分成 Kotlin 和 XML 部分。使用 Compose 编写的代码都很简洁且易于维护。“Compose 的布局系统在概念上更简单,因此可以更轻松地推断。查看复杂组件的代码也更轻松。

直观

Compose 使用声明性 API。可以构建不与特定 activity 或 fragment 相关联的小型无状态组件。

加速开发

Compose 与所有的现有代码兼容:您可以从 View 调用 Compose 代码,也可以从 Compose 调用 View。

功能强大

Compose 不仅解决了声明性界面的问题,还改进了无障碍功能 API、布局等各种内容。将设想变为现实所需的步骤更少了。利用 Compose,可以轻松快速地通过动画让应用变得生动有趣。无论是使用 Material Design 还是自己的设计系统进行构建,Compose 都可以灵活地实现所需的设计。

三、如何使用Compose?

Compose只是注解接口,内部原理在自己看懂后,会在往后出一篇介绍原理,现在我们简单介绍如何使用。温馨提示Compose对学习过flutter的开发者来说非常容易入门。 Compose支持的布局: 一、Column(列排列的组件); 二、Row(行排列的组件); 三、Statck(叠加的组件); 四、Scaffold(Scaffold、MaterialTheme等是MaterialDesign风格的组件等。这里简单只写出4个是因为这对于学习过flutter的开发者来说,它们4个已经是非常熟悉的老朋友了。 和flutter一样,Compose让开发者可以专心使用代码写开发UI界面,无需再写一份xml代码。

声明依赖项

如需添加 Compose 的依赖项,您必须将 Google Maven 制品库添加到项目中。

在应用或模块的 build.gradle 文件中添加所需工件的依赖项:

组说明compose.animation在 Jetpack Compose 应用中构建动画,丰富用户体验。compose.compiler借助 Kotlin 编译器插件,转换 @Composable functions(可组合函数)并启用优化功能。compose.foundation使用现成可用的构建块编写 Jetpack Compose 应用,还可扩展 Foundation 以构建您自己的设计系统元素。compose.material使用现成可用的 Material Design 组件构建 Jetpack Compose UI。这是更高层级的 Compose 入口点,旨在提供与 www.material.io 上描述的组件一致的组件。compose.material3使用 Material Design 3(下一代 Material Design)组件构建 Jetpack Compose 界面。Material 3 中包括了更新后的主题和组件,以及动态配色等 Material You 个性化功能,旨在与新的 Android 12 视觉风格和系统界面相得益彰。compose.runtimeCompose 编程模型和状态管理的基本构建块,以及 Compose Compiler 插件针对的核心运行时。compose.ui与设备互动所需的 Compose UI 的基本组件,包括布局、绘图和输入。

四、使用Compose

使用Compose第一个基本程序:

package com.example.compose

import android.os.Bundle

import androidx.activity.ComponentActivity

import androidx.activity.compose.setContent

import androidx.compose.foundation.layout.fillMaxSize

import androidx.compose.material3.MaterialTheme

import androidx.compose.material3.Surface

import androidx.compose.material3.Text

import androidx.compose.runtime.Composable

import androidx.compose.ui.Modifier

import androidx.compose.ui.tooling.preview.Preview

import com.example.compose.ui.theme.ComposeTheme

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContent {

ComposeTheme {

// A surface container using the 'background' color from the theme

Surface(

modifier = Modifier.fillMaxSize(),

color = MaterialTheme.colorScheme.background

) {

Greeting("Android")

}

}

}

}

}

@Composable

fun Greeting(name: String) {

Text(text = "Hello $name!")

}

@Preview(showBackground = true)

@Composable

fun DefaultPreview() {

ComposeTheme {

Greeting("Android")

}

}

效果图:

五、例子

创建一个项目

点击 Empty Compose Activity

然后改名字改成自己想要的 点击完成即可创建一个项目 (注:我之前已经创建一个名字为Compose的项目所以下面有一个警告)

跑马灯例子~ 添加启动类

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContent {

MyApplicationTheme {

// A surface container using the 'background' color from the theme

Surface(

modifier = Modifier.fillMaxSize(),

color = MaterialTheme.colors.background

) {

TestScreen()

}

}

}

}

}

创建方法:

@Composable

fun MarqueeText(

text: String,

modifier: Modifier = Modifier,

textModifier: Modifier = Modifier,

gradientEdgeColor: Color = Color.White,

color: Color = Color.Unspecified,

fontSize: TextUnit = TextUnit.Unspecified,

fontStyle: FontStyle? = null,

fontWeight: FontWeight? = null,

fontFamily: FontFamily? = null,

letterSpacing: TextUnit = TextUnit.Unspecified,

textDecoration: TextDecoration? = null,

textAlign: TextAlign? = null,

lineHeight: TextUnit = TextUnit.Unspecified,

overflow: TextOverflow = TextOverflow.Clip,

softWrap: Boolean = true,

onTextLayout: (TextLayoutResult) -> Unit = {},

style: TextStyle = LocalTextStyle.current,

) {

// 创建Text控件方法,相当于@Composable fun createText(localModifier: Modifier)

val createText = @Composable { localModifier: Modifier ->

Text(

text,

textAlign = textAlign,

modifier = localModifier,

color = color,

fontSize = fontSize,

fontStyle = fontStyle,

fontWeight = fontWeight,

fontFamily = fontFamily,

letterSpacing = letterSpacing,

textDecoration = textDecoration,

lineHeight = lineHeight,

overflow = overflow,

softWrap = softWrap,

maxLines = 1,

onTextLayout = onTextLayout,

style = style,

)

}

var offset by remember { mutableStateOf(0) }

val textLayoutInfoState = remember { mutableStateOf(null) }

LaunchedEffect(textLayoutInfoState.value) {

val textLayoutInfo = textLayoutInfoState.value ?: return@LaunchedEffect

if (textLayoutInfo.textWidth <= textLayoutInfo.containerWidth) return@LaunchedEffect

if(textLayoutInfo.containerWidth == 0) return@LaunchedEffect

// 计算播放一遍的总时间

val duration = 7500 * textLayoutInfo.textWidth / textLayoutInfo.containerWidth// 父层不要有其他元素,不然这句很容易发生Error java.lang.ArithmeticException: divide by zero(除以零)

// 动画间隔时间

val delay = 1000L

do {

// 定义动画,文字偏移量从0到-文本宽度

val animation = TargetBasedAnimation(

animationSpec = infiniteRepeatable(

animation = tween(

durationMillis = duration,

delayMillis = 1000,

easing = LinearEasing,

),

repeatMode = RepeatMode.Restart

),

typeConverter = Int.VectorConverter,

initialValue = 0,

targetValue = -textLayoutInfo.textWidth

)

// 根据动画帧时间,获取偏移量值。

// 起始帧时间

val startTime = withFrameNanos { it }

do {

val playTime = withFrameNanos { it } - startTime

offset = animation.getValueFromNanos(playTime)

} while (!animation.isFinishedFromNanos(playTime))

// 延迟重新播放

delay(delay)

} while (true)

}

SubcomposeLayout(

modifier = modifier.clipToBounds()

) { constraints ->

// 测量文本总宽度

val infiniteWidthConstraints = constraints.copy(maxWidth = Int.MAX_VALUE)

var mainText = subcompose(MarqueeLayers.MainText) {

createText(textModifier)

}.first().measure(infiniteWidthConstraints)

var gradient: Placeable? = null

var secondPlaceableWithOffset: Pair? = null

if (mainText.width <= constraints.maxWidth) {// 文本宽度小于容器最大宽度, 则无需跑马灯动画

mainText = subcompose(MarqueeLayers.SecondaryText) {

createText(textModifier.fillMaxWidth())

}.first().measure(constraints)

textLayoutInfoState.value = null

} else {

// 循环文本增加间隔

val spacing = constraints.maxWidth * 2 / 3

textLayoutInfoState.value = TextLayoutInfo(

textWidth = mainText.width + spacing,

containerWidth = constraints.maxWidth

)

// 第二遍文本偏移量

val secondTextOffset = mainText.width + offset + spacing

val secondTextSpace = constraints.maxWidth - secondTextOffset

if (secondTextSpace > 0) {

secondPlaceableWithOffset = subcompose(MarqueeLayers.SecondaryText) {

createText(textModifier)

}.first().measure(infiniteWidthConstraints) to secondTextOffset

}

// 测量左右两边渐变控件

gradient = subcompose(MarqueeLayers.EdgesGradient) {

Row {

GradientEdge(gradientEdgeColor, Color.Transparent)

Spacer(Modifier.weight(1f))

GradientEdge(Color.Transparent, gradientEdgeColor)

}

}.first().measure(constraints.copy(maxHeight = mainText.height))

}

// 将文本、渐变控件 进行位置布局

layout(

width = constraints.maxWidth,

height = mainText.height

) {

mainText.place(offset, 0)

secondPlaceableWithOffset?.let {

it.first.place(it.second, 0)

}

gradient?.place(0, 0)

}

}

}

/**

* 渐变侧边

*/

@Composable

private fun GradientEdge(

startColor: Color, endColor: Color,

) {

Box(

modifier = Modifier

.width(10.dp)

.fillMaxHeight()

.background(

brush = Brush.horizontalGradient(

0f to startColor, 1f to endColor,

)

)

)

}

private enum class MarqueeLayers { MainText, SecondaryText, EdgesGradient }

/**

* 文字布局信息

* @param textWidth 文本宽度

* @param containerWidth 容器宽度

*/

private data class TextLayoutInfo(val textWidth: Int, val containerWidth: Int)

调用方法:

@Composable

fun TestScreen() {

val content = "FJNU哈哈哈哈哈啊哈哈哈哈哈哈哈哈哈"

Column(modifier = Modifier.padding(top = 200.dp)) {

MarqueeText(

text = content,

color = Color.Black,

fontSize = 24.sp,

modifier = Modifier

.padding(start = 50.dp, end = 50.dp)

.background(Color.White)

)

}

}

最后记得在xml文件里面修改一下你自己改的名字即可

最终实现效果

(因为我不会做动图所以就分两次截图)…

总结:

Compose的所有控件都是独立于android平台(这里的独立是相对原生Android View系统而言的,指上层独立,底层还是依赖原声,这样设计是为了与原生View系统仍保持交互)。原生Android可以实现的都可以使用Compose来加速开发,无法做到原生Android做不到的。Compose与原生Android各有各的好处,Compose 对于大多数开发者指标产生的影响是积极的。考虑到这一点,再加上 Compose 大大提高了开发人员的生产力。对大部分人来说,Compose 无疑是 Android UI 开发的未来。

部分参考来源: 1.Android JetpackCompose官网 2.https://www.jianshu.com/p/67b5b38c9aa7 3.站内搜索Compose跑马灯关键词相关的信息

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: