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

news/2024/10/22 18:30:19/

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.ppmy.cn/news/670682.html

相关文章

深度强化学习在机器人领域的研究与应用

前言 机器学习方法主要可以分为四类&#xff0c;监督学习、半监督学习、无监督学习、以及强化学习。其中&#xff0c;强化学习不同于连接主义的监督学习方法&#xff0c;是智能体通过与环境的交互&#xff0c;观测交互结果以及获得相应的回报。这种学习的方式是模拟人或动物的一…

深度学习的应用

参考书籍《Tensorflow实战Google深度学习框架》郑泽宇等 深度学习最早兴起于图像识别,但是在短短的几年之内,深度学习推广到了机器学习的各个领域,并且都有很出色的表现。具体领域包含图像识别、语音识别、自然语言处理、机器人、生物信息处理、化学、电脑游戏、搜索引…

机器学习的应用–大数据

说完机器学习的方法&#xff0c;下面要谈一谈机器学习的应用了。无疑&#xff0c;在2010年以前&#xff0c;机器学习的应用在某些特定领域发挥了巨大的作用&#xff0c;如车牌识别&#xff0c;网络攻击防范&#xff0c;手写字符识别等等。但是&#xff0c;从2010年以后&#xf…

【一】机器学习在网络空间安全研究中的应用

本文为清华大学计算机系团队于2017年发表于计算机学报的一篇文章&#xff0c;作者为张蕾&#xff0c;崔勇&#xff0c;刘静&#xff0c;江勇和吴建平。 摘要 文章首先阐述机器学习技术在网络空间安全应用研究中的应用流程&#xff0c;然后从系统安全&#xff0c;网络安全和应…

Few-Shot Learning (FSL): 小样本学习简介及其应用

原文链接&#xff1a;Few-Shot Learning (FSL): What it is & its Applications 如果手机需要成千上万张照片来训练才能进行人脸识别解锁&#xff0c;这是很不友好的。在机器学习应用领域&#xff0c;小样本学习(Few-shot Learning)&#xff08;在刚刚描述的情况下称为单样…

强化学习的10个现实应用

在强化学习中&#xff0c;我们使用奖惩机制来训练agents。Agent做出正确的行为会得到奖励&#xff0c;做出错误的行为就会受到惩罚。这样的话&#xff0c;agent就会试着将自己的错误行为最少化&#xff0c;将自己的正确行为最多化。 本文我们将会聚焦于强化学习在现实生活中的实…

无监督学习应用在反欺诈中

传统对于欺诈行为的几种处理方式&#xff1a; 黑名单、信誉库和设备指纹&#xff0c;这种方法的缺点是覆盖率和准确率有限&#xff0c;而且虚拟机等可逃避设备指纹监测&#xff1b;规则系统&#xff0c;这种方法需要深入了解欺诈模式&#xff0c;但不能够有效应对不断变化的欺…

深度学习的应用介绍

深度学习已经在图像、语音、自然语言处理等各个不同的领域展现出了优异的性能。本文将带着大家看看深度学习在机器视觉领域的应用现状及其他方面的应用。 目录 物体检测 图像分割 图像标题的生成 图像风格变换 图像的生成 自动驾驶 强化学习&#xff08;Deep Q-Network&…