深入Jetpack Compose——布局原理与自定义布局(四)
上一篇文章,我们接触了固有特性测量。这一篇,我们将探索ParentData
ParentData曾经的例子让我们回忆一下第一篇文章中提到的例子,为了实现如下效果
我们当时使用了这样一串修饰符:
12345Box(modifier = Modifier .fillMaxSize() .wrapContentSize(align = Alignment.Center) .size(50.dp) .background(Color.Blue))
也就是说,子微件的居中是它自己的wrapContentSize(align = Alignment.Center)调整的结果。那么,如果我们现在知道了子微件(小的蓝色方块)被包裹在另一个方块(Box)里,我们能不能让父布局帮忙确定居中位置呢?
答案是可以的!Box 在其content作用域中提供了align 方法,这可以让子微件自行告知父布局:我需要居中
12345678910111213@Composableinline fun Box( modifi ...
深入Jetpack Compose——布局原理与自定义布局(三)
在上一篇[文章](深入Jetpack Compose——布局原理与自定义布局(二))中,我们探索了Modifier的本质和原理。这一次我们看看Compose体系中的一个重要特性:固有特性测量。
固有特性测量或许不少人已经知道,Compose为了提高测绘性能,强行规定了每个微件只能被测量一次。也就是说,我们不能写出类似下面这样的代码:
123val placeables = measurables.map { it.measure(constrains) }// 尝试测量第二次,直接报错val placeablesSecond = measurables.map { it.measure(constrains) }
一个小问题那么接下来我们看一个小例子。我们想实现一个菜单,菜单里面有几个菜单栏。于是我们写出了类似这样按的代码
但是效果不怎么样,因为每个Text的宽度不一样。看起来有点丑
你可能会说,要解决这个问题很简单,为每个Text 添加修饰符fillMaxWidth,让它占满即可。效果如下:
但是这样新的问题来了:由于每个Text的C ...
深入Jetpack Compose——布局原理与自定义布局(二)
在上一篇文章深入Jetpack Compose——布局原理与自定义布局(一) - 掘金 (juejin.cn) 中,我们大致了解了Layout过程并简单实现了两个自定义布局。本次让我们将目光转向Modifier和固有特性测量
本文部分参考自Android官方视频:Deep dive into Jetpack Compose layouts
Modifier本质关于Modifier的本质,RugerMc大佬在图解 Modifier 实现原理 ,竟然如此简单这篇文章中已经解释地非常清楚了,我就不画蛇添足了。不过为了后续行文方便,我还是在此简单说几点:
Modifier 是个接口,包含三个直接实现类或接口:伴生对象 Modifier、内部子接口Modifier.Element和CombinedModifier。
伴生对象Modifier是日常使用最多的,后面两者均为内部实现,实际开发中无需关注
Modifier.xxx()方法实际上会创建一个Modifier接口的实现类的实例。如Modifier.size()会创建SizeModifer实例
123456@Stablefun Modifi ...
深入Jetpack Compose——布局原理与自定义布局(一)
Jetpack Compose 正式版发布也已半年了,对我来说,应用到项目中也很久了(参见本人开源项目:译站)。 目前很多文章还集中于初探上,因此萌生了写作本文的想法,算是为Compose中文资料提供绵薄之力。
本文的内容来自Android官方视频:Deep dive into Jetpack Compose layouts
总览Jetpack Compose 中,单个可组合项被显示出来,总体上经历三个过程
Composition(组合) -> Layout(布局) -> Drawing(绘制) ,其中Layout阶段又存在两个方面的内容:Measure(测量) 和 Place(摆放)
今天我们主要着眼于 Layout 阶段,看看各个 Composable 是如何正确确定各自位置和大小的
LayoutLayout阶段主要做三件事情:
测量所有子微件的大小
确定自己的大小
正确摆放所有子元素的位置
为简化说明,我们先给出一个简单例子。该例子中,所有元素只需要遍历一次。
如下图的 SearchResult微件,它的构成如下:
现在我们来看看Layout过程在这个例子中是什 ...
Jetpack Compose 中优雅完成数据持久化
Compose出来也好久了,各种remember和LocalXXX.current也是用得越来越熟。如果能在保持上述写法一致性的情况下完成数据的持久化工作,不是显得挺优雅的吗?基于此,我写出了开源库:ComposeDataSaver: 在Jetpack Compose中优雅完成数据持久化简单一瞥:
12345// booleanExample 初始化值为false// 之后会自动读取本地数据var booleanExample by rememberDataSaverState(KEY_BOOLEAN_EXAMPLE, false)// 直接赋值即可完成持久化booleanExample = true
可还行?
ComposeDataSaver项目有以下特点:
简洁:近似原生的写法
低耦合:抽象接口,不限制底层保存算法实现
轻巧:默认不引入除Compose外任何第三方库
灵活:支持基本的数据类型和自定义类型
引入在settings.gradle引入jitpack仓库位置
12345dependencyResolutionManagement { reposito ...
Jetpack Compose LazyColumn列表项动画
具体包括:列表项数据顺序变更时的轮换动画、添加/删除列表项时的小动画、侧滑删除动画
不废话,本文主要介绍的就是一个修饰符:Modifier.animateItemPlacement()。该修饰符首次出现于Jetpack Compose 1.1.0-beta03版本(此版本于2021年11月17日发布),目前尚处于试验性阶段。但它能实现的效果是非常有趣的。
顺序变更 动画简单看一个例子:
1234567891011var list by remember { mutableStateOf(listOf("A", "B", "C", "D", "E")) } LazyColumn { item { Button(onClick = { list = list.shuffled() }) { Text("打乱顺序") ...
Jetpack Compose 通用加载微件的实现
加载数据在Android开发中应该算是非常频繁的操作了,因此简单在Jetpack Compose中实现一个通用的加载微件
效果如下:
加载中(转圈圈)
加载中
加载完成
另外加载失败后显示失败并可以点击重试
实现实现这个微件其实非常简单,无非就是根据不同的状态加载不同页面
加载状态Bean首先把加载的状态抽象出来,写个数据类
12345678910 sealed class LoadingState<out R> { object Loading : LoadingState<Nothing>() data class Failure(val error : Throwable) : LoadingState<Nothing>() data class Success<T>(val data : T) : LoadingState<T>() val isLoading get() = this is Loading val isSucc ...
基于Jetpack Compose打造一款翻译APP
前言FunnyTranslation 是基于Jetpack Compose写成的翻译软件。它首先是个可以用的软件,其次也是个开源项目。
开源地址:FunnySaltyFish/FunnyTranslation: 基于Jetpack Compose开发的翻译软件,支持多引擎、插件化~ | Jetpack Compose+MVVM+协程+Room (github.com)
运行截图
图片
图片
上面截图显示的所有UI都是基于Jetpack Compose搭建的
挑点啥说一说项目开始时觉得没什么,但是真的写起来却发现不少坑。下面简单挑一个例子。
导航栏内容与底部同步软件的导航栏是基于Row自己定义的,摘录部分如下:
12345678910111213141516171819202122@ExperimentalAnimationApi@Composablefun CustomNavigation( screens : Array<TranslateScreen>, currentScreen: TranslateScreen = s ...
Jetpack Compose 输入法的弹出与隐藏
本文基于Jetpack Compose1.0.4
Jetpack Compose 提供了 SoftwareKeyboardController用于控制软键盘的显示与隐藏,可在Composable中通过LocalSoftwareKeyboardController.current获取
使用隐藏12345val keyboard = LocalSoftwareKeyboardController.current// ...onClick = { keyboard?.hide()}
该操作会试图关闭软键盘,如果因为各种原因软键盘暂时无法关闭,则此操作会被忽略
打开打开软键盘涉及到焦点的获取
12345678910111213141516// 以下代码均在 @Composable 函数中// 焦点请求器val focusRequester = remember { FocusRequester()}// 为需要获取焦点的TextField添加此ModifierBasicTextField( modifier = Modifier ...
我们开启爱发电啦
发电为了支持项目更好地发展,自2021年10月27日起,FunnyTranslation启动爱发电。你可以在这里为项目出一份力!
感谢各位小伙伴的支持!你的每一次发电都会被应用永远记录!
爱你,Mua~