Android中构建多视图 RecyclerView的正确打开方式

Android中构建多视图 RecyclerView的正确打开方式

简介

漂亮的UI能极大提高用户留存率,相反糟糕的UI将导致App安装率下降。
漂亮的UI设计
UI体验对用户留存率有特别大的影响,较差的体验app我可能用不了2s就要卸载掉。


你需要学习内容如下:

  1. 使用单个RecyclerView来处理多种视图类型
  2. 整洁的代码 - MVVM架构
  3. 显示来自外部API的数据
  4. 使用Motion Layout进行动画

完整的代码:
https://github.com/ibrajix/NftApp

DATA

我们使用了一个外部API,我通过https://mockapi.io/ 进行了模拟。

1、2、3和4是上图显示的布局的一部分,可以是动态的。因此,我们将为每个创建一个布局项。请查看完整代码以获取各种RecyclerView布局文件。

NftData.kt

sealed class NftData {class Title(val id: Int,val title: String,val viewAll: String,) : NftData()class Featured(val image: String,val title: String) : NftData()class Top(val id: Int,val image: String) : NftData()class Trending(val id: Int,val image: String,val name: String,val category: String) : NftData()}

RETROFIT

我们正在使用retrofit来向外部 API 发送请求。
ApiService.kt

interface ApiService {//get top nft@GET(EndPoints.TOP_NFT)suspend fun getTopNft() : List<NftData.Top>//get trending nft@GET(EndPoints.TRENDING_NFT)suspend fun getTrendingNft() : List<NftData.Trending>}

ApiDataSource.kt

class ApiDataSource @Inject constructor(private val apiService: ApiService) {//get top nftsuspend fun getTopNft() = apiService.getTopNft()//get trending nftsuspend fun getTrendingNft() = apiService.getTrendingNft()}

Repository

正如你所看到的,我正在使用Hilt进行依赖注入,以注入所需的类。请查看NetworkModule.kt的完整代码,了解我如何提供所需的Retrofit类和依赖项。

NftRepository.kt

class NftRepository @Inject constructor(private val apiDataSource: ApiDataSource) : SafeApiCall {suspend fun getTopNft() = safeApiCall { apiDataSource.getTopNft() }suspend fun getTrendingNft() = safeApiCall { apiDataSource.getTrendingNft() }}

Recyclerview

一个RecyclerView需要一个ViewHolder和一个Adapter。这个应用程序由一个单独的RecyclerView组成,请检查activity_main.xml文件。

NftViewHolder.kt

  • 这将是一个密封类,因为我们希望对继承有更多控制。
  • 我们将使用viewBinding与每个RecyclerView布局文件进行交互。
  • 我使用一个叫做coil的库从外部API中加载图像,同时还提供了一些转换(例如圆角等)(再见Glide)。
sealed class NftViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {var itemClickListener: ((view: View, item: NftData, position: Int) -> Unit)? = nullclass TitleViewHolder(private val binding: RcvLytTitleBinding) : NftViewHolder(binding){fun bind(title: NftData.Title) {binding.txtFeatured.text = title.titlebinding.txtViewAll.text = title.viewAllbinding.txtViewAll.setOnClickListener {itemClickListener?.invoke(it, title, adapterPosition)}}}class FeaturedViewHolder(private val binding: RcvLytFeaturedBinding) : NftViewHolder(binding){fun bind(featured: NftData.Featured){binding.imgFeatured.load(FEATURED_IMAGE){crossfade(true)transformations(RoundedCornersTransformation(20F))}binding.imgFeatured.setOnClickListener {itemClickListener?.invoke(it, featured, adapterPosition)}binding.txtFeaturedTitle.text = FEATURED_IMAGE_TITLE}}class TopPicksViewHolder(private val binding: RcvLytTopPicksBinding) : NftViewHolder(binding){fun bind(topPicks: NftData.Top){binding.imgTopPicks.load(topPicks.image){crossfade(true)transformations(RoundedCornersTransformation(20F))}binding.imgTopPicks.setOnClickListener {itemClickListener?.invoke(it, topPicks, adapterPosition)}}}class TrendingViewHolder(private val binding: RcvLytTrendingBinding) : NftViewHolder(binding){fun bind(trending: NftData.Trending){binding.imgTrending.load(trending.image){crossfade(true)transformations(CircleCropTransformation())}binding.topNftContainer.setOnClickListener {itemClickListener?.invoke(it, trending, adapterPosition)}binding.txtNftTitle.text = trending.namebinding.txtCategory.text = trending.category}}}

NftAdapter.kt

  • 我们的适配器继承ListAdapter类,这是现在推荐的方法。
  • 我们使用DiffUtil来避免使用recyclerview的adapter的notifyDataSetChanged(),因为当可能只有几件事情发生变化时,它重新绘制整个UI是不高效的。
  • onCreateViewHolder():检查存在的视图类型并填充相应的布局文件。
  • onBindViewHolder():根据viewHolder与视图绑定数据。
  • getItemViewType():如名称所示,在recyclerview中确定在特定位置显示哪种类型的视图。

NftAdapter.kt

class NftAdapter : ListAdapter<NftData, NftViewHolder>(NftDiffCallBack()) {var itemClickListener: ((view: View, item: NftData, position: Int) -> Unit)? = nulloverride fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NftViewHolder {return when(viewType){R.layout.rcv_lyt_title -> NftViewHolder.TitleViewHolder(RcvLytTitleBinding.inflate(LayoutInflater.from(parent.context), parent, false))R.layout.rcv_lyt_featured -> NftViewHolder.FeaturedViewHolder(RcvLytFeaturedBinding.inflate(LayoutInflater.from(parent.context), parent, false))R.layout.rcv_lyt_top_picks -> NftViewHolder.TopPicksViewHolder(RcvLytTopPicksBinding.inflate(LayoutInflater.from(parent.context), parent, false))R.layout.rcv_lyt_trending -> NftViewHolder.TrendingViewHolder(RcvLytTrendingBinding.inflate(LayoutInflater.from(parent.context), parent, false))else -> throw IllegalArgumentException("Invalid view type")}}override fun onBindViewHolder(holder: NftViewHolder, position: Int) {holder.itemClickListener = itemClickListenerval item = getItem(position)when(holder){is NftViewHolder.FeaturedViewHolder -> holder.bind(item as NftData.Featured)is NftViewHolder.TitleViewHolder -> holder.bind(item as NftData.Title)is NftViewHolder.TopPicksViewHolder -> holder.bind(item as NftData.Top)is NftViewHolder.TrendingViewHolder -> holder.bind(item as NftData.Trending)}}override fun getItemViewType(position: Int): Int {return when(getItem(position)){is NftData.Title -> R.layout.rcv_lyt_titleis NftData.Featured -> R.layout.rcv_lyt_featuredis NftData.Top -> R.layout.rcv_lyt_top_picksis NftData.Trending -> R.layout.rcv_lyt_trending}}class NftDiffCallBack : DiffUtil.ItemCallback<NftData>(){override fun areItemsTheSame(oldItem: NftData, newItem: NftData): Boolean {return when {oldItem is NftData.Top && newItem is NftData.Top -> {oldItem.id == newItem.id}oldItem is NftData.Trending && newItem is NftData.Trending -> {oldItem.id == newItem.id}else -> {false}}}override fun areContentsTheSame(oldItem: NftData, newItem: NftData): Boolean {return when {oldItem is NftData.Top && newItem is NftData.Top -> {oldItem == newItem}oldItem is NftData.Trending && newItem is NftData.Trending -> {oldItem == newItem}else -> {false}}}}}

UI

NftViewModel.kt

使用状态流(state flow)我们可以获得一个可观测的流,从数据源中发出当前和新的状态更新。

@HiltViewModel
class NftViewModel @Inject constructor(private val nftRepository: NftRepository) : ViewModel() {private val _nft = MutableStateFlow<Resource<List<NftData>>>(Resource.Loading)val nft: StateFlow<Resource<List<NftData>>> get() = _nftinit {getNft()}private fun getNft() = viewModelScope.launch {_nft.emit(Resource.Loading)val topNftDeferred = async { nftRepository.getTopNft() }val trendingNftDeferred = async { nftRepository.getTrendingNft() }val topNft = topNftDeferred.await()val trendingNft = trendingNftDeferred.await()val nftList = mutableListOf<NftData>()if(topNft is Resource.Success && trendingNft is Resource.Success){nftList.add(NftData.Title(1, "Featured", ""))nftList.add(NftData.Featured(FEATURED_IMAGE, FEATURED_IMAGE_TITLE))nftList.add(NftData.Title(2, "Top Pick", "View all"))nftList.addAll(topNft.value)nftList.add(NftData.Title(2, "Trending", ""))nftList.addAll(trendingNft.value)_nft.emit(Resource.Success(nftList))}else{Resource.Failure(false, null, null)}}}

MainActivity.kt

我们设置了我们的RecyclerView布局管理器,并在UI层使用了推荐的新方法来收集流。

 //set up recycler viewbinding.rcvNft.apply {val gridLayoutManager = GridLayoutManager(this@MainActivity, 6)gridLayoutManager.spanSizeLookup = object : SpanSizeLookup() {override fun getSpanSize(position: Int): Int {return when (nftAdapter.getItemViewType(position)) {R.layout.rcv_lyt_title -> 6R.layout.rcv_lyt_featured -> 6R.layout.rcv_lyt_top_picks -> 3R.layout.rcv_lyt_trending -> 6else -> 1}}}layoutManager = gridLayoutManagersetHasFixedSize(true)adapter = nftAdapter}//handle clicksnftAdapter.itemClickListener = { view, item, position ->when(item) {is NftData.Title -> Toast.makeText(this, "View all clicked", Toast.LENGTH_LONG).show()is NftData.Featured -> Toast.makeText(this, "Featured nft clicked", Toast.LENGTH_LONG).show()is NftData.Top -> Toast.makeText(this, "Top nft clicked", Toast.LENGTH_LONG).show()is NftData.Trending -> Toast.makeText(this, "Trending nft clicked", Toast.LENGTH_LONG).show()}}//best way to collect flows in UI layerlifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {nftViewModel.nft.collect{ result ->when (result) {Resource.Loading ->  binding.loading.changeVisibility(View.VISIBLE)is Resource.Failure -> {binding.loading.changeVisibility(View.GONE)}is Resource.Success -> {binding.loading.changeVisibility(View.GONE)nftAdapter.submitList(result.value)}}}}}

GitHub

https://github.com/ibrajix/NftApp

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

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

相关文章

【unity实战】制作一个类帝国时代、红警——RTS战略性游戏

文章目录 先来看看实现的最终效果什么是RTS游戏一、两种方法实现相机的移动旋转缩放以及拖拽功能前言准备第一种办法1. 移动1.1 代码实现&#xff0c;里面都写了详细的中文注释&#xff0c;就不过多解释了1.2 效果&#xff1a;1.3 问题&#xff1a; 2. 缩放2.1 代码 3. 限制范围…

考场作弊行为自动抓拍告警算法 yolov7

考场作弊行为自动抓拍告警系统通过yolov7python网络模型算法&#xff0c;考场作弊行为自动抓拍告警算法实时监测考场内所有考生的行为&#xff0c;对考生的行为进行自动抓拍&#xff0c;并分析判断是否存在作弊行为。YOLOv7 的发展方向与当前主流的实时目标检测器不同&#xff…

【杂谈】关于Huawei S5720巡检过程中的“CPCAR_DROP_MPU”告警

背景 本年度二季度为某客户巡检数通设备&#xff08;Huawei居多&#xff09;时&#xff0c;在某楼宇汇聚设备上display logbuffer发现存在大量告警&#xff0c;如下&#xff1a; Jun 8 2023 15:34:24 AGG-S5720-1 %%01INFO/4/SUPPRESS_LOG(l)[58]:Last message repeated 2 t…

基于深度学习FasterRCNN模型Restnet50 的生活垃圾智能分类(准确率达84%)-含python工程全源码

目录 前言总体设计系统整体结构图系统流程图 运行环境1. 硬件环境2. Python 环境 模块实现1. 数据预处理2. 数据加载3. 模型构建4. 模型训练及保存5. 模型加载与调用 系统测试1. 模型准确率2. 分类别准确率 工程源代码下载其它资料下载 前言 本项目基于Faster R-CNN模型&#…

Service 基础

今天开始来分享Service 的基础知识&#xff0c;后续我们可以慢慢打磨&#xff0c;分享 Service 的进阶知识和原理 Service 基本概念 Service 是 K8S 最核心的概念了 我们可以通过创建 Service &#xff0c;为一组具有相同功能的容器应用提供一个统一的入口地址&#xff0c;并…

【Java】Java核心 80:Git 教程(3)初始化工作区 add与commit

文章目录 04.GIT本地操作-初始化工作区目标内容小结 05.GIT本地操作-add与commit目标内容小结 在Git中&#xff0c;初始化工作区并使用add和commit命令是进行版本控制的基本操作。 下面是对这些操作的简要解释&#xff1a; 初始化工作区&#xff1a;在使用Git之前&#xff0c…

Chapter 1: Introduction - Why Program? | Python for Everybody 讲义_Cn

文章目录 Python for Everybody课程简介适合所有人的 Python (Why Program?)为什么要学习写程序&#xff1f;创造力和动力计算机硬件架构了解编程单词和句子与 Python 对话术语&#xff1a;解释器和编译器Writing a program什么是程序&#xff1f;The building blocks of prog…

隐式迭代是什么意思?jQuery选择器隐式迭代

在使用jQuery 选择器获取元素后&#xff0c;如果不考虑获取到的元素数量&#xff0c;直接对元素进行操作&#xff0c;则在操作时会发生隐式迭代。隐式迭代是指&#xff0c;当要操作的元素实际有多个时&#xff0c;jQuery 会自动对所有的元素进行操作&#xff0c;示例代码如下。…

“暗网议会”如今已成为现实

图片来源:Marcin Balcerzak 最近&#xff0c;“暗网议会”已经成为了网络犯罪分子试图证明自己影响力的最新流行语&#xff0c;安全内部人士对这个词也很感兴趣。 上周五&#xff0c;臭名昭著的亲俄黑客组织Killnet在其电报威胁帖子中使用了这个词语。随后&#xff0c;twitte…

MySql学习2:SQL分类、数据库操作、表操作、数据的增删改查

SQL分类 SQL分类&#xff1a; DDL&#xff1a;数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09;DML&#xff1a;数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQL&#xff1a;数据库查询语言&#xff0c;用来查询数据库表中…

Spring Boot中的事务隔离级别

Spring Boot中的事务隔离级别 介绍 Spring Boot是一个流行的Java框架&#xff0c;它提供了许多方便的注解和工具&#xff0c;使得数据库操作变得更加容易。其中&#xff0c;事务隔离级别是Spring Boot中一个非常重要的概念&#xff0c;它可以帮助开发者控制数据库操作的并发性…

JumpServer开源堡垒机安装配置

JumpServer开源堡垒机安装与配置 一、简介二、下载与安装2.1、下载2.2、安装2.3、其他 一、简介 JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产。 支持 官网地址&#xff1a;https://www.jumpserver.org/index.html JumpServer 采用分层架构&#xff0c;…