我在“Now In Android”中学到的 9 件事
我在“Now In Android”中学到的 9 件事
Now in Android是一款功能齐全的 Android 应用程序,完全使用 Kotlin 和 Jetpack Compose 构建。它遵循 Android 设计和开发最佳实践,旨在为开发人员提供有用的参考。
https://github.com/android/nowinandroid
UI 状态类
sealed interface InterestsUiState {object Loading : InterestsUiStatedata class Interests(val authors: List<FollowableAuthor>,val topics: List<FollowableTopic>) : InterestsUiStateobject Empty : InterestsUiState
}
此类旨在保存屏幕上显示的流数据,这些数据将随时间或事件发生变化,例如:调用 API 显示动作电影的流将需要视图来显示加载屏幕(以防请求花费太长时间)在显示查询结果之前,如果发现异常则显示错误屏幕。
在发现这一点之前,我是这样管理视图模型中屏幕上显示的流数据的:
var authors: LiveData<List<FollowableAuthor>> = MutableLiveData(emptyList())
var topics: LiveData<List<FollowableTopic>> = MutableLiveData(emptyList())val isLoading = MutableLiveData(true)val isEmpty: LiveData<Boolean> = authors.switchMap { authors ->topics.map { topics ->topics.isEmpty() && authors.isEmpty()}
}init {viewModelScope.launch {authors = authorsRepository.getAuthorsStream()topics = topicsRepository.getTopicsStream()}.invokeOnCompletion {isLoading.value = false}
}
现在代码看起来像这样:
val uiState = combine(authorsRepository.getAuthorsStream(),topicsRepository.getTopicsStream(),
) { availableAuthors, availableTopics ->InterestsUiState.Interests(authors = availableAuthors,topics = availableTopics)
}.stateIn(scope = viewModelScope,started = SharingStarted.WhileSubscribed(5_000),initialValue = InterestsUiState.Loading
)
(在这个例子中,我从 LiveData 切换到 StateFlow,但实际上使用“asLiveData()”和“asFlow()”方法仍然很容易互换)
State holder & UI State
https://developer.android.com/topic/architecture/ui-layer/state-production#stream-apis
使用构造函数引用转换流
val myStream = combine(followedAuthorIdsStream,authorStream,::Pair
)
而不是“手动”映射它…
val myStream =
combine(followedAuthorIdsStream,authorStream,
) { followedAuthorIds, author -> Pair(followedAuthorIds, author)
}
“::Class”被称为构造函数引用。构造函数可以像方法和属性一样被引用。您可以在程序需要函数类型对象的任何地方使用它们,该对象采用与构造函数相同的参数并返回适当类型的对象。
轻松将其映射到密封接口结果中
return combine(followedAuthorIdsStream,authorStream,::Pair
).asResult() // <- The change is here !
因为我们之前将我们的流配对成单个流,所以我们可以将它映射到一个会改变的结果类中,就像UIState
类一样,但是以一种更通用的方式,因为 Result.Success
将公开一个字段:数据。
sealed interface Result<out T> {data class Success<T>(val data: T) : Result<T>data class Error(val exception: Throwable? = null) : Result<Nothing>object Loading : Result<Nothing>
}
Flow
上的 Kotlin 扩展可自动映射到Result
中
fun <T> Flow<T>.asResult(): Flow<Result<T>> {return this.map<T, Result<T>> {Result.Success(it)}.onStart { emit(Result.Loading) }.catch { emit(Result.Error(it)) }
}
将其映射到 UI 状态类中
return combine(followedAuthorIdsStream,authorStream,::Pair
).asResult()
.map { followedAuthorToAuthorResult ->when (followedAuthorToAuthorResult) {is Result.Success -> {val (followedAuthors, author) = followedAuthorToAuthorResult.dataAuthorUiState.Success()}is Result.Loading -> AuthorUiState.Loadingis Result.Error -> AuthorUiState.Error}
}
使用生命周期安全方法搜集状态
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
我正在使用 collectAsState()
并且没有注意到生命周期版本已经结束。
collectAsState()
方法的提醒:
@Composable
public fun <T> StateFlow<T>.collectAsState(context: CoroutineContext
): State<T>
从此
StateFlow
收集值并通过State
表示其最新值。StateFlow.value
用作初始值。每次有新值发布到StateFlow
时,返回的State
都会更新,从而导致每个State.value
用法的重组。
Composable
中的方法引用
@Composable
fun MyScreen(viewModel: ForYouViewModel) {ForYouScreen(onTopicCheckedChanged = viewModel::updateTopicSelection,onAuthorCheckedChanged = viewModel::updateAuthorSelection,saveFollowedTopics = viewModel::saveFollowedInterests,onNewsResourcesCheckedChanged = viewModel::updateNewsResourceSaved,)
}
这使代码更具可读性,特别是在这里,您的眼睛需要关注 UI。
调用参考:
@Composable
fun MyScreen(viewModel: ForYouViewModel) {ForYouScreen(onTopicCheckedChanged = { topicId, isChecked ->viewModel.updateTopicSelection(topicId = topicId, isChecked = isChecked)},onAuthorCheckedChanged = { authorId, isChecked ->viewModel.updateAuthorSelection(authorId = authorId, isChecked = isChecked)},saveFollowedTopics = { viewModel.saveFollowedInterests() },onNewsResourcesCheckedChanged = { newsResourceId, isChecked ->viewModel.updateNewsResourceSaved(newsResourceId = newsResourceId,isChecked = isChecked)},)
}
多预览的注解
表示各种设备尺寸的多预览的注解。将此注解添加到Composable
以呈现各种设备。
@Preview(name = "phone", device = "spec:shape=Normal,width=360,height=640,unit=dp,dpi=480")
@Preview(name = "landscape", device = "spec:shape=Normal,width=640,height=360,unit=dp,dpi=480")
@Preview(name = "foldable", device = "spec:shape=Normal,width=673,height=841,unit=dp,dpi=480")
@Preview(name = "tablet", device = "spec:shape=Normal,width=1280,height=800,unit=dp,dpi=480")
annotation class DevicePreviews
Multipreview
注解表示不同设备尺寸的多个预览。可以将这个注解添加到Compose
中的某个组件上,以在不同设备上渲染多个预览界面。
Android允许您通过继承IssueRegistry来创建自定义lint规则,示例如下(我不会解释如何做,因为我在我的项目中还没有成功地让它正常工作):
class DesignSystemIssueRegistry : IssueRegistry()
在最新的Android版本中,他们创建了一个类来检查是否使用了他们自定义的组件而不是通用的组件。以下是他们编写的代码片段:
class DesignSystemDetector : Detector(), Detector.UastScanner {...val METHOD_NAMES = mapOf("MaterialTheme" to "NiaTheme","Button" to "NiaFilledButton","OutlinedButton" to "NiaOutlinedButton",...)
}
我发现这对不知道项目中已经存在特定组件的新开发人员很有用。
Composable 扩展
它只适用于 LazyListScope
、ColumnScope
……不适用于 @Composable
。
fun LazyListScope.MyItem() {item {// Your Composable}
}@Composable
fun MyList() {LazyColumn {MyItem()}
}
我发现这很有用,例如:在多个屏幕上共享可组合的粘性标头。
先前:
@Composable
fun MyStickyHeader() {// Composable
}@Composable
fun MyList() {LazyColumn {stickyHeader {MyStickyHeader()}}
}
之后:
fun LazyListScope.MyStickyHeader() {stickyHeader {// Composable}
}@Composable
fun MyList() {LazyColumn {MyStickyHeader()}
}
参考链接
https://medium.com/@romeo.prosecco/top-things-i-learned-on-now-in-android-dba991da1c0
相关文章:

我在“Now In Android”中学到的 9 件事
我在“Now In Android”中学到的 9 件事 Now in Android是一款功能齐全的 Android 应用程序,完全使用 Kotlin 和 Jetpack Compose 构建。它遵循 Android 设计和开发最佳实践,旨在为开发人员提供有用的参考。 https://github.com/android/nowinandroid UI…...

ChatGPT宝藏插件丨装上之后,上网、语音聊天、一键分享对话……简直让你爽到起飞!
今天分享4个让你的 ChatGPT 功能更强大的浏览器插件,装上就能用,每一个都是精挑细选。 1. WebChatGPT 很多小伙伴在用 ChatGPT查阅信息时,发现它有一个致命的问题: ChatGPT的知识库全部截止到 2021年9月,正常情况下…...

私有句柄表
私有句柄表 实验环境 win7 x86 什么是私有句柄表? 私有句柄表是操作系统内部的一种数据结构,用于存储一个进程所拥有的句柄(或称为句柄对象)的信息。在操作系统中,句柄是一个标识符,用于唯一标识一个对…...
Vue——类与样式绑定
目录 Class 与 Style 绑定 绑定 HTML class 绑定对象 绑定数组 在组件上使用 绑定内联样式 绑定对象 绑定数组 自动前缀 样式多值 Class 与 Style 绑定 数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class 和 styl…...
软考中项计算题总结
计算题在下午的考试属于重中之重,可以说得计算题得天下,先把计算题搞定,再看案例找错题,这2个是最容易得分的,所以对于进度、成本类的计算题一定要搞懂: 所属项目过程计算计算公式说明进度管理三点估算&am…...

如何使用基于GPT-4的Cursor编辑器提升开发效率
程序员最恨两件事情:一是别人代码不写文档,二是要让自己写文档。随着 GPT-4 的到来这些都不是问题了,顺带可能连程序员都解决了。。。 之前一直觉得 AI 生成的代码也就写个面试题的水平,小打小闹,现在时代可变了。Curs…...

压箱底教程分享,手把手教会你如何注册target账号和下单
喜欢套利的朋友肯定都认识target这个平台吧,它是美国热门的综合性海淘网站之一。东哥近日收到私信有朋友向我请教在注册target账号时遇到的一些问题,所以今天东哥想跟大家分享的就是就是target账号注册教程和下单流程,让也想注册target账号的…...

一次性搞懂dBSPL、dBm、dBu、dBV、dBFS的区别!
相信学习音乐制作的同学在混音阶段经常会碰到各种关于声音的单位,其中最具代表性的可能就是分贝家族的单位了,如dBSPL、dBm、dBu、dBV、dBFS等。 那么,这些单位分别表示什么,又有什么区别呢? 描述声音信号强弱的单位…...

漂亮实用的15个脑图模板,你知道哪些是AI做的吗?
对于很多第一次接触到思维导图的朋友,看到软件的时候往往找不到方向,不知道如何创作? 今天大家的好助手来了。 一是有大量的思维导图模板,大家看着模板做,慢慢就会做了。 二是ProcessOn 思维导图已经可以用AI 做思维…...
历代程序员都无法逃脱的诅咒 -- 低代码
1764年5月4日星期四 愤怒的纺织工人 纵火烧毁了哈格里夫斯的家 因为他发明的珍妮纺织机 让很多当地的手工纺织工人失业了 这也被认为是第一次工业革命的开端 由于事发的星期四 所以这一事件也被称作疯狂星期四 类似的变革 也一次次的出现在软件行业 他是历代程序员都无法逃脱的…...

14Exceptional Control Flow Exceptions and Process(异常控制流,异常和进程)
异常控制流 异常控制流出现的地方: 异常控制流(Exceptional Control Flow,ECF)是程序执行过程中由于某些特殊事件或条件而导致的控制流的改变。异常控制流通常出现在以下几种情况: 硬件异常和中断:硬件异…...
LeetCode - 两数之和
题目信息 源地址:两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不…...

Python 小型项目大全 31~35
三十一、猜数字 原文:http://inventwithpython.com/bigbookpython/project31.html 猜数字是初学者练习基本编程技术的经典游戏。在这个游戏中,电脑会想到一个介于 1 到 100 之间的随机数。玩家有 10 次机会猜出数字。每次猜中后,电脑会告诉玩…...

他又赚了一万美金
有一些学员真的挺能干的,收了一万刀,感到欣慰,毕竟在国外lead这条路,有很多人被骗,也有很多人赚钱。 但是大部分人跟着某一些所谓的大佬,最后自己却不动手操作。 从一开始怕跟我学习,到最后选…...
企业工程项目管理系统+spring cloud 系统管理+java 系统设置+二次开发
工程项目各模块及其功能点清单 一、系统管理 1、数据字典:实现对数据字典标签的增删改查操作 2、编码管理:实现对系统编码的增删改查操作 3、用户管理:管理和查看用户角色 4、菜单管理:实现对系统菜单的增删改查操…...

教你使用Apache搭建Http
Apache2默认采用的是80端口号,因此直接通过公网ip或域名就能访问。现实中,很多服务器本身就部署了许多其它服务,80端口号往往被占用,因此就需要将Apache2改成其它访问端口。 修改端口,首先需要修改/etc/apache2/ports…...

ZooKeeper+Kafka+ELK+Filebeat集群搭建实现大批量日志收集和展示
文章目录一、集群环境准备二、搭建 ZooKeeper 集群和配置三、搭建 Kafka 集群对接zk四、搭建 ES 集群和配置五、部署 Logstash 消费 Kafka数据写入至ES六、部署 Filebeat 收集日志七、安装 Kibana 展示日志信息一、集群环境准备 1.1 因为资源原因这里我就暂时先一台机器部署多…...

数据结构初阶 - 总结
-0- 数据结构前言 什么是数据结构 什么是算法 数据结构和算法的重要性-1- 时间复杂度和空间复杂度 👉数据结构 -1- 时间复杂度和空间复杂度 | C 算法效率 时间复杂度大O的渐进表示法eg 空间复杂度 常见复杂度对比OJ 消失的数组 轮转数组-2- 顺序表 与 链表 &am…...
代码随想录算法训练营第四十四天-动态规划6|518. 零钱兑换 II ,377. 组合总和 Ⅳ (遍历顺序决定是排列还是组合)
如果求组合数就是外层for循环遍历物品,内层for遍历背包。 如果求排列数就是外层for遍历背包,内层for循环遍历物品。 求物品可以重复使用时,最好是用一维数组,会比较方便。二维数组不想思考了,二维还是用在01背吧吧。…...

wma格式怎么转换mp3,4种方法超快学
其实我们在任何电子设备上所获取的音频文件都具有自己的格式,每种格式又对应着自己的属性特点。比如wma就是一种音质优于MP3的音频格式,虽然很多小伙伴比较青睐于wma所具有的音质效果,但也不得不去考虑因wma自身兼容性而引起很多播放器不能支…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...