
在移动应用程序中逐页加载是一个相当简单的主题,没有任何陷阱。
开发人员经常面临类似的任务,因此,一遍又一遍地做同一件事是无聊而懒惰的。
但是,即使是最普通的任务,也可以通过另一种更有趣的方式解决,从而获得新的知识并伸张大脑。
分页
简而言之,所有实现分页的工作都包含以下各项:
- 将侦听器添加到回收者视图;
- 下载主要数据;
- 当用户滚动列表时,捕获一个回调;
- 在所有项目之后的列表中显示下载;
- 发送新项目的请求;
- 再次显示数据。
在我们的例子中,实现了页面分页机制,但是当用户滚动列表时没有显示负载。
好的,一次完成一个应用程序就没问题了。 如果有几个需要此功能的屏幕,则可以编写一个可以与其他视图一起使用的基本适配器。
但是,如果任务比较复杂怎么办?
假设有几个并非新项目,它们的一个核心模块具有基本功能。
由于项目距离第一年还很遥远,因此,已经积累了许多与该任务有关的旧代码,即,许多具有不同基类的适配器。
我该如何解决这个问题?
创建一个新的基本适配器,它将能够显示下载进度,重写所有使用该适配器的旧适配器+屏幕,因为您必须控制每个演示者或视图中每个屏幕上负载的显示状态。
这可能会非常耗时。 大型重构也可能导致许多新错误,尤其是在项目的测试覆盖率趋于零的情况下。 此外,质量保证部门对所有屏幕进行分页分页的回归测试对几种应用程序的负担很大。
编写这样的代码非常好,它需要开发客户端花费最少的时间将基本解决方案集成到其功能中。 为了不必实现显示加载进度的控制逻辑-显示和隐藏。
此时,我考虑过使用
ItemDecoration类。
为什么不使用此类封装与之相关的所有工作
不幸的是,在Internet上找不到一个现成的解决方案-要么没有人尝试实现它,要么在实现后根本没有分享经验。
使用ItemDecoration在分页过程中实现加载显示需要什么?
- 缩进以呈现负载;
- 需要显示进度时,了解ItemDecoration的实现;
- 抽奖下载;
压痕
任何创建过SpacingItemDecoration的开发人员都至少知道如何缩进。 ItemDecoration的getItemOffsets方法将帮助我们:
override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, parent, state) }
对于列表中的任何元素,我们都可以在任一侧添加缩进。 具体来说,在我们的情况下,我们需要了解用户已将列表滚动到末尾,并且在最后一个元素中从底部开始缩进,该缩进等于将来显示的下载进度的值+进度本身的缩进。
如何理解列表滚动到底部?
下面的代码将帮助我们:
private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 }
总共,我们有用于定义和实现缩进的代码:
override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, recyclerView, state) when (isLastItem(recyclerView, view)) { true -> outRect.set(Rect(0, 0, 0, 120)) else -> outRect.set(Rect(0, 0, 0, 0)) } } private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 }
三分之一的工作已经完成!
我们确定显示下载进度的时间,并称为进度渲染
ItemDecoration onDrawOver方法将帮助我们:
override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) }
onDrawOver方法与onDraw方法的区别仅在于呈现顺序。 仅在绘制列表项本身之后才渲染OnDrawOver装饰。
在onDrawOver方法中,我们需要了解在什么时候需要绘制进度,什么时候要删除进度并导致对列表项进行更新,以便隐藏已经缩进的缩进。
用于执行上述操作的代码:
override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) when (showLoading(recyclerView)) { true -> { PaginationProgressDrawer.drawSpinner(recyclerView, canvas) isProgressVisible = true } else -> { if (isProgressVisible) { isProgressVisible = false recyclerView.invalidateItemDecorations() } } } } private fun showLoading(recyclerView: RecyclerView): Boolean { val manager = recyclerView.layoutManager as LinearLayoutManager val lastVisibleItemPos = manager.findLastCompletelyVisibleItemPosition() return lastVisibleItemPos != -1 && lastVisibleItemPos >= recyclerView.adapter!!.itemCount - 1 }
绘图进度
渲染代码非常多,将在一个单独的文件中显示,我将在下面提供指向该文件的链接。
我只想谈谈实施所必需的细微差别。
所有绘图工作都在画布上完成。 因此,必须配置Paint对象的实例并绘制圆弧,以指示起始角度和结束角度。
一个重要的细微差别是,要尽可能地类似于常规的ProgressBar进行渲染,您需要创建自己的内插器模拟,以便渲染动画非线性地发生。
为了了解插值器的工作并了解您想从动画中获得什么,我建议您熟悉描述各种插值器工作的图形,例如,在
本文中代码参考:
分页加载装饰PaginationProgressDrawer如有必要,您可以创建一个ProgressDrawer接口,并替换PaginationLoadingDecoration中的实现。
下载演示视频:
谢谢您的阅读,喜欢编码:)