Android Studio compose的简单使用与案例实现

news2024/12/23 14:22:30

Compose是Android团队与JetBrain大力推动的新一代UI框架,它能够简化安卓界面的开发,让本来繁琐的xml文件写法变为简便的kt文件写法。

其声明式 UI、更简单的自定义、实时且带交互的预览功能更是让安卓开发锦上添花

android compose框架的使用

  • 一.前置知识:Jetpack
    • 1.什么是Jetpack?
    • 2.为什么要使用Jetpack
    • 3.常用的Jetpack库
  • 二.正片:Compose入门
    • 1.Compose的编程思想
      • 简单的可组合函数
    • 2.创建一个Compose项目
  • 三.使用Compose完成“跑马灯”案例
    • 1.构建项目
    • 2.在主包下创建一个新的kt文件
    • 3.构建MarqueeText方法
      • 引用Compose中定义的ui参数
      • 创建控件方法
      • 对动态效果进行编码
      • 调用上述的方法
      • 实现启动类
      • 在xml启动文件中,添加这个kt文件
      • 实现效果
  • 4.总结

一.前置知识:Jetpack

1.什么是Jetpack?

Jetpack并不是一个框架或者组件,它是一套库、工具和指南的集合,可帮助开发者更轻松地编写优质应用。这些组件可帮助开发者遵循最佳做法、摆脱编写样板代码的工作并简化复杂任务,以便将精力集中放在所需的代码上。

2.为什么要使用Jetpack

1.替换以前的support库,提供一个更好,更加独立和稳健的兼容方案
2.官方实现了很多日常软件开发中需要用到的组件,消除重复样板代码
3.提高软件品质,核心库帮你绑定数据,管理生命周期和内容,让你专注在对自身业务重要的部分

3.常用的Jetpack库

图片取自安卓开发文档

二.正片:Compose入门

1.Compose的编程思想

Compose 是一个声明性界面框架。在过去的几年中,整个行业已开始转向声明性界面模型,该模型大大简化了与构建和更新界面关联的工程任务。该技术的工作原理是在概念上从头开始重新生成整个屏幕,然后仅执行必要的更改。此方法可避免手动更新有状态视图层次结构的复杂性。

简单的可组合函数

使用 Compose,可以通过定义一组接受数据而发出界面元素的可组合函数来构建界面。一个简单的示例是 Greeting widget,它接受 String 并发出一个显示问候消息的 Text widget。
简单的案例此函数带有 @Composable 注释。所有可组合函数都必须带有此注释;此注释可告知 Compose 编译器:此函数旨在将数据转换为界面。(类似于Spring框架的@Component

此函数接受数据。可组合函数可以接受一些参数,这些参数可让应用逻辑描述界面。在本例中,我们的 widget 接受一个 String,因此它可以按名称问候用户。

此函数可以在界面中显示文本。为此,它会调用 Text() 可组合函数,该函数实际上会创建文本界面元素。可组合函数通过调用其他可组合函数来发出界面层次结构。

此函数不会返回任何内容。发出界面的 Compose 函数不需要返回任何内容,因为它们描述所需的屏幕状态,而不是构造界面 widget。

2.创建一个Compose项目

1.如果您位于 Welcome to Android Studio 窗口中,请点击 Start a new Android Studio project。如果您已打开 Android Studio 项目,请从菜单栏中依次选择 File > New > New Project。

在这里插入图片描述
2.在 Select a Project Template 窗口中,选择 Empty Compose Activity,然后点击 Next。
在这里插入图片描述3.在 Configure your project 窗口中,执行以下操作:

按照常规方法设置 Name、Package name 和 Save location。请注意,在 Language 下拉菜单中,Kotlin 是唯一可用的选项,因为 Jetpack Compose 仅适用于使用 Kotlin 编写的类。
在 Minimum API level dropdown 菜单中,*选择 API 级别 21 或更高级别*。

在这里插入图片描述4.点击finish。

三.使用Compose完成“跑马灯”案例

1.构建项目

这一部分参照上文;

2.在主包下创建一个新的kt文件

我这里命名为“test”
在这里插入图片描述

3.构建MarqueeText方法

这是跑马灯实现的主要部分,在此部分来编辑跑马灯效果的静态参数与动态效果

引用Compose中定义的ui参数

在这里插入图片描述说明:

循环滚动跑马灯Text控件

  • text 文字内容
  • modifier 控件修饰器
  • textModifier 文字修饰器
  • gradientEdgeColor 左右边界渐变透明色,默认白色渐变。
  • color 文字颜色
  • letterSpacing 字符间距
  • textDecoration 文字装饰

创建控件方法

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

在这里插入图片描述

对动态效果进行编码

var offset by remember { mutableStateOf(0) }
        val textLayoutInfoState = remember { mutableStateOf<TextLayoutInfo?>(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<Placeable, Int>? = 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 = "这是一段很长很长很长很长很长很长很长很长很长很长的跑马灯文字;"
    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)
        )
    }
}

实现启动类

class test : 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()
                }
            }
        }
    }
}

在xml启动文件中,添加这个kt文件

<activity
            android:name=".test"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyApplication">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

实现效果

在这里插入图片描述

4.总结

Compose框架是个高效率的安卓开发框架,它能避免xml的layout写法的繁琐、易错和难以调试,并且它提供了丰富的API供开发者使用,使得开发者能够将大量时间用于面向过程编程与面向结果编程,减少面向切片编程。

曾建城 https://blog.csdn.net/weixin_57235263/article/details/128127787?spm=1001.2014.3001.5501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66308.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue+elementUI 使用腾讯地图

效果如下 引入地图qqmap 刚开始我是直接用 npm install qqmap&#xff0c;但是好像只有v1版本的&#xff0c;我需要用v2版本的&#xff0c;所以直接使用script标签加载API服务。 文件&#xff1a;/public/index.html <script charset"utf-8" src"https:…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java双笙映画ou5oj

毕业设计也不需要做多高端的程序&#xff0c;毕业设计对于大多数同学来说&#xff0c;为什么感觉到难&#xff0c;最重要的一个原因&#xff0c;那就是理论课到实践课的转变&#xff0c;很多人一下不适应&#xff0c;本能开始拒绝&#xff0c;如果是一个考试&#xff0c;大家都…

spring boot基于Java的电影院售票与管理系统毕业设计源码011449

电影院售票与管理系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对电影院售…

SpringBoot —— 整合RabbitMQ常见问题及解决方案

前言 企业中最常用的消息中间件既不是RocketMQ&#xff0c;也不是Kafka&#xff0c;而是RabbitMQ。 RocketMQ很强大&#xff0c;但主要是阿里推广自己的云产品而开源出来的一款消息队列&#xff0c;其实中小企业用RocketMQ的没有想象中那么多。 至于Kafka&#xff0c;主要还是…

常见的推荐算法原理介绍

常见的推荐算法原理介绍&#xff0c;随着互联网的发展短视频运营越来越精准化&#xff0c;我们身边常见的抖音、火山小视频等软件让你刷的停不下来&#xff0c;这些软件会根据你的浏览行为推荐你感兴趣的相关内容&#xff0c;这就用到了很多推荐算法在里面。 在淘宝购物&#…

Linux 负载均衡介绍之LVS工作模式-DR直接路由模式

Linux 负载均衡介绍之LVS工作模式-DR直接路由模式 图示&#xff1a; 工作原理&#xff1a; ①.客户端将请求发往前端的负载均衡器&#xff0c;请求报文源地址是CIP&#xff0c;目标地址为VIP。 ②.负载均衡器收到报文后&#xff0c;发现请求的是在规则里面存在的地址&#x…

[Java反序列化]—Shiro反序列化(二)

0x01 这篇利用CC链来进行RCE 利用分析 在shiro-web 中加上CC依赖 <dependency><groupId>commons-collections</groupId><artifactId>commons-collections</artifactId><version>3.2.1</version><scope>compile</scope>…

ZKP方案衍变及对比

1. 引言 2019年是ZKP方案创新井喷的一年。 2019年10月&#xff0c;Chiesa在#zk0x04上的分享 State of the SNARG-scape - Alessandro Chiesa (UC Berkeley, StarkWare, Zcash)&#xff0c;有&#xff1a; 根据reference string的类型&#xff0c;可将zk-SNARKs分类为&#…

1.集群环境搭建

1.集群信息概览 2.集群环境搭建 2.1第一台服务器 修改静态ipvim /etc/sysconfig/network-scripts/ifcfg-ens33修改主机名echo first-node /etc/hostname修改主机名映射echo 192.168.226.140 first-node >> /etc/hosts echo 192.168.226.141 second-node >> /…

Redis缓存 缓存穿透+缓存雪崩+缓存击穿的原因及其解决方案

Redis缓存 缓存穿透缓存雪崩缓存击穿的原因及其解决方案 文章目录Redis缓存 缓存穿透缓存雪崩缓存击穿的原因及其解决方案一、缓存穿透是什么&#xff1f;解决方案&#xff1a;二、缓存雪崩是什么&#xff1f;解决方案三、缓存击穿是什么&#xff1f;解决方案一、缓存穿透是什么…

【保姆级·创建对象】如何通过factory-method创建对象

这个步骤在createBeanInstance()方法中有使用&#xff0c;我们先来看下这个方法中都干了些啥(&#xff61;&#xff65;ω&#xff65;&#xff61;)&#xff89; 首先&#xff0c;方法开头确认了beanClass是否被加载&#xff08;因为只有被加载叻的对象才是可以实例化的&#…

深入浅出MySQL事务和锁定语句

https://dev.mysql.com/doc/refman/8.0/en/sql-transactional-statements.html 13.3事务和锁定语句 13.3.1启动事务、提交和回滚语句 开启事务 begin START TRANSACTION提交事务 COMMIT回滚事务 ROLLBACK查询自动提交 show SESSION VARIABLES where variable_name "…

深入浅出InnoDB Locking

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html 本节讨论的所有锁都是在 InnoDB 引擎下 MySQL 实现行锁定主要使用共享锁和排他锁。也就是常说的读写锁。 A shared (S) lock permits the transaction that holds the lock to read a row. An exclusive (X) l…

若依多租户集成浅析(基于数据源隔离)

背景 这边有个做 saas 化应用的需求&#xff0c;要求做到数据源级别隔离&#xff0c;选了 RuoyiCRM: 基于若依Vue平台搭建的多租户独立数据库CRM系统&#xff0c; 项目不断迭代中。欢迎提BUG交流~ (gitee.com) 这个项目做分析 先放一下码云上作者画的图&#xff0c;后面我把整…

股票量化怎样分析股票数据精准选股?

在日常的股票量化交易过程中&#xff0c;通常有不少的交易者会借助股票数据接口来分析股票数据&#xff0c;并且经过一番股票量化分析之后&#xff0c;做到精准选股也是很有可能的事情。那么&#xff0c;普通投资者进行股票量化怎样分析股票数据选好股呢&#xff1f; 首先来了…

springboot:集成Kaptcha实现图片验证码

文章目录springboot&#xff1a;集成Kaptcha实现图片验证码一、导入依赖系统配置文件二、生成验证码1、Kaptcha的配置2、自定义验证码文本生成器3、具体实现三、校验验证码1、controller接口2、自定义前端过滤器3、自定义验证码处理过滤器4、自定义BodyReaderFilter解决读取bod…

Redis——Jedis的使用

前言 接上文&#xff0c;上一篇文章分享了在Linux下安装redis&#xff0c;以及redis的一些命令的使用。本文要分享的内容是java使用代码连接操作redis。 一、连接redis 这里我们要用到Jedis&#xff0c;那么什么是Jedis 简单来说&#xff0c;Jedis就是Redis官方推荐的Java连接…

【元胞自动机】模拟电波在整个心脏中的传导和传播的时空动力学研究(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

(八)SpringCloud+Security+Oauth2--token增强个性化和格式化输出

一 token的个性化输出 我们知道token默认的输出格式是: {"access_token": "21bd6b0b-0c24-40d1-8928-93274aa1180f","token_type": "bearer","refresh_token": "2c38965b-d4ce-4151-b88d-e39f278ce1bb","e…

[思考进阶]02 如何进行认知升级?

除了要提升自己的技术能力&#xff0c;思维的学习和成长也非常非常重要&#xff0c;特推出此[思考进阶]系列&#xff0c;进行刻意练习&#xff0c;从而提升自己的认知。 最近在看东野的《无名之町》&#xff0c;这本书写于2021年&#xff0c;日本正值疫情&#xff0c;书中也有大…