我在“Now In Android”中学到的 9 件事

news/2024/5/19 20:59:02/文章来源:https://blog.csdn.net/u011897062/article/details/130078812

我在“Now In Android”中学到的 9 件事

Now In Android

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 扩展

它只适用于 LazyListScopeColumnScope ……不适用于 @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

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

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

相关文章

【软考备战·希赛网每日一练】2023年4月11日

文章目录一、今日成绩二、错题总结第一题第二题第三题第四题第五题三、知识查缺题目及解析来源&#xff1a;2023年04月11日软件设计师每日一练 一、今日成绩 二、错题总结 第一题 解析&#xff1a; 策略模式&#xff1a;定义一系列算法&#xff0c;把它们一个个封装起来&#…

c++学习之c++对c的扩展1

目录 1.面向过程与面向对象的编程 2.面向对象编程的三大特点 3.c对c的扩展&#xff1a; 1.作用域运算符&#xff1a;&#xff1a; 2.命名空间 1.c命名空间&#xff08;namespace&#xff09; 2.命名空间的使用 1.在不同命名空间内可以创建相同的名称 2.命名空间只能在全…

2.30、守护进程(1)

2.30、守护进程&#xff08;1&#xff09;1.终端是什么2.进程组是什么3.会话是什么4.进程组、会话、控制终端之间的关系5.进程组、会话操作有哪些函数①pid_t getpgrp(void);②pid_t getpgid(pid_t pid);③int setpgid(pid_t pid, pid_t pgid);④pid_t getsid(pid_t pid);⑥pid…

Java 在循环的try catch中使用continue、break

循环的try catch中使用continue、break。 结论&#xff1a;1. 循环内catch代码端中的的continue、break可以正常生效。 2. 无论是continue还是break&#xff0c;退出循环前都会执行finally中的代码 文章目录代码&#xff1a;情形1&#xff08;无continue、break&#xff09;结果…

HTTP协议状态码大全 | 汇总HTTP所有状态码

&#x1f50a; HTTP 状态码 当浏览者访问一个网页时&#xff0c;浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前&#xff0c;此网页所在的服务器会返回一个包含 HTTP 状态码的信息头&#xff08;server header&#xff09;用以响应浏览器的请求。 HTTP…

有反爬机制就爬不了吗?那是你还不知道反反爬,道高一尺魔高一丈啊

文章目录一、从用户请求的Headers反爬虫二、基于用户行为反爬虫&#xff08;1&#xff09;方法1&#xff08;2&#xff09;方法2三、动态页面的反爬虫四.总结不知道你们在用爬虫爬数据的时候是否有发现&#xff0c;越来越多的网站都有自己的反爬机制&#xff0c;抓取数据已经不…

300元左右的蓝牙耳机哪个好?300左右音质最好的蓝牙耳机

无线耳机是人们日常生活中必不可少的设备&#xff0c;无论是听音乐化石看电影都能获得身临其境的感觉&#xff0c;由于科技真在发展中&#xff0c;不断地的发生变化&#xff0c;百元价位就可以感受到不错的音色&#xff0c;下面小编整理了几款300左右音质表现不错的蓝牙耳机。 …

Git这么香,为啥还要可视化?

一、降低入门门槛 Github Desktop / Sourcetree / TortoiseGit “工欲善其事&#xff0c;必先利其器”&#xff0c;团队项目开发中的高效协作管理非常重要。 对于高级程序员及IT老兵来说&#xff0c;有了Git直接用命令行管理也许就足够了&#xff0c; 但可视化的工具会降低技术…

【pta刷题】小技巧

好久没更了 写天梯模拟L1都有题不能AC&#xff0c;是什么品种的蒟蒻 L1-7 谷歌的招聘 题目详情 - L1-7 谷歌的招聘 (pintia.cn) 自己写半天都是Segmentation Fault&#xff0c; 学习一下几个函数叭// 1.substr&#xff08;&#xff09;函数 获取子串 #include<bits/st…

Distilling Knowledge via Knowledge Review(引言翻译)

翻译得可能不太准确&#xff0c;希望有能力的各位批评指正&#xff01; Introduction 第一段 深度卷积神经网络&#xff08;CNN&#xff09;在计算机视觉多数任务中取得了显著的成功。 然而&#xff0c;卷积网络的成功往往伴随着相当大的计算和内存消耗&#xff0c; 使得将…

企业级信息系统开发讲课笔记2.3 利用MyBatis实现关联查询

文章目录零、本节学习目标一、查询需求&#xff08;一&#xff09;针对三张表关联查询&#xff08;二&#xff09;按班级编号查询班级信息&#xff08;三&#xff09;查询全部班级信息二、创建数据库表&#xff08;一&#xff09;创建教师表&#xff08;二&#xff09;创建班级…

我的世界服务器配置推荐,搭建我的世界服务器教程

以下是我整理的部分服务器配置建议与教程&#xff0c;供你参考。 服务器配置推荐&#xff1a; 1. CPU&#xff1a;至少是四核心2.5GHz以上的处理器&#xff0c;建议使用Intel Xeon E3系列或AMD Ryzen 5以上系列 2. 内存&#xff1a;建议至少8GB&#xff0c;如果服务器人数较多…

【Linux】工具(5)——gdb

今天我们来到Linux工具的最后一篇博客&#xff1a;gdb的使用 目录 一、Linux下的release和debug 二、gdb常用指令选项 一、Linux下的release和debug 我们先来写一个Makfile&#xff0c;来方便我们编译代码&#xff1a; 再来写一个test.c的源文件&#xff1a; 接着我们使用m…

unity的下载和安装

我做了一个不知道算不算好的决定&#xff0c;其实还是很难过的。但过去的事就让他过去吧&#xff0c;现在开始学习一些其他的东西吧&#xff0c;比如说unity吧。 一、下载安装工具 Unity的国内下载网址&#xff1a;https://unity.cn/ 进入官网后&#xff0c;选择想要的版本 …

CI570 3BSE001440R1适用于数字功能需求较多的设计

CI570 3BSE001440R1适用于数字功能需求较多的设计 尽管纯硅的CMOS 制程被认为仅适用于数字功能需求较多的设计&#xff0c;而不适用于以模拟电路为主的射频IC 设计&#xff0c;不过历经十几年的努力后&#xff0c;随着CMOS 性能的提升、晶圆代工厂在0.25mm 以下制程技术的配合、…

哪个品牌的蓝牙耳机便宜耐用?内行公认四大便宜耐用的蓝牙耳机

蓝牙耳机发展至今&#xff0c;品牌众多&#xff0c;且各品牌旗下拥有无数不同价格的耳机&#xff0c;各自的主打优势又不一样。那么&#xff0c;哪个品牌的蓝牙耳机便宜耐用&#xff1f;下面&#xff0c;我来给大家推荐四款便宜耐用的蓝牙耳机&#xff0c;一起来看看吧。 一、…

Verilog Tutorial(7)If语句和Case语句

写在前面在自己准备写verilog教程之前&#xff0c;参考了许多资料----FPGA Tutorial网站的这套verilog教程即是其一。这套教程写得不错&#xff0c;只是没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。这是网站原文&#xff1a;h…

webassembly——同源策略问题的处理(浏览器不能加载本地资源的问题)

原因&#xff1a;在用chatGPT生成可视化地图前端文件后&#xff0c;打开不能正常显示 WebAssembly是一种新的二进制代码格式&#xff0c;它可以提供更高的性能和更好的安全性。WebAssembly遵循同源策略&#xff0c;这意味着只有与运行WebAssembly代码相同域名下的JavaScript代码…

FPGA采集AD7606数据UDP网络传输 提供工程源码和技术支持 附带上位机接收软件

目录1、前言2、我这里已有的UDP方案3、AD7606采集详解4、UDP设计方案5、AD7606 UDP传输详细设计方案UDP应用的设计思路获取FPGA网卡信息获取数据UDP发送数据组包UDP发送流程6、vivado工程详解7、上板调试验证并演示8、福利&#xff1a;工程代码的获取1、前言 目前网上的fpga实…

将excel导入到sqlite的方法代码

Python实现excel转sqlite的方法&#xff0c;具体如下&#xff1a; Python环境的安装配置就不说了&#xff0c;个人喜欢pydev的开发环境。 python解析excel需要使用第三方的库&#xff0c;这里选择使用xlrd 下面是源代码&#xff1a; #!/usr/bin/python # encodingutf-8 Creat…