Android compose学习笔记

如标题所言,就是一篇学习笔记而已,没有看的必要,只是写给自己看的,内容是慢慢更新的。
因为白天要上班,有时还会加班。而我自己也经常写一些个人项目,还会花时间玩游戏,而且现在所在的公司也不会用compose去开发,所以就慢慢学,慢慢记。

Build

必须在build文件的android下面设置下面的属性

buildFeatures {compose true
}

compose的依赖,这些是android studio自动生成的,每个的作用不清楚

implementation 'androidx.activity:activity-compose:1.5.1'
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'

在构建时,可能会由于kotlin版本的问题而构建失败,按照提示修改为合适的版本即可

Activity

compose必须继承哪个Activity还不清楚,android studio生成的代码继承的是androidx.activity.ComponentActivity。
这个Activity与其他的ComponentActivity有所不同,代码已经是不一样的。而且setContent这个方法,就是这个ComponentActivity的扩展方法。
建议直接用这个Activity,下面是使用原生Activity的情况,出现了很多问题。最终结果是失败的,如果不想看这个过程,可以跳过这部分。

compose还提供了一个ComposeView。
先看看ComposeView的注释
Composes the given composable into the given activity. The content will become the root view of the given activity.
This is roughly equivalent to calling ComponentActivity.setContentView with a ComposeView i.e.:

setContentView(ComposeView(this).apply {setContent {MyComposableContent()}}
)

如果在Activity直接使用这个View,会抛出:“IllegalStateException: ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView”。
经过测试,如果只是new,而没有调用setContentView,就没有这个异常,说明是setContentView调用了View的某段代码出现的。
阅读源码后发现,问题是出在ComposeView的onAttachedToWindow方法上,在这个方法里面,最终会调用WindowRecomposer.android.kt:349这行代码检查是否设置了Lifecycle。
这行代码所在的方法有一个Lifecycle参数,这里应该是空的,所以是调用ViewTreeLifecycleOwner.kt的View.findViewTreeLifecycleOwner获取的。
最终获取到的值也是空的,所以就抛出异常。在这个kt文件也可以看到,它还提供了set方法,所以可以调用set方法设置Lifecycle。但设置之后,出现了新的问题。
这次没有报Lifecycle的异常了,但有了新的异常:“IllegalStateException: Composed into the View which doesn’t propagateViewTreeSavedStateRegistryOwner!”。
阅读代码之后,发现是少了SavedStateRegistryOwner。这个是调用了AndroidComposeView.android.kt的代码,这个文件里面,同样有一个set方法。
同样的套路设置,不过这个不能像Lifecycle那样直接设置。我的做法是:实现SavedStateRegistryOwner,此时,需要在savedStateRegistry返回一个SavedStateRegistry。
本来想要直接new的,但发现构造方法是internal。然后我在源码中发现,可以这样获取SavedStateRegistryController.create(this).savedStateRegistry。
经过一番折腾后,项目总算不抛异常了。但是,当调用ComposeView的setContent方法添加View之后。会发现,View并没有显示在屏幕上。
所以还要看看怎么样才能显示出来,这个我就不折腾了,这个难度比找异常大多了。
所以可以发现,除非有什么定制需求,或者自己对这块已经了然于心,否则没事不要用Activity去玩compose。
看一下最终代码:

override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val composeView = ComposeView(this)composeView.setViewTreeLifecycleOwner(this)composeView.setViewTreeSavedStateRegistryOwner(this)composeView.setContent {Text(text = "text")}setContentView(composeView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT))
}
override val lifecycle: Lifecycle = LifecycleRegistry(this)
override val savedStateRegistry: SavedStateRegistry = SavedStateRegistryController.create(this).savedStateRegistry

而如果是继承ComponentActivity,就会发现很简单

setContentView(ComposeView(this).apply {setContent {Text(text = "text")}
})

不过都继承ComponentActivity了,那就没有太大的必要使用这个了。而且,在setContent的代码里面,还加了很多处理compose的代码。

然后有一点需要注意,compose的ComponentActivity和非compose的ComponentActivity是不一样的。
虽然两者的包名一样,但实现的类是不完全一样的。
如果没有使用compose,那ComponentActivity是这样的

public class ComponentActivity extends androidx.core.app.ComponentActivity implementsLifecycleOwner,ViewModelStoreOwner,SavedStateRegistryOwner,OnBackPressedDispatcherOwner{// ...
}

而如果引入compose的ComponentActivity依赖则是

public class ComponentActivity extends androidx.core.app.ComponentActivity implementsContextAware,LifecycleOwner,ViewModelStoreOwner,HasDefaultViewModelProviderFactory,SavedStateRegistryOwner,OnBackPressedDispatcherOwner,ActivityResultRegistryOwner,ActivityResultCaller,OnConfigurationChangedProvider,OnTrimMemoryProvider,OnNewIntentProvider,OnMultiWindowModeChangedProvider,OnPictureInPictureModeChangedProvider,MenuHost{// ...
}

实现的类明显多了。

使用

在setContent里面,可以调用带有@Composable注解的方法来构建UI。
google自带的compose方法都是大写开头,猜测是为了让开发者能够快速看出是compose方法。
可以在compose方法上面使用@Preview注解,用来预览界面。但使用该注解的compose方法必须没有参数,否则没办法预览。
code:

// 这样就能够显示 hello world
setContent {Text(text = "hello world")
}// 如果希望可以预览,可以这样
setContent {HelloWorldPreview()
}@Preview
@Composable
fun HelloWorldPreview() {Text(text = "hello world")
}

如果想要预览,可以在Activity的右上角点击"Split",如果compose没有及时更新,可以在预览界面的右上角找到Build & Refresh。
一个元素想要预览,必须在没有参数的方法里面编写界面,如果一个方法有参数,是没办法预览的,官方文档就是这样写的。

布局

把布局放在控件前面是因为常用布局不多,而且还需要通过布局引出Modifier,Modifier也是compose重要的组成部分。
Modifier无论是在布局,还是控件上,都有重要的作用。
默认的布局

setContent{Message()    
}@Preview
@Composable
fun Message() {Text("Text1")Text("Text2")
}

这段代码会在内容视图中创建两个文本元素。不过,由于您未提供有关如何排列这两个文本元素的信息,因此它们会相互重叠,使文本无法阅读。
Column

setContent {Column {Message()}
}

Column函数可以垂直排列控件。Column方法有4个参数,分别是:Modifier、Arrangement.Vertical、Alignment.Horizontal和content。
Modifier功能强大,下面再说。content就是显示控件,不用说。 Arrangement.Vertical和Alignment.Horizontal和content从名字就可以看出,是指定子控件的位置。
比如给Arrangement.Vertical设置一个Arrangement.Bottom,所有子控件就会在底部。给Alignment.Horizontal设置一个Alignment.End,所有子控件就会在屏幕右边。
两者的默认值分别是Top和Start,所以不指定该参数时,子元素就显示在左上角。
在设置这两个参数时,还需要设置Modifier,可以将Column大小设置为最大:Modifier.fillMaxSize(),这样才能看到实际效果。如果不设置,相当于宽高都为WRAP_CONTENT。
Row

setContent {Row {Message()}
}

Row函数可以水平排列元素。Row的参数和Column差不多,只是第2个和第3个参数变成了Arrangement.Horizontal和Alignment.Vertical。
用法上,和Column差不多。默认值和Column一样,也是Top和Start。
Box

setContent {Box {Message()}
}

这个有点像RelativeLayout,如果配合Modifier使用,好像功能会变得很强大。我在官方文档看到很多示例代码,但不知道那些代码的作用,后面再看看吧。
Box第2个参数和第3个参数分别为:Alignment和propagateMinConstraints: Boolean。两个的默认值分别为:Alignment.TopStart和false。
Alignment和Column和Row有点不一样,但也没有太大区别。该对象可以指定CenterEnd、BottomStart等参数,看名称也知道是什么意思,不用解释。
propagateMinConstraints:Whether the incoming min constraints should be passed to content.
TODO,测试了一下,设置为true之后,即使size为最大,还是没办法显示在屏幕底部。这个参数应该会影响大小,但不敢肤浅地就这样下定论,所以会好好查一下这个参数的真正作用。

不同测量单位

在将Modifier之前,先搞定这个,这个不止能用在Modifier,文本大小也需要用到。
在layout文件里面,通常想要指定大小为dp时,只需在数字后面写上dp即可。而java/kotlin方面,则需要获取屏幕宽度换算,或者使用TypedValue.applyDimension计算。
而在compose里面,这两种方式都用不了。需要使用Dp对象,Sp需要使用TextUnit对象。

// dp
Dp(100f)
100f.dp
// sp
100f.sp
// em
100f.em

sp和em用的都是TextUtil对象,没办法直接new,只能用扩展变量。
上面声明时都是用Float类型,实际上,除了第一个使用new的方式,像100f.dp这种,还有Int和Double两种声明方式。
Dp为plus、minus、unaryMinus、div和times编写了operator,所以可以直接使用+、-、*和/这些符号操作dp。
对于plus和minus来说,需要传入一个Dp对象。times可以Int和Float。div可以Int、Float和Dp对象。unaryMinus则是在数字前面加上"-“。
Dp里面还有Dp.Hairline、Dp.Infinity和Dp.Unspecified可以使用。
sp和em用的是TextUtil,所以看看TextUtil。TextUtil有unaryMinus、times和div。 times和div都有Int、Float和Double3个参数。
无论是Dp还是TextUtil,都实现了Comparable,所以可以还能使用compareTo或者”<>"来比较两个的值大小。
DpSize
除了上面提到的dp和sp,compose还提供了DpSize,DpSize可以用在Modifier里面。
DpSize有两个参数,分别是width和height,都是Dp类型。可以通过dpSize.width和dpSize.height分别获取宽高。
DpSize也提供了plus、minus、times和div用来操作DpSize,提供了copy用于深拷贝。除此之外,还提供了component1和component2。所以可以这样获取宽高。

val (width, height) = DpSize(100.dp, 100.dp)

DpOffset
这个目前不知道有什么用,但被我看到了,就提出来。DpOffset两个参数分别为x和y。operator有plus和minus,也有copy方法,其他方法就没有了。
DpRect
有4个初始参数,分别是left、top、right、bottom。DpRect还提供了一个有两个参数的构造方法,为origin: DpOffset, size: DpSize。
DpRect只有参数,没有其他方法。DpRect是一个data class,所以可以这样获取left、top、right、bottom。

val (left, top, right, bottom) = DpRect()

DpRect有3个扩展字段,width、height、size,size返回的是一个DpSize对象。

Modifier

在xml编写View时,可以给一个View指定background、size、padding等参数,到了compose,这一切都交给了Modifier,所以我才在上面提到,这个的功能很强大。
首先,需要知道一件事,Modifier可以链式调用。比如:Modifier.fillMaxSize().background(Color.Black)。指定了maxSize和background。
知道了Modifier可以这样使用之后,就会发现Modifier是很强大的,可以调用Modifier不同的方法,组合出不同的效果。

Modifier.size(100.dp, 100.dp)

控件

自定义View

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

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

相关文章

c# Outlook检索设定问题

基于c# 设定outlook约会予定&#xff0c;时间格式是YYYY-MM-DD HH:mm 的情报。 问题发生&#xff1a; 根据开始时间&#xff08;2023/01/01 7:00&#xff09;条件查询该时间是否存在outlook信息时&#xff0c;明明存在一条数据&#xff0c;就是查询不出来数据 c#代码 Strin…

Observability:Synthetic monitoring - 动手实践

在我之前的如下文章里&#xff1a; Observability&#xff1a;Synthetic monitoring - 合成监测入门&#xff08;一&#xff09;&#xff08;二&#xff09; Observability&#xff1a;Synthetic monitoring - 创建浏览器监测&#xff0c;配置单独的浏览器监测器及项目 我详…

[flutter][报错]One or more plugins require a higher Android SDK version.

报文 One or more plugins require a higher Android SDK version. Fix this issue by adding the following to D:\github\flutter_password_saving_software\android\app\build.gradle: android {compileSdkVersion 33... }解决 修改方案&#xff1a;找到本地flutter安装目…

nginx路由

一般我们经常在访问网站时&#xff0c;通常会遇到输入某个页面的网址时&#xff0c;出现路由的转发&#xff0c;重定向等。可能访问的是一个网址&#xff0c;出来的时候就显示的是另外的地址。这是由于使用了nginx的缘故&#xff0c;保护了网址的安全性 &#xff08;1&#xf…

数据预处理matlab

matlab数据的获取、预处理、统计、可视化、降维 数据的预处理 - MATLAB & Simulink - MathWorks 中国https://ww2.mathworks.cn/help/matlab/preprocessing-data.html 一、数据的获取 1.1 从Excel中获取 使用readtable() 例1&#xff1a; 使用spreadsheetImportOption…

跟着gpt学算法(c和python)-排序-冒泡排序

排序算法是将一组数据按照特定规则进行排列的算法。排序算法可以按照不同的标准进行分类&#xff0c;比如稳定性、时间复杂度、空间复杂度等。以下是一些常见的排序算法&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a;相邻元素进行比较&#xff0c;较大…

JavaWeb_LeadNews_Day3-图片管理, 文章管理

JavaWeb_LeadNews_Day3-图片管理, 文章管理 图片管理图片上传实现思路获取用户信息将图片上传至minio 图片列表 文章管理频道列表查询文章列表查询文章发布实现思路具体代码 来源 图片管理 图片上传 实现思路 在GateWay解析前端请求, 获取用户信息, 存储在header中在Interce…

Vue整体架构分解

Vue.js的整体架构可以分解为以下几个部分: 文章目录 1. 数据驱动2. 组件化3. 响应式系统4. 虚拟DOM5. 插件系统6. 单文件组件7. 模板编译总结 1. 数据驱动 Vue的一个核心特点是数据驱动。Vue会在初始化的时候给数据提供一个observe监听&#xff0c;当数据变化时&#xff0c;会…

uniapp 微信小程序 input详解 带小数点的input、可查看密码的输入框input

官网文档地址 1、template <!-- 本示例未包含完整css&#xff0c;获取外链css请参考上文&#xff0c;在hello uni-app项目中查看 --> <template><view><view class"uni-common-mt"><view class"uni-form-item uni-column"&g…

【C++】开源:跨平台轻量日志库easyloggingpp

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍跨平台轻量日志库easyloggingpp。 无专精则不能成&#xff0c;无涉猎则不能通。。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&am…

详细介绍Matlab中线性规划算法的使用

Matlab中提供了用于线性规划的优化工具箱&#xff0c;其中包含了多种算法&#xff0c;如单纯形法、内点法等。线性规划是一种优化问题&#xff0c;旨在找到一组变量的最佳值&#xff0c;以最大化或最小化线性目标函数&#xff0c;同时满足一组线性约束条件。 下面将详细介绍Ma…

【C++11】智能指针的定义 和 种类 及 使用

智能指针 定义 为什么需要智能指针 在C中&#xff0c;动态分配内存是一项常见的任务&#xff0c;但手动管理分配和释放内存可能会导致很多问题&#xff0c;如内存泄漏、悬垂指针以及多次释放同一块内存等。为了避免这些问题&#xff0c;引入了智能指针的概念&#xff0c;它们…

(笔记)插入排序

插入排序 插入排序是一种简单且常见的排序算法&#xff0c;它通过重复将一个元素插入到已经排好序的一组元素中&#xff0c;来达到排序的目的。在插入排序算法中&#xff0c;将待排序序列分为已排序和未排序两个部分。初始时&#xff0c;已排序部分只包含一个记录&#xff0c;…

LiveGBS流媒体平台GB/T28181功能-海康NVR摄像机自带物联网卡摄像头注册GB/T28181国标平台看不到设备的时候如何抓包及排查

海康大华宇视华为等硬件NVR摄像机注册到LiveGBS国标平台看不到设备的时候如何抓包及排查 1、设备注册后查看不到1.1、是否是自带物联网卡的摄像头1.2、关闭萤石云1.3、防火墙排查1.4、端口排查1.5、IP地址排查1.6、设备TCP/IP配置排查1.7、设备多网卡排查1.8、设备接入配置参数…

Docker(四)

文章目录 1. docker其他命令补充2. docker-registry使用3. docker-hub的使用4. 企业级私有仓库harbor4.1 harbor安装4.2 harbor配置https4.3 harbor常见使用4.3.1 harbor新建项目仓库4.3.2 harbor创建用户4.3.3 harbor仓库管理4.3.4 harbor复制管理4.3.5 harbor删除镜像 5. doc…

K8S下如何搭建eureka集群

背景 传统应用上云&#xff0c;基于传统应用需要考虑上云的方案和改造成本&#xff0c;这也是传统应用上云过程中的难点&#xff0c;本篇介绍3台eureka搭建的方案。 方案一 此方案借助了K8S中Service的一些功能。 这种方案是传统方案的简单迁移版本&#xff0c;比较易于理解…

深度学习:tf.keras实现模型搭建、模型训练和预测

在sklearn中&#xff0c;模型都是现成的。tf.Keras是一个神经网络库,我们需要根据数据和标签值构建神经网络。神经网络可以发现特征与标签之间的复杂关系。神经网络是一个高度结构化的图&#xff0c;其中包含一个或多个隐藏层。每个隐藏层都包含一个或多个神经元。神经网络有多…

【微信小程序】使用iView组件库中的icons资源

要在微信小程序中使用iView组件库中的icons资源&#xff0c;需要先下载并引入iView组件库&#xff0c;并按照iView的文档进行配置和使用。 以下是一般的使用步骤&#xff1a; 下载iView组件库的源码或使用npm安装iView。 在小程序项目的app.json文件中添加iView组件库的引入配…

mac端好用的多功能音频软件 AVTouchBar for mac 3.0.7

AVTouchBar是来自触摸栏的视听播放器&#xff0c;将跳动笔记的内容带到触摸栏&#xff0c;触摸栏可显示有趣的音频内容&#xff0c;拥有更多乐趣&#xff0c;以一种有趣的方式播放音乐&#xff0c;该软件支持多种音频播放软件&#xff0c;可在Mac上自动更改音乐~ 音频选择-与内…

Flask Bootstrap 导航条

(43条消息) Flask 导航栏&#xff0c;模版渲染多页面_U盘失踪了的博客-CSDN博客 (43条消息) 学习记录&#xff1a;Bootstrap 导航条示例_bootstrap导航栏案例_U盘失踪了的博客-CSDN博客 1&#xff0c;引用Bootstrap css样式&#xff0c;导航栏页面跳转 2&#xff0c;页面两列…